{"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":"Use model trained with the notebook below.\n\nhttps://www.kaggle.com/code/harsha1999/rsna-2-5d-cnn-training-pytorch","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","metadata":{"execution":{"iopub.status.busy":"2023-10-04T04:28:21.846259Z","iopub.execute_input":"2023-10-04T04:28:21.846517Z","iopub.status.idle":"2023-10-04T04:29:29.94479Z","shell.execute_reply.started":"2023-10-04T04:28:21.846493Z","shell.execute_reply":"2023-10-04T04:29:29.943575Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import os\nimport random\nimport pickle\nimport gc\nfrom pathlib import Path\n\nimport cv2\nfrom glob import glob\nimport gdcm\nimport pydicom\nimport zipfile\nimport numpy as np\nimport pandas as pd\nimport seaborn as sns\nimport matplotlib.pyplot as plt\nfrom joblib import Parallel, delayed\nfrom pydicom.pixel_data_handlers.util import apply_voi_lut\nfrom tqdm.notebook import tqdm\nfrom tabulate import tabulate\nfrom sklearn.model_selection import KFold, StratifiedKFold\nfrom sklearn.metrics import accuracy_score, roc_auc_score\n\nimport torch\nimport torch.nn as nn\nimport torch.nn.functional as F\nfrom torch.utils.data import Dataset, DataLoader, Subset\nfrom torch.optim.lr_scheduler import ReduceLROnPlateau\nfrom torch.optim import Adam\nimport torchvision\nfrom torchvision import models\nfrom torchvision.transforms.v2 import Resize, Compose, RandomHorizontalFlip, ColorJitter, RandomAffine, RandomErasing, ToTensor\nimport torchvision.transforms.v2 as t","metadata":{"execution":{"iopub.status.busy":"2023-10-04T04:29:29.946906Z","iopub.execute_input":"2023-10-04T04:29:29.947483Z","iopub.status.idle":"2023-10-04T04:29:34.36649Z","shell.execute_reply.started":"2023-10-04T04:29:29.947447Z","shell.execute_reply":"2023-10-04T04:29:34.365541Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def standardize_pixel_array(dcm: pydicom.dataset.FileDataset) -> np.ndarray:\n    pixel_array = dcm.pixel_array\n    if dcm.PixelRepresentation == 1:\n        bit_shift = dcm.BitsAllocated - dcm.BitsStored\n        dtype = pixel_array.dtype \n        pixel_array = (pixel_array << bit_shift).astype(dtype) >>  bit_shift\n\n    intercept = float(dcm.RescaleIntercept)\n    slope = float(dcm.RescaleSlope)\n    center = int(dcm.WindowCenter)\n    width = int(dcm.WindowWidth)\n    low = center - width / 2\n    high = center + width / 2    \n    \n    pixel_array = (pixel_array * slope) + intercept\n    pixel_array = np.clip(pixel_array, low, high)\n\n    return pixel_array\n\ndef select_elements_with_spacing(input_list, spacing):        \n    lower_bound = int(len(input_list) * 0.4)\n    upper_bound = int(len(input_list) * 0.6)\n\n    spacing = (upper_bound - lower_bound) // 3\n    \n    selected_indices = [\n        lower_bound, \n        lower_bound + spacing, \n        lower_bound + (2 * spacing), \n        upper_bound\n    ]\n    selected_elements = [input_list[index] for index in selected_indices]\n    \n    return selected_elements\n\ndef standardize_pixel_array(dicom_image):\n    pixel_array = dicom_image.pixel_array\n    \n    if dicom_image.PixelRepresentation == 1:\n        bit_shift = dicom_image.BitsAllocated - dicom_image.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, dicom_image)\n\n    if dicom_image.PhotometricInterpretation == \"MONOCHROME1\":\n        pixel_array = 1 - pixel_array\n\n    # transform to hounsfield units\n    intercept = dicom_image.RescaleIntercept\n    slope = dicom_image.RescaleSlope\n    pixel_array = pixel_array * slope + intercept\n\n    # windowing\n    window_center = int(dicom_image.WindowCenter)\n    window_width = int(dicom_image.WindowWidth)\n    img_min = window_center - window_width // 2\n    img_max = window_center + window_width // 2\n    pixel_array = pixel_array.copy()\n    pixel_array[pixel_array < img_min] = img_min\n    pixel_array[pixel_array > img_max] = img_max\n\n    # normalization\n    if pixel_array.max() == pixel_array.min():\n        pixel_array = np.zeros_like(pixel_array)  # Handle case of constant array\n    else:\n        pixel_array = (pixel_array - pixel_array.min()) / (pixel_array.max() - pixel_array.min())\n\n    return pixel_array\n\ndef preprocess_jpeg(jpeg_path):\n    img = cv2.imread(jpeg_path)\n    greyscale = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)/255\n    return greyscale\n    \nclass MetricsCalculator:\n    def __init__(self, mode='binary'):\n        self.probabilities = []\n        self.predictions = []\n        self.targets = []\n        self.mode = mode\n    \n    def update(self, logits, target):\n        if self.mode == 'binary':\n            probabilities = torch.sigmoid(logits)\n            predicted = (probabilities > 0.5)\n        else:\n            probabilities = F.softmax(logits, dim = 1)\n            predicted = torch.argmax(probabilities, dim=1)\n            \n        self.probabilities.extend(probabilities.detach().cpu().numpy())\n        self.predictions.extend(predicted.detach().cpu().numpy())\n        self.targets.extend(target.detach().cpu().numpy())\n    \n    def reset(self):\n        self.probabilities = []\n        self.predictions = []\n        self.targets = []\n    \n    def compute_accuracy(self):\n        return accuracy_score(self.targets, self.predictions)\n    \n    def compute_auc(self):\n        if self.mode == 'multi':\n            return roc_auc_score(self.targets, self.probabilities, multi_class = 'ovo', labels=[0, 1, 2])\n    \n        else:\n            return roc_auc_score(self.targets, self.probabilities)","metadata":{"execution":{"iopub.status.busy":"2023-10-04T04:29:34.367951Z","iopub.execute_input":"2023-10-04T04:29:34.368466Z","iopub.status.idle":"2023-10-04T04:29:34.383523Z","shell.execute_reply.started":"2023-10-04T04:29:34.368434Z","shell.execute_reply":"2023-10-04T04:29:34.382676Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"USE_GPU = True\n\nif USE_GPU and torch.cuda.is_available():\n    device = 'cuda'\nelse:\n    device = 'cpu'","metadata":{"execution":{"iopub.status.busy":"2023-10-04T04:29:34.386386Z","iopub.execute_input":"2023-10-04T04:29:34.387066Z","iopub.status.idle":"2023-10-04T04:29:34.428128Z","shell.execute_reply.started":"2023-10-04T04:29:34.386995Z","shell.execute_reply":"2023-10-04T04:29:34.427072Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"BASE_PATH = \"/kaggle/input/rsna-2023-abdominal-trauma-detection\"\ntest_image_dirpath = \"/tmp/rsna-2023-test-images-256\"\ntest_meta_filepath = f\"{BASE_PATH}/test_series_meta.csv\"\ntest_dcm_dirpath = f\"{BASE_PATH}/test_images\"","metadata":{"execution":{"iopub.status.busy":"2023-10-04T04:29:34.42932Z","iopub.execute_input":"2023-10-04T04:29:34.429651Z","iopub.status.idle":"2023-10-04T04:29:34.445305Z","shell.execute_reply.started":"2023-10-04T04:29:34.429621Z","shell.execute_reply":"2023-10-04T04:29:34.444457Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# ready dataframe\nTARGET_COLS  = [\n    \"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\"\n]\nmeta_df = pd.read_csv(test_meta_filepath)\nnum_rows = meta_df.shape[0]\nunique_patients = meta_df[\"patient_id\"].nunique()\nmeta_df[\"dicom_folder\"] = test_dcm_dirpath + \"/\" + meta_df.patient_id.astype(str) + \"/\" + meta_df.series_id.astype(str)\n\ntest_folders = meta_df.dicom_folder.tolist()\ntest_dcm_filepaths = []\nfor folder in tqdm(test_folders):\n    dcm_filepaths = sorted(glob(os.path.join(folder, \"*dcm\")))\n    if len(dcm_filepaths) <= 4:\n        pass\n    elif len(dcm_filepaths) == 5:\n        dcm_filepaths = [dcm_filepaths[0], dcm_filepaths[1], dcm_filepaths[3], dcm_filepaths[4]]\n    elif len(dcm_filepaths) == 6:\n        dcm_filepaths = [dcm_filepaths[0], dcm_filepaths[2], dcm_filepaths[3], dcm_filepaths[5]]\n    elif len(dcm_filepaths) == 7:\n        dcm_filepaths = [dcm_filepaths[0], dcm_filepaths[2], dcm_filepaths[4], dcm_filepaths[6]]\n    elif len(dcm_filepaths) >= 8:\n        dcm_filepaths = select_elements_with_spacing(dcm_filepaths, spacing=2)\n    test_dcm_filepaths += dcm_filepaths\n\ntest_df = pd.DataFrame(test_dcm_filepaths, columns=[\"dicom_filepath\"])\ntest_df[\"patient_id\"] = test_df.dicom_filepath.map(lambda x: x.split(\"/\")[-3]).astype(int)\ntest_df[\"series_id\"] = test_df.dicom_filepath.map(lambda x: x.split(\"/\")[-2]).astype(int)\ntest_df[\"instance_number\"] = test_df.dicom_filepath.map(lambda x: x.split(\"/\")[-1].replace(\".dcm\",\"\")).astype(int)\ntest_df[\"image_filepath\"] = f\"{test_image_dirpath}\"\\\n                    + \"/\" + test_df.patient_id.astype(str)\\\n                    + \"/\" + test_df.series_id.astype(str)\\\n                    + \"/\" + test_df.instance_number.astype(str) +\".png\"\n\ntest_df.head(4)","metadata":{"execution":{"iopub.status.busy":"2023-10-04T04:29:34.446727Z","iopub.execute_input":"2023-10-04T04:29:34.447364Z","iopub.status.idle":"2023-10-04T04:29:34.517647Z","shell.execute_reply.started":"2023-10-04T04:29:34.447334Z","shell.execute_reply":"2023-10-04T04:29:34.517019Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# preprocess test images\ndef _process(dcm_filepath):\n    dicom = pydicom.dcmread(dcm_filepath)\n    pos_z = dicom[(0x20, 0x32)].value[-1]\n    image = standardize_pixel_array(dicom)\n    image = (image - image.min()) / (image.max() - image.min() + 1e-6)\n    if dicom.PhotometricInterpretation == \"MONOCHROME1\":\n        image = 1 - image\n    return image\n\ndef process(dicom_filepath, image_filepath, size=512):\n    image_dirpath = str(Path(image_filepath).parent)\n    os.makedirs(image_dirpath, exist_ok=True)\n    image = _process(dicom_filepath)\n    if size is not None:\n        image = cv2.resize(image, (size, size))\n    cv2.imwrite(image_filepath, (image * 255).astype(np.uint8))\n\n\npatients = os.listdir(test_dcm_dirpath)\nfor idx, row in tqdm(test_df.iterrows()):\n    patient_id = row[\"patient_id\"]\n    series_id = row[\"series_id\"]\n    instance_number = row[\"instance_number\"]\n    dicom_filepath = row[\"dicom_filepath\"]\n    image_filepath = row[\"image_filepath\"]\n    process(dicom_filepath, image_filepath, size=256)","metadata":{"execution":{"iopub.status.busy":"2023-10-04T04:29:34.518784Z","iopub.execute_input":"2023-10-04T04:29:34.519319Z","iopub.status.idle":"2023-10-04T04:29:34.648273Z","shell.execute_reply.started":"2023-10-04T04:29:34.51929Z","shell.execute_reply":"2023-10-04T04:29:34.647373Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"class CNNModel(nn.Module):\n    def __init__(self):\n        super().__init__()\n        self.input = nn.Conv2d(4, 3, kernel_size=3)\n        model = models.efficientnet_b0(weights='IMAGENET1K_V1')\n        \n        self.features = model.features\n        self.avgpool = model.avgpool\n        \n        self.bowel = nn.Linear(1280, 1)\n        self.extravasation = nn.Linear(1280, 1)\n        self.kidney = nn.Linear(1280, 3)\n        self.liver = nn.Linear(1280,3) \n        self.spleen = nn.Linear(1280, 3)\n    \n    def forward(self, x):\n        x = self.input(x)\n        x = self.features(x)\n        x = self.avgpool(x)\n        x = torch.flatten(x, 1)\n        \n        bowel = self.bowel(x)\n        extravsation = self.extravasation(x)\n        kidney = self.kidney(x)\n        liver = self.liver(x)\n        spleen = self.spleen(x)\n        \n        return bowel, extravsation, kidney, liver, spleen\n\n# build model\nRNSAModel = CNNModel\nmodel = torch.load(\"/kaggle/input/rnsa-2023-atd-b0-2433/efficientnet_b0_2.433.pth\", map_location=torch.device(device))\nmodel.eval()","metadata":{"execution":{"iopub.status.busy":"2023-10-04T04:29:34.649659Z","iopub.execute_input":"2023-10-04T04:29:34.650206Z","iopub.status.idle":"2023-10-04T04:29:37.783289Z","shell.execute_reply.started":"2023-10-04T04:29:34.650176Z","shell.execute_reply":"2023-10-04T04:29:37.782096Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"class AbdominalData(Dataset):\n    def __init__(self, df, spacing=2):\n        super().__init__()\n        self.data = self.fetch(df)\n        self.spacing = spacing\n        self.transform = Compose([\n            ToTensor()\n        ])\n        \n    def fetch(self, df):\n        data = []\n        print('Scanning dirs...')\n        patient_ids = df['patient_id'].unique()\n        for patient_idx, patient_id in tqdm(enumerate(patient_ids)):\n            patient_df = df[df['patient_id'] == patient_id]\n            series_ids = patient_df['series_id']\n            for series_id in series_ids:\n                series_df = patient_df[patient_df['series_id'] == series_id]\n                image_filepaths = list(series_df['image_filepath'].astype(str))\n                data.append({\n                    'patient_idx': patient_idx, \n                    'patient_id': patient_id, \n                    'image_filepaths': image_filepaths\n                })\n            \n        return data\n    \n    def __len__(self):\n        return len(self.data)\n    \n    def __getitem__(self, idx):\n        cur_data = self.data[idx]\n        patient_idx, patient_id, image_filepaths = cur_data['patient_idx'], cur_data['patient_id'], cur_data['image_filepaths']\n        if len(image_filepaths) == 1:\n            dicom_images = [image_filepaths[0]] * 4\n        elif len(image_filepaths) == 2:\n            dicom_images = [image_filepaths[0], image_filepaths[0], image_filepaths[1], image_filepaths[1]]\n        elif len(image_filepaths) == 3:\n            dicom_images = [image_filepaths[0], image_filepaths[1], image_filepaths[2], image_filepaths[2]]\n        elif len(image_filepaths) == 4:\n            dicom_images = [image_filepaths[0], image_filepaths[1], image_filepaths[2], image_filepaths[3]]\n        elif len(image_filepaths) >= 5:\n            raise NotImplementedError()\n        \n        images = [preprocess_jpeg(_) for _ in dicom_images]\n        images = np.stack(images)\n        image = torch.tensor(images, dtype=torch.float).unsqueeze(dim=1)\n        image = self.transform(image).squeeze(dim = 1)\n        item = {'image': image, 'patient_idx': patient_idx, 'patient_id': patient_id}\n        \n        return item\n\ntest_data = AbdominalData(test_df)\ntest_dataloader = DataLoader(test_data, batch_size=1, shuffle=False)","metadata":{"execution":{"iopub.status.busy":"2023-10-04T04:29:37.784387Z","iopub.execute_input":"2023-10-04T04:29:37.78519Z","iopub.status.idle":"2023-10-04T04:29:37.827932Z","shell.execute_reply.started":"2023-10-04T04:29:37.785158Z","shell.execute_reply":"2023-10-04T04:29:37.8271Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def post_proc(bowel, extravasation, kidney, liver, spleen):\n    proc_pred = np.empty((2 * 2 + 3 * 3), dtype=\"float32\")\n    \n    # bowel, extravasation\n    proc_pred[0] = bowel\n    proc_pred[1] = 1 - bowel\n    proc_pred[2] = extravasation\n    proc_pred[3] = 1 - extravasation\n\n    # kidney, liver, spleen\n    proc_pred[4:7] = kidney\n    proc_pred[7:10] = liver\n    proc_pred[10:13] = spleen\n\n    return proc_pred\n\npatient_ids = test_df[\"patient_id\"].unique()\npatient_preds = np.zeros(\n    shape=(len(patient_ids), 2 * 2 + 3 * 3),\n    dtype=\"float32\"\n)\npatient_pred_counts = np.zeros(\n    shape=(len(patient_ids), 1),\n    dtype=\"float32\"\n)\n\nfor batch_idx, batch_data in enumerate(tqdm(test_dataloader)):                                  \n    batch_inputs = batch_data['image'].to(device)\n    batch_patient_idxs = batch_data['patient_idx']\n    batch_patient_ids = batch_data['patient_id']\n    \n    bowels, extravsations, kidneys, livers, spleens = model(batch_inputs)\n    bowels = torch.sigmoid(bowels).detach().cpu().numpy()\n    extravsations = torch.sigmoid(extravsations).detach().cpu().numpy()\n    kidneys = torch.softmax(kidneys, dim=1).detach().cpu().numpy()\n    livers = torch.softmax(livers, dim=1).detach().cpu().numpy()\n    spleens = torch.softmax(spleens, dim=1).detach().cpu().numpy()\n    \n    for patient_idx, bowel, extravsation, kidney, liver, spleen in zip(batch_data['patient_idx'], bowels, extravsations, kidneys, livers, spleens):\n        patient_preds[patient_idx, :] += post_proc(bowel, extravsation, kidney, liver, spleen)\n        patient_pred_counts[patient_idx, 0] += 1.0\n    \n    del bowels, extravsations, kidneys, livers, spleens\n    del batch_inputs, batch_patient_ids\n    gc.collect()\n    \npatient_preds = patient_preds / patient_pred_counts","metadata":{"execution":{"iopub.status.busy":"2023-10-04T04:29:37.830416Z","iopub.execute_input":"2023-10-04T04:29:37.831196Z","iopub.status.idle":"2023-10-04T04:29:42.620889Z","shell.execute_reply.started":"2023-10-04T04:29:37.831167Z","shell.execute_reply":"2023-10-04T04:29:42.619926Z"},"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[TARGET_COLS] = 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(4)","metadata":{"execution":{"iopub.status.busy":"2023-10-04T04:29:42.622159Z","iopub.execute_input":"2023-10-04T04:29:42.623001Z","iopub.status.idle":"2023-10-04T04:29:42.661867Z","shell.execute_reply.started":"2023-10-04T04:29:42.622956Z","shell.execute_reply":"2023-10-04T04:29:42.660847Z"},"trusted":true},"execution_count":null,"outputs":[]}]}