{"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_minor":4,"nbformat":4,"cells":[{"cell_type":"markdown","source":"<div style=\"text-align: center\"><h1><font size='10' face = \"Comic sans MS\" color=\"#008e94\">HappierWhale 🐳 </font></h1></div>\n<div style=\"text-align: center\"><h1><font size='6' face = \"Comic sans MS\" color=\"#008e94\">3% Annotated!</font></h1></div>","metadata":{}},{"cell_type":"markdown","source":"<div class=\"alert alert-block alert-info\"> \n    Please upvote if you find this helpful. It will help me to indentify what is helpful in a competition and what it not. Thanks!\n</div>","metadata":{}},{"cell_type":"markdown","source":"Hello! This notebook is to give you an overview of the \"HappierWhale\" dataset, containing BB annotations for ~1500 images. 🕺🏻   \n      \nThe scope of the notebook is to walk you through all the steps, so you can decide whether or not you want to use/explore it.  \n\nYes, I could have trained a model on this and shared all cropped train images. I shall give it a go, but meanwhile if someone can do it better with longer training, we can all benefit from a strong model!  \n    \n    \nHope this helps the challenge a little, Cheers!🍻","metadata":{}},{"cell_type":"markdown","source":"# 🌟 Motivation\n\nBy now most of us understood that this is a pretty challenging task with exteremely diverse training images from different distributions, views, time periods and so on. Comparitively, the dataset from the previous challenge of Re-ID with flukes was insanely clean. Even then top solutions had to use manual annotations to improve it, so maybe this does too.\n\nThere are couple cropped datasets, thanks to their efforts and for sharing them with others. Especially the one from @phalanx [link to dataset](https://www.kaggle.com/phalanx/whale2-cropped-dataset) using Facebook Detic seems to be a reliable approach. \n\n","metadata":{}},{"cell_type":"markdown","source":"# 🏷 Labelling \n\nLabelling these ~1500 images, I realised that **many** of the samples lack any distinct features to id or even classify them among the 26 species. Again comparing to the flukes dataset, its highly inconsistent. Using **all** the training images for contrastive learning seems like a bad idea. Unlike the last competition, this seems to be about decreasing the training samples to have a more consistent and learnable data. 😅\n\nI initally started with labelling fin, body, zoomed-out etc but quickly gave up and just fixed to single class. Perhaps you can use the species label as class when training detector but you mostly likely would end up confusing the mode. The code used to select these samples is in the following section.\n\nI have to confess that the labelling was not consistent. Some contain just fin or fin plus part of the body and sometimes the whole body depending upon the pattern or what other images focused on. Some examples would give you a better idea.","metadata":{}},{"cell_type":"code","source":"import pandas as pd\nimport numpy as np\nimport cv2\nfrom skimage import io\nfrom skimage.color import gray2rgb\nfrom typing import Tuple\nimport os\nimport shutil\nimport plotly.express as px\nimport plotly.graph_objects as go\nfrom skimage.transform import resize\n\nseed = 400\n\nsubset_1_df = pd.read_csv(\"/kaggle/input/happierwhale/subset_1_df.csv\")\nsubset_1_df.describe(include='all').loc[['count', 'unique', 'freq', 'top']]","metadata":{"execution":{"iopub.status.busy":"2022-02-20T18:19:54.252544Z","iopub.execute_input":"2022-02-20T18:19:54.252939Z","iopub.status.idle":"2022-02-20T18:19:55.335972Z","shell.execute_reply.started":"2022-02-20T18:19:54.252794Z","shell.execute_reply":"2022-02-20T18:19:55.334796Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"annotation_files = os.listdir(\"/kaggle/input/happierwhale/yolo_annotations/yolo_annotations/\")\nprint(\"Number of annotations - \", len(annotation_files))\nprint(\"Number of samples - \", len(subset_1_df))","metadata":{"_kg_hide-input":false,"execution":{"iopub.status.busy":"2022-02-20T18:19:55.337076Z","iopub.execute_input":"2022-02-20T18:19:55.337234Z","iopub.status.idle":"2022-02-20T18:19:55.345036Z","shell.execute_reply.started":"2022-02-20T18:19:55.337214Z","shell.execute_reply":"2022-02-20T18:19:55.344363Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"One species with too samples to get 3% from did not make it 😕  \n18 samples not labelled!","metadata":{}},{"cell_type":"markdown","source":"# 🦈 Annotations","metadata":{}},{"cell_type":"code","source":"read_img = lambda file: cv2.imread(f\"/kaggle/input/happy-whale-and-dolphin/train_images/{file}\")\nread_ann = lambda file: np.loadtxt(f\"/kaggle/input/happierwhale/yolo_annotations/yolo_annotations/{file}\")\n\n# easier to draw with cv, ref - https://stackoverflow.com/questions/64096953/how-to-convert-yolo-format-bounding-box-coordinates-into-opencv-format\ndef get_ann_image(ann_file):\n    image_file = ann_file.split(\".txt\")[0] + \".jpg\"\n    img = read_img(image_file)\n    dh, dw, _ = img.shape\n    _, x, y, w, h = read_ann(ann_file)\n    l = int((x - w / 2) * dw)\n    r = int((x + w / 2) * dw)\n    t = int((y - h / 2) * dh)\n    b = int((y + h / 2) * dh)\n    if l < 0:\n        l = 0\n    if r > dw - 1:\n        r = dw - 1\n    if t < 0:\n        t = 0\n    if b > dh - 1:\n        b = dh - 1\n    cv2.rectangle(img, (l, t), (r, b), (0, 255, 255), 8)\n    img = cv2.resize(img, (300,300), interpolation = cv2.INTER_AREA)\n    img = cv2.cvtColor(img, cv2.COLOR_RGB2BGR)\n    return img","metadata":{"_kg_hide-input":true,"execution":{"iopub.status.busy":"2022-02-20T18:22:21.472174Z","iopub.execute_input":"2022-02-20T18:22:21.472401Z","iopub.status.idle":"2022-02-20T18:22:21.480935Z","shell.execute_reply.started":"2022-02-20T18:22:21.472376Z","shell.execute_reply":"2022-02-20T18:22:21.480196Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# sample and plot annotations\nannotation_df = pd.DataFrame(annotation_files)\nexample_annotations = annotation_df.sample(10, random_state=seed).reset_index(drop=True)\nannotated_images = np.asarray([get_ann_image(x) for x in example_annotations[0]])\n\nfig = px.imshow(annotated_images, facet_col=0, facet_col_wrap=5, binary_string=True, facet_row_spacing=0.0, facet_col_spacing=0)\nfig.update_xaxes(showticklabels=False).update_yaxes(showticklabels=False)\nfig.update_layout(title=\"Sample Annotations\", autosize=True, hovermode=False, height=900, width=1500, margin=dict(l=0, r=0, t=150, b=80))\nfig.show()","metadata":{"_kg_hide-input":true,"execution":{"iopub.status.busy":"2022-02-20T18:22:22.172639Z","iopub.execute_input":"2022-02-20T18:22:22.172861Z","iopub.status.idle":"2022-02-20T18:22:23.30696Z","shell.execute_reply.started":"2022-02-20T18:22:22.172839Z","shell.execute_reply":"2022-02-20T18:22:23.30628Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Skipped Images ","metadata":{}},{"cell_type":"code","source":"# intermediate output of modified script of https://gist.github.com/Amir22010/a99f18ca19112bc7db0872a36a03a1ec\nmissed_samples = ['8c5221ff535f10.jpg', 'cf4f3fe6605328.jpg', 'f8ec1df54bba10.jpg', '9c96a72250084c.jpg', '9a8b39b1b6d472.jpg', '60de2ea6b649a8.jpg', 'dda4bba985a0f0.jpg', 'c45d288419aba6.jpg', 'dd46ff0597c1aa.jpg', 'fdf98dfb01d768.jpg', 'baf82000789858.jpg', 'ebc7e212d6e505.jpg', 'a8e7f0347b7c62.jpg', 'a6d9a9d4a8a444.jpg', 'b58f4f1d9f6247.jpg', 'abe8de4c14ddbc.jpg', 'aa16947ce5316e.jpg', 'af64d05f30daca.jpg']\n\nread_img = lambda file: io.imread(f\"/kaggle/input/happy-whale-and-dolphin/train_images/{file}\")\nmissed_imgs = []\nfor x in missed_samples:\n    img = read_img(x)\n    if len(img.shape) == 2: \n        img = gray2rgb(img)\n    missed_imgs.append(resize(img, (300,300)))\nmissed_imgs = np.asarray(missed_imgs)\n\n# plot image grid\nfig = px.imshow(missed_imgs, facet_col=0, facet_col_wrap=10, binary_string=True, facet_row_spacing=0.0, facet_col_spacing=0)\nfig.update_xaxes(showticklabels=False).update_yaxes(showticklabels=False)\nfig.update_layout(title=\"Missed/skipped images\", autosize=True, hovermode=False, height=900, width=1500, margin=dict(l=0, r=0, t=150, b=80))\nfig.show()","metadata":{"_kg_hide-input":true,"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Bad views and last but one was a mistake, donno how that happened, its fine 😅 but this doesnt mean all that are annotated are better samples than these","metadata":{"execution":{"iopub.status.busy":"2022-02-20T16:25:57.565652Z","iopub.execute_input":"2022-02-20T16:25:57.565924Z","iopub.status.idle":"2022-02-20T16:25:57.570805Z","shell.execute_reply.started":"2022-02-20T16:25:57.565898Z","shell.execute_reply":"2022-02-20T16:25:57.569731Z"}}},{"cell_type":"markdown","source":"# 👨🏻‍💻 Sampling Code ","metadata":{"execution":{"iopub.status.busy":"2022-02-20T14:57:31.770937Z","iopub.execute_input":"2022-02-20T14:57:31.771197Z","iopub.status.idle":"2022-02-20T14:57:31.776687Z","shell.execute_reply.started":"2022-02-20T14:57:31.771172Z","shell.execute_reply":"2022-02-20T14:57:31.775297Z"}}},{"cell_type":"markdown","source":"```\n# just read the train csv with known corrections\ndef get_train_csv(\n    path:str=\"/kaggle/input/happy-whale-and-dolphin/train.csv\"\n    )->Tuple[pd.DataFrame, pd.DataFrame]:\n    # fix spelling of two species and merging subspecies, reduces the unique species from 30 -> 26\n    train_df = pd.read_csv(path)\n    train_df['species'].replace({'bottlenose_dolpin': 'bottlenose_dolphin', \n                                 'kiler_whale': 'killer_whale',\n                                 'pilot_whale': 'short_finned_pilot_whale',\n                                 'globis': 'short_finned_pilot_whale'\n                                }, inplace=True)\n\n    # assign id to species\n    train_df['species_id'], species_index = train_df['species'].factorize()\n    return train_df, species_index\n\n# df with all samples\ntrain_df, species_index = get_train_csv(path=\"/kaggle/input/happy-whale-and-dolphin/train.csv\")\ntrain_df.describe(include='all').loc[['count', 'unique', 'freq', 'top']]\n\n# sampling ~3% from every species randomly\nsubset_1_df = train_df.groupby('species').apply(pd.DataFrame.sample, frac=0.03, random_state=seed).reset_index(drop=True)\nsubset_1_df.describe(include='all').loc[['count', 'unique', 'freq', 'top']]\n\n# save the df and zip image subset\n# subset_1 if at someone wants to annotate more\nos.mkdir('subset_1')\nfor image_name in subset_1_df.image:\n    src = \"/kaggle/input/happy-whale-and-dolphin/train_images/\" + image_name\n    dest = \"/kaggle/working/subset_1/\" + image_name\n    shutil.copy(src, dest)\n    \n!zip -r /kaggle/working/subset_1.zip /kaggle/working/subset_1/ -qq\nsubset_1_df.to_csv(\"subset_1_df.csv\")\n```","metadata":{"execution":{"iopub.status.busy":"2022-02-20T15:10:32.614468Z","iopub.execute_input":"2022-02-20T15:10:32.614774Z","iopub.status.idle":"2022-02-20T15:10:32.818605Z","shell.execute_reply.started":"2022-02-20T15:10:32.614744Z","shell.execute_reply":"2022-02-20T15:10:32.81727Z"}}},{"cell_type":"markdown","source":"<div class=\"alert alert-block alert-success\"> \nThat's it for now. Thanks for reading, good luck! 🍀\n</div>","metadata":{}}]}