{"cells":[{"metadata":{},"cell_type":"markdown","source":"# Cars clustering\n**What is this notebook about:**\n\nThere are 79 car models provided in this competition.  \nI think, maybe I will try to make use of them somehow, for example, for classification.  \nBut there are too many of them, and some are really similar.  \n\nSo the goal of this notebook is to find several \"classical\" car models, which will describe all others.  \n**Spoiler:** here they are:  \n![](https://i.ibb.co/BLGLHV7/Screenshot-from-2019-11-18-05-56-20.png)"},{"metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true},"cell_type":"code","source":"import numpy as np # linear algebra\nimport pandas as pd # data processing, CSV file I/O (e.g. pd.read_csv)\nimport cv2\nfrom tqdm import tqdm#_notebook as tqdm\nimport matplotlib.pyplot as plt\nimport seaborn as sns\nfrom functools import reduce\nimport os\nfrom sklearn.model_selection import train_test_split\nfrom scipy.optimize import minimize\nimport json\nfrom math import sin, cos","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"d629ff2d2480ee46fbb7e2d37f6b5fab8052498a","_cell_guid":"79c7e3d0-c299-4dcb-8224-4455121ee9b0","trusted":true},"cell_type":"code","source":"!wget https://raw.githubusercontent.com/ApolloScapeAuto/dataset-api/master/car_instance/car_models.py\nimport car_models","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# Load 3D models of cars\ncars = {}\n\nfor car_name in car_models.car_name2id:\n    with open('../input/pku-autonomous-driving/car_models_json/{}.json'.format(car_name)) as json_file:\n        data = json.load(json_file)\n    vertices = np.array(data['vertices'])\n    vertices[:, 1] = -vertices[:, 1]\n    faces = np.array(data['faces']) - 1\n    cars[car_name] = vertices, faces","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# From camera.zip\ncamera_matrix = np.array([[2304.5479, 0,  1686.2379],\n                          [0, 2305.8757, 1354.9849],\n                          [0, 0, 1]], dtype=np.float32)\ncamera_matrix_inv = np.linalg.inv(camera_matrix)\n\n# convert euler angle to rotation matrix\ndef euler_to_Rot(yaw, pitch, roll):\n    Y = np.array([[cos(yaw), 0, sin(yaw)],\n                  [0, 1, 0],\n                  [-sin(yaw), 0, cos(yaw)]])\n    P = np.array([[1, 0, 0],\n                  [0, cos(pitch), -sin(pitch)],\n                  [0, sin(pitch), cos(pitch)]])\n    R = np.array([[cos(roll), -sin(roll), 0],\n                  [sin(roll), cos(roll), 0],\n                  [0, 0, 1]])\n    return np.dot(Y, np.dot(P, R))","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"img_shape = (2710, 3384)\n\ndef get_mask(car_model):\n    verts, facs = car_model\n    mask = np.zeros([img_shape[0], img_shape[1]], dtype=np.float32)\n    # Get values\n    x, y, z = 0, 0, 4\n    yaw, pitch, roll = np.pi/2, 0, np.pi\n    # Math\n    Rt = np.eye(4)\n    t = np.array([x, y, z])\n    Rt[:3, 3] = t\n    Rt[:3, :3] = euler_to_Rot(yaw, pitch, roll).T\n    Rt = Rt[:3, :]\n    P = np.ones((verts.shape[0], verts.shape[1]+1))\n    P[:, :-1] = verts\n    P = P.T\n    img_cor_points = np.dot(camera_matrix, np.dot(Rt, P))\n    img_cor_points = img_cor_points.T\n    img_cor_points[:, 0] /= img_cor_points[:, 2]\n    img_cor_points[:, 1] /= img_cor_points[:, 2]\n    img_cor_points = img_cor_points[:,:2].astype(int)\n    # Drawing\n    for face in facs:\n        cv2.fillConvexPoly(mask, img_cor_points[face], 1)\n    mask = cv2.resize(mask, (img_shape[1]//4, img_shape[0]//4))\n    return mask","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"car_masks = {car_name: get_mask(cars[car_name]) for car_name in tqdm(cars)}","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"fig, axes = plt.subplots(10, 8, figsize=(30, 30))\n\nfor i, car_name in enumerate(cars):\n    ax = axes[i//8, i%8]\n    ax.set_title(car_name)\n    ax.axis('off')\n    mask = car_masks[car_name]\n    ax.imshow(mask)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"from sklearn.metrics import pairwise_distances\n\ndef sim_affinity(X):\n    return pairwise_distances(X, metric=car_iou)\n\ndef car_iou(car1, car2):\n    car1 = car1 > 0.5\n    car2 = car2 > 0.5\n    return 1 - (car1 & car2).sum() / (car1 | car2).sum()\n\ncar_names = sorted(list(car_masks.keys()))\nX = np.array([car_masks[n] for n in car_names]).reshape(len(cars), -1)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"plt.figure(figsize=(20,20))\n\nplt.title('IOU Distances between car models')\ndist_matrix = pd.DataFrame(sim_affinity(X), columns=car_names, index=car_names)\nsns.heatmap(dist_matrix);","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"sns.distplot(np.array(dist_matrix).reshape(-1));","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"from sklearn.cluster import AgglomerativeClustering\n\nclustering = AgglomerativeClustering(affinity=sim_affinity, linkage='complete', distance_threshold=0.2, n_clusters=None)\nclustering.fit(X)\nprint('Num clusters:', max(clustering.labels_) + 1)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"cluster_centers = []\ncluster_dict = {}\n\nfor label in np.unique(clustering.labels_):\n    this_cl_names = list(np.array(car_names)[clustering.labels_ == label])\n    center = dist_matrix.loc[this_cl_names, this_cl_names].sum(0).idxmin()\n    cluster_centers.append(center)\n    \n    fig, axes = plt.subplots(1, len(this_cl_names), figsize=(30, 8))\n    print('Cluster #{}. Center: {}'.format(label + 1, center))\n    if len(this_cl_names) == 1:\n        axes = [axes]\n    for i, car_name in enumerate(this_cl_names):\n        cluster_dict[car_name] = center\n        axes[i].set_title(car_name)\n        if car_name == center:\n            axes[i].set_title('(*) ' + car_name, fontweight=\"bold\")\n        axes[i].axis('off')\n        axes[i].imshow(car_masks[car_name])\n    plt.show()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# Draw cluster centers\nfig, axes = plt.subplots(2, len(cluster_centers) // 2, figsize=(30, 8))\nfor i, car_name in enumerate(cluster_centers):\n    ax = axes[i // (len(cluster_centers) // 2), i % (len(cluster_centers) // 2)]\n    ax.set_title(car_name)\n    ax.axis('off')\n    ax.imshow(car_masks[car_name])","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"These are the most common types of cars  \n\nAnd here are the main results in the python format, which can be copied to another notebook:"},{"metadata":{"trusted":true},"cell_type":"code","source":"print('cluster_centers =', cluster_centers)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# this dict maps car name to its corresponding cluster center\nprint('cluster_dict =', cluster_dict)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"","execution_count":null,"outputs":[]}],"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"pygments_lexer":"ipython3","nbconvert_exporter":"python","version":"3.6.4","file_extension":".py","codemirror_mode":{"name":"ipython","version":3},"name":"python","mimetype":"text/x-python"}},"nbformat":4,"nbformat_minor":1}