{"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":"try: \n    import dicomsdl\nexcept:\n    !pip install /kaggle/input/rsna-2023-abdominal-trauma/dicomsdl-0.109.2-cp310-cp310-manylinux_2_12_x86_64.manylinux2010_x86_64.whl\n    \nimport os \nimport sys \nsys.path.append('/kaggle/input/rsna-2023-abdominal-trauma')\nsys.path.append('/kaggle/input/rsna-2023-abdominal-trauma-weight-00')\n\n\nimport matplotlib\nimport matplotlib.pyplot as plt\n\nimport numpy as np\nimport pandas as pd\nfrom glob import glob\nimport dicomsdl\n\nimport torch\nimport torch.nn as nn\nimport torch.nn.functional as F\n\nimport cv2\nfrom timeit import default_timer as timer\n    \nfrom kaggle_helper import * \nfrom kaggle_metric import * \n    \nprint('IMPORT OK!!!!')","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","execution":{"iopub.status.busy":"2023-09-05T17:46:48.023322Z","iopub.execute_input":"2023-09-05T17:46:48.023665Z","iopub.status.idle":"2023-09-05T17:47:29.047571Z","shell.execute_reply.started":"2023-09-05T17:46:48.023636Z","shell.execute_reply":"2023-09-05T17:47:29.046521Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"cfg = dotdict(\n    max_size=256,\n    slice_scan_size=(96,160,160),\n    liver_scan_size=(96,256,256),\n    \n    device='cuda',#'cuda' #cpu\n)\n\n\n\nmode = 'local' #submit #local\n\nif mode =='local':\n    image_dir  = '/kaggle/input/rsna-2023-abdominal-trauma-detection/train_images'\n    #series_df = pd.read_csv('/kaggle/input/rsna-2023-abdominal-trauma-detection/train_series_meta.csv')\n    fold_df = pd.read_csv('/kaggle/input/rsna-2023-abdominal-trauma-weight-00/valid_df.fold2.csv') \n    fold_df = fold_df[:20]    \n    valid_id  = list(zip(fold_df.patient_id, fold_df.series_id))\n         \n    train_df = pd.read_csv('/kaggle/input/rsna-2023-abdominal-trauma-detection/train.csv') \n    valid_df = train_df[train_df.patient_id.isin(fold_df.patient_id)].reset_index(drop=True)\n\n    \nif mode =='submit':\n    image_dir = '/kaggle/input/rsna-2023-abdominal-trauma-detection/test_images'\n    series_df = pd.read_csv('/kaggle/input/rsna-2023-abdominal-trauma-detection/test_series_meta.csv')\n    valid_df  = pd.read_csv( '/kaggle/input/rsna-2023-abdominal-trauma-detection/sample_submission.csv') \n    valid_id  = list(zip(series_df.patient_id, series_df.series_id))\n \n    \n\npatient_id = valid_df.patient_id.unique()\n    \nprint('len(patient_id)',len(patient_id))   \nprint('patient_id',patient_id[:10])   \nprint('')\n \nprint('len(valid_id)',len(valid_id))   \nprint('valid_id',valid_id[:10]) \nprint('')\n\n#---\n\n\nprint('MODE SETTING OK!!!!')","metadata":{"execution":{"iopub.status.busy":"2023-09-05T17:47:29.049639Z","iopub.execute_input":"2023-09-05T17:47:29.050961Z","iopub.status.idle":"2023-09-05T17:47:29.102968Z","shell.execute_reply.started":"2023-09-05T17:47:29.050907Z","shell.execute_reply":"2023-09-05T17:47:29.101974Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"#model\n\nfrom slice_model import Net as SliceNet\nfrom liver_model import Net as LiverNet\n\nslice_checkpoint=\\\n    '/kaggle/input/rsna-2023-abdominal-trauma-weight-00/00000448.pth'\nliver_checkpoint=\\\n    '/kaggle/input/rsna-2023-abdominal-trauma-weight-00/00004313.pth'\n\n#---\nslice_net = SliceNet(cfg=cfg)\nprint(slice_net.load_state_dict(\n    torch.load(slice_checkpoint, map_location=lambda storage, loc: storage)['state_dict'],\n    strict=False\n)) # True\nslice_net = slice_net.eval()\n\n#---\n\nliver_net = LiverNet(cfg=cfg)\nprint(liver_net.load_state_dict(\n    torch.load(liver_checkpoint, map_location=lambda storage, loc: storage)['state_dict'],\n    strict=False\n)) # True\nliver_net = liver_net.eval()\n\nprint('MODEL OK!!!!')","metadata":{"execution":{"iopub.status.busy":"2023-09-05T17:47:29.104467Z","iopub.execute_input":"2023-09-05T17:47:29.104798Z","iopub.status.idle":"2023-09-05T17:47:35.7516Z","shell.execute_reply.started":"2023-09-05T17:47:29.104766Z","shell.execute_reply":"2023-09-05T17:47:35.750534Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"#dataset\n\ndef do_pad_to_square(image):\n    l, h, w = image.shape\n    if w > h:\n        pad = w - h\n        pad0 = pad // 2\n        pad1 = pad - pad0\n        image = F.pad(image, [0, 0, pad0, pad1], mode='constant', value=0)\n    if w < h:\n        pad = h - w\n        pad0 = pad // 2\n        pad1 = pad - pad0\n        image = F.pad(image, [pad0, pad1, 0, 0], mode='constant', value=0)\n    return image\n\ndef do_scale_to_size(image, spacing, max_size):\n    dz, dy, dx = spacing\n    l, s, s = image.shape # scale to max size\n    if max_size != s:\n        scale = max_size / s\n\n        l = int(dz / dy * l * 0.5)  # we use sapcing dz,dy,dx = 2,1,1\n        l = int(scale * l)\n        h = int(scale * s)\n        w = int(scale * s)\n\n        image = F.interpolate(\n            image.unsqueeze(0).unsqueeze(0),\n            size=(l, h, w),\n            mode='trilinear',\n            align_corners=False,\n        ).squeeze(0).squeeze(0)\n\n    return image\n\n\ndef dicomsdl_to_numpy_image(ds, index=0):\n    info = ds.getPixelDataInfo()\n    if info['SamplesPerPixel'] != 1:\n        raise RuntimeError('SamplesPerPixel != 1')  # number of separate planes in this image\n    shape = [info['Rows'], info['Cols']]\n    dtype = info['dtype']\n    outarr = np.empty(shape, dtype=dtype)\n    ds.copyFrameData(index, outarr)\n    return outarr\n\ndef load_dicomsdl_dir(dcm_dir, slice_range=None):\n    dcm_file = sorted(glob(f'{dcm_dir}/*.dcm'), key=lambda x: int(x.split('/')[-1].split('.')[0]))\n     \n    #fake some slice so that it won't cause error ....\n    if len(dcm_file)==1:\n        dcm = dicomsdl.open(dcm_file[0])\n        pixel_array = dicomsdl_to_numpy_image(dcm) \n        pixel_array = pixel_array.astype(np.float32)\n        image = np.stack([pixel_array]*16)\n        dz,dy,dx = 1,1,1\n        return image, (dz,dy,dx)\n    \n    \n    #------------------------------------\n    if slice_range is None: \n        slice_min = int(dcm_file[0].split('/')[-1].split('.')[0])\n        slice_max = int(dcm_file[-1].split('/')[-1].split('.')[0])+1\n        slice_range=(slice_min, slice_max)\n\n    slice_min, slice_max = slice_range\n    sz0, szN = None, None\n\n    image = []\n    for s in range(slice_min, slice_max):\n        f = f'{dcm_dir}/{s}.dcm'\n\n        #dcm = pydicom.read_file(f)\n        #m = dcm.pixel_array\n        #m = standardize_pixel_array(dcm)\n\n        dcm = dicomsdl.open(f)\n        pixel_array = dicomsdl_to_numpy_image(dcm)\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        #processing\n        pixel_array = pixel_array.astype(np.float32)\n        pixel_array = dcm.RescaleSlope * pixel_array + dcm.RescaleIntercept\n        xmin = dcm.WindowCenter-0.5-(dcm.WindowWidth-1)* 0.5\n        xmax = dcm.WindowCenter-0.5+(dcm.WindowWidth-1)* 0.5\n        norm = np.empty_like(pixel_array, dtype=np.uint8)\n        dicomsdl.util.convert_to_uint8(pixel_array, norm, xmin, xmax)\n\n        if dcm.PhotometricInterpretation == 'MONOCHROME1':\n            norm = 255 - norm\n        image.append(norm)\n\n    if 1: #check inversion\n        dcm0 = dicomsdl.open(f'{dcm_dir}/{slice_min}.dcm')\n        dcmN = dicomsdl.open(f'{dcm_dir}/{slice_max-1}.dcm')\n        sx0, sy0, sz0 = dcm0.ImagePositionPatient\n        sxN, syN, szN = dcmN.ImagePositionPatient\n        if szN > sz0:\n            image=image[::-1]\n\n        dx, dy = dcm0.PixelSpacing\n        dz = np.abs((szN - sz0) / (slice_max - slice_min-1))\n\n    image = np.stack(image)\n    return image, (dz,dy,dx)\n\n\n\ndef pre_process_slice_predictor(image):\n    l,s,s = image.shape\n    L,S,S = cfg.slice_scan_size\n\n    l1 = int(S / s * l)\n    image = F.interpolate(\n            image.unsqueeze(0).unsqueeze(0),\n            size=[l1,S,S],\n            mode='trilinear'\n        ).squeeze(0).squeeze(0)\n\n    # pad or crop to max length L\n    if L > l1:\n        image = F.pad(image, [0, 0, 0, 0, 0, L - l1], mode='constant', value=0)\n    if L < l1:\n        image = image[:L]\n    return image\n\ndef post_process_slice_predictor(x, image, slice_prob):\n    l,s,s = x.shape\n    L,S,S = image.shape\n\n    l1 = int(S / s * l)\n    p = F.interpolate(\n            slice_prob.unsqueeze(0),\n            size=[l1],\n            mode='linear'\n        ).squeeze(0).squeeze(0)\n\n    # unpad or uncrop to max length L\n    if L > l1:\n        p = F.pad(p, [0, L - l1], mode='constant', value=0)\n    return p\n\n#---\n\ndef pre_process_liver_predictor(image, slice_predict):\n    z = torch.where(slice_predict > 0)[0]\n    if len(z)==0:\n        z0, z1 = 0, 96 \n    else: \n        z0, z1 = z.min().item(), z.max().item()\n\n    sub_image = image[z0:z1]\n\n    L,S,S = cfg.liver_scan_size\n    zz=0\n    sub_image = F.interpolate(\n        sub_image.unsqueeze(0).unsqueeze(0),\n        size=[L,S,S],\n        mode='trilinear'\n    ).squeeze(0).squeeze(0)\n    return sub_image\n\nprint('DATASET SETTING OK!!!!')","metadata":{"execution":{"iopub.status.busy":"2023-09-05T17:47:35.754783Z","iopub.execute_input":"2023-09-05T17:47:35.755072Z","iopub.status.idle":"2023-09-05T17:47:35.921034Z","shell.execute_reply.started":"2023-09-05T17:47:35.755046Z","shell.execute_reply":"2023-09-05T17:47:35.919872Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"#### START HERE !!!! ##################\ndummy={\n    'liver_healthy' : 1,\n    'liver_low'     : 0,\n    'liver_high'    : 0,\n    'spleen_healthy': 1,\n    'spleen_low'    : 0,\n    'spleen_high'   : 0,\n    'kidney_healthy': 1,\n    'kidney_low'    : 0,\n    'kidney_high'   : 0, \n    'bowel_healthy'        : 1,\n    'bowel_injury'         : 0,\n    'extravasation_healthy': 1,\n    'extravasation_injury' : 0,\n\n    #'any_injury' : 0, # auto generated\n}\n\ndef np_min_max_norm(x):\n    return (x-x.min())/(x.max()-x.min()+0.001)\n\ndef norm_to_one(x):\n    s = sum(x)\n    x=[xx/s for xx in x]\n    return x\n\n\nif cfg.device=='cuda':\n    slice_net = slice_net.cuda()\n    liver_net = liver_net.cuda()\n\nif 1:\n    submit_df_data = []\n    start_timer = timer()\n    for t,(patient_id, series_id) in enumerate(valid_id):\n        # https://www.kaggle.com/competitions/rsna-2023-abdominal-trauma-detection/discussion/435815\n        # Single corrupt image in the hidden test set\n        # test_images/3124/5842/514.dcm\n\n        if series_id in [5842]:\n            submit_df_data.append({\n                'patient_id': patient_id,\n                'series_id' : series_id, \n            } | dummy)\n            continue\n            \n        #---------------------------------------------------------------------------   \n        print('\\r', f'{t}/{len(valid_id)} : {patient_id},{series_id}', time_to_str(timer() - start_timer, 'min'), end='', flush=True)\n        try: \n            dcm_dir = f'{image_dir}/{patient_id}/{series_id}'\n            image, (dz, dy, dx) = load_dicomsdl_dir(dcm_dir, slice_range=None) #byte\n            image = torch.from_numpy(image).float()\n            image = do_pad_to_square(image)\n            image = do_scale_to_size(image, (dz, dy, dx), max_size=cfg.max_size) #torch.Size([193, 256, 256])\n\n            if cfg.device=='cuda':\n                image = image.cuda()\n            #print('image.shape', image.shape)\n\n            with torch.cuda.amp.autocast(enabled=True):\n                with torch.no_grad():\n                    #stage.1 : predict slice\n                    x = pre_process_slice_predictor(image)\n                    slice_prob = slice_net.infer(x.unsqueeze(0))\n                    slice_prob = post_process_slice_predictor(x, image, slice_prob)\n                    slice_predict = (slice_prob>0.5).byte()\n\n\n                    #stage.2 : predict for liver, spleen, kidney\n                    x = pre_process_liver_predictor(image, slice_predict)\n                    liver_prob, spleen_prob, kidney_prob = liver_net.infer(x.unsqueeze(0))\n                    liver_prob, spleen_prob, kidney_prob = \\\n                        liver_prob [0].data.cpu().numpy().tolist(), \\\n                        spleen_prob[0].data.cpu().numpy().tolist(), \\\n                        kidney_prob[0].data.cpu().numpy().tolist()\n\n\n                    #stage.3 : predict for bowel, active_extravasation\n                    #\n                    # we train different model because the ground truth are given differently,\n                    # one at series(volume) level, another at instance(image) level\n                    # further, active_extravasation uses multiple series\n                    #\n                    # this is not shown here, so we just put in default values ....\n\n                    bowel_prob=norm_to_one([0.979663, 0.020337*6])\n                    extravasation_prob=norm_to_one([0.936447, 0.063553*6])\n\n            #=============================================\n            submit_df_data.append({\n                'patient_id': patient_id,\n                'series_id' : series_id,\n\n                'liver_healthy' : liver_prob[0],\n                'liver_low'     : liver_prob[1],\n                'liver_high'    : liver_prob[2],\n                'spleen_healthy': spleen_prob[0],\n                'spleen_low'    : spleen_prob[1],\n                'spleen_high'   : spleen_prob[2],\n                'kidney_healthy': kidney_prob[0],\n                'kidney_low'    : kidney_prob[1],\n                'kidney_high'   : kidney_prob[2],\n\n                'bowel_healthy'        : bowel_prob[0],\n                'bowel_injury'         : bowel_prob[1],\n                'extravasation_healthy': extravasation_prob[0],\n                'extravasation_injury' : extravasation_prob[1],\n\n                #'any_injury' : 0, # auto generated\n\n            })\n        except:\n            submit_df_data.append({\n                'patient_id': patient_id,\n                'series_id' : series_id, \n            } | dummy)\n            \n        if t<3: #debug and show results\n            L, S, S = image.shape\n\n            v = image.float().data.cpu().numpy() / 255\n            v0_mean = np.clip(v.mean(0), 0, 1)\n            v1_mean = np.clip(v.mean(1), 0, 1)\n            v2_mean = np.clip(v.mean(2), 0, 1)\n            v0_mean = np_min_max_norm(v0_mean)\n            v1_mean = np_min_max_norm(v1_mean)\n            v2_mean = np_min_max_norm(v2_mean)\n\n            overlay1 = cv2.cvtColor(np.hstack([v0_mean, np.zeros((S, S), dtype=np.float32)]), cv2.COLOR_GRAY2RGB)\n            overlay2 = cv2.cvtColor(np.hstack([v1_mean, v2_mean]), cv2.COLOR_GRAY2RGB)\n            overlay1 = (overlay1 * 255).astype(np.uint8)\n            overlay2 = (overlay2 * 255).astype(np.uint8)\n\n            #---\n            p = slice_predict.data.cpu().numpy()\n            z = np.where(p>0)[0]\n            if len(z)==0:\n                z0, z1 = 0,1\n            else:\n                z0, z1 = z.min(), z.max()\n\n            cv2.line(overlay2, (0, z0  ), (S*2, z0  ), (0, 255, 0), 1)\n            cv2.line(overlay2, (0, z1-1), (S*2, z1-1), (0, 255, 0), 1)\n            #---\n            overlay = np.vstack([overlay1,overlay2])\n            cv2.line(overlay, (0, S-1), (S*2, S-1), (255, 255, 255), 1)\n            cv2.line(overlay, (S-1, 0), (S-1, S + L), (255, 255, 255), 1)\n            \n            print('')\n            print('liver_prob: ', liver_prob)\n            print('spleen_prob:', spleen_prob)\n            print('kidney_prob:', kidney_prob)\n            print('image.shape',  image.shape)\n            print('')\n            #image_show_norm('overlay', overlay, resize=1)\n            #cv2.waitKey(1)\n            plt.imshow(overlay)\n            plt.show()\n            \n    #-------------------------------------------------------------------------------------------     \n    print('')\n    submit_col=[\n        #'patient_id',\n        'bowel_healthy','bowel_injury','extravasation_healthy','extravasation_injury','kidney_healthy','kidney_low','kidney_high','liver_healthy','liver_low','liver_high','spleen_healthy','spleen_low','spleen_high'\n    ]\n    submit_df = pd.DataFrame(submit_df_data)\n    #submit_df.to_csv('submit_df.ungroup.csv',index=False)\n\n    gb = submit_df.groupby('patient_id').mean()\n    gb = gb.drop('series_id', axis=1)\n    \n    submit_df = gb\n    #submit_df = submit_df.set_index('patient_id') \n    submit_df = submit_df[submit_col]\n    submit_df = submit_df.loc[valid_df.patient_id]\n    submit_df = submit_df.reset_index(drop=False)\n    submit_df.to_csv('submission.csv',index=False)\n\n    print('submit_df')\n    print('\\t', submit_df.shape)\n    print('\\t', submit_df.patient_id.values.tolist()[:5])\n    print('')\n    print('valid_df')\n    print('\\t', valid_df.shape)\n    print('\\t', valid_df.patient_id.values.tolist()[:5])\n    print('')\n    assert(all(valid_df.patient_id==submit_df.patient_id))\n    #seems that kaggler evaluation server did not check the order of patient_id\n    \n    for i in range(3):\n        print(submit_df.iloc[i])\n        print('-----')\n        \nprint('SUBMISSION OK !!!!')","metadata":{"execution":{"iopub.status.busy":"2023-09-05T17:47:35.923039Z","iopub.execute_input":"2023-09-05T17:47:35.923449Z","iopub.status.idle":"2023-09-05T17:49:29.307043Z","shell.execute_reply.started":"2023-09-05T17:47:35.92341Z","shell.execute_reply":"2023-09-05T17:49:29.305951Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"if mode=='local':\n    #submit_df = pd.read_csv('/kaggle/working/submission.csv')\n    submit_df = pd.read_csv('submission.csv')\n    truth_df = valid_df \n    truth_df = add_weight_to_truth_df(truth_df) \n    lb_score = do_lb_score(truth_df, submit_df, row_id_column_name='patient_id')\n    print('lb_score', lb_score)\n    \n'''\n\n920/921 : 9813,24149  1 hr 19 minn\nsubmit_df\n\t (613, 14)\n\t [10026, 10065, 10228, 10557, 10683]\n\nvalid_df\n\t (613, 15)\n\t [10026, 10065, 10228, 10557, 10683]\n\n\nlb_score 0.44936877206751086\n'''","metadata":{"execution":{"iopub.status.busy":"2023-09-05T17:49:29.308853Z","iopub.execute_input":"2023-09-05T17:49:29.309449Z","iopub.status.idle":"2023-09-05T17:49:29.374879Z","shell.execute_reply.started":"2023-09-05T17:49:29.309329Z","shell.execute_reply":"2023-09-05T17:49:29.373937Z"},"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"}}