{"cells":[{"metadata":{},"cell_type":"markdown","source":"Hi everyone! This is my first kaggle notebook, but I hope that you'll find it usefull.\n\nMost significant features:\n* useful train.csv unpacking procedure\n* EDA of full data, not \"first car only\"\n* proper masking and vizualization without alpha hacks\n* x, y, z mapping to image plane\n* baseline submission without ml"},{"metadata":{"trusted":true},"cell_type":"code","source":"import pandas as pd\nimport os.path\nimport PIL\nimport PIL.ImageChops  \nimport PIL.ImageOps\nimport numpy as np\n\nfrom collections import OrderedDict\n\nimport matplotlib as mpl\nimport matplotlib.pyplot as plt\n%matplotlib inline\nmpl.rcParams['figure.figsize'] = [15, 5]","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Unpack train.csv"},{"metadata":{},"cell_type":"markdown","source":"There is a one to many relationship: ImageId -> Cars. It's not very convenient to analyze it this way. So lets construct a table, that has a one to one relationship."},{"metadata":{"trusted":true},"cell_type":"code","source":"def get_path(path):\n    return os.path.join('/kaggle/input/pku-autonomous-driving/', path)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"train_df = pd.read_csv(get_path('train.csv'))","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"train_df.head()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"def unpack(group):\n    row = group.iloc[0]\n    result = []\n    data = row['PredictionString']\n    while data:\n        data = data.split(maxsplit=7)\n        result.append(OrderedDict((\n            ('image_id', row['ImageId']),\n            ('model_type', int(data[0])),\n            ('yaw', float(data[1])), \n            ('pitch', float(data[2])), \n            ('roll', float(data[3])), \n            ('x', float(data[4])), \n            ('y', float(data[5])),\n            ('z', float(data[6]))\n        )))\n        data = data[7] if len(data) == 8 else ''\n    return pd.DataFrame(result)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"unpacked_train_df = train_df.groupby('ImageId', group_keys=False).apply(unpack).reset_index(drop=True)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"unpacked_train_df.head()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# 3D to 2D"},{"metadata":{},"cell_type":"markdown","source":"It's rather straightforward to convert x, y, z to image plane coordinates, but it took me some time to figure this out, so here it is."},{"metadata":{"trusted":true},"cell_type":"code","source":"!cat /kaggle/input/pku-autonomous-driving/camera/camera_intrinsic.txt","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"def convert_3d_to_2d(x, y, z, fx = 2304.5479, fy = 2305.8757, cx = 1686.2379, cy = 1354.9849):\n    return x * fx / z + cx, y * fy / z + cy","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Number of cars per image distribution"},{"metadata":{"trusted":true},"cell_type":"code","source":"unpacked_train_df.groupby('image_id').size().describe()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"unpacked_train_df.groupby('image_id').size().hist(bins=44);","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"len(train_df), len(unpacked_train_df)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Visualization"},{"metadata":{"trusted":true},"cell_type":"code","source":"def read_masked_image(image_id, partition='train'):\n    i = PIL.Image.open(get_path('{}_images/{}.jpg'.format(partition, image_id)))\n    try:\n        m = PIL.Image.open(get_path('{}_masks/{}.jpg'.format(partition, image_id)))\n        return PIL.Image.composite(m, i, m.convert(mode='L'))\n    except FileNotFoundError:\n        return i","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"def highlight(df, image_id):\n    df = df[df['image_id'] == image_id]\n    coords = df.apply(lambda row: pd.Series(convert_3d_to_2d(row['x'], row['y'], row['z'])), axis=1).values\n    plt.figure(figsize=(10, 10))\n    plt.imshow(read_masked_image(image_id))\n    plt.plot(coords[:, 0], coords[:, 1], 'ro', alpha=0.8, markersize=10)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## Sanity check on one car images"},{"metadata":{"trusted":true},"cell_type":"code","source":"s = unpacked_train_df.groupby('image_id').size()\nfor image_id in s[s == 1].index:\n    highlight(unpacked_train_df, image_id)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Sanity check with multiple cars"},{"metadata":{},"cell_type":"markdown","source":"Notice the occlusion here."},{"metadata":{"trusted":true},"cell_type":"code","source":"highlight(unpacked_train_df, 'ID_001d6829a')","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Car models distribution"},{"metadata":{"trusted":true},"cell_type":"code","source":"unpacked_train_df['model_type'].value_counts().sort_index().plot(grid=True);","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"unpacked_train_df['model_type'].value_counts().describe()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Yaw, pitch, roll distributions"},{"metadata":{},"cell_type":"markdown","source":"Let's define a helper function that will allow us to find some specific examples in the dataset."},{"metadata":{"trusted":true},"cell_type":"code","source":"def find_example(df, column, func):\n    row = df[df[column] == func(df[column])][:1]\n    highlight(row, row['image_id'].iloc[0])\n    return row","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"unpacked_train_df['yaw'].hist(bins=100);","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"unpacked_train_df['pitch'].hist(bins=100);","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"unpacked_train_df['roll'].hist(bins=100);","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"According to this distributions \"pitch\" is actually yaw, \"yaw\" is pitch, \"roll\" is roll, but upside down for some reason. More over input is stated as 'model type, yaw, pitch, roll, x, y, z', but output should be 'pitch, yaw, roll, x, y, z and confidence'. Notice the yaw - pitch switch.\n\nLet's take a look at some examples. Feal free to experiment on your own. You'll find out some really weird markup errors."},{"metadata":{"trusted":true},"cell_type":"code","source":"find_example(unpacked_train_df, 'pitch', lambda x: x.min())","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"find_example(unpacked_train_df, 'pitch', lambda x: -1.58834)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"find_example(unpacked_train_df, 'pitch', lambda x: 1.58841)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Random submission "},{"metadata":{},"cell_type":"markdown","source":"Lets simply submit the most likely (independently though) parameters over the train dataset."},{"metadata":{"trusted":true},"cell_type":"code","source":"submission_df = pd.read_csv(get_path('sample_submission.csv'))","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"df = unpacked_train_df.rename({'pitch': 'yaw', 'yaw': 'pitch'})","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"columns = ('pitch', 'yaw', 'roll', 'x', 'y', 'z')\nbest_guess = []\nfor c in columns:\n    best_guess.append(df[c].median())\nbest_guess.append(1.0)  # confidence","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"submission_df['PredictionString'] = ' '.join(map(str, best_guess))","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"submission_df.to_csv('best_guess_submission.csv', index=False)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"submission_df.head()","execution_count":null,"outputs":[]}],"metadata":{"kernelspec":{"display_name":"Python 3","language":"python","name":"python3"},"language_info":{"codemirror_mode":{"name":"ipython","version":3},"file_extension":".py","mimetype":"text/x-python","name":"python","nbconvert_exporter":"python","pygments_lexer":"ipython3","version":"3.6.8"}},"nbformat":4,"nbformat_minor":1}