{"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* In this notebook will do inference for `Detect and classify traumatic abdominal injuries` from CT scans.\n* Dicom images are saved to `512 x 512` without keeping aspect ratio. Modify `resize_dim` in config file to change them.\n* Batch Size is chosen adaptly depending on the image_size, you can change it on the `inference` cell\n* You can play with notebook by simply changing `tta` parameter in config.","metadata":{}},{"cell_type":"markdown","source":"# Notebooks\n* CNN:\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:\n* `v14` : Correction for prediction formatting. Order of `healthy` and `injured` was reversed. Thanks to [@coderrkj](https://www.kaggle.com/code/coderrkj/rsna-atd-cnn-tpu-infer-corrections) for noticing that.\n* `v12` : Same as `v10` but with `float32` in prediction\n* `v11` : Two models, no thresholding\n* `v10` : One model, no threhsolding (LB: 0.81)\n* `v09` : One model, thresholding for both single and multiclass (LB: 12+)","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-07-28T12:44:22.126189Z","iopub.execute_input":"2023-07-28T12:44:22.126624Z","iopub.status.idle":"2023-07-28T12:46:41.146238Z","shell.execute_reply.started":"2023-07-28T12:44:22.126589Z","shell.execute_reply":"2023-07-28T12:46:41.144633Z"},"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":{"execution":{"iopub.status.busy":"2023-07-28T12:46:41.153485Z","iopub.execute_input":"2023-07-28T12:46:41.156364Z","iopub.status.idle":"2023-07-28T12:46:52.637347Z","shell.execute_reply.started":"2023-07-28T12:46:41.156319Z","shell.execute_reply":"2023-07-28T12:46:52.636159Z"},"_kg_hide-output":true,"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-07-28T12:46:52.642057Z","iopub.execute_input":"2023-07-28T12:46:52.642837Z","iopub.status.idle":"2023-07-28T12:46:52.651658Z","shell.execute_reply.started":"2023-07-28T12:46:52.642804Z","shell.execute_reply":"2023-07-28T12:46:52.649179Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Configuration","metadata":{}},{"cell_type":"code","source":"class CFG:\n    debug         = False\n    \n    # use verbose=0 for silent, vebose=1 for interactive,\n    verbose      = 1 if debug else 0\n    \n    # device\n    device = \"GPU\" # or \"GPU\"\n\n    # seed for data-split, layer init, augs\n    seed = 42\n    \n    # which folds of ckpt to use\n    num_folds = 1\n\n    # dicom to png size\n    resize_dim = 512\n    \n    # size of training image\n    img_size = [512, 512]\n\n    # augmentation\n    augment   = True\n\n    # scale-shift-rotate-shear\n    transform = False\n    fill_mode = 'constant'\n    rot    = 2.0\n    shr    = 2.0\n    hzoom  = 50.0\n    wzoom  = 50.0\n    hshift = 10.0\n    wshift = 10.0\n\n    # flip\n    hflip = False\n    vflip = False\n\n    # clip\n    clip = False\n\n    # lr-scheduler\n    scheduler   = 'exp' # cosine\n\n    # dropout\n    drop_prob   = 0.0\n    drop_cnt    = 10\n    drop_size   = 0.08\n\n    # pixel-augment\n    pixel_aug = False\n    sat  = [0.7, 1.3]\n    cont = [0.8, 1.2]\n    bri  = 0.15\n    hue  = 0.05\n\n    # threshold\n    thr = 0.50\n    \n    # test-time augs\n    flip_model = False\n    tta = 1\n    \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-07-28T12:46:52.658267Z","iopub.execute_input":"2023-07-28T12:46:52.659754Z","iopub.status.idle":"2023-07-28T12:46:52.683992Z","shell.execute_reply.started":"2023-07-28T12:46:52.658698Z","shell.execute_reply":"2023-07-28T12:46:52.682528Z"},"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-07-28T12:46:52.690843Z","iopub.execute_input":"2023-07-28T12:46:52.691619Z","iopub.status.idle":"2023-07-28T12:46:52.703156Z","shell.execute_reply.started":"2023-07-28T12:46:52.691576Z","shell.execute_reply":"2023-07-28T12:46:52.701359Z"},"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-07-28T12:46:52.70563Z","iopub.execute_input":"2023-07-28T12:46:52.706125Z","iopub.status.idle":"2023-07-28T12:46:52.972964Z","shell.execute_reply.started":"2023-07-28T12:46:52.706087Z","shell.execute_reply":"2023-07-28T12:46:52.971882Z"},"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'\nSTRIDE = 10 # take one patient after each (STRIDE - 1) patients","metadata":{"execution":{"iopub.status.busy":"2023-07-28T12:46:52.975555Z","iopub.execute_input":"2023-07-28T12:46:52.976274Z","iopub.status.idle":"2023-07-28T12:46:52.983795Z","shell.execute_reply.started":"2023-07-28T12:46:52.976235Z","shell.execute_reply":"2023-07-28T12:46:52.982596Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Test Paths","metadata":{}},{"cell_type":"code","source":"meta_df = pd.read_csv(f'{BASE_PATH}/test_series_meta.csv')\nmeta_df['dicom_folder'] = BASE_PATH + '/' + 'test_images/'\\\n                                    + '/' + meta_df.patient_id.astype(str)\\\n                                    + '/' + meta_df.series_id.astype(str)\ntest_folders = meta_df.dicom_folder.tolist()\n\ntest_paths = []\nfor folder in tqdm(test_folders):\n    test_paths += sorted(glob(os.path.join(folder, '*dcm')))[::STRIDE]","metadata":{"execution":{"iopub.status.busy":"2023-07-28T12:46:56.274449Z","iopub.execute_input":"2023-07-28T12:46:56.274907Z","iopub.status.idle":"2023-07-28T12:46:56.330006Z","shell.execute_reply.started":"2023-07-28T12:46:56.274873Z","shell.execute_reply":"2023-07-28T12:46:56.328838Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Test Dataframe","metadata":{}},{"cell_type":"code","source":"test_df = pd.DataFrame(test_paths, columns=[\"dicom_path\"])\ntest_df['patient_id'] = test_df.dicom_path.map(lambda x: x.split('/')[-3]).astype(int)\ntest_df['series_id'] = test_df.dicom_path.map(lambda x: x.split('/')[-2]).astype(int)\ntest_df['instance_number'] = test_df.dicom_path.map(lambda x: x.split('/')[-1].replace('.dcm','')).astype(int)\n\ntest_df['image_path'] = f'{IMG_DIR}/test_images'\\\n                    + '/' + test_df.patient_id.astype(str)\\\n                    + '/' + test_df.series_id.astype(str)\\\n                    + '/' + test_df.instance_number.astype(str) +'.png'\n\nprint('Test:')\nprint(f'# Size: {len(test_df)}')\ndisplay(test_df.head())","metadata":{"execution":{"iopub.status.busy":"2023-07-28T12:46:57.450773Z","iopub.execute_input":"2023-07-28T12:46:57.451692Z","iopub.status.idle":"2023-07-28T12:46:57.476574Z","shell.execute_reply.started":"2023-07-28T12:46:57.451651Z","shell.execute_reply":"2023-07-28T12:46:57.475347Z"},"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_path.iloc[0])","metadata":{"execution":{"iopub.status.busy":"2023-07-28T12:47:01.546448Z","iopub.execute_input":"2023-07-28T12:47:01.546882Z","iopub.status.idle":"2023-07-28T12:47:01.555943Z","shell.execute_reply.started":"2023-07-28T12:47:01.546848Z","shell.execute_reply":"2023-07-28T12:47:01.554623Z"},"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-07-28T12:47:04.224455Z","iopub.execute_input":"2023-07-28T12:47:04.225601Z","iopub.status.idle":"2023-07-28T12:47:04.232222Z","shell.execute_reply.started":"2023-07-28T12:47:04.22556Z","shell.execute_reply":"2023-07-28T12:47:04.230849Z"},"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\nos.makedirs('/tmp/dataset/rsna-atd/train_images', exist_ok = True)\nos.makedirs('/tmp/dataset/rsna-atd/test_images', exist_ok = True)","metadata":{"_kg_hide-input":true,"execution":{"iopub.status.busy":"2023-07-28T12:47:05.859273Z","iopub.execute_input":"2023-07-28T12:47:05.859698Z","iopub.status.idle":"2023-07-28T12:47:06.93169Z","shell.execute_reply.started":"2023-07-28T12:47:05.859664Z","shell.execute_reply":"2023-07-28T12:47:06.930289Z"},"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\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    return data\n\ndef resize_and_save(file_path):\n    img = read_xray(file_path)\n    h, w = img.shape[:2]  # orig hw\n    img = cv2.resize(img, (CFG.resize_dim, CFG.resize_dim), cv2.INTER_LINEAR)\n    img = (img * 255).astype(np.uint8)\n    \n    sub_path = file_path.split(\"/\",4)[-1].split('.dcm')[0] + '.png'\n    infos = sub_path.split('/')\n    sub_path = file_path.split(\"/\",4)[-1].split('.dcm')[0] + '.png'\n    infos = sub_path.split('/')\n    pid = infos[-3]\n    sid = infos[-2]\n    iid = infos[-1]; iid = iid.replace('.png','')\n    new_path = os.path.join(IMG_DIR, sub_path)\n    os.makedirs(new_path.rsplit('/',1)[0], exist_ok=True)\n    cv2.imwrite(new_path, img)\n    return ","metadata":{"_kg_hide-input":true,"execution":{"iopub.status.busy":"2023-07-28T12:47:07.815951Z","iopub.execute_input":"2023-07-28T12:47:07.818826Z","iopub.status.idle":"2023-07-28T12:47:08.0414Z","shell.execute_reply.started":"2023-07-28T12:47:07.818789Z","shell.execute_reply":"2023-07-28T12:47:08.040257Z"},"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_path.tolist()\n_ = Parallel(n_jobs=2,backend='threading')(delayed(resize_and_save)(file_path)\\\n                                                  for file_path in tqdm(file_paths, leave=True, position=0))\ndel _; gc.collect()","metadata":{"execution":{"iopub.status.busy":"2023-07-28T12:47:09.033497Z","iopub.execute_input":"2023-07-28T12:47:09.034435Z","iopub.status.idle":"2023-07-28T12:47:09.161838Z","shell.execute_reply.started":"2023-07-28T12:47:09.034393Z","shell.execute_reply":"2023-07-28T12:47:09.160712Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Data Augmentation\nUsed simple augmentations, some of them may hurt the model.\n* RandomFlip (Left-Right)\n* No Rotation\n* RandomBrightness\n* RndomContrast\n* Shear\n* Zoom\n* Coarsee Dropout/Cutout","metadata":{}},{"cell_type":"code","source":"def get_mat(shear, height_zoom, width_zoom, height_shift, width_shift):\n    # returns 3x3 transformmatrix which transforms indicies\n        \n    # CONVERT DEGREES TO RADIANS\n    #rotation = math.pi * rotation / 180.\n    shear    = math.pi * shear    / 180.\n\n    def get_3x3_mat(lst):\n        return tf.reshape(tf.concat([lst],axis=0), [3,3])\n    \n    # ROTATION MATRIX\n#     c1   = tf.math.cos(rotation)\n#     s1   = tf.math.sin(rotation)\n    one  = tf.constant([1],dtype='float32')\n    zero = tf.constant([0],dtype='float32')\n    \n#     rotation_matrix = get_3x3_mat([c1,   s1,   zero, \n#                                    -s1,  c1,   zero, \n#                                    zero, zero, one])    \n    # SHEAR MATRIX\n    c2 = tf.math.cos(shear)\n    s2 = tf.math.sin(shear)    \n    \n    shear_matrix = get_3x3_mat([one,  s2,   zero, \n                               zero, c2,   zero, \n                                zero, zero, one])        \n    # ZOOM MATRIX\n    zoom_matrix = get_3x3_mat([one/height_zoom, zero,           zero, \n                               zero,            one/width_zoom, zero, \n                               zero,            zero,           one])    \n    # SHIFT MATRIX\n    shift_matrix = get_3x3_mat([one,  zero, height_shift, \n                                zero, one,  width_shift, \n                                zero, zero, one])\n    \n\n    return  K.dot(shear_matrix,K.dot(zoom_matrix, shift_matrix)) #K.dot(K.dot(rotation_matrix, shear_matrix), K.dot(zoom_matrix, shift_matrix))                  \n\ndef transform(image, DIM=CFG.img_size):#[rot,shr,h_zoom,w_zoom,h_shift,w_shift]):\n    if DIM[0]>DIM[1]:\n        diff  = (DIM[0]-DIM[1])\n        pad   = [diff//2, diff//2 + diff%2]\n        image = tf.pad(image, [[0, 0], [pad[0], pad[1]],[0, 0]])\n        NEW_DIM = DIM[0]\n    elif DIM[0]<DIM[1]:\n        diff  = (DIM[1]-DIM[0])\n        pad   = [diff//2, diff//2 + diff%2]\n        image = tf.pad(image, [[pad[0], pad[1]], [0, 0],[0, 0]])\n        NEW_DIM = DIM[1]\n    \n    rot = CFG.rot * tf.random.normal([1], dtype='float32')\n    shr = CFG.shr * tf.random.normal([1], dtype='float32') \n    h_zoom = 1.0 + tf.random.normal([1], dtype='float32') / CFG.hzoom\n    w_zoom = 1.0 + tf.random.normal([1], dtype='float32') / CFG.wzoom\n    h_shift = CFG.hshift * tf.random.normal([1], dtype='float32') \n    w_shift = CFG.wshift * tf.random.normal([1], dtype='float32') \n    \n    transformation_matrix=tf.linalg.inv(get_mat(shr,h_zoom,w_zoom,h_shift,w_shift))\n    \n    flat_tensor=tfa.image.transform_ops.matrices_to_flat_transforms(transformation_matrix)\n    \n    image=tfa.image.transform(image,flat_tensor, fill_mode=CFG.fill_mode)\n    \n    rotation = math.pi * rot / 180.\n    \n    image=tfa.image.rotate(image,-rotation, fill_mode=CFG.fill_mode)\n    \n    if DIM[0]>DIM[1]:\n        image=tf.reshape(image, [NEW_DIM, NEW_DIM,3])\n        image = image[:, pad[0]:-pad[1],:]\n    elif DIM[1]>DIM[0]:\n        image=tf.reshape(image, [NEW_DIM, NEW_DIM,3])\n        image = image[pad[0]:-pad[1],:,:]\n    image = tf.reshape(image, [*DIM, 3])    \n    return image\n\ndef dropout(image,DIM=CFG.img_size, PROBABILITY = 0.6, CT = 5, SZ = 0.1):\n    # input image - is one image of size [dim,dim,3] not a batch of [b,dim,dim,3]\n    # output - image with CT squares of side size SZ*DIM removed\n    \n    # DO DROPOUT WITH PROBABILITY DEFINED ABOVE\n    P = tf.cast( tf.random.uniform([],0,1)<PROBABILITY, tf.int32)\n    if (P==0)|(CT==0)|(SZ==0): \n        return image\n    \n    for k in range(CT):\n        # CHOOSE RANDOM LOCATION\n        x = tf.cast( tf.random.uniform([],0,DIM[1]),tf.int32)\n        y = tf.cast( tf.random.uniform([],0,DIM[0]),tf.int32)\n        # COMPUTE SQUARE \n        WIDTH = tf.cast( SZ*min(DIM),tf.int32) * P\n        ya = tf.math.maximum(0,y-WIDTH//2)\n        yb = tf.math.minimum(DIM[0],y+WIDTH//2)\n        xa = tf.math.maximum(0,x-WIDTH//2)\n        xb = tf.math.minimum(DIM[1],x+WIDTH//2)\n        # DROPOUT IMAGE\n        one = image[ya:yb,0:xa,:]\n        two = tf.zeros([yb-ya,xb-xa,3], dtype = image.dtype) \n        three = image[ya:yb,xb:DIM[1],:]\n        middle = tf.concat([one,two,three],axis=1)\n        image = tf.concat([image[0:ya,:,:],middle,image[yb:DIM[0],:,:]],axis=0)\n        image = tf.reshape(image,[*DIM,3])\n\n#     image = tf.reshape(image,[*DIM,3])\n    return image","metadata":{"_kg_hide-input":true,"execution":{"iopub.status.busy":"2023-07-28T12:47:12.234768Z","iopub.execute_input":"2023-07-28T12:47:12.235125Z","iopub.status.idle":"2023-07-28T12:47:12.264239Z","shell.execute_reply.started":"2023-07-28T12:47:12.235095Z","shell.execute_reply":"2023-07-28T12:47:12.262935Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Data Pipeline\n* Reads the raw file and then decodes it to tf.Tensor\n* Resizes the image in desired size\n* Chages the datatype to **float32**\n* Caches the Data for boosting up the speed.\n* Uses Augmentations to reduce overfitting and make model more robust.\n* Finally, splits the data into batches.\n","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=3, 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, 3])\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_augmenter(with_labels=True, dim=CFG.img_size):\n    def augment(img, dim=dim):\n        img = transform(img,DIM=dim) if CFG.transform else img\n        img = tf.image.random_flip_left_right(img) if CFG.hflip else img\n        img = tf.image.random_flip_up_down(img) if CFG.vflip else img\n        if CFG.pixel_aug:\n            img = tf.image.random_hue(img, CFG.hue)\n            img = tf.image.random_saturation(img, CFG.sat[0], CFG.sat[1])\n            img = tf.image.random_contrast(img, CFG.cont[0], CFG.cont[1])\n            img = tf.image.random_brightness(img, CFG.bri)\n        img = dropout(img, DIM=dim, PROBABILITY = CFG.drop_prob, CT = CFG.drop_cnt, SZ = CFG.drop_size)\n        img = tf.clip_by_value(img, 0, 1)  if CFG.clip else img         \n        img = tf.reshape(img, [*dim, 3])\n        return img\n    \n    def augment_with_labels(img, label):    \n        return augment(img), label\n    \n    return augment_with_labels if with_labels else augment\n\n\ndef build_dataset(paths, labels=None, batch_size=32, cache=True,\n                  decode_fn=None, augment_fn=None,\n                  augment=True, 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    if augment_fn is None:\n        augment_fn = build_augmenter(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.map(augment_fn, num_parallel_calls=AUTO) if augment else ds\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-07-28T12:47:13.052891Z","iopub.execute_input":"2023-07-28T12:47:13.053318Z","iopub.status.idle":"2023-07-28T12:47:13.077128Z","shell.execute_reply.started":"2023-07-28T12:47:13.053283Z","shell.execute_reply":"2023-07-28T12:47:13.075853Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Visualization\n* Check if augmentation is working properly or not.","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-07-28T12:47:13.638687Z","iopub.execute_input":"2023-07-28T12:47:13.641297Z","iopub.status.idle":"2023-07-28T12:47:13.652452Z","shell.execute_reply.started":"2023-07-28T12:47:13.641262Z","shell.execute_reply":"2023-07-28T12:47:13.648284Z"},"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, augment=True)\nds = ds.unbatch().batch(20)\nbatch = next(iter(ds))\ndisplay_batch(batch, 5);","metadata":{"_kg_hide-input":true,"execution":{"iopub.status.busy":"2023-07-28T12:47:21.514852Z","iopub.execute_input":"2023-07-28T12:47:21.515275Z","iopub.status.idle":"2023-07-28T12:47:28.034067Z","shell.execute_reply.started":"2023-07-28T12:47:21.515242Z","shell.execute_reply":"2023-07-28T12:47:28.03232Z"},"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-cnn-tputrain-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-07-28T12:47:32.435888Z","iopub.execute_input":"2023-07-28T12:47:32.436315Z","iopub.status.idle":"2023-07-28T12:47:32.459527Z","shell.execute_reply.started":"2023-07-28T12:47:32.436284Z","shell.execute_reply":"2023-07-28T12:47:32.458404Z"},"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-07-28T12:47:33.235696Z","iopub.execute_input":"2023-07-28T12:47:33.236509Z","iopub.status.idle":"2023-07-28T12:47:33.246705Z","shell.execute_reply.started":"2023-07-28T12:47:33.236436Z","shell.execute_reply":"2023-07-28T12:47:33.245229Z"},"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        if dim >= 1024:\n            CFG.batch_size = REPLICAS * int(4 * 2)\n        elif dim >= 768:\n            CFG.batch_size = REPLICAS * int(16 * 2)\n        elif dim >= 640:\n            CFG.batch_size = REPLICAS * int(28 * 2)\n        else:\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, augment=CFG.tta > 1, cache=False,\n            decode_fn=build_decoder(with_labels=False, target_size=img_size),\n            augment_fn=build_augmenter(with_labels=False, dim=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 = CFG.tta * len(patient_paths) / CFG.batch_size, verbose=1)\n            pred = np.concatenate(pred, axis=-1).astype('float32') # reducing memory footprint\n            pred = pred[:CFG.tta * len(patient_paths), :]\n            pred = np.mean(pred.reshape(CFG.tta, len(patient_paths), 11), axis=0)\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-07-28T13:09:03.792961Z","iopub.execute_input":"2023-07-28T13:09:03.793397Z","iopub.status.idle":"2023-07-28T13:09:17.552976Z","shell.execute_reply.started":"2023-07-28T13:09:03.793345Z","shell.execute_reply":"2023-07-28T13:09:17.551671Z"},"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-07-28T13:09:26.430607Z","iopub.execute_input":"2023-07-28T13:09:26.431019Z","iopub.status.idle":"2023-07-28T13:09:26.478091Z","shell.execute_reply.started":"2023-07-28T13:09:26.430985Z","shell.execute_reply":"2023-07-28T13:09:26.476544Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"","metadata":{},"execution_count":null,"outputs":[]}]}