{"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-05T14:30:56.740009Z","iopub.execute_input":"2022-07-05T14:30:56.740269Z","iopub.status.idle":"2022-07-05T14:30:56.763917Z","shell.execute_reply.started":"2022-07-05T14:30:56.740205Z","shell.execute_reply":"2022-07-05T14:30:56.763312Z"},"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-05T14:30:56.76504Z","iopub.execute_input":"2022-07-05T14:30:56.765344Z","iopub.status.idle":"2022-07-05T14:31:03.66283Z","shell.execute_reply.started":"2022-07-05T14:30:56.765307Z","shell.execute_reply":"2022-07-05T14:31:03.662073Z"},"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-05T14:31:03.66458Z","iopub.execute_input":"2022-07-05T14:31:03.664841Z","iopub.status.idle":"2022-07-05T14:31:03.671537Z","shell.execute_reply.started":"2022-07-05T14:31:03.664808Z","shell.execute_reply":"2022-07-05T14:31:03.670807Z"},"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-05T14:31:03.673863Z","iopub.execute_input":"2022-07-05T14:31:03.674343Z","iopub.status.idle":"2022-07-05T14:31:03.685472Z","shell.execute_reply.started":"2022-07-05T14:31:03.674308Z","shell.execute_reply":"2022-07-05T14:31:03.684695Z"},"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-05T14:31:03.687674Z","iopub.execute_input":"2022-07-05T14:31:03.68825Z","iopub.status.idle":"2022-07-05T14:31:05.952396Z","shell.execute_reply.started":"2022-07-05T14:31:03.688214Z","shell.execute_reply":"2022-07-05T14:31:05.951676Z"},"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-05T14:31:05.956563Z","iopub.execute_input":"2022-07-05T14:31:05.958512Z","iopub.status.idle":"2022-07-05T14:32:38.148566Z","shell.execute_reply.started":"2022-07-05T14:31:05.958472Z","shell.execute_reply":"2022-07-05T14:32:38.147882Z"},"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-05T14:32:38.150441Z","iopub.execute_input":"2022-07-05T14:32:38.150728Z","iopub.status.idle":"2022-07-05T14:32:38.177556Z","shell.execute_reply.started":"2022-07-05T14:32:38.150691Z","shell.execute_reply":"2022-07-05T14:32:38.176896Z"},"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-05T14:32:38.178859Z","iopub.execute_input":"2022-07-05T14:32:38.179121Z","iopub.status.idle":"2022-07-05T14:32:38.310569Z","shell.execute_reply.started":"2022-07-05T14:32:38.179087Z","shell.execute_reply":"2022-07-05T14:32:38.309963Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"","metadata":{"execution":{"iopub.status.busy":"2022-07-05T14:32:47.325405Z","iopub.execute_input":"2022-07-05T14:32:47.325657Z","iopub.status.idle":"2022-07-05T14:32:47.337326Z","shell.execute_reply.started":"2022-07-05T14:32:47.32563Z","shell.execute_reply":"2022-07-05T14:32:47.336348Z"},"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-05T14:33:04.843843Z","iopub.execute_input":"2022-07-05T14:33:04.844114Z","iopub.status.idle":"2022-07-05T14:33:04.905524Z","shell.execute_reply.started":"2022-07-05T14:33:04.844085Z","shell.execute_reply":"2022-07-05T14:33:04.904728Z"},"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-05T14:33:07.016504Z","iopub.execute_input":"2022-07-05T14:33:07.017002Z","iopub.status.idle":"2022-07-05T14:33:07.026329Z","shell.execute_reply.started":"2022-07-05T14:33:07.016963Z","shell.execute_reply":"2022-07-05T14:33:07.025498Z"},"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.2,\n    bias=True,\n    dimensions=None,\n).to(cfg.device)","metadata":{"execution":{"iopub.status.busy":"2022-07-05T14:33:09.394126Z","iopub.execute_input":"2022-07-05T14:33:09.394664Z","iopub.status.idle":"2022-07-05T14:33:12.631495Z","shell.execute_reply.started":"2022-07-05T14:33:09.394625Z","shell.execute_reply":"2022-07-05T14:33:12.63075Z"},"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-05T14:32:38.71711Z","iopub.status.idle":"2022-07-05T14:32:38.717723Z","shell.execute_reply.started":"2022-07-05T14:32:38.717494Z","shell.execute_reply":"2022-07-05T14:32:38.717518Z"},"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-05T14:32:38.718884Z","iopub.status.idle":"2022-07-05T14:32:38.719461Z","shell.execute_reply.started":"2022-07-05T14:32:38.719235Z","shell.execute_reply":"2022-07-05T14:32:38.719259Z"},"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-05T14:32:38.720599Z","iopub.status.idle":"2022-07-05T14:32:38.721185Z","shell.execute_reply.started":"2022-07-05T14:32:38.72094Z","shell.execute_reply":"2022-07-05T14:32:38.720964Z"},"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-05T14:32:38.722403Z","iopub.status.idle":"2022-07-05T14:32:38.723073Z","shell.execute_reply.started":"2022-07-05T14:32:38.722789Z","shell.execute_reply":"2022-07-05T14:32:38.722816Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"submit[ submit['predicted'] == '']","metadata":{},"execution_count":null,"outputs":[]}]}