{"cells":[{"metadata":{"_uuid":"74cdbfd9a0560b7478685a3aaf7c29460ca264bb"},"cell_type":"markdown","source":"# Google PAIR Facets\n#### https://github.com/PAIR-code/facets\n\nThis notebooks uses `Facets` to visualize the 33,321 images in dataset. Basics steps to do this:\n    1. Create dataframe with all image paths and any interesting metadata\n    2. Feed this dataframe to the `Atlasmaker` tool to create a montage of all the images\n    3. Use example Jupyter Notebook snippet to display HTML of visualization (https://colab.research.google.com/github/PAIR-code/facets/blob/master/colab_facets.ipynb)\n    \nIdeas for additional faceting visualizations:\n    - Visuals groups of incorrectly labelled images in validation. Could scatter by distance from threshold.\n    - Draw bounding boxes to verify generalization of segmentation model\n    - Use bounding box data to add fluke size and then use this to scatter images across an axis\n    - Add B&W / RGB column\n    - Add image ratio, image sizes to scatter on or group by\n    - Add corner cases column with labels like \"heavily occluded\", \"image with text\", etc.\n    - If doing metric learning reduce dimensionality with PCA / tSNE and plot the images in that space\n    \nIt takes a minute to load completely (~100MB), but you can view this full screen here: https://davidwagnerkc.github.io/\n\nGlad I finally got to try Facets out. It needs a pip installable package with a one liner to get from DataFrame to notebook output. "},{"metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true,"_kg_hide-input":false,"_kg_hide-output":false},"cell_type":"code","source":"from IPython.core.display import display, HTML\nfrom multiprocessing import Pool\nfrom pathlib import Path\n\nimport numpy as np\nimport pandas as pd\nfrom PIL import Image\nfrom PIL import ImageDraw","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"5b8f1948944863be9ae9282f84664031b21cfea5"},"cell_type":"code","source":"# Bounding boxes from this kernel (@suicaokhoailang ran Martin Piotte's model on the current competition dataset)\n# https://www.kaggle.com/suicaokhoailang/generating-whale-bounding-boxes\nbb_df = pd.read_csv('../input/boundingbox/bounding_boxes.csv')","execution_count":null,"outputs":[]},{"metadata":{"_cell_guid":"79c7e3d0-c299-4dcb-8224-4455121ee9b0","_uuid":"d629ff2d2480ee46fbb7e2d37f6b5fab8052498a","trusted":true,"_kg_hide-input":false,"_kg_hide-output":false},"cell_type":"code","source":"DATA_DIR = Path('/kaggle/input/humpback-whale-identification/')\nTRAIN_DIR = DATA_DIR / 'train'\nTEST_DIR =  DATA_DIR / 'test'\n\ntrain_df = pd.read_csv(DATA_DIR / 'train.csv')\ntest_df = pd.read_csv(DATA_DIR / 'sample_submission.csv')","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"244cb42bb1a4181dea297740c878cc15411cc053"},"cell_type":"code","source":"w, h = (bb_df.x1 - bb_df.x0), (bb_df.y1 - bb_df.y0)\nbb_df['crop_size'] = w * h\nbb_df['crop_ratio'] = w / h","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"6694e061be9ed0a9ba1741da985c1bd679718604"},"cell_type":"code","source":"bb_df.head()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"a166cc1a5e3fe982c2545f8145ac0f7ff733b64b","_kg_hide-input":false,"_kg_hide-output":false},"cell_type":"code","source":"train_df['freq'] = train_df.groupby('Id')['Id'].transform('count')\ntrain_df['set'] = 'train'\ntrain_df = train_df.sort_values('freq', ascending=False)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"d1a730fc6d15bf2edb7aa8a10f93eb824c5d9753","_kg_hide-input":false,"_kg_hide-output":false},"cell_type":"code","source":"test_df['Id'] = 'unknown'\ntest_df['freq'] = 1\ntest_df['set'] = 'test'","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"9a331022755ddea2a44b5fb9b23d34eceb7bcbeb","_kg_hide-input":false,"_kg_hide-output":false},"cell_type":"code","source":"df = pd.concat([train_df, test_df]).reset_index(drop=True)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"769b17e960f57b01a73be734a8db1e72718c5a72"},"cell_type":"code","source":"df = pd.merge(df, bb_df, on='Image')","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"d8b73693e9ff8ec5f9cae0dff080b66556c00f6b"},"cell_type":"code","source":"# Add image ratio data\ndef ratio(row):\n    im_path = TRAIN_DIR / row.Image if 'train' in row.set else TEST_DIR / row.Image\n    im = Image.open(im_path)\n    return im.width / im.height\n\ndef total_size(row):\n    im_path = TRAIN_DIR / row.Image if 'train' in row.set else TEST_DIR / row.Image\n    im = Image.open(im_path)\n    return im.width * im.height","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"529b2a7c34dd26e84e02be2f64c6ef878509a299"},"cell_type":"code","source":"def draw_bb(row):\n    im_path = TRAIN_DIR / row.Image if 'train' in row.set else TEST_DIR / row.Image\n    im = Image.open(im_path)\n    bb = row.x0, row.y0, row.x1, row.y1\n    draw = ImageDraw.Draw(im) \n    draw.rectangle(bb, outline=255)\n    im.save(Path('/kaggle/working/draw_crops/') / row.Image)\n    return True","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"24a49bb6d46d32c6801f8b5ec303c759cd758059"},"cell_type":"code","source":"p = Pool()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"0f9c42769969449c461117e7f08651711b01717d"},"cell_type":"code","source":"%%time\ndf['ratio'] = p.map(ratio, [x[1] for x in list(df.iterrows())]) #df.apply(ratio, axis=1)\ndf['total_size'] = p.map(ratio, [x[1] for x in list(df.iterrows())]) #df.apply(total_size, axis=1)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"f588c0a30e53589698bc7a679e4c95f770ed2e0f"},"cell_type":"code","source":"df['crop_perc'] = df.crop_size / df.total_size","execution_count":null,"outputs":[]},{"metadata":{"_kg_hide-input":false,"_kg_hide-output":false,"trusted":true,"_uuid":"f8b9dcd40277fed0040e84c91b6371d1711ed61e"},"cell_type":"code","source":"df[::3000]","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"fb497dd104ff2b0169de0673091d248b4221a86a"},"cell_type":"code","source":"# !mkdir draw_crops","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"f7069429dfd2ba836f6f67190f31cc6ebd45a943"},"cell_type":"code","source":"# %%time\n# p.map(draw_bb, [x[1] for x in list(df.iterrows())])","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"6268e80961aecba65875e9e1a3c77aace5a7c660"},"cell_type":"code","source":"df = df.drop(['x0', 'x1', 'y0', 'y1'], axis=1)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"af545ab1e78c019fd3f45a258770ac682489a44c"},"cell_type":"code","source":"df['x_rand'] = np.random.random(len(df))\ndf['y_rand'] = np.random.random(len(df))","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"94b8a97511cb84c942a78c9e4591b2503b6d8b4b","_kg_hide-output":true,"_kg_hide-input":false},"cell_type":"code","source":"!git clone https://github.com/PAIR-code/facets.git","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"a1efca05a84e7f0d94a46b137d429270987b0f2e","_kg_hide-input":true,"_kg_hide-output":true},"cell_type":"code","source":"# If anybody is interested in building Facets themselves this might be useful. Turns out I didn't need to build Atlasmaker since it is just three Python modules.\n\n# !pip install -r facets/facets_atlasmaker/requirements.txt\n# !apt-get install -y pkg-config zip g++ zlib1g-dev unzip python\n# !curl -LOk https://github.com/bazelbuild/bazel/releases/download/0.21.0/bazel-0.21.0-installer-linux-x86_64.sh\n# !chmod +x bazel-0.21.0-installer-linux-x86_64.sh\n# !bash bazel-0.21.0-installer-linux-x86_64.sh\n\n# cd /kaggle/working/facets/facets_atlasmaker/\n# %%time\n# !bazel build :atlasmaker\n\n# cd /kaggle/working/facets/bazel-bin/facets_atlasmaker/","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"c043c810067c24e5c90a9e1b8335a3dce0a6abf6","_kg_hide-input":false,"_kg_hide-output":false},"cell_type":"code","source":"cd /kaggle/working/facets/facets_atlasmaker/","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"d87e7659bf89543ee700c30ad4e812881c927dc1","_kg_hide-input":false,"_kg_hide-output":false},"cell_type":"code","source":"# Does anybody use Python 2 anymore?\n!sed -i 's/from urlparse import urlparse/from urllib.parse import urlparse/g' atlasmaker_io.py\n# Let's pretend tensorflow isn't available to avoid another Python 2 problem \n!sed -i 's/import tensorflow as tf/import tensorflop/g' atlasmaker_io.py","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"ab6c2e46c4786f79d8b4a210e554a48989d409ec"},"cell_type":"code","source":"#df.apply(lambda x: str(Path('/kaggle/working/draw_crops/') / x.Image), axis=1).to_csv('absolute_paths.csv', index=False)\ndf.apply(lambda x: str(TRAIN_DIR / x.Image) if 'train' in x.set else str(TEST_DIR / x.Image), axis=1).to_csv('absolute_paths.csv', index=False)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"c01f4779466a6b19cc92ad0b4897e69e6cc7b5c3"},"cell_type":"code","source":"df.ratio.mean()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"412fba6d37e3b71c04821920e26107359bd56db5","_kg_hide-output":true,"scrolled":false,"_kg_hide-input":false},"cell_type":"code","source":"%%time\n!python atlasmaker.py --sourcelist=absolute_paths.csv --image_width=58 --image_height=29 --output_dir=/kaggle/working/","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"d601d632e3b6592ccc5410a520845f3d871ec60b","scrolled":true,"_kg_hide-input":false},"cell_type":"code","source":"#Image.open('/kaggle/working/spriteatlas.png').convert('L').save('/kaggle/working/spriteatlas.png', optimize=True)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"scrolled":false,"_uuid":"8190cb9a5bcab6f57dccf42dd469f7253eb9526f"},"cell_type":"code","source":"cd /kaggle/working/","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"34433b54a301febde57eb4d0380eb58621e445f8"},"cell_type":"markdown","source":"# View in notebook"},{"metadata":{"trusted":true,"_uuid":"fb94a04df3e208c6f5e4527c17a9cb04523ae2af","scrolled":false},"cell_type":"code","source":"sprite_width, sprite_height = 58, 29\natlas_path = 'spriteatlas.png'\njsonstr = df.to_json(orient='records')\nhtml = f\"\"\"<link rel=\"import\" href=\"https://raw.githubusercontent.com/PAIR-code/facets/master/facets-dist/facets-jupyter.html\">\n           <facets-dive atlas-url=\"{atlas_path}\" fit-grid-aspect-ratio-to-viewport=\"true\" sprite-image-width=\"{sprite_width}\" sprite-image-height=\"{sprite_height}\" height=\"800\" id=\"elem\"></facets-dive>\n           <script>document.querySelector(\"#elem\").data = {jsonstr};</script>\"\"\"\ndisplay(HTML(html))","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"c518737f0ac3605e1148906a1188258841368bc1"},"cell_type":"markdown","source":"# View full screen from Kaggle kernel"},{"metadata":{"trusted":true,"_uuid":"f4101022da7157c1f4c091d610236175cd4f54f1"},"cell_type":"code","source":"html = f\"\"\"<link rel=\"import\" href=\"https://raw.githubusercontent.com/PAIR-code/facets/master/facets-dist/facets-jupyter.html\">\n           <facets-dive atlas-url=\"{atlas_path}\" fit-grid-aspect-ratio-to-viewport=\"true\" cross-origin=\"anonymous\" sprite-image-width=\"{sprite_width}\" sprite-image-height=\"{sprite_height}\" id=\"elem\"></facets-dive>\n           <script>document.querySelector(\"#elem\").data = {jsonstr};</script>\"\"\"","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"266beaa7861ee150132caeec8c7897c0db05c658"},"cell_type":"code","source":"with open('facets_static.html', 'w') as out_file:\n    out_file.write(html)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"6a46637e01f4d0aa793b54cf7f8c32c949a5d9ac"},"cell_type":"code","source":"!(jupyter notebook list | grep http | awk '{printf $1}'; printf \"files/facets_static.html\") | sed \"s/http:\\/\\/localhost:8888/https:\\/\\/www\\.kaggleusercontent\\.com/\"","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"e7778c33bb5190cfacbb7a1627a764a8e73fbb4d"},"cell_type":"markdown","source":"# To host locally\n\n1. Download facets_static.html and spriteatlas.png and make a folder structure like this:\n        facets_server/\n                facets_static.html\n                spriteatlas.png\n2. cd to facets_server/ and run this command `python -m http.server`\n3. Access locally @ `localhost:8000`"},{"metadata":{"_kg_hide-input":true,"_kg_hide-output":true,"trusted":true,"_uuid":"ec5f86cc48f77f9bf085f4c02898318b1d9e1bf5"},"cell_type":"code","source":"!rm -rf facets/\n!rm -rf draw_crops/\n!rm im_paths.csv\n!rm mani","execution_count":null,"outputs":[]}],"metadata":{"kernelspec":{"display_name":"Python 3","language":"python","name":"python3"},"language_info":{"name":"python","version":"3.6.6","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"}},"nbformat":4,"nbformat_minor":1}