{"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":"# [RSNA 2023 Abdominal Trauma Detection](https://www.kaggle.com/competitions/rsna-2023-abdominal-trauma-detection)\n\n> Detect and classify traumatic abdominal injuries\n\n![](https://www.kaggle.com/competitions/52254/images/header)","metadata":{}},{"cell_type":"markdown","source":"# Idea:\n* Same as [RSNA-ATD: CNN [TPU][Train]](https://www.kaggle.com/awsaf49/rsna-atd-cnn-tpu-train/) but with **2.5D** data.\n* 2.5D method is simpling stacking different scan of same series to created a `RGB` like image but here each channel is associated with different scans.","metadata":{}},{"cell_type":"markdown","source":"# Notebooks\n* 2.5D:\n    * Train: [RSNA-ATD: 2.5D Series Image [Train]](https://www.kaggle.com/awsaf49/rsna-atd-2-5d-series-image-train)\n    * Infer: [RSNA-ATD: 2.5D Series Image [Infer]](https://www.kaggle.com/awsaf49/rsna-atd-2-5d-series-image-infer)\n* 2D:\n    * Train: [RSNA-ATD: CNN [TPU][Train]](https://www.kaggle.com/awsaf49/rsna-atd-cnn-tpu-train/)\n    * Infer: [RSNA-ATD: CNN [TPU][Infer]](https://www.kaggle.com/awsaf49/rsna-atd-cnn-tpu-infer/)","metadata":{}},{"cell_type":"markdown","source":"# Logging:","metadata":{}},{"cell_type":"markdown","source":"# Install Libraries","metadata":{}},{"cell_type":"code","source":"!pip install -q /kaggle/input/rsna-atd-whl-ds/python_gdcm-3.0.22-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl\n!pip install -q /kaggle/input/rsna-atd-whl-ds/pylibjpeg-1.4.0-py3-none-any.whl\n!cp -r /kaggle/input/efficientnet-keras-dataset/efficientnet_kaggle /tmp/ && pip install -q /tmp/efficientnet_kaggle","metadata":{"_kg_hide-output":true,"_kg_hide-input":false,"execution":{"iopub.status.busy":"2023-08-03T05:46:26.616019Z","iopub.execute_input":"2023-08-03T05:46:26.616572Z","iopub.status.idle":"2023-08-03T05:48:04.344995Z","shell.execute_reply.started":"2023-08-03T05:46:26.61653Z","shell.execute_reply":"2023-08-03T05:48:04.343694Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Import Libraries","metadata":{}},{"cell_type":"code","source":"import os\nos.environ['TF_CPP_MIN_LOG_LEVEL'] = '3'  # to avoid too many logging messages\nimport pandas as pd, numpy as np, random, shutil\nimport tensorflow as tf, re, math\nimport tensorflow.keras.backend as K\nimport efficientnet.tfkeras as efn\nimport sklearn\nimport matplotlib.pyplot as plt\nimport tensorflow_addons as tfa\nimport yaml\nimport gc\n\n\n\nfrom IPython import display as ipd\nfrom glob import glob\nfrom tqdm.notebook import tqdm","metadata":{"_kg_hide-output":true,"execution":{"iopub.status.busy":"2023-08-03T05:48:04.348675Z","iopub.execute_input":"2023-08-03T05:48:04.349345Z","iopub.status.idle":"2023-08-03T05:48:13.097014Z","shell.execute_reply.started":"2023-08-03T05:48:04.349305Z","shell.execute_reply":"2023-08-03T05:48:13.095954Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Version Check","metadata":{}},{"cell_type":"code","source":"print('np:', np.__version__)\nprint('pd:', pd.__version__)\nprint('sklearn:', sklearn.__version__)\nprint('tf:',tf.__version__)\nprint('tfa:', tfa.__version__)","metadata":{"execution":{"iopub.status.busy":"2023-08-03T05:48:13.098631Z","iopub.execute_input":"2023-08-03T05:48:13.099405Z","iopub.status.idle":"2023-08-03T05:48:13.106332Z","shell.execute_reply.started":"2023-08-03T05:48:13.09937Z","shell.execute_reply":"2023-08-03T05:48:13.105319Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Configuration","metadata":{}},{"cell_type":"code","source":"class CFG:\n    debug         = False\n    # device\n    device = \"GPU\" # or \"GPU\"\n    # seed for data-split, layer init, augs\n    seed = 42\n    # which folds of ckpt to use\n    num_folds = 1\n    # dicom to png size\n    resize_dim = 512\n    # size of training image\n    img_size = [512, 512]\n    # clip\n    clip = False\n    # lr-scheduler\n    scheduler   = 'exp' # cosine\n    # threshold\n    thr = 0.50\n    # target column\n    target_col  = [\"bowel_healthy\", \"bowel_injury\", \"extravasation_healthy\",\n                   \"extravasation_injury\", \"kidney_healthy\", \"kidney_low\",\n                   \"kidney_high\", \"liver_healthy\", \"liver_low\", \"liver_high\",\n                   \"spleen_healthy\", \"spleen_low\", \"spleen_high\"]","metadata":{"execution":{"iopub.status.busy":"2023-08-03T05:48:13.109284Z","iopub.execute_input":"2023-08-03T05:48:13.109609Z","iopub.status.idle":"2023-08-03T05:48:13.121224Z","shell.execute_reply.started":"2023-08-03T05:48:13.109577Z","shell.execute_reply":"2023-08-03T05:48:13.120339Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Reproducibility","metadata":{}},{"cell_type":"code","source":"def seeding(SEED):\n    np.random.seed(SEED)\n    random.seed(SEED)\n    os.environ['PYTHONHASHSEED'] = str(SEED)\n#     os.environ['TF_CUDNN_DETERMINISTIC'] = str(SEED)\n    tf.random.set_seed(SEED)\n    print('seeding done!!!')\nseeding(CFG.seed)","metadata":{"execution":{"iopub.status.busy":"2023-08-03T05:48:13.122434Z","iopub.execute_input":"2023-08-03T05:48:13.123012Z","iopub.status.idle":"2023-08-03T05:48:13.137223Z","shell.execute_reply.started":"2023-08-03T05:48:13.122976Z","shell.execute_reply":"2023-08-03T05:48:13.136208Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Device Configs\nThis notebook is compatible for **remote-tpu**, **local-tpu**, **multi-gpu** and **single-gpu**. Simple change to `device=\"TPU\"` for **remote-tpu** and `device=\"TPU-1VM\"` for **local-tpu** and finally, `device=\"GPU\"` for single or multi-gpu.","metadata":{}},{"cell_type":"code","source":"if \"TPU\" in CFG.device:\n    tpu = 'local' if CFG.device=='TPU-1VM' else None\n    print(\"connecting to TPU...\")\n    try:\n        tpu = tf.distribute.cluster_resolver.TPUClusterResolver.connect(tpu=tpu)\n        strategy = tf.distribute.TPUStrategy(tpu)\n    except:\n        CFG.device = \"GPU\"\n        \nif CFG.device == \"GPU\"  or CFG.device==\"CPU\":\n    ngpu = len(tf.config.experimental.list_physical_devices('GPU'))\n    if ngpu>1:\n        print(\"Using multi GPU\")\n        strategy = tf.distribute.MirroredStrategy()\n    elif ngpu==1:\n        print(\"Using single GPU\")\n        strategy = tf.distribute.get_strategy()\n    else:\n        print(\"Using CPU\")\n        strategy = tf.distribute.get_strategy()\n        CFG.device = \"CPU\"\n\nif CFG.device == \"GPU\":\n    print(\"Num GPUs Available: \", ngpu)\n    \n\nAUTO     = tf.data.experimental.AUTOTUNE\nREPLICAS = strategy.num_replicas_in_sync\nprint(f'REPLICAS: {REPLICAS}')","metadata":{"_kg_hide-input":true,"_kg_hide-output":true,"execution":{"iopub.status.busy":"2023-08-03T05:48:13.13856Z","iopub.execute_input":"2023-08-03T05:48:13.138896Z","iopub.status.idle":"2023-08-03T05:48:13.364052Z","shell.execute_reply.started":"2023-08-03T05:48:13.13886Z","shell.execute_reply":"2023-08-03T05:48:13.362836Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Meta Data","metadata":{}},{"cell_type":"code","source":"BASE_PATH = '/kaggle/input/rsna-2023-abdominal-trauma-detection'\nIMG_DIR = '/tmp/dataset/rsna-atd'\nNUM_SLICES = 4 # how many dicom files to take per series (scans)","metadata":{"execution":{"iopub.status.busy":"2023-08-03T05:48:13.365406Z","iopub.execute_input":"2023-08-03T05:48:13.365832Z","iopub.status.idle":"2023-08-03T05:48:13.370562Z","shell.execute_reply.started":"2023-08-03T05:48:13.365799Z","shell.execute_reply":"2023-08-03T05:48:13.369675Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Test Paths","metadata":{}},{"cell_type":"code","source":"test_df = pd.read_csv(f'{BASE_PATH}/test_series_meta.csv')\ntest_df['dicom_folder'] = BASE_PATH + '/' + 'test_images'\\\n                                    + '/' + test_df.patient_id.astype(str)\\\n                                    + '/' + test_df.series_id.astype(str)\ntest_folders = test_df.dicom_folder.tolist()\n\ntest_paths = []\nfor folder in tqdm(test_folders):\n    paths = sorted(glob(os.path.join(folder, '*dcm')),\n                   key=lambda x: int(x.split('/')[-1].split('.')[0]))\n    NUM_DICOM = len(paths)\n    if len(test_folders)>6: # private test; contains all dicom files/folders\n        STRIDE = -(-NUM_DICOM // (NUM_SLICES + 4))\n        test_paths += [paths[STRIDE:NUM_DICOM-3*STRIDE:STRIDE]]\n    else: # we can't access all the test dicom files in public test\n        test_paths += [paths]\n\ntest_df['dicom_paths'] = test_paths\ntest_df = test_df[test_df.dicom_paths.map(len)>0] # in public test not all folder contains dicom file\n\ntest_df['image_path'] = f'{IMG_DIR}/test_images'\\\n                    + '/' + test_df.patient_id.astype(str)\\\n                    + '/' + test_df.series_id.astype(str) +'.png'\n# test_df = test_df.drop_duplicates()\n\ntest_df.head(2)","metadata":{"execution":{"iopub.status.busy":"2023-08-03T05:48:13.372062Z","iopub.execute_input":"2023-08-03T05:48:13.372715Z","iopub.status.idle":"2023-08-03T05:48:13.45693Z","shell.execute_reply.started":"2023-08-03T05:48:13.372685Z","shell.execute_reply":"2023-08-03T05:48:13.456117Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Check If Data Exist?","metadata":{}},{"cell_type":"code","source":"tf.io.gfile.exists(test_df.dicom_paths.iloc[0][0])","metadata":{"execution":{"iopub.status.busy":"2023-08-03T05:48:13.458021Z","iopub.execute_input":"2023-08-03T05:48:13.458281Z","iopub.status.idle":"2023-08-03T05:48:13.465013Z","shell.execute_reply.started":"2023-08-03T05:48:13.458259Z","shell.execute_reply":"2023-08-03T05:48:13.464115Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Test Ditribution","metadata":{}},{"cell_type":"code","source":"print('test_files:',test_df.shape[0])","metadata":{"execution":{"iopub.status.busy":"2023-08-03T05:48:13.470189Z","iopub.execute_input":"2023-08-03T05:48:13.470929Z","iopub.status.idle":"2023-08-03T05:48:13.477117Z","shell.execute_reply.started":"2023-08-03T05:48:13.470883Z","shell.execute_reply":"2023-08-03T05:48:13.476191Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# DICOM to PNG","metadata":{}},{"cell_type":"markdown","source":"## Create Directory","metadata":{}},{"cell_type":"code","source":"!rm -r /tmp/Dataset/rsna-atd\n# os.makedirs('/tmp/dataset/rsna-atd/train_images', exist_ok = True)\nos.makedirs(f'{IMG_DIR}/test_images', exist_ok = True)","metadata":{"_kg_hide-input":true,"execution":{"iopub.status.busy":"2023-08-03T05:48:13.478691Z","iopub.execute_input":"2023-08-03T05:48:13.479392Z","iopub.status.idle":"2023-08-03T05:48:14.444753Z","shell.execute_reply.started":"2023-08-03T05:48:13.479361Z","shell.execute_reply":"2023-08-03T05:48:14.443573Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Dicom Utils","metadata":{}},{"cell_type":"code","source":"import cv2\nimport pydicom\n\ndef standardize_pixel_array(dcm: pydicom.dataset.FileDataset) -> np.ndarray:\n    # Correct DICOM pixel_array if PixelRepresentation == 1.\n    pixel_array = dcm.pixel_array\n    if dcm.PixelRepresentation == 1:\n        bit_shift = dcm.BitsAllocated - dcm.BitsStored\n        dtype = pixel_array.dtype \n        new_array = (pixel_array << bit_shift).astype(dtype) >>  bit_shift\n        pixel_array = pydicom.pixel_data_handlers.util.apply_modality_lut(new_array, dcm)\n    return pixel_array\n\n\ndef read_xray(path, fix_monochrome = True):\n    dicom = pydicom.dcmread(path)\n    data = standardize_pixel_array(dicom)\n    data = data - np.min(data)\n    data = data / (np.max(data) + 1e-5)\n    if fix_monochrome and dicom.PhotometricInterpretation == \"MONOCHROME1\":\n        data = 1.0 - data\n    IMG_SIZE = [CFG.resize_dim, CFG.resize_dim]\n    data = cv2.resize(data, IMG_SIZE, cv2.INTER_LINEAR)\n    data = (data * 255).astype(np.uint8)\n    return data\n\ndef load_scan(paths):\n    IMG_SIZE = [CFG.resize_dim, CFG.resize_dim]\n    img = np.empty(shape=(*IMG_SIZE, NUM_SLICES), dtype=np.uint8)\n    for i, path in enumerate(paths):\n        img[...,i] = read_xray(path)\n    return img\n\ndef load_img(path):\n    img = cv2.imread(path, -1)[...,::-1]\n    return img\n    \ndef resize_and_save(paths):\n    img = load_scan(paths)\n    file_path = paths[0]\n    sub_path = file_path.split(\"/\",4)[-1].split('.dcm')[0] + '.png'\n    infos = sub_path.split('/')\n    split = infos[-4]\n    pid = infos[-3]\n    sid = infos[-2]\n    iid = infos[-1]; iid = iid.replace('.png','')\n    new_path = os.path.join(IMG_DIR, split, pid, sid + '.png')\n    os.makedirs(new_path.rsplit('/',1)[0], exist_ok=True)\n    cv2.imwrite(new_path, img[...,::-1])\n    del img; gc.collect()\n    return \n\ndef show_img(img):\n    num_channels = img.shape[-1]\n    fig, axes = plt.subplots(1, num_channels+1, figsize=(num_channels*5, 5))\n    axes[0].imshow(img)\n    axes[0].set_title('Original Image')\n    axes[0].axis('off')\n\n    for i in range(num_channels):\n        axes[i+1].imshow(img[:, :, i], cmap='gray')\n        axes[i+1].set_title(f'Channel: {i:02d}')\n        axes[i+1].axis('off')\n\n    plt.tight_layout()\n    plt.show()","metadata":{"_kg_hide-input":true,"execution":{"iopub.status.busy":"2023-08-03T06:12:49.084015Z","iopub.execute_input":"2023-08-03T06:12:49.084758Z","iopub.status.idle":"2023-08-03T06:12:49.101774Z","shell.execute_reply.started":"2023-08-03T06:12:49.084719Z","shell.execute_reply":"2023-08-03T06:12:49.100841Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Data Check","metadata":{}},{"cell_type":"code","source":"test_df.dicom_paths.iloc[0]","metadata":{"execution":{"iopub.status.busy":"2023-08-03T05:48:14.845694Z","iopub.execute_input":"2023-08-03T05:48:14.846039Z","iopub.status.idle":"2023-08-03T05:48:14.854402Z","shell.execute_reply.started":"2023-08-03T05:48:14.846006Z","shell.execute_reply":"2023-08-03T05:48:14.853443Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"img = load_scan(test_df.dicom_paths.iloc[0])\nshow_img(img)","metadata":{"execution":{"iopub.status.busy":"2023-08-03T06:11:56.632815Z","iopub.execute_input":"2023-08-03T06:11:56.633385Z","iopub.status.idle":"2023-08-03T06:11:57.315242Z","shell.execute_reply.started":"2023-08-03T06:11:56.633352Z","shell.execute_reply":"2023-08-03T06:11:57.313197Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Conversion","metadata":{}},{"cell_type":"code","source":"%%time\nfrom joblib import Parallel, delayed\nfile_paths = test_df.dicom_paths.tolist()\n_ = Parallel(n_jobs=-1,backend='loky')(delayed(resize_and_save)(file_path)\\\n                                                  for file_path in tqdm(file_paths))\ndel _; gc.collect()","metadata":{"execution":{"iopub.status.busy":"2023-08-03T06:13:16.722095Z","iopub.execute_input":"2023-08-03T06:13:16.722719Z","iopub.status.idle":"2023-08-03T06:13:17.294868Z","shell.execute_reply.started":"2023-08-03T06:13:16.722687Z","shell.execute_reply":"2023-08-03T06:13:17.293927Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Check Image","metadata":{}},{"cell_type":"code","source":"img = load_img(f'{IMG_DIR}/test_images/50046/24574.png')\nshow_img(img)","metadata":{"execution":{"iopub.status.busy":"2023-08-03T06:13:18.751517Z","iopub.execute_input":"2023-08-03T06:13:18.751878Z","iopub.status.idle":"2023-08-03T06:13:19.421187Z","shell.execute_reply.started":"2023-08-03T06:13:18.751851Z","shell.execute_reply":"2023-08-03T06:13:19.420113Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"img = load_img(f'{IMG_DIR}/test_images/48843/62825.png')\nshow_img(img)","metadata":{"execution":{"iopub.status.busy":"2023-08-03T06:13:19.423115Z","iopub.execute_input":"2023-08-03T06:13:19.423563Z","iopub.status.idle":"2023-08-03T06:13:20.156916Z","shell.execute_reply.started":"2023-08-03T06:13:19.42353Z","shell.execute_reply":"2023-08-03T06:13:20.156065Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Data Pipeline","metadata":{}},{"cell_type":"code","source":"def build_decoder(with_labels=True, target_size=CFG.img_size, ext='png'):\n    def decode(path):\n        file_bytes = tf.io.read_file(path)\n        if ext == 'png':\n            img = tf.image.decode_png(file_bytes, channels=4, dtype=tf.uint8)\n        elif ext in ['jpg', 'jpeg']:\n            img = tf.image.decode_jpeg(file_bytes, channels=3)\n        else:\n            raise ValueError(\"Image extension not supported\")\n\n        img = tf.image.resize(img, target_size, method='bilinear')\n        img = tf.cast(img, tf.float32) / 255.0\n        img = tf.reshape(img, [*target_size, 4])\n\n        return img\n    \n    def decode_with_labels(path, label):\n        return decode(path), tf.cast(label, tf.float32)\n    \n    return decode_with_labels if with_labels else decode\n\n\ndef build_dataset(paths, labels=None, batch_size=32, cache=True,\n                  decode_fn=None, repeat=True, shuffle=1024, \n                  cache_dir=\"\", drop_remainder=False):\n    if cache_dir != \"\" and cache is True:\n        os.makedirs(cache_dir, exist_ok=True)\n    \n    if decode_fn is None:\n        decode_fn = build_decoder(labels is not None)\n    \n    AUTO = tf.data.experimental.AUTOTUNE\n    slices = paths if labels is None else (paths, labels)\n    \n    ds = tf.data.Dataset.from_tensor_slices(slices)\n    ds = ds.map(decode_fn, num_parallel_calls=AUTO)\n    ds = ds.cache(cache_dir) if cache else ds\n    ds = ds.repeat() if repeat else ds\n    if shuffle: \n        ds = ds.shuffle(shuffle, seed=CFG.seed)\n        opt = tf.data.Options()\n        opt.experimental_deterministic = False\n        ds = ds.with_options(opt)\n    ds = ds.batch(batch_size, drop_remainder=drop_remainder)\n    ds = ds.prefetch(AUTO)\n    return ds","metadata":{"_kg_hide-input":true,"execution":{"iopub.status.busy":"2023-08-03T05:48:16.76718Z","iopub.execute_input":"2023-08-03T05:48:16.767523Z","iopub.status.idle":"2023-08-03T05:48:16.786787Z","shell.execute_reply.started":"2023-08-03T05:48:16.767492Z","shell.execute_reply":"2023-08-03T05:48:16.785911Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Visualization","metadata":{}},{"cell_type":"code","source":"def display_batch(batch, size=2):\n    if isinstance(batch, tuple):\n        imgs, tars = batch\n        tars = tf.concat(tars,axis=-1).numpy()\n    else:\n        imgs = batch\n        tars = None\n    \n    plt.figure(figsize=(size*5, 10))\n    for img_idx in range(size):\n        plt.subplot(1, size, img_idx+1)\n        if tars is not None:\n            plt.title(f'{tars[img_idx].round(2)}', fontsize=12)\n        img = imgs[img_idx,]\n        plt.imshow(img)\n        plt.xticks([]); plt.yticks([])\n    plt.tight_layout()\n    plt.show() ","metadata":{"_kg_hide-input":true,"execution":{"iopub.status.busy":"2023-08-03T05:48:16.788064Z","iopub.execute_input":"2023-08-03T05:48:16.788689Z","iopub.status.idle":"2023-08-03T05:48:16.803574Z","shell.execute_reply.started":"2023-08-03T05:48:16.788658Z","shell.execute_reply":"2023-08-03T05:48:16.802277Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"fold_df = test_df.copy()\npaths  = fold_df.image_path.tolist()\nlabels = None\nds = build_dataset(paths, labels, cache=False, batch_size=32,\n                   repeat=True, shuffle=True)\nds = ds.unbatch().batch(20)\nbatch = next(iter(ds))\ndisplay_batch(batch, 5);","metadata":{"_kg_hide-input":true,"execution":{"iopub.status.busy":"2023-08-03T05:48:16.805064Z","iopub.execute_input":"2023-08-03T05:48:16.805412Z","iopub.status.idle":"2023-08-03T05:48:22.387493Z","shell.execute_reply.started":"2023-08-03T05:48:16.805381Z","shell.execute_reply":"2023-08-03T05:48:22.386588Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Model Configs","metadata":{}},{"cell_type":"code","source":"CKPT_DIRS = [\n    ([512, 512], '/kaggle/input/rsna-atd-25d-series-image-train-ds'),\n]\n\nMODEL_CONFIGS = []\nfor img_size, ckpt_dir in  CKPT_DIRS:\n    paths = sorted(glob(os.path.join(ckpt_dir, '*h5')))[0:CFG.num_folds]\n    if len(paths)==0:\n        print('no model found for :',base_dir)\n    MODEL_CONFIGS.append([img_size, paths])\ndisplay(MODEL_CONFIGS)","metadata":{"execution":{"iopub.status.busy":"2023-08-03T05:52:52.914366Z","iopub.execute_input":"2023-08-03T05:52:52.914781Z","iopub.status.idle":"2023-08-03T05:52:52.924717Z","shell.execute_reply.started":"2023-08-03T05:52:52.91475Z","shell.execute_reply":"2023-08-03T05:52:52.923687Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Utility","metadata":{}},{"cell_type":"code","source":"def mc_proc(pred):\n    argmax = np.argmax(pred, axis=1).astype('uint8')\n    one_hot = tf.keras.utils.to_categorical(argmax, num_classes=3)\n    return one_hot.astype('uint8')\n\ndef sc_proc(pred, thr=0.5):\n    proc_pred = (pred > thr).astype('uint8')\n    return proc_pred\n\ndef post_proc(pred):\n    proc_pred = np.empty((pred.shape[0], 2 + 2 + 3*3), dtype=np.uint8)\n\n    # bowel, extravasation\n    proc_pred[:, 0] = sc_proc(pred[:, 0])\n    proc_pred[:, 1] = 1 - proc_pred[:, 0]\n    proc_pred[:, 2] = sc_proc(pred[:, 1])\n    proc_pred[:, 3] = 1 - proc_pred[:, 2]\n    \n    # liver, kidney, sneel\n    proc_pred[:, 4:7] = mc_proc(pred[:, 2:5])\n    proc_pred[:, 7:10] = mc_proc(pred[:, 5:8])\n    proc_pred[:, 10:13] = mc_proc(pred[:, 8:11])\n\n    return proc_pred\n\ndef post_proc_v2(pred):\n    proc_pred = np.empty((pred.shape[0], 2*2 + 3*3), dtype='float32')\n\n    # bowel, extravasation\n    proc_pred[:, 0] = 1 - pred[:, 0] # bowel-healthy\n    proc_pred[:, 1] = pred[:, 0] # bowel-injured\n    proc_pred[:, 2] = 1 - pred[:, 1] # extra-healthy\n    proc_pred[:, 3] = pred[:, 1] # extra-injured\n    \n    # liver, kidney, sneel\n    proc_pred[:, 4:7] = pred[:, 2:5]\n    proc_pred[:, 7:10] = pred[:, 5:8]\n    proc_pred[:, 10:13] = pred[:, 8:11]\n\n    return proc_pred","metadata":{"execution":{"iopub.status.busy":"2023-08-03T05:52:54.963538Z","iopub.execute_input":"2023-08-03T05:52:54.963897Z","iopub.status.idle":"2023-08-03T05:52:54.977066Z","shell.execute_reply.started":"2023-08-03T05:52:54.963869Z","shell.execute_reply":"2023-08-03T05:52:54.976101Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Inference","metadata":{}},{"cell_type":"code","source":"# Getting unique patient IDs from test dataset\npatient_ids = test_df['patient_id'].unique()\n\n# Initializing array to store predictions\npatient_preds = np.zeros(shape=(len(patient_ids), 2*2 + 3*3), dtype='float32')\n\n# Iterating over each patient\nfor pidx, patient_id in tqdm(enumerate(patient_ids), total=len(patient_ids), desc=\"Patients \"):\n    # Query the dataframe for a particular patient\n    patient_df = test_df.query(\"patient_id == @patient_id\")\n    \n    # Initializing model predictions array\n    model_preds = np.zeros(shape=(1, 11), dtype=np.float32)\n    \n    print(\"=\"*25)\n    print(f\"   Patient ID: {patient_id}\")\n    print(\"=\"*25)\n    \n    # Iterating over each model\n    for midx, (img_size, fold_paths) in enumerate(MODEL_CONFIGS):\n        \n        # Getting image paths for a patient\n        patient_paths = patient_df.image_path.tolist()\n\n        # Setting batch size based on number of patient paths and dimension of image\n        dim = np.prod(img_size)**0.5\n        CFG.batch_size = REPLICAS * int(32 * 2)\n            \n        # Clip batch_sizs to min\n        min_bs = 2**np.floor(np.log2(len(patient_paths)))\n        CFG.batch_size = min(min_bs, CFG.batch_size)\n\n        # Building dataset for prediction\n        dtest = build_dataset(\n            patient_paths, \n            batch_size=CFG.batch_size, repeat=True, \n            shuffle=False, cache=False,\n            decode_fn=build_decoder(with_labels=False, target_size=img_size),\n        )\n        \n        # Iterating over each fold\n        for fold_path in fold_paths:\n            with strategy.scope():\n                # Loading a model from a fold path\n                model = tf.keras.models.load_model(fold_path, compile=False)\n            \n            # Predicting with the model\n            pred = model.predict(dtest, steps = len(patient_paths) / CFG.batch_size, verbose=1)\n            pred = np.concatenate(pred, axis=-1).astype('float32') # reducing memory footprint\n            pred = pred[:len(patient_paths), :]\n            pred = pred.reshape(len(patient_paths), 11)\n            pred = np.max(pred, axis=0) # taking max prediction of all ct scans for a patient\n            \n            # Store model's prediction\n            model_preds += pred / (len(fold_paths)*len(MODEL_CONFIGS))\n            \n            # Deleting variables to free up memory\n            del model, pred; gc.collect()\n            \n            print('\\n')\n        \n        del dtest, patient_paths; gc.collect()\n            \n    # Adding processed predictions to patient_preds\n    patient_preds[pidx, :] += post_proc_v2(model_preds)[0]\n    \n    del model_preds; gc.collect()\n\nprint(\"Prediction Done!\")","metadata":{"execution":{"iopub.status.busy":"2023-08-03T05:52:56.167932Z","iopub.execute_input":"2023-08-03T05:52:56.168326Z","iopub.status.idle":"2023-08-03T05:53:16.158422Z","shell.execute_reply.started":"2023-08-03T05:52:56.168294Z","shell.execute_reply":"2023-08-03T05:53:16.157437Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Submission","metadata":{}},{"cell_type":"code","source":"# Create Submission\npred_df = pd.DataFrame({'patient_id':patient_ids,})\npred_df[CFG.target_col] = patient_preds.astype('float32')\n\n# Align with sample submission\nsub_df = pd.read_csv(f'{BASE_PATH}/sample_submission.csv')\nsub_df = sub_df[['patient_id']]\nsub_df = sub_df.merge(pred_df, on='patient_id', how='left')\n\n# Store submission\nsub_df.to_csv('submission.csv',index=False)\nsub_df.head()","metadata":{"execution":{"iopub.status.busy":"2023-08-03T05:53:30.657816Z","iopub.execute_input":"2023-08-03T05:53:30.658212Z","iopub.status.idle":"2023-08-03T05:53:30.704962Z","shell.execute_reply.started":"2023-08-03T05:53:30.658173Z","shell.execute_reply":"2023-08-03T05:53:30.704078Z"},"trusted":true},"execution_count":null,"outputs":[]}]}