{"cells":[{"metadata":{},"cell_type":"markdown","source":"In this notebook, I use masks I created of the floor hallway areas to push my predictions into hallway areas. I had this idea in my head for a while, but I am a novice coder and was not sure how to accomplish it. \n\nWhen I saw Rob Mulla's https://www.kaggle.com/robikscube/indoor-navigation-snap-to-grid-post-processing, I thought I would try to adapt his code to accomplish my task. This notebook is just a copy of his notebook that I modified.\nI mostly wanted to see if I could do it and also for practice. Please excuse my amateur/messy code and I appreciate any feedback.\n\nOriginally, I used predictions from my copy of https://www.kaggle.com/mehrankazeminia/part-a-indoor-navigation-comparative-method which has a lb score 6.742. After my push to hallway, it scores 6.678.\n\nIn this version (16), I use the submission from the public notebook https://www.kaggle.com/museas/order-to-use-post-processing/output?scriptVersionId=57406736&select=sub_cost_snap.csv which has a lb score of 4.909. After my push to hallway, it scores 4.889.\n\nOne thing to note: My \"hallway masks\" include the area surrounding the building which I would like fix, but I am still working on it. I create the hallway masks in https://www.kaggle.com/therocket290/make-hallway-masks.\n"},{"metadata":{"trusted":true},"cell_type":"code","source":"import joblib\nimport os\nfrom PIL import Image,ImageOps\nimport cv2","execution_count":null,"outputs":[]},{"metadata":{"_kg_hide-input":true,"trusted":true},"cell_type":"code","source":"# Helper Functions\nimport pandas as pd\nimport numpy as np\n\nimport json\nimport matplotlib.pylab as plt\n\ndef split_col(df):\n    df = pd.concat([\n        df['site_path_timestamp'].str.split('_', expand=True) \\\n        .rename(columns={0:'site',\n                         1:'path',\n                         2:'timestamp'}),\n        df\n    ], axis=1).copy()\n    return df\n\nfloor_map = {\"B2\":-2, \"B1\":-1, \"F1\":0, \"F2\": 1, \"F3\":2,\n             \"F4\":3, \"F5\":4, \"F6\":5, \"F7\":6,\"F8\":7,\"F9\":8,\n             \"1F\":0, \"2F\":1, \"3F\":2, \"4F\":3, \"5F\":4, \"6F\":5,\n             \"7F\":6, \"8F\": 7, \"9F\":8}\n\n\ndef plot_preds(\n    site,\n    floorNo,\n    sub=None,\n    true_locs=None,\n    base=\"../input/indoor-location-navigation\",\n    show_train=True,\n    show_preds=True,\n    fix_labels=True,\n    map_floor=None\n):\n    \"\"\"\n    Plots predictions on floorplan map.\n    \n    map_floor : use a different floor's map\n    \"\"\"\n    if map_floor is None:\n        map_floor = floorNo\n    # Prepare width_meter & height_meter (taken from the .json file)\n    floor_plan_filename = f\"{base}/metadata/{site}/{map_floor}/floor_image.png\"\n    json_plan_filename = f\"{base}/metadata/{site}/{map_floor}/floor_info.json\"\n    with open(json_plan_filename) as json_file:\n        json_data = json.load(json_file)\n\n    width_meter = json_data[\"map_info\"][\"width\"]\n    height_meter = json_data[\"map_info\"][\"height\"]\n    floor_img_0 = plt.imread(f\"{base}/metadata/{site}/{map_floor}/floor_image.png\")\n    \n    \n    thresh_path = '../input/threshold-images/'\n    mask_path = '../input/hallwaymask/'\n    meta_path = '../input/indoor-location-navigation/metadata/'\n    floor_array = joblib.load(thresh_path+'thresh_list_'+site+'.pkl')\n    floors = os.listdir(meta_path+site)\n    floor_array = floor_array[floors.index(floorNo)]\n    floor_array = floor_array[:,:,1]\n    floor_img = Image.fromarray(floor_array)\n    \n    adj = np.zeros(floor_array.shape) + 255\n    floor_inv = np.abs(floor_array - adj)\n    floor_img_inv = Image.fromarray(floor_inv)\n    \n\n    fig, ax = plt.subplots(figsize=(12, 12))\n    plt.imshow(floor_img_inv)\n    \n    true_floor_locs = pd.read_csv('../input/hallwaymask/white_area_'+site+'_'+floorNo+'.csv')\n    plt.scatter(true_floor_locs['y'], true_floor_locs['x'])\n\n    if show_train:\n        true_locs = true_locs.query('site == @site and floorNo == @map_floor').copy()\n        true_locs[\"x_\"] = true_locs[\"x\"] * floor_array.shape[0] / height_meter\n        true_locs[\"y_\"] = (\n            true_locs[\"y\"] * -1 * floor_array.shape[1] / width_meter\n        ) + floor_array.shape[0]\n        true_locs.query(\"site == @site and floorNo == @map_floor\").groupby(\"path\").plot(\n            x=\"x_\",\n            y=\"y_\",\n            style=\"+\",\n            ax=ax,\n            label=\"train waypoint location\",\n            color=\"grey\",\n            alpha=0.5,\n        )\n\n    if show_preds:\n        sub = sub.query('site == @site and floorNo == @floorNo').copy()\n        sub[\"x_\"] = sub[\"x\"] * floor_array.shape[0] / height_meter\n        sub[\"y_\"] = (\n            sub[\"y\"] * -1 * floor_array.shape[1] / width_meter\n        ) + floor_array.shape[0]\n        for path, path_data in sub.query(\n            \"site == @site and floorNo == @floorNo\"\n        ).groupby(\"path\"):\n            path_data.plot(\n                x=\"x_\",\n                y=\"y_\",\n                style=\".-\",\n                ax=ax,\n                title=f\"{site} - floor - {floorNo}\",\n                alpha=1,\n                label=path,\n            )\n    if fix_labels:\n        handles, labels = ax.get_legend_handles_labels()\n        by_label = dict(zip(labels, handles))\n        plt.legend(\n            by_label.values(), by_label.keys(), loc=\"center left\", bbox_to_anchor=(1, 0.5)\n        )\n    return fig, ax\n\ndef sub_process(sub, train_waypoints):\n    train_waypoints['isTrainWaypoint'] = True\n    sub = split_col(sub[['site_path_timestamp','floor','x','y']]).copy()\n    sub = sub.merge(train_waypoints[['site','floorNo','floor']].drop_duplicates(), how='left')\n    sub = sub.merge(\n        train_waypoints[['x','y','site','floor','isTrainWaypoint']].drop_duplicates(),\n        how='left',\n        on=['site','x','y','floor']\n             )\n    sub['isTrainWaypoint'] = sub['isTrainWaypoint'].fillna(False)\n    return sub.copy()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## Plot an example\nI'll plot the hallway mask, together with the training waypoints on the map for a given floor."},{"metadata":{"trusted":true},"cell_type":"code","source":"\n# Define train_waypoints to be white pixel area\ntrain_waypoints = pd.read_csv('../input/indoor-location-train-waypoints/train_waypoints.csv')\n\nsub = sub_process(pd.read_csv('../input/sub-cost-snap-from-kaggle/sub_cost_snap.csv'),\n                 train_waypoints)\n\n# Plot the training Data For an example Floor\nexample_site = '5d27096c03f801723c31e5e0'\n\nexample_floorNo = 'F2'\n\n\nplot_preds(example_site, example_floorNo, sub,\n           train_waypoints, show_preds=False)\nplt.show()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## Find the closest \"Hallway\" point for each prediction."},{"metadata":{"trusted":true},"cell_type":"code","source":"base=\"../input/indoor-location-navigation\"\nthresh_path = '../input/threshold-images/'\nmeta_path = '../input/indoor-location-navigation/metadata/'\n\nfrom scipy.spatial.distance import cdist\n\ndef add_xy(df):\n    df['xy'] = [(x, y) for x,y in zip(df['x'], df['y'])]\n    return df\n\ndef add_xy2(df):\n    df['xy2'] = [(x, y) for x,y in zip(df['x2'], df['y2'])]\n    return df\n\ndef add_xy2_(df):\n    df['xy2_'] = [(x, y) for x,y in zip(df['x2_'], df['y2_'])]\n    return df\n\ndef add_xy2_(df):\n    df['xy2_'] = [(x, y) for x,y in zip(df['y2_'], df['x2_'])]\n    return df\n\ndef closest_point(point, points):\n    \"\"\" Find closest point from a list of points. \"\"\"\n    return points[cdist([point], points).argmin()]\n\nsub = add_xy(sub)\n\ntrain_waypoints = add_xy(train_waypoints)\n\nds = []\n#for (site, myfloor), d in sub[sub['site']==example_site].groupby(['site','floorNo']):\nfor (site, myfloor), d in sub.groupby(['site','floorNo']):\n    \n    #print('Working on site ', site, ', floor ', myfloor)\n    \n    map_floor = myfloor\n    \n    floor_plan_filename = f\"{base}/metadata/{site}/{map_floor}/floor_image.png\"\n    json_plan_filename = f\"{base}/metadata/{site}/{map_floor}/floor_info.json\"\n    with open(json_plan_filename) as json_file:\n        json_data = json.load(json_file)\n\n    width_meter = json_data[\"map_info\"][\"width\"]\n    height_meter = json_data[\"map_info\"][\"height\"]\n    \n    floor_array = joblib.load(thresh_path+'thresh_list_'+site+'.pkl')\n    floors = os.listdir(meta_path+site)\n    floor_array = floor_array[floors.index(map_floor)]\n    floor_array = floor_array[:,:,1]\n    \n    true_floor_locs = pd.read_csv('../input/hallwaymask/white_area_'+site+'_'+myfloor+'.csv')\n    \n    # Convert to meters\n    true_floor_locs['x2_'] = true_floor_locs['x'] * height_meter / floor_array.shape[0]\n    true_floor_locs['y2_'] = true_floor_locs['y'] * width_meter / floor_array.shape[1]\n    true_floor_locs['x2_'] = height_meter - true_floor_locs['x2_']\n    \n    \n    true_floor_locs = add_xy(true_floor_locs)\n    true_floor_locs = add_xy2(true_floor_locs)\n    true_floor_locs = add_xy2_(true_floor_locs)\n     \n    if len(true_floor_locs) == 0:\n        print(f'Skipping {site} {myfloor}')\n        continue\n       \n    d['matched_point'] = [closest_point(x, list(true_floor_locs['xy2_'])) for x in d['xy']]    \n    \n    d['x_'] = d['matched_point'].apply(lambda x: x[0])\n    d['y_'] = d['matched_point'].apply(lambda x: x[1])\n    ds.append(d)\n\nsub = pd.concat(ds)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# Example of raw predictions\nplot_preds(example_site, example_floorNo, sub,\n           train_waypoints, show_preds=True)\nplt.show()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## Push predictions to hallways.\n\nI wanted to push every point for now, so I set a large threshold."},{"metadata":{"trusted":true},"cell_type":"code","source":"def snap_to_grid(sub, threshold):\n    \"\"\"\n    Snap to grid if within a threshold.\n    \n    x, y are the predicted points.\n    x_, y_ are the closest grid points.\n    _x_, _y_ are the new predictions after post processing.\n    \"\"\"\n    sub['_x_'] = sub['x']\n    sub['_y_'] = sub['y']\n    sub.loc[sub['dist'] < threshold, '_x_'] = sub.loc[sub['dist'] < threshold]['x_']\n    sub.loc[sub['dist'] < threshold, '_y_'] = sub.loc[sub['dist'] < threshold]['y_']\n    return sub.copy()\n\n# Calculate the distances\nsub['dist'] = np.sqrt( (sub.x-sub.x_)**2 + (sub.y-sub.y_)**2 )\n\nsub.to_csv('sub_with_dist.csv')\n\nsub_pp = snap_to_grid(sub, threshold=10000)\n\nsub_pp = sub_pp[['site_path_timestamp','floor','_x_','_y_','site','path','floorNo']] \\\n    .rename(columns={'_x_':'x', '_y_':'y'})","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Lets take a look at the predictions after post processing."},{"metadata":{"trusted":true},"cell_type":"code","source":"# Plot example after post processing\nplot_preds(example_site, example_floorNo, sub_pp,\n           train_waypoints, show_preds=True)\nplt.show()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Not ideal, but at least the paths are in the hallways now."},{"metadata":{},"cell_type":"markdown","source":"Save Post Processed Submission."},{"metadata":{"trusted":true},"cell_type":"code","source":"sub_pp[['site_path_timestamp','floor','x','y']] \\\n    .to_csv('submission_push_to_hallway_ensemble.csv', index=False)","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":4}