{"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":"# SIIM-FISABIO-RSNA COVID-19 Detection\n\nIdentify and localize COVID-19 abnormalities on chest radiographs","metadata":{}},{"cell_type":"code","source":"# list input folder\n! ls /kaggle/input/siim-covid19-detection -l","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","execution":{"iopub.status.busy":"2021-08-09T21:22:23.715377Z","iopub.execute_input":"2021-08-09T21:22:23.715753Z","iopub.status.idle":"2021-08-09T21:22:24.444516Z","shell.execute_reply.started":"2021-08-09T21:22:23.715666Z","shell.execute_reply":"2021-08-09T21:22:24.443529Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Setup environment\n\nInstalling needed packages and list versions for reproducibility","metadata":{}},{"cell_type":"code","source":"# ! pip install -qU \"numpy>=1.20\" --no-binary numpy --no-build-isolation\n! pip install -q python-gdcm\n# ! pip install -q pylibjpeg-libjpeg pylibjpeg-openjpeg\n# ! pip install -qU \"pylibjpeg==1.2\" --no-binary :all:\n! pip install -qU pydicom opencv-python-headless pycocotools # \"torchvision==0.8\" \"torch==1.7\"\n# ! pip install -q https://github.com/PyTorchLightning/lightning-flash/archive/master.zip\n! pip list | grep torch\n! pip list | grep lightning\n! pip list | grep dicom\n! pip list | grep jpeg\n! nvidia-smi\n\n%load_ext autoreload\n%autoreload 2\n\nimport pydicom  # , pylibjpeg, openjpeg, libjpeg\nprint(getattr(pydicom.config, \"gdcm_handler\").is_available())\nprint(getattr(pydicom.config, \"pylibjpeg_handler\").is_available())","metadata":{"execution":{"iopub.status.busy":"2021-08-09T21:22:24.446415Z","iopub.execute_input":"2021-08-09T21:22:24.446996Z","iopub.status.idle":"2021-08-09T21:23:10.974133Z","shell.execute_reply.started":"2021-08-09T21:22:24.446956Z","shell.execute_reply":"2021-08-09T21:23:10.973179Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Data exploration\n\nChecking what data do we have available and what is the labels distribution...\n\nWe start with:\n- naive loading tables\n- see distributions\n- visualuze images","metadata":{}},{"cell_type":"markdown","source":"### Overview & Annotations\n\nStarting with checking what is the provided tables...","metadata":{}},{"cell_type":"code","source":"%matplotlib inline\n\nimport os\nimport glob\nimport json\nimport numpy as np\nimport pandas as pd\nimport matplotlib.pyplot as plt\n\nBASE_PATH = '/kaggle/input/siim-covid19-detection'\nLABELS = (\"Negative for Pneumonia\", \"Typical Appearance\", \"Indeterminate Appearance\", \"Atypical Appearance\")\npd.set_option('display.max_columns', None)\npd.set_option('display.max_rows', None)","metadata":{"execution":{"iopub.status.busy":"2021-08-09T21:23:10.976099Z","iopub.execute_input":"2021-08-09T21:23:10.976460Z","iopub.status.idle":"2021-08-09T21:23:11.007784Z","shell.execute_reply.started":"2021-08-09T21:23:10.976398Z","shell.execute_reply":"2021-08-09T21:23:11.006879Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"**train_image_level.csv** - the train image-level metadata, with one row for each image, including both correct labels and any bounding boxes in a dictionary format. Some images in both test and train have multiple bounding boxes.\n\n- `id` - unique image identifier\n- `boxes` - bounding boxes in easily-readable dictionary format\n- `label` - the correct prediction label for the provided bounding boxes\n\nEnrich table with some countings and parsing the base name","metadata":{}},{"cell_type":"code","source":"path_csv_image = os.path.join(BASE_PATH, 'train_image_level.csv')\ntrain_images = pd.read_csv(path_csv_image, index_col=\"id\").sort_values(\"StudyInstanceUID\")\ntrain_images[\"name\"] = [v.split('_')[0] for v in train_images.index]\ntrain_images[\"boxes\"] = train_images[\"boxes\"].apply(lambda v: eval(v) if not isinstance(v, float) else None)\ntrain_images[\"#boxes\"] = train_images[\"boxes\"].apply(lambda v: len(v) if v else 0)\n\nimgs_paths = [glob.glob(os.path.join(BASE_PATH, 'train', row['StudyInstanceUID'], '*', f\"{row['name']}.*\")) for _, row in train_images.iterrows()]\nprint(f\"max images shall be one and is: {max([len(p) for p in imgs_paths])}\")\ntrain_images[\"path\"] = [os.path.sep.join(p[0].split(os.path.sep)[-4:]) for p in imgs_paths]\n\ndisplay(train_images.head())\nprint(len(train_images))","metadata":{"execution":{"iopub.status.busy":"2021-08-09T21:23:11.009589Z","iopub.execute_input":"2021-08-09T21:23:11.010010Z","iopub.status.idle":"2021-08-09T21:23:43.449375Z","shell.execute_reply.started":"2021-08-09T21:23:11.009923Z","shell.execute_reply":"2021-08-09T21:23:43.448425Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"train_images[\"label_\"] = train_images[\"label\"].apply(lambda lb: lb.split()[::6])\ntrain_images[\"label__\"] = train_images[\"label_\"].apply(lambda lb: set(lb))\nax = train_images[\"label__\"].value_counts().plot.pie(ylabel=\"\", autopct=\"%.1f%%\")","metadata":{"execution":{"iopub.status.busy":"2021-08-09T21:23:43.450567Z","iopub.execute_input":"2021-08-09T21:23:43.450894Z","iopub.status.idle":"2021-08-09T21:23:43.636307Z","shell.execute_reply.started":"2021-08-09T21:23:43.450860Z","shell.execute_reply":"2021-08-09T21:23:43.635525Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"labels_none = [lb for lb in train_images[\"label\"] if lb.startswith(\"none\")]\nprint(set(labels_none))","metadata":{"execution":{"iopub.status.busy":"2021-08-09T21:23:43.637674Z","iopub.execute_input":"2021-08-09T21:23:43.638066Z","iopub.status.idle":"2021-08-09T21:23:43.668974Z","shell.execute_reply.started":"2021-08-09T21:23:43.638031Z","shell.execute_reply":"2021-08-09T21:23:43.668040Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"labels_1 = [tuple(lb.split()[1::6]) for lb in train_images[\"label\"]]\nprint(set(labels_1))","metadata":{"execution":{"iopub.status.busy":"2021-08-09T21:23:43.670337Z","iopub.execute_input":"2021-08-09T21:23:43.670709Z","iopub.status.idle":"2021-08-09T21:23:43.706409Z","shell.execute_reply.started":"2021-08-09T21:23:43.670673Z","shell.execute_reply":"2021-08-09T21:23:43.705602Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"**train_study_level.csv** - the train study-level metadata, with one row for each study, including correct labels.\n\n- `id` - unique study identifier\n- `Negative for Pneumonia` - 1 if the study is negative for pneumonia, 0 otherwise\n- `Typical Appearance` - 1 if the study has this appearance, 0 otherwise\n- `Indeterminate Appearance`  - 1 if the study has this appearance, 0 otherwise\n- `Atypical Appearance` - if the study has this appearance, 0 otherwise\n","metadata":{}},{"cell_type":"code","source":"path_csv_study = os.path.join(BASE_PATH, 'train_study_level.csv')\ntrain_study = pd.read_csv(path_csv_study, index_col=\"id\").sort_values(\"id\")\ntrain_study[\"id_\"] = [v.split('_')[0] for v in train_study.index]\ntrain_study[\"class\"] = [np.argmax(row.values) for _, row in train_study[list(LABELS)].iterrows()]\ndisplay(train_study.head())\nprint(len(train_study))","metadata":{"execution":{"iopub.status.busy":"2021-08-09T21:23:43.709360Z","iopub.execute_input":"2021-08-09T21:23:43.709710Z","iopub.status.idle":"2021-08-09T21:23:44.293376Z","shell.execute_reply.started":"2021-08-09T21:23:43.709676Z","shell.execute_reply":"2021-08-09T21:23:44.292493Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"From previous we ca see nb images is larger nb studies...","metadata":{}},{"cell_type":"code","source":"counts = train_images[\"StudyInstanceUID\"].value_counts()\ndisplay(dict(enumerate(np.bincount(counts))))\nax = counts.hist(bins=2*max(counts))","metadata":{"execution":{"iopub.status.busy":"2021-08-09T21:23:44.295501Z","iopub.execute_input":"2021-08-09T21:23:44.296118Z","iopub.status.idle":"2021-08-09T21:23:44.503813Z","shell.execute_reply.started":"2021-08-09T21:23:44.296079Z","shell.execute_reply":"2021-08-09T21:23:44.502869Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"See sanity chek that sumof labels is equls to nb samples and show case/label distibution","metadata":{}},{"cell_type":"code","source":"train_study_ids = set(train_study[\"id_\"])\nmiss = [id_ for id_ in train_images[\"StudyInstanceUID\"] if id_ not in train_study_ids]\nprint(f\"Missed: {len(miss)}\")\nprint(f\"{len(train_study)} == {train_study[list(LABELS)].sum().sum()}\")\n\nax = train_study[list(LABELS)].sum().plot.pie(ylabel=\"\", autopct=\"%.1f%%\")","metadata":{"execution":{"iopub.status.busy":"2021-08-09T21:23:44.505122Z","iopub.execute_input":"2021-08-09T21:23:44.505479Z","iopub.status.idle":"2021-08-09T21:23:44.637717Z","shell.execute_reply.started":"2021-08-09T21:23:44.505443Z","shell.execute_reply":"2021-08-09T21:23:44.636627Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### Fuse the two tables\n\nlets trasfer the labels to the images","metadata":{}},{"cell_type":"code","source":"train_images = pd.merge(train_images, train_study, how=\"left\", left_on=\"StudyInstanceUID\", right_on=\"id_\")\ndisplay(train_images.head())","metadata":{"execution":{"iopub.status.busy":"2021-08-09T21:23:44.639093Z","iopub.execute_input":"2021-08-09T21:23:44.639457Z","iopub.status.idle":"2021-08-09T21:23:44.698055Z","shell.execute_reply.started":"2021-08-09T21:23:44.639421Z","shell.execute_reply":"2021-08-09T21:23:44.696978Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"fig, axarr = plt.subplots(ncols=2, figsize=(7, 3))\n\ntrain_images_none = train_images[train_images[\"label\"].str.startswith(\"none\")]\naxarr[0].set_title(\"classes with labels None\")\nax = train_images_none[\"class\"].value_counts().plot.pie(ax=axarr[0], ylabel=\"\", autopct=\"%.1f%%\")\n\ntrain_images_other = train_images[~ train_images[\"label\"].str.startswith(\"none\")]\naxarr[1].set_title(\"classes with any labels\")\nax = train_images_other[\"class\"].value_counts().plot.pie(ax=axarr[1], ylabel=\"\", autopct=\"%.1f%%\")","metadata":{"execution":{"iopub.status.busy":"2021-08-09T21:23:44.699440Z","iopub.execute_input":"2021-08-09T21:23:44.699802Z","iopub.status.idle":"2021-08-09T21:23:44.909001Z","shell.execute_reply.started":"2021-08-09T21:23:44.699764Z","shell.execute_reply":"2021-08-09T21:23:44.908122Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### Show sample image\n\nloading the mages from DICOM format and show then in standard figures...","metadata":{}},{"cell_type":"code","source":"import pydicom\nfrom pydicom.pixel_data_handlers import apply_voi_lut\n\nidx_ = 0\ndicom_path = os.path.join(BASE_PATH, train_images[\"path\"][idx_])\ndicom = pydicom.dcmread(dicom_path)\nprint(vars(dicom).keys())\nprint(dicom)\nimg = apply_voi_lut(dicom.pixel_array, dicom)\n\n\nimport matplotlib.pyplot as plt\nfrom matplotlib import patches\n\nfig, ax = plt.subplots()\nax_im = ax.imshow(img, cmap=\"gray\")\nfor bbox in train_images[\"boxes\"][idx_]:\n    # Create a Rectangle patch\n    rect = patches.Rectangle((bbox['x'], bbox['y']), bbox['width'], bbox['height'], linewidth=1, edgecolor='r', facecolor='none')\n    # Add the patch to the Axes\n    ax.add_patch(rect)\n\n_= plt.colorbar(ax_im)","metadata":{"execution":{"iopub.status.busy":"2021-08-09T21:23:44.910210Z","iopub.execute_input":"2021-08-09T21:23:44.910707Z","iopub.status.idle":"2021-08-09T21:23:46.180575Z","shell.execute_reply.started":"2021-08-09T21:23:44.910668Z","shell.execute_reply":"2021-08-09T21:23:46.179811Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### Samples per class\n\ngroup images per class and show a few sample images per class together with detection bounding boxes","metadata":{}},{"cell_type":"code","source":"import cv2\nfrom copy import deepcopy\n\ndef load_image(path_file: str, meta: dict, spacing: float = 1.0, percentile: bool = True):\n    dicom = pydicom.dcmread(path_file)\n    try:\n        img = apply_voi_lut(dicom.pixel_array, dicom)\n    except RuntimeError as err:\n        print(err)\n        return None, {}\n    if dicom.PhotometricInterpretation == 'MONOCHROME1':\n        img = img.max() - img\n    p_low = np.percentile(img, 1) if percentile else img.min()\n    p_high = np.percentile(img, 99) if percentile else img.max()\n    # normalize\n    img = (img.astype(float) - p_low) / (p_high - p_low)\n    meta.update(dict(\n        boxes=deepcopy(meta.get(\"boxes\")) or [],\n        body=dicom.BodyPartExamined,\n        interpret=dicom.PhotometricInterpretation,\n        spacing=dicom.ImagerPixelSpacing,\n    ))\n    if spacing:\n        factor = np.array(meta['spacing']) / spacing\n        dims = tuple((np.array(img.shape[::-1]) * factor).astype(int))\n        img = cv2.resize(img, dsize=dims, interpolation=cv2.INTER_LINEAR)\n        for bbox in meta[\"boxes\"]:\n            bbox['x'] *= factor[0]\n            bbox['y'] *= factor[1]\n            bbox['width'] *= factor[0]\n            bbox['height'] *= factor[1]\n        meta.update(dict(spacing=(spacing, spacing)))\n    return img, meta","metadata":{"execution":{"iopub.status.busy":"2021-08-09T21:23:46.181867Z","iopub.execute_input":"2021-08-09T21:23:46.182204Z","iopub.status.idle":"2021-08-09T21:23:46.229126Z","shell.execute_reply.started":"2021-08-09T21:23:46.182168Z","shell.execute_reply":"2021-08-09T21:23:46.228353Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"NB_SAMPLES = 5\nNB_CLASSES = max(train_images[\"class\"]) + 1\nrect_property = dict(linewidth=1, edgecolor='r', facecolor='none')\n\nfig, axarr = plt.subplots(nrows=NB_CLASSES, ncols=NB_SAMPLES, figsize=(NB_SAMPLES * 4, NB_CLASSES * 5))\nfor cls, df in train_images.groupby(\"class\"):\n    for i, (_, row) in enumerate(df[:NB_SAMPLES].iterrows()):\n        img, meta = load_image(os.path.join(BASE_PATH, row[\"path\"]), dict(row), spacing=1.)\n        axarr[cls, i].set_title(f\"label: {cls}; body: {meta['body']}\\n interpret: {meta['interpret']}\\n spacing: {meta['spacing']}\")\n        if img is None:\n            continue\n        _ = axarr[cls, i].imshow(img.astype(float) / img.max(), cmap=\"gray\")\n        if not meta[\"boxes\"]:\n            continue\n        for bbox in meta[\"boxes\"]:\n            rect = patches.Rectangle((bbox['x'], bbox['y']), bbox['width'], bbox['height'], **rect_property)\n            axarr[cls, i].add_patch(rect)","metadata":{"execution":{"iopub.status.busy":"2021-08-09T21:23:46.230578Z","iopub.execute_input":"2021-08-09T21:23:46.231076Z","iopub.status.idle":"2021-08-09T21:23:54.064507Z","shell.execute_reply.started":"2021-08-09T21:23:46.230899Z","shell.execute_reply":"2021-08-09T21:23:54.063570Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### Number of detections per case\n\nHistgram of number of detections in subject depending on type","metadata":{}},{"cell_type":"code","source":"counts, cases = [], []\nfor cls, df in train_images.groupby(\"class\"):\n    counts.append(dict(df[\"#boxes\"].value_counts()))\n    cases.append(LABELS[cls])\n\ndf = pd.DataFrame(counts, index=cases)\ndisplay(df)\nax = df[sorted(df.columns)].T.plot.bar(grid=True, xlabel=\"#boxes per image\", ylabel=\"#images per class\", figsize=(7, 3))","metadata":{"execution":{"iopub.status.busy":"2021-08-09T21:23:54.065598Z","iopub.execute_input":"2021-08-09T21:23:54.065927Z","iopub.status.idle":"2021-08-09T21:23:54.495691Z","shell.execute_reply.started":"2021-08-09T21:23:54.065896Z","shell.execute_reply":"2021-08-09T21:23:54.494801Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# from tqdm.autonotebook import tqdm\n\n# train_images['BodyPart'] = [pydicom.dcmread(os.path.join(BASE_PATH, p)).BodyPartExamined for p in tqdm(train_images['path'])]\n\n# counts, cases = [], []\n# for cls, df in train_images.groupby(\"class\"):\n#     counts.append(dict(df[\"BodyPart\"].value_counts()))\n#     cases.append(LABELS[cls])\n\n# ax = pd.DataFrame(counts, index=cases).T.plot.bar(grid=True, xlabel=\"BodyPart\", ylabel=\"#images\", figsize=(8, 3))","metadata":{"execution":{"iopub.status.busy":"2021-08-09T21:23:54.498022Z","iopub.execute_input":"2021-08-09T21:23:54.498425Z","iopub.status.idle":"2021-08-09T21:23:54.530143Z","shell.execute_reply.started":"2021-08-09T21:23:54.498368Z","shell.execute_reply":"2021-08-09T21:23:54.529133Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Convert to COCO\n\nThe *.txt file specifications are:\n\n- One row per object - class x_center y_center width height\n- Box coordinates must be in normalized xywh format (from 0 - 1)\n- Class numbers are zero-indexed\n\n<img src=\"https://user-images.githubusercontent.com/26833433/91506361-c7965000-e886-11ea-8291-c72b98c25eec.jpg\" width=\"480\">","metadata":{}},{"cell_type":"code","source":"def convert_boxes_to_coco(meta, image_hw):\n    # ih, iw = img.shape[:2]\n    ih, iw = image_hw\n    bboxes = []\n    for bbox in meta[\"boxes\"]:\n        # cls, x_center, y_center, width, height\n        bboxes.append({\n            \"cls\": meta[\"class\"],\n            \"x_center\": float(bbox['x'] + bbox['width'] / 2) / iw,\n            \"y_center\": float(bbox['y'] + bbox['height'] / 2) / ih,\n            \"width\":  bbox['width'] / iw,\n            \"height\": bbox['height'] / ih\n        })\n    return bboxes","metadata":{"execution":{"iopub.status.busy":"2021-08-09T21:23:54.532622Z","iopub.execute_input":"2021-08-09T21:23:54.533028Z","iopub.status.idle":"2021-08-09T21:23:54.566225Z","shell.execute_reply.started":"2021-08-09T21:23:54.532986Z","shell.execute_reply":"2021-08-09T21:23:54.565360Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"prepare the new dataset folders","metadata":{}},{"cell_type":"code","source":"PATH_OUT = \"/kaggle/working\"\nPATH_OUT_IMAGE = \"/kaggle/working/images\"\nPATH_OUT_LABEL = \"/kaggle/working/labels\"\nSPACING = 1.0\n\nfor d in (PATH_OUT_IMAGE, PATH_OUT_LABEL):\n    os.makedirs(d, exist_ok=True)\n    for dd in (\"train\", \"test\"):\n        os.makedirs(os.path.join(d, dd), exist_ok=True)","metadata":{"execution":{"iopub.status.busy":"2021-08-09T21:23:54.567538Z","iopub.execute_input":"2021-08-09T21:23:54.567898Z","iopub.status.idle":"2021-08-09T21:23:54.597026Z","shell.execute_reply.started":"2021-08-09T21:23:54.567861Z","shell.execute_reply":"2021-08-09T21:23:54.596230Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"conver train images and save metadata to the overview table","metadata":{}},{"cell_type":"code","source":"from tqdm.autonotebook import tqdm\nfrom multiprocessing import Pool\nfrom functools import partial\n\n\ndef conver_image(id_row, dir_name):\n    _, row = id_row\n    # phase = \"train\" if np.random.random() < 0.8 else \"valid\"\n    img, meta = load_image(os.path.join(BASE_PATH, row['path']), dict(row), spacing=SPACING)\n    plt.imsave(os.path.join(PATH_OUT_IMAGE, dir_name, f\"{row['name']}.jpg\"), img, cmap='gray')\n    bboxes = convert_boxes_to_coco(meta, image_hw=img.shape[:2])\n    df = pd.DataFrame(bboxes)[[\"cls\", \"x_center\", \"y_center\", \"width\", \"height\"]] if bboxes else pd.DataFrame(bboxes)\n    df.to_csv(os.path.join(PATH_OUT_LABEL, dir_name, f\"{row['name']}.txt\"), sep=\" \", index=None, header=None)\n    meta.update({\"bboxes\": bboxes, \"image_size\": img.shape})\n    return meta\n\nmetas = []\npool = Pool(os.cpu_count())\nfor meta in pool.map(partial(conver_image, dir_name=\"train\"), tqdm(train_images.iterrows(), total=len(train_images))):\n    metas.append(meta)\npool.close()\npool.join()","metadata":{"execution":{"iopub.status.busy":"2021-08-09T21:23:54.598575Z","iopub.execute_input":"2021-08-09T21:23:54.598950Z","iopub.status.idle":"2021-08-09T21:42:18.331208Z","shell.execute_reply.started":"2021-08-09T21:23:54.598912Z","shell.execute_reply":"2021-08-09T21:42:18.330254Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Creating the COCO coordinate file which contains:\n\n- bounding boxes\n- images with dimensions\n- class describtion","metadata":{}},{"cell_type":"code","source":"import json\n\nannots = []\nrunning_id = 0\nfor idx, meta in enumerate(metas):\n    ih, iw = meta[\"image_size\"]\n    for i, box in enumerate(meta[\"bboxes\"]):\n        w = int(box['width'] * iw)\n        h = int(box['height'] * ih)\n        x = int(box['x_center'] * iw) - np.ceil(w / 2.)\n        y = int(box['y_center'] * ih) - np.ceil(h / 2.)\n        rec = {\n            \"id\": running_id,\n            \"image_id\": idx,\n            \"category_id\": meta['class'],\n            \"area\": w * h,\n            \"bbox\": [max(0, x), max(0, y), w, h],\n            \"segmentation\": [],\n            \"iscrowd\": 0,\n        }\n        annots.append(rec)\n        running_id += 1\n\ncoco = {\n    \"annotations\": annots,\n    \"categories\": [{\"id\": i, \"name\": n, \"supercategory\": \"\"} for i, n in enumerate(LABELS)],\n    \"images\": [{\"id\": idx, \"file_name\": f\"{meta['name']}.jpg\", \"height\": meta[\"image_size\"][0], \"width\": meta[\"image_size\"][1]} for idx, meta in enumerate(metas)],\n}\n\npath_json = os.path.join(PATH_OUT, \"covid_train.json\")\nwith open(path_json, \"w\") as fp:\n    json.dump(coco, fp)","metadata":{"execution":{"iopub.status.busy":"2021-08-09T21:42:18.332556Z","iopub.execute_input":"2021-08-09T21:42:18.332930Z","iopub.status.idle":"2021-08-09T21:42:18.793086Z","shell.execute_reply.started":"2021-08-09T21:42:18.332891Z","shell.execute_reply":"2021-08-09T21:42:18.792210Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Converting the test images","metadata":{}},{"cell_type":"code","source":"found_images = glob.glob(os.path.join(BASE_PATH, 'test', '*', '*', '*.dcm'))\n\ntest_images = pd.DataFrame({\n    \"name\": os.path.splitext(os.path.basename(p))[0],\n    \"path\": os.path.sep.join(p.split(os.path.sep)[-4:])\n} for p in found_images)\ndisplay(test_images.head())\n\npool = Pool(os.cpu_count())\nlist(pool.imap_unordered(partial(conver_image, dir_name=\"test\"), tqdm(test_images.iterrows(), total=len(test_images))))\npool.close()\npool.join()","metadata":{"execution":{"iopub.status.busy":"2021-08-09T21:42:18.794593Z","iopub.execute_input":"2021-08-09T21:42:18.794987Z","iopub.status.idle":"2021-08-09T21:45:53.299186Z","shell.execute_reply.started":"2021-08-09T21:42:18.794946Z","shell.execute_reply":"2021-08-09T21:45:53.298168Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"! cd /kaggle/working\n! zip covid-dataset.zip -q -r *","metadata":{"execution":{"iopub.status.busy":"2021-08-09T21:45:53.307100Z","iopub.execute_input":"2021-08-09T21:45:53.309298Z","iopub.status.idle":"2021-08-09T21:45:59.643071Z","shell.execute_reply.started":"2021-08-09T21:45:53.309219Z","shell.execute_reply":"2021-08-09T21:45:59.641893Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Training with Flash","metadata":{}},{"cell_type":"code","source":"! rm -rf lightning-flash\n! pip uninstall -y lightning-flash\n! git clone https://github.com/PyTorchLightning/lightning-flash.git\n! cd lightning-flash && git checkout feature/icevision && pip install -q .[image]\n# ! pip install -q https://github.com/PyTorchLightning/lightning-flash/archive/refs/heads/feature/icevision.zip#egg=lightning-flash[image]\n! pip uninstall -y fiftyone wandb\n# ! pip install -q effdet","metadata":{"execution":{"iopub.status.busy":"2021-08-09T21:45:59.645522Z","iopub.execute_input":"2021-08-09T21:45:59.645940Z","iopub.status.idle":"2021-08-09T21:46:53.847810Z","shell.execute_reply.started":"2021-08-09T21:45:59.645896Z","shell.execute_reply":"2021-08-09T21:46:53.846832Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import flash\nfrom flash.image import ObjectDetectionData, ObjectDetector\n\n# 1. Create the DataModule\ndm = ObjectDetectionData.from_coco(\n    train_folder=os.path.join(PATH_OUT_IMAGE, 'train'),\n    train_ann_file=os.path.join(PATH_OUT, \"covid_train.json\"),\n    val_split=0.1,\n    batch_size=6,\n    image_size=640,\n)","metadata":{"execution":{"iopub.status.busy":"2021-08-09T21:46:53.851270Z","iopub.execute_input":"2021-08-09T21:46:53.851581Z","iopub.status.idle":"2021-08-09T21:47:08.671206Z","shell.execute_reply.started":"2021-08-09T21:46:53.851549Z","shell.execute_reply":"2021-08-09T21:47:08.670367Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# 2. Build the task\nmodel = ObjectDetector(\n    head=\"efficientdet\",\n    backbone=\"tf_d3_ap\",\n    learning_rate=1.5e-5,\n    num_classes=dm.num_classes,\n    image_size=640,\n)","metadata":{"execution":{"iopub.status.busy":"2021-08-09T21:47:08.672574Z","iopub.execute_input":"2021-08-09T21:47:08.672946Z","iopub.status.idle":"2021-08-09T21:47:11.011770Z","shell.execute_reply.started":"2021-08-09T21:47:08.672907Z","shell.execute_reply":"2021-08-09T21:47:11.010763Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### Run traning","metadata":{}},{"cell_type":"code","source":"import pytorch_lightning as pl\nprint(pl.__version__)\nlogger = pl.loggers.CSVLogger(save_dir='logs/')\n                              \n# 3. Create the trainer and finetune the model\ntrainer = flash.Trainer(\n    max_epochs=20,\n    gpus=1,\n    precision=16,\n    accumulate_grad_batches=12,\n    logger=logger,\n    val_check_interval=0.5,\n)\ntrainer.finetune(model, datamodule=dm, strategy=\"freeze_unfreeze\")\n\n# 3. Save the model!\ntrainer.save_checkpoint(\"object_detection_model.pt\")","metadata":{"execution":{"iopub.status.busy":"2021-08-09T21:47:11.013255Z","iopub.execute_input":"2021-08-09T21:47:11.013677Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### Show training charts","metadata":{}},{"cell_type":"code","source":"metrics = pd.read_csv(f'{trainer.logger.log_dir}/metrics.csv')\ndisplay(metrics.head())\n\naggreg_metrics = []\nagg_col = \"epoch\"\nfor i, dfg in metrics.groupby(agg_col):\n    agg = dict(dfg.mean())\n    agg[agg_col] = i\n    aggreg_metrics.append(agg)\n\ndf_metrics = pd.DataFrame(aggreg_metrics)\ndf_metrics[['loss', 'class_loss', 'box_loss']].plot(grid=True, legend=True, xlabel=agg_col)\ndf_metrics[['Precision (IoU=0.50:0.95,area=all)', 'Recall (IoU=0.50:0.95,area=all,maxDets=100)']].plot(grid=True, legend=True, xlabel=agg_col)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### Predict labels for test images","metadata":{}},{"cell_type":"code","source":"# 4. Detect objects in a few images!\npredictions = []\nmodel.to(\"cuda\")\nfor _, row in tqdm(test_images.iterrows(), total=len(test_images)):\n    p_img = os.path.join(PATH_OUT_IMAGE, \"test\", f\"{row['name']}.jpg\")\n    preds = model.predict([p_img])\n    rec = {**dict(row), \"predictions\": preds[0]}\n    predictions.append(rec)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"print(predictions[0])\ndisplay(predictions[0]['predictions'].as_dict)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"pred_boxes = [len(p['predictions'].as_dict()['detection']['bboxes']) for p in predictions]\nprint(dict(enumerate(np.bincount(pred_boxes))))\nprint(pred_boxes)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# ! ls /kaggle/working/images/test/\n# ! ls /kaggle/working/images/test/","metadata":{"trusted":true},"execution_count":null,"outputs":[]}]}