{"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":"# UW-Madison GI Tract Image Segmentation\n\n\nThanks [Yiheng Wang](https://www.kaggle.com/competitions/uw-madison-gi-tract-image-segmentation/discussion/325646) and [Yash Goel](https://www.kaggle.com/code/goelyash/3d-solution-with-monai)","metadata":{}},{"cell_type":"markdown","source":"## Load Libs","metadata":{}},{"cell_type":"code","source":"import sys\n\nsys.path.append('../input/monai-v081/')","metadata":{"execution":{"iopub.status.busy":"2022-07-08T00:37:52.094832Z","iopub.execute_input":"2022-07-08T00:37:52.095086Z","iopub.status.idle":"2022-07-08T00:37:52.098931Z","shell.execute_reply.started":"2022-07-08T00:37:52.095057Z","shell.execute_reply":"2022-07-08T00:37:52.097945Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import gc\nfrom glob import glob\nimport os\nimport numpy as np\nimport pandas as pd\nimport torch\nfrom torch import nn\nfrom monai.inferers import sliding_window_inference\nfrom monai.data import decollate_batch\nfrom monai.handlers.utils import from_engine\nfrom monai.networks.nets import UNet\nfrom torch.cuda.amp import GradScaler, autocast\nfrom tqdm import tqdm\nimport json","metadata":{"execution":{"iopub.status.busy":"2022-07-08T00:37:52.100741Z","iopub.execute_input":"2022-07-08T00:37:52.101664Z","iopub.status.idle":"2022-07-08T00:37:52.110673Z","shell.execute_reply.started":"2022-07-08T00:37:52.101627Z","shell.execute_reply":"2022-07-08T00:37:52.109921Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"from monai.data import CacheDataset, DataLoader\nfrom monai.transforms import (\n    Compose,\n    Activations,\n    AsDiscrete,\n    Activationsd,\n    AsDiscreted,\n    KeepLargestConnectedComponentd,\n    Invertd,\n    LoadImage,\n    Transposed,\n    LoadImaged,\n    AddChanneld,\n    CastToTyped,\n    Lambdad,\n    Resized,\n    EnsureTyped,\n    SpatialPadd,\n    EnsureChannelFirstd,\n)","metadata":{"execution":{"iopub.status.busy":"2022-07-08T00:37:52.112285Z","iopub.execute_input":"2022-07-08T00:37:52.113029Z","iopub.status.idle":"2022-07-08T00:37:52.120150Z","shell.execute_reply.started":"2022-07-08T00:37:52.112994Z","shell.execute_reply":"2022-07-08T00:37:52.119316Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Prepare meta info.","metadata":{}},{"cell_type":"markdown","source":"### Thanks awsaf49, this section refers to:\nhttps://www.kaggle.com/code/awsaf49/uwmgi-2-5d-infer-pytorch","metadata":{}},{"cell_type":"code","source":"def get_metadata(row):\n    data = row['id'].split('_')\n    case = int(data[0].replace('case',''))\n    day = int(data[1].replace('day',''))\n    slice_ = int(data[-1])\n    row['case'] = case\n    row['day'] = day\n    row['slice'] = slice_\n    return row\n\ndef path2info(row):\n    path = row['image_path']\n    data = path.split('/')\n    slice_ = int(data[-1].split('_')[1])\n    case = int(data[-3].split('_')[0].replace('case',''))\n    day = int(data[-3].split('_')[1].replace('day',''))\n    width = int(data[-1].split('_')[2])\n    height = int(data[-1].split('_')[3])\n    row['height'] = height\n    row['width'] = width\n    row['case'] = case\n    row['day'] = day\n    row['slice'] = slice_\n    return row","metadata":{"execution":{"iopub.status.busy":"2022-07-08T00:37:52.121592Z","iopub.execute_input":"2022-07-08T00:37:52.122112Z","iopub.status.idle":"2022-07-08T00:37:52.133094Z","shell.execute_reply.started":"2022-07-08T00:37:52.122077Z","shell.execute_reply":"2022-07-08T00:37:52.132377Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"sub_df = pd.read_csv('../input/uw-madison-gi-tract-image-segmentation/sample_submission.csv')\nif not len(sub_df):\n    debug = True\n    sub_df = pd.read_csv('../input/uw-madison-gi-tract-image-segmentation/train.csv')[:1000*3]\n    sub_df = sub_df.drop(columns=['class','segmentation']).drop_duplicates()\nelse:\n    debug = False\n    sub_df = sub_df.drop(columns=['class','predicted']).drop_duplicates()\nsub_df = sub_df.apply(lambda x: get_metadata(x),axis=1)","metadata":{"execution":{"iopub.status.busy":"2022-07-08T00:37:52.136289Z","iopub.execute_input":"2022-07-08T00:37:52.136473Z","iopub.status.idle":"2022-07-08T00:37:54.223422Z","shell.execute_reply.started":"2022-07-08T00:37:52.136445Z","shell.execute_reply":"2022-07-08T00:37:54.222714Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"if debug:\n    paths = glob(f'/kaggle/input/uw-madison-gi-tract-image-segmentation/train/**/*png',recursive=True)\n#     paths = sorted(paths)\nelse:\n    paths = glob(f'/kaggle/input/uw-madison-gi-tract-image-segmentation/test/**/*png',recursive=True)\n#     paths = sorted(paths)\npath_df = pd.DataFrame(paths, columns=['image_path'])\npath_df = path_df.apply(lambda x: path2info(x),axis=1)\npath_df.head()","metadata":{"execution":{"iopub.status.busy":"2022-07-08T00:37:54.225141Z","iopub.execute_input":"2022-07-08T00:37:54.225570Z","iopub.status.idle":"2022-07-08T00:39:23.291359Z","shell.execute_reply.started":"2022-07-08T00:37:54.225534Z","shell.execute_reply":"2022-07-08T00:39:23.290569Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Produce 3d data list for MONAI DataSet","metadata":{}},{"cell_type":"code","source":"test_df = sub_df.merge(path_df, on=['case','day','slice'], how='left')\ntest_df[\"case_id_str\"] = test_df[\"id\"].apply(lambda x: x.split(\"_\", 2)[0])\ntest_df[\"day_num_str\"] = test_df[\"id\"].apply(lambda x: x.split(\"_\", 2)[1])\ntest_df[\"slice_id\"] = test_df[\"id\"].apply(lambda x: x.split(\"_\", 2)[2])","metadata":{"execution":{"iopub.status.busy":"2022-07-08T00:39:23.292430Z","iopub.execute_input":"2022-07-08T00:39:23.293225Z","iopub.status.idle":"2022-07-08T00:39:23.314025Z","shell.execute_reply.started":"2022-07-08T00:39:23.293182Z","shell.execute_reply":"2022-07-08T00:39:23.313390Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"test_data = []\n\nfor group in test_df.groupby([\"case_id_str\", \"day_num_str\"]):\n\n    case_id_str, day_num_str = group[0]\n    group_id = case_id_str + \"_\" + day_num_str\n    group_df = group[1].sort_values(\"slice_id\", ascending=True)\n    n_slices = group_df.shape[0]\n    group_slices, group_ids = [], []\n    for idx in range(n_slices):\n        slc = group_df.iloc[idx]\n        group_slices.append(slc.image_path)\n        group_ids.append(slc.id)\n    test_data.append({\"image\": group_slices, \"id\": group_ids})","metadata":{"execution":{"iopub.status.busy":"2022-07-08T00:39:23.315369Z","iopub.execute_input":"2022-07-08T00:39:23.315612Z","iopub.status.idle":"2022-07-08T00:39:23.449289Z","shell.execute_reply.started":"2022-07-08T00:39:23.315578Z","shell.execute_reply":"2022-07-08T00:39:23.448583Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Prepare Transforms, Dataset, DataLoader","metadata":{}},{"cell_type":"code","source":"class cfg:\n    img_size = (224, 224, 80)\n    in_channels = 1\n    out_channels = 3\n    device = torch.device(\"cuda:0\" if torch.cuda.is_available() else \"cpu\")\n    weights = ['../input/uwmadison-gi-tract-image-segmentation-weights/best_weights_fold_0.pth', '../input/uwmadison-gi-tract-image-segmentation-weights/best_weights_fold_1.pth']\n    \n    weights2 = glob(\"../input/uw3dweights/large*\")\n    batch_size = 1\n    sw_batch_size = 4","metadata":{"execution":{"iopub.status.busy":"2022-07-08T00:39:23.450347Z","iopub.execute_input":"2022-07-08T00:39:23.450575Z","iopub.status.idle":"2022-07-08T00:39:23.458579Z","shell.execute_reply.started":"2022-07-08T00:39:23.450543Z","shell.execute_reply":"2022-07-08T00:39:23.456915Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"test_transforms = Compose(\n    [\n        LoadImaged(keys=\"image\"), # d, h, w\n        AddChanneld(keys=\"image\"), # c, d, h, w\n        Transposed(keys=\"image\", indices=[0, 2, 3, 1]), # c, w, h, d\n        Lambdad(keys=\"image\", func=lambda x: x / x.max()),\n#         SpatialPadd(keys=\"image\", spatial_size=cfg.img_size),  # in case less than 80 slices\n        EnsureTyped(keys=\"image\", dtype=torch.float32),\n    ]\n)\n\ntest_ds = CacheDataset(\n        data=test_data,\n        transform=test_transforms,\n        cache_rate=0.0,\n        num_workers=2,\n    )\n\ntest_dataloader = DataLoader(\n    test_ds,\n    batch_size=cfg.batch_size,\n    num_workers=2,\n    pin_memory=True,\n)","metadata":{"execution":{"iopub.status.busy":"2022-07-08T00:39:23.461033Z","iopub.execute_input":"2022-07-08T00:39:23.461656Z","iopub.status.idle":"2022-07-08T00:39:23.472516Z","shell.execute_reply.started":"2022-07-08T00:39:23.461628Z","shell.execute_reply":"2022-07-08T00:39:23.471641Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Prepare Network","metadata":{}},{"cell_type":"code","source":"model = UNet(\n    spatial_dims=3,\n    in_channels=cfg.in_channels,\n    out_channels=cfg.out_channels,\n    channels=(32, 64, 128, 256, 512),\n    strides=(2, 2, 2, 2),\n    kernel_size=3,\n    up_kernel_size=3,\n    num_res_units=2,\n    act=\"PRELU\",\n    norm=\"BATCH\",\n    dropout=0.8,\n    bias=True,\n    dimensions=None,\n).to(cfg.device)","metadata":{"execution":{"iopub.status.busy":"2022-07-08T00:39:23.475417Z","iopub.execute_input":"2022-07-08T00:39:23.475888Z","iopub.status.idle":"2022-07-08T00:39:23.655251Z","shell.execute_reply.started":"2022-07-08T00:39:23.475839Z","shell.execute_reply":"2022-07-08T00:39:23.654555Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Infer","metadata":{}},{"cell_type":"code","source":"# ref.: https://www.kaggle.com/stainsby/fast-tested-rle\ndef rle_encode(img):\n    \"\"\" TBD\n    \n    Args:\n        img (np.array): \n            - 1 indicating mask\n            - 0 indicating background\n    \n    Returns: \n        run length as string formated\n    \"\"\"\n    \n    pixels = img.flatten()\n    pixels = np.concatenate([[0], pixels, [0]])\n    runs = np.where(pixels[1:] != pixels[:-1])[0] + 1\n    runs[1::2] -= runs[::2]\n    return ' '.join(str(x) for x in runs)","metadata":{"execution":{"iopub.status.busy":"2022-07-08T00:39:23.658331Z","iopub.execute_input":"2022-07-08T00:39:23.658515Z","iopub.status.idle":"2022-07-08T00:39:23.663527Z","shell.execute_reply.started":"2022-07-08T00:39:23.658490Z","shell.execute_reply":"2022-07-08T00:39:23.662876Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"outputs = []\n\npost_pred = Compose([\n    Activations(sigmoid=True),\n    AsDiscrete(threshold=0.28), # 0.5 -> 0.28\n])\n\nmodel.eval()\ntorch.set_grad_enabled(False)\nprogress_bar = tqdm(range(len(test_dataloader)))\nval_it = iter(test_dataloader)\nfor itr in progress_bar:\n    batch = next(val_it)\n    test_inputs = batch[\"image\"].to(cfg.device)\n\n    pred_all = []\n    for weights in cfg.weights:\n        model.load_state_dict(torch.load(weights)['model'])\n        pred = sliding_window_inference(test_inputs, cfg.img_size, cfg.sw_batch_size, model)\n        pred_all.append(pred)\n        # do 4 tta\n        for dims in [[2], [3], [2, 3]]:\n            flip_pred = sliding_window_inference(torch.flip(test_inputs, dims=dims), cfg.img_size, cfg.sw_batch_size, model)\n            flip_pred = torch.flip(flip_pred, dims=dims)\n            pred_all.append(flip_pred)\n            \n    for weights in cfg.weights2:\n        model.load_state_dict(torch.load(weights))\n        pred = sliding_window_inference(test_inputs, cfg.img_size, cfg.sw_batch_size, model)\n        pred_all.append(pred)\n        # do 4 tta\n        for dims in [[2], [3], [2, 3]]:\n            flip_pred = sliding_window_inference(torch.flip(test_inputs, dims=dims), cfg.img_size, cfg.sw_batch_size, model)\n            flip_pred = torch.flip(flip_pred, dims=dims)\n            pred_all.append(flip_pred)\n    \n    pred_all = torch.mean(torch.stack(pred_all), dim=0)[0]\n    pred_all = post_pred(pred_all)\n    # c, w, h, d to d, c, h, w\n    pred_all = torch.permute(pred_all, [3, 0, 2, 1]).cpu().numpy().astype(np.uint8)\n    id_outputs = from_engine([\"id\"])(batch)[0]\n\n    for test_output, id_output in zip(pred_all, id_outputs):\n        id_name = id_output[0]\n        lb, sb, st = test_output\n        outputs.append([id_name, \"large_bowel\", rle_encode(lb)])\n        outputs.append([id_name, \"small_bowel\", rle_encode(sb)])\n        outputs.append([id_name, \"stomach\", rle_encode(st)])\n","metadata":{"execution":{"iopub.status.busy":"2022-07-08T00:39:23.664578Z","iopub.execute_input":"2022-07-08T00:39:23.665249Z","iopub.status.idle":"2022-07-08T00:40:23.949874Z","shell.execute_reply.started":"2022-07-08T00:39:23.664909Z","shell.execute_reply":"2022-07-08T00:40:23.948948Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"submit = pd.DataFrame(data=np.array(outputs), columns=[\"id\", \"class\", \"predicted\"])","metadata":{"execution":{"iopub.status.busy":"2022-07-08T00:40:23.951799Z","iopub.execute_input":"2022-07-08T00:40:23.952051Z","iopub.status.idle":"2022-07-08T00:40:24.014560Z","shell.execute_reply.started":"2022-07-08T00:40:23.952012Z","shell.execute_reply":"2022-07-08T00:40:24.013710Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Fix sub error, refers to: https://www.kaggle.com/competitions/uw-madison-gi-tract-image-segmentation/discussion/320541\nif not debug:\n    sub_df = pd.read_csv('../input/uw-madison-gi-tract-image-segmentation/sample_submission.csv')\n    del sub_df['predicted']\n    sub_df = sub_df.merge(submit, on=['id','class'])\n    sub_df.to_csv('submission.csv',index=False)\nelse:\n    submit.to_csv('submission.csv', index=False)","metadata":{"execution":{"iopub.status.busy":"2022-07-08T00:40:24.015876Z","iopub.execute_input":"2022-07-08T00:40:24.016150Z","iopub.status.idle":"2022-07-08T00:40:24.051309Z","shell.execute_reply.started":"2022-07-08T00:40:24.016110Z","shell.execute_reply":"2022-07-08T00:40:24.050638Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"submit[ submit['predicted'] == '']","metadata":{"execution":{"iopub.status.busy":"2022-07-08T00:40:24.052588Z","iopub.execute_input":"2022-07-08T00:40:24.052815Z","iopub.status.idle":"2022-07-08T00:40:24.068852Z","shell.execute_reply.started":"2022-07-08T00:40:24.052783Z","shell.execute_reply":"2022-07-08T00:40:24.068162Z"},"trusted":true},"execution_count":null,"outputs":[]}]}