{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.10.12","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"A simple baseline for 3d classification task\n\nAll CTs are resized into 128 slices x 256x256 resolution (less than 128 ->randomly copy, more than 128-> randomly downsample)\n\nYou need to train your own 3d resnet10 weights","metadata":{}},{"cell_type":"code","source":"import os\nimport gc\nimport cv2\nimport pydicom\nfrom joblib import Parallel, delayed\nimport numpy as np\nimport pandas as pd\nfrom tqdm.notebook import tqdm\nimport glob\nimport sys\nimport json\nfrom collections import Counter\nimport tifffile as tiff\nimport matplotlib.pyplot as plt\nfrom tqdm import tqdm\nimport torch\nimport cv2\nfrom types import SimpleNamespace","metadata":{"execution":{"iopub.status.busy":"2023-09-13T12:42:54.089325Z","iopub.execute_input":"2023-09-13T12:42:54.089599Z","iopub.status.idle":"2023-09-13T12:42:58.768932Z","shell.execute_reply.started":"2023-09-13T12:42:54.089574Z","shell.execute_reply":"2023-09-13T12:42:58.767984Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"!pip install -q /kaggle/input/rsna2023-abd-wheels/monai-1.2.0-202306081546-py3-none-any.whl","metadata":{"execution":{"iopub.status.busy":"2023-09-13T12:42:58.771127Z","iopub.execute_input":"2023-09-13T12:42:58.771915Z","iopub.status.idle":"2023-09-13T12:43:32.851459Z","shell.execute_reply.started":"2023-09-13T12:42:58.77188Z","shell.execute_reply":"2023-09-13T12:43:32.849938Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"DEBUG = False","metadata":{"execution":{"iopub.status.busy":"2023-09-13T12:43:32.853305Z","iopub.execute_input":"2023-09-13T12:43:32.853695Z","iopub.status.idle":"2023-09-13T12:43:32.861794Z","shell.execute_reply.started":"2023-09-13T12:43:32.853659Z","shell.execute_reply":"2023-09-13T12:43:32.860808Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"BASE_PATH = \"/kaggle/input/rsna-2023-abdominal-trauma-detection\"\nIMAGE_DIR = \"/kaggle/working/tmp/dataset/rsna-atd\"","metadata":{"execution":{"iopub.status.busy":"2023-09-13T12:43:32.864903Z","iopub.execute_input":"2023-09-13T12:43:32.86522Z","iopub.status.idle":"2023-09-13T12:43:32.871034Z","shell.execute_reply.started":"2023-09-13T12:43:32.865196Z","shell.execute_reply":"2023-09-13T12:43:32.869888Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"cfg = SimpleNamespace(**{})\nclass cfg:\n    img_size = 256\n    num_imgs = 128\n    batch_size = 1\n    num_workers = 1\n    columns  = [\"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-09-13T12:43:32.879037Z","iopub.execute_input":"2023-09-13T12:43:32.879316Z","iopub.status.idle":"2023-09-13T12:43:32.884705Z","shell.execute_reply.started":"2023-09-13T12:43:32.879294Z","shell.execute_reply":"2023-09-13T12:43:32.883648Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def standardize_pixel_array(dcm: pydicom.dataset.FileDataset) -> np.ndarray:\n    \"\"\"\n    Source : https://www.kaggle.com/competitions/rsna-2023-abdominal-trauma-detection/discussion/427217\n    \"\"\"\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        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","metadata":{"execution":{"iopub.status.busy":"2023-09-13T12:43:32.88567Z","iopub.execute_input":"2023-09-13T12:43:32.887112Z","iopub.status.idle":"2023-09-13T12:43:32.896482Z","shell.execute_reply.started":"2023-09-13T12:43:32.88708Z","shell.execute_reply":"2023-09-13T12:43:32.895561Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Saving dicom to images(size:256*256) with 128 slices each\n# More than 128 -> random downsample, Less than 128 -> random copy\nTEST_PATH = BASE_PATH + '/test_images/'\nif DEBUG == True:\n    TEST_PATH = BASE_PATH + '/train_images/'\npatientlist = sorted(os.listdir(TEST_PATH))\nfor patient in patientlist:\n    for study in os.listdir(TEST_PATH + patient):\n        print(f'Saving {patient}/{study} ...')\n        dicom_list = glob.glob(TEST_PATH + f\"{patient}/{study}/*.dcm\")\n        idx_list = sorted([int(idx.split('/')[-1].split('.')[0]) for idx in dicom_list])\n        min_idx,max_idx = idx_list[0],idx_list[-1]\n        sampled_numbers = np.linspace(min_idx, max_idx, 128, dtype=int) #less->copy,more->downsample\n        seleted_dicom_paths = [ TEST_PATH + f\"{patient}/{study}/{idx}.dcm\" for idx in sampled_numbers]\n        assert len(seleted_dicom_paths) == 128\n        os.makedirs(f'{IMAGE_DIR}/{patient}/{study}',exist_ok=True)\n\n        for i,f in enumerate(seleted_dicom_paths):\n            dicom = pydicom.dcmread(f)\n\n            img = standardize_pixel_array(dicom)\n\n            img = (img - img.min()) / (img.max() - img.min() + 1e-6)\n\n            if dicom.PhotometricInterpretation == \"MONOCHROME1\":\n                img = 1 - img\n\n            img = cv2.resize(img, (256, 256))\n            save_path = f'{IMAGE_DIR}/{patient}/{study}/{i}.png'\n            #print(save_path)\n            cv2.imwrite(save_path,(img * 255).astype(np.uint8))","metadata":{"execution":{"iopub.status.busy":"2023-09-13T12:46:37.20994Z","iopub.execute_input":"2023-09-13T12:46:37.210343Z","iopub.status.idle":"2023-09-13T12:46:42.929883Z","shell.execute_reply.started":"2023-09-13T12:46:37.210304Z","shell.execute_reply":"2023-09-13T12:46:42.928862Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# dataset\nfrom torch.utils.data import Dataset\nfrom torch.utils.data import DataLoader\nimport numpy as np\nimport pandas as pd\nimport cv2\nimport os\nimport torch\nfrom PIL import Image\nimport random\nfrom scipy.ndimage import rotate, zoom\nfrom tqdm import tqdm\n\nclass rsna2023abd_Dataset_multihead(Dataset):\n    def __init__(self,patient_id_lists,cfg,train = True):\n        super().__init__()\n        self.train = train\n        self.cfg = cfg\n        self.patient_id_lists = patient_id_lists\n        self.epoch_len = len(self.patient_id_lists)\n        self.root = '/kaggle/working/tmp/dataset/rsna-atd'\n    \n    def __getitem__(self, idx):\n        patient_id = self.patient_id_lists[idx]\n        patient_id = int(patient_id)\n        _3d_img = self.load_image_3d(patient_id,self.cfg.num_imgs,self.cfg.img_size)\n        _3d_img = np.expand_dims(_3d_img, 0)\n        _3d_images = torch.tensor(_3d_img).float()\n        \n        data = {\n            \"image\": _3d_images,\n            \"index\": patient_id,\n        }\n        return data\n    \n    def load_image_3d(self, patient_id, num_imgs = 128, img_size = 256):\n        seriesid = os.listdir(f'{self.root}/{patient_id}')[0]# default choose first CT per patient\n        stacked_images = np.zeros((128, img_size, img_size))\n        for i in range(num_imgs):\n            img = cv2.imread(f'{self.root}/{patient_id}/{seriesid}/{i}.png')\n            img = np.squeeze(img[:,:,0])\n            div = np.max(img) - np.min(img)\n            div = div if div != 0 else 255# assertation，avoid bug\n            img = (img - np.min(img))/div# normalization\n            stacked_images[i, :, :] = np.array(img)\n        return stacked_images\n        \n    def __len__(self):\n        return self.epoch_len","metadata":{"execution":{"iopub.status.busy":"2023-09-13T12:46:47.542866Z","iopub.execute_input":"2023-09-13T12:46:47.543568Z","iopub.status.idle":"2023-09-13T12:46:47.558703Z","shell.execute_reply.started":"2023-09-13T12:46:47.543534Z","shell.execute_reply":"2023-09-13T12:46:47.557596Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"inferDataset = rsna2023abd_Dataset_multihead(patient_id_lists=patientlist,cfg=cfg,train=False)\ninfer_dataloader = DataLoader(\n    inferDataset,\n    shuffle=False,\n    batch_size=cfg.batch_size,\n    num_workers=cfg.num_workers,\n    pin_memory=False,\n    collate_fn=None,\n)","metadata":{"execution":{"iopub.status.busy":"2023-09-13T12:46:50.598742Z","iopub.execute_input":"2023-09-13T12:46:50.599114Z","iopub.status.idle":"2023-09-13T12:46:50.605318Z","shell.execute_reply.started":"2023-09-13T12:46:50.599085Z","shell.execute_reply":"2023-09-13T12:46:50.603838Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"#Networks\nimport torch\nfrom torch import nn\nimport timm\nfrom safetensors import safe_open\nimport torch.nn.functional as F\nimport monai\nimport numpy as np\nprint(monai.__version__)\nclass monai_3d_multihead(nn.Module):\n    def __init__(self,train = True):\n        super().__init__()\n        self.model = monai.networks.nets.resnet10(spatial_dims=3, n_input_channels=1) #(fc): Linear(in_features=512, out_features=400, bias=True)\n        self.bowel = nn.Linear(400, 1)\n        self.extravasation = nn.Linear(400, 1)\n        self.kidney = nn.Linear(400, 3)\n        self.liver = nn.Linear(400,3) \n        self.spleen = nn.Linear(400, 3)\n    \n    def forward(self , x):\n        x = self.model(x)\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","metadata":{"execution":{"iopub.status.busy":"2023-09-13T12:46:53.21154Z","iopub.execute_input":"2023-09-13T12:46:53.211905Z","iopub.status.idle":"2023-09-13T12:46:53.22319Z","shell.execute_reply.started":"2023-09-13T12:46:53.211874Z","shell.execute_reply":"2023-09-13T12:46:53.221986Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"#just for convenient\nclass MetricsCalculator:\n    \n    def __init__(self, mode = 'binary'):\n        \n        self.probabilities = []\n        self.predictions = []\n        self.mode = mode\n    \n    def update(self, logits):\n        \"\"\"\n        Update the metrics calculator with predicted values and corresponding targets.\n        \n        Args:\n            predicted (torch.Tensor): Predicted values.\n            target (torch.Tensor): Ground truth targets.\n        \"\"\"\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    \n    def reset(self):\n        \"\"\"Reset the stored predictions and targets.\"\"\"\n        \n        self.probabilities = []\n        self.predictions = []\n    \n    def get_probs(self):\n        \n        return np.array(self.probabilities)\n    ","metadata":{"execution":{"iopub.status.busy":"2023-09-13T12:46:56.693942Z","iopub.execute_input":"2023-09-13T12:46:56.694365Z","iopub.status.idle":"2023-09-13T12:46:56.706258Z","shell.execute_reply.started":"2023-09-13T12:46:56.694333Z","shell.execute_reply":"2023-09-13T12:46:56.705023Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"model = monai_3d_multihead()\nmodel = torch.nn.DataParallel(model)\nmodel.to(\"cuda\")","metadata":{"execution":{"iopub.status.busy":"2023-09-13T12:47:00.430372Z","iopub.execute_input":"2023-09-13T12:47:00.430742Z","iopub.status.idle":"2023-09-13T12:47:00.700547Z","shell.execute_reply.started":"2023-09-13T12:47:00.430712Z","shell.execute_reply":"2023-09-13T12:47:00.699636Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import glob\nweights_path = '/kaggle/input/rsna2023-abd-weights/'\nweights_path = glob.glob(weights_path + f\"resnet10-fold4.pth\")\nprint(weights_path)","metadata":{"execution":{"iopub.status.busy":"2023-09-13T12:47:04.195516Z","iopub.execute_input":"2023-09-13T12:47:04.195872Z","iopub.status.idle":"2023-09-13T12:47:04.202127Z","shell.execute_reply.started":"2023-09-13T12:47:04.195844Z","shell.execute_reply":"2023-09-13T12:47:04.201099Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"#predict\n\nall_ids = []\n\nval_acc_bowel = MetricsCalculator('binary')\nval_acc_extravasation = MetricsCalculator('binary')\nval_acc_liver = MetricsCalculator('multi')\nval_acc_kidney = MetricsCalculator('multi')\nval_acc_spleen = MetricsCalculator('multi')\n\nfor weights in weights_path:\n    torch.cuda.empty_cache()\n    assert os.path.exists(weights) == True\n    model.load_state_dict(torch.load(weights)[\"model\"])\n    model.eval()\n    torch.set_grad_enabled(False)\n    for batch in infer_dataloader:\n        inputs = batch[\"image\"].float().to(\"cuda\")\n        ids = batch[\"index\"]\n        b, e, k, l, s = model(inputs)\n        print(b, e, k, l, s)\n        \n        val_acc_bowel.update(b)\n        val_acc_extravasation.update(e)\n        val_acc_liver.update(l)\n        val_acc_kidney.update(k)\n        val_acc_spleen.update(s)\n        \n        ids = list(ids.detach().cpu().numpy())\n        all_ids.extend(ids)\n    \n    \n    _b = val_acc_bowel.get_probs()\n    _e = val_acc_extravasation.get_probs()\n    _k = val_acc_kidney.get_probs()\n    _l = val_acc_liver.get_probs()\n    _s = val_acc_spleen.get_probs()\n    \n    all_outputs = np.concatenate((1-_b,_b,1-_e,_e,_k,_l,_s),axis=1)\n    \ndf_pred = pd.DataFrame.from_dict(all_ids)\ndf_pred.columns = [\"patient_id\"]\nfor column in cfg.columns:\n    df_pred[column] = float('nan')\ndf_pred[cfg.columns] = all_outputs\ndf_pred.set_index('patient_id')","metadata":{"execution":{"iopub.status.busy":"2023-09-13T12:47:06.566971Z","iopub.execute_input":"2023-09-13T12:47:06.567982Z","iopub.status.idle":"2023-09-13T12:47:08.241474Z","shell.execute_reply.started":"2023-09-13T12:47:06.567935Z","shell.execute_reply":"2023-09-13T12:47:08.24033Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"!rm -r /kaggle/working/tmp","metadata":{"execution":{"iopub.status.busy":"2023-09-13T12:47:13.783798Z","iopub.execute_input":"2023-09-13T12:47:13.784684Z","iopub.status.idle":"2023-09-13T12:47:14.82186Z","shell.execute_reply.started":"2023-09-13T12:47:13.784641Z","shell.execute_reply":"2023-09-13T12:47:14.820558Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"#align\nsub_df = pd.read_csv(f'{BASE_PATH}/sample_submission.csv')\nsub_df = sub_df[['patient_id']]\nsub_df = sub_df.merge(df_pred, on='patient_id', how='left')\n\n#postprocessing,optional,best LB with no postprocessing\n# scale_by_2 = ['bowel_injury','kidney_low','liver_low','spleen_low']\n# scale_by_4 = ['spleen_high','kidney_high','liver_high']\n# scale_by_6 = ['extravasation_injury']\n\n# sf_2 = 2\n# sf_4 = 4\n# sf_6 = 6\n\n# sub_df[scale_by_2] *=sf_2\n# sub_df[scale_by_4] *=sf_4\n# sub_df[scale_by_6] *=sf_6\n\nsub_df.to_csv('submission.csv',index=False)\nsub_df.head()","metadata":{"execution":{"iopub.status.busy":"2023-09-13T12:47:18.469746Z","iopub.execute_input":"2023-09-13T12:47:18.470784Z","iopub.status.idle":"2023-09-13T12:47:18.522138Z","shell.execute_reply.started":"2023-09-13T12:47:18.470737Z","shell.execute_reply":"2023-09-13T12:47:18.520991Z"},"trusted":true},"execution_count":null,"outputs":[]}],"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"}}