{"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":"# 1st Place Solution Inference\n\nHi all,\n\nI'm very exciting to writing this notebook and the summary of our solution here.\n\nThis is a small version of inference, using: \n\n* 5-fold stage1 models (128x128x128)\n* 5-fold stage2 type1 models (224x224)\n* 5-fold stage2 type2 models (224x224)\n\n15 models in total.\n\nAfter ALL models are trained, we now are able to do a submission.\n\nI've already uploaded the models that using public notebooks but trained locally and made it public.\n\nThe submission time is around 130 min (mostly due to data loading) and is able to get such scores ↓\n\n![image.png](attachment:92760e1c-8709-41db-8973-2f98f35b0989.png)\n\nTo get higher score, you only need to:\n\n* use higher input resolution to train models.\n* use bigger backbones.\n* ensemble more models.\n\nTo see more details of my solution: https://www.kaggle.com/competitions/rsna-2022-cervical-spine-fracture-detection/discussion/362607\n\n* Train Stage1 Notebook: https://www.kaggle.com/code/haqishen/rsna-2022-1st-place-solution-train-stage1\n* Train Stage2 (Type1) Notebook: https://www.kaggle.com/code/haqishen/rsna-2022-1st-place-solution-train-stage2-type1\n* Train Stage2 (Type2) Notebook: https://www.kaggle.com/code/haqishen/rsna-2022-1st-place-solution-train-stage2-type2\n* Inference Notebook: This notebook\n\nThanks!","metadata":{},"attachments":{"92760e1c-8709-41db-8973-2f98f35b0989.png":{"image/png":"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2SQgAQlIQAISkIBCymtAAhKQgAQkIAEJTCSgkJoIzmwSkIAEJCABCUhAIeU1IAEJSEACEpCABCYSUEhNBGc2CUhAAhKQgAQkoJDyGpCABCQgAQlIQAITCSikJoIzmwQkIAEJSEACElBIeQ1IQAISkIAEJCCBiQQUUhPBmU0CEpCABCQgAQkopLwGJCABCUhAAhKQwEQCCqmJ4MwmAQlIQAISkIAEFFJeAxKQgAQkIAEJSGAiAYXURHBmk4AEJCABCUhAAgoprwEJSEACEpCABCQwkYBCaiI4s0lAAhKQgAQkIAGFlNeABCQgAQlIQAISmEhAITURnNkkIAEJSEACEpCAQsprQAISkIAEJCABCUwkoJCaCM5sEpCABCQgAQlIQCHlNSABCUhAAhKQgAQmElBITQRnNglIQAISkIAEJKCQStfAP/7xj+quu+6qXvWqV1VbbbVV2uJHCUhAAhJYKgJPPPFE9fvf/74u7jWveU31nOc8Z6mKthwJrHMCsxVSK1eurO65555OIM9//vOrLbbYonrZy15WC59NNtmkc98hG/72t79V5513XrPr0UcfXb30pS9tvvthVQIPPvhgdffdd1d//etfq7/85S8VInTrrbeu22Obbbap24T2MUlAAsMI0In75S9/2bnzpptuWr3gBS+oO3mIj+c973md+67phmuuuab6z3/+Uxfztre9rXrFK14xushf/epX1Z/+9Kc6H53TXXbZpSnj0ksvrX73u9/V31/3utdV73vf+5pt6+rDU089VdcP248du/feeyueI5wrzxX+XvnKV66r6nic9ZjAbIXUlVdeWf32t78dhHbzzTevPvjBD1Y8wKemn//859Xy5cub7O9617uq3Xbbrfn+bH+47777qscee6yuxote9KKKc3620h133FFdfvnlvYd/7nOfW7fJtttu27ufGyUggf8RQHggYIamAw88sNppp52G7j5qvy996UuNvTn44IOr17/+9aPys/NVV11V/eY3v6nzIaL233//+vOTTz5ZnXrqqfXn+HfSSSfVIia+r+3lo48+Wl1xxRXVH//4x95DvelNb6r222+/ChFrkkAXgQ1CSHFyPLg//OEPTxZTDzzwQPXNb36z4XTsscdWCJa5pDPOOKP617/+VVfnPe95T8UN/mykG264oVqxYsXgQ2M8c090cEZ3lMBGRmCskALP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J5208N5PPPGEe/zxxwvPF53YZJNNsu9SdD48/t5777nTTjstYzHjjDO6ySef3O2zzz5u1FFHDau29s866yz3yy+/tPbLPsw///xuxRVXLKvCOQhAoEsEvv76a3fnnXdmPke+TWWaaaZxc8wxh1t99dUzX9eNR/3222+ZTxs8eLB79dVXM/8xyyyzuNlnn93NPPPMbt555y31IWU2NPFJup+ue+CBB9xrr73mPv74YzfGGGM42dSvXz+38soru4kmmqjssa1zv/76q5P/ffTRR7P7fPrpp06+0b7X4osv3qrLhzQJIKR60G5///23u+CCC9y9995beJdJJpnEHXnkkZnzKaxU48QjjzySCZSyqsccc0wmqPLqXH/99e6aa67JO1V67MQTT8ycWWml/zv5119/uYEDB7p33nmnrfqtt95aKCZ///13t+6667bVL9uR895hhx3KqnAOAhDoAgGJCHVyysp+++3nllpqqbIqled++OGHzEe+8cYbhXUXW2yxrEMmMdNJaeKTdP+q765O7M477+z69+9fao6+20EHHZQJw6KKiyyySHavusKs6D4cH3kEEFI9YC8nox9cVdEI1cknn5yNzlTVzTs/aNAgd8kll+SdGubY/vvv75Zccslhjo8IIaWe60UXXTTMs8uElHpn22233TDXFB1ASBWR4TgEukegTsfNnnbwwQe7RRdd1HY72n711VdO13/00UeV12kU7NBDD3XjjDNOZV2r0MQnPfTQQ+6MM86wW5Rut912W7fmmmvm1pGIkr1hxzKvsv5GyHf27t077zTHIieAkGrYQC+88II77LDDWldr5Gn77bd3c845ZzZNddttt2XTfVZBU1Iameq0aHh59913b1022WSTud122y0bFtZw+EsvvZSJtFaF/3y47rrr3Ljjjusfct9//7374IMP2o7l7cjxPPnkk61T+nFreq6qfP75526bbbbJrVYmpDRNqV6tinp5PtO8m6nXNsUUU+Sd4hgEINAFAt99953bbLPN2u604447uoUWWij7jWqKSiPxVvS7veKKKxqJgOOOO8499dRTdiu38cYbu2WXXdb16dMnC5OQL/P9kc4r3KBOaeKTQr+u76YR8LnnnjubWpS/vfTSS50YWbnqqqvcBBNMYLutrUIa/FG2ZZZZxq2yyirZtJ7CQNQJ9zvi66+/vtt8881b1/MhHQIIqQZt9c8//7i99tqr1dPQj+300093U045ZdvdNIqk0SQrxx9/fCa0bL/O1r+HniMHFg4Bv/XWW27vvfdu3U6f5Yw6LRJmEkPmJDSypRGuOuWoo45yzz77bG7VMiH12GOPuZNOOim7brrppnNnn3127j04CAEIjBgCEkW33HJL62F5o9xPP/20O/bYY1t1JLw23HDD1n6dD6FgyxNJCp+Q2HrmmWeyW2rk5uqrr65ze9fEJx1++OHu+eefz+4vfyufrXgmv3z44YdZCIPFdUoc7bLLLn6VLKbMOog6oVjZPfbYw/Xq1autnjqq6rxaqdtxtfps4yCAkGrQDvohaX7cStHwroIM1cOwH9xqq63mdtppJ7us1tb/YevHuOeee+Zepx6jDY/nOaTci4KDCpi/8MILW0fPPffcWrFdCmJXLJUVOYwzzzzTdl2ZkLr99tuzHp4qK1bgkEMOaV3HBwhAYMQSUCdRgsh8lhbMaHoqr/i+SSPlF198cV61wmMardGojZUiERH6F4m8qlip8Jo6PimM19x0003dRhttZOa1be++++62UTmFTvhTjmEoxc033+zGHHPMtntoRzFc8tfGe7311nNbbLHFMPU4EDcBhFSD9rnvvvucRIYV9eAmnnhi223bSphIoKho+u/yyy9vO1+1o1iFTz75JKumlStaxZJX/GHkAQMGuA022CCvWuGxcDRq6aWXdvvuu29hfTvx448/Ook4G8WSI9AQtqYfrZQJKX/EbY011simR+06thCAwIgloOl/f3QlbzTKLNJKtBNOOMF23WWXXZZNybUOVHz48ssv3f3335/VGmWUUTIBF47Y6KRWKPtiTmETo402WuHdm/ok+VmFZ1jRiJum9PKKpuZ8H3f00UdnKwutrmKsFGulotgun5PVsa06obaiWqsCTznlFDvFNhECCKkGDaXl/RI4KlX/8bWi77zzzms95corrxxmaq51suGHn3/+uW1YXXFGimfopNxxxx1tPUrZrBQOVUX1bNWihKKmHuWQfCdTJqQ0dG4xEFtttVVmtxynYsM0rN+3b1+n+DJxpkAAAsOXwIMPPtg2mnzTTTe5scYaK/ehSsey6667ts4dcMABbokllmjtd+vDtddem8V96n6aZqsKBG/qkzSir06hFYUcKLVMXglFVzgr4f+NUAdYQquo+KJLdfypvqJrOB4X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"}}},{"cell_type":"code","source":"DEBUG = False","metadata":{"execution":{"iopub.status.busy":"2023-01-16T16:26:08.124571Z","iopub.execute_input":"2023-01-16T16:26:08.125057Z","iopub.status.idle":"2023-01-16T16:26:08.153142Z","shell.execute_reply.started":"2023-01-16T16:26:08.124967Z","shell.execute_reply":"2023-01-16T16:26:08.152212Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import os\nimport sys\nsys.path = [\n    '../input/covn3d-same',\n    '../input/timm20221011/pytorch-image-models-master',\n    '../input/smp20210127/segmentation_models.pytorch-master/segmentation_models.pytorch-master',\n    '../input/smp20210127/pretrained-models.pytorch-master/pretrained-models.pytorch-master',\n    '../input/smp20210127/EfficientNet-PyTorch-master/EfficientNet-PyTorch-master',\n] + sys.path\n\n!pip -q install ../input/pylibjpeg140py3/pylibjpeg-1.4.0-py3-none-any.whl\n!pip -q install ../input/pylibjpeg140py3/python_gdcm-3.0.17.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl\n\n!cp -r ../input/timm-20220211/pytorch-image-models-master/timm ./timm4smp","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","execution":{"iopub.status.busy":"2023-01-16T16:26:23.30285Z","iopub.execute_input":"2023-01-16T16:26:23.303239Z","iopub.status.idle":"2023-01-16T16:27:29.4174Z","shell.execute_reply.started":"2023-01-16T16:26:23.303209Z","shell.execute_reply":"2023-01-16T16:27:29.416125Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import gc\nimport ast\nimport cv2\nimport time\nimport timm\nimport timm4smp\nimport pickle\nimport random\nimport pydicom\nimport argparse\nimport warnings\nimport threading\nimport numpy as np\nimport pandas as pd\nfrom PIL import Image\nfrom tqdm import tqdm\nfrom glob import glob\nimport albumentations\nimport matplotlib.pyplot as plt\nimport segmentation_models_pytorch as smp\n\nimport torch\nimport torch.nn as nn\nimport torch.optim as optim\nimport torch.cuda.amp as amp\nimport torch.nn.functional as F\nfrom torch.utils.data import DataLoader, Dataset\nfrom pylab import rcParams\n\n%matplotlib inline\ndevice = torch.device('cuda')\ntorch.backends.cudnn.benchmark = True\n\ntimm.__version__, timm4smp.__version__","metadata":{"execution":{"iopub.status.busy":"2023-01-16T16:27:29.420369Z","iopub.execute_input":"2023-01-16T16:27:29.420886Z","iopub.status.idle":"2023-01-16T16:27:37.120227Z","shell.execute_reply.started":"2023-01-16T16:27:29.420837Z","shell.execute_reply":"2023-01-16T16:27:37.119401Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"data_dir = '../input/rsna-2022-cervical-spine-fracture-detection/'\nimage_size_seg = (128, 128, 128)\nmsk_size = image_size_seg[0]\nimage_size_cls = 224\nn_slice_per_c = 15\nn_ch = 5\n\nbatch_size_seg = 1\nnum_workers = 2","metadata":{"execution":{"iopub.status.busy":"2023-01-16T16:27:37.121345Z","iopub.execute_input":"2023-01-16T16:27:37.12194Z","iopub.status.idle":"2023-01-16T16:27:37.12911Z","shell.execute_reply.started":"2023-01-16T16:27:37.121906Z","shell.execute_reply":"2023-01-16T16:27:37.127159Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"if DEBUG:\n    df = pd.read_csv(os.path.join(data_dir, 'train.csv')).head(1500)\n    df = pd.DataFrame({\n        'StudyInstanceUID': df['StudyInstanceUID'].unique().tolist()\n    })\n    df['image_folder'] = df['StudyInstanceUID'].apply(lambda x: os.path.join(data_dir, 'train_images', x))\nelse:\n    df = pd.read_csv(os.path.join(data_dir, 'test.csv'))\n    if df.iloc[0].row_id == '1.2.826.0.1.3680043.10197_C1':\n        # test_images and test.csv are inconsistent in the dev dataset, fixing labels for the dev run.\n        df = pd.DataFrame({\n            \"row_id\": ['1.2.826.0.1.3680043.22327_C1', '1.2.826.0.1.3680043.25399_C1', '1.2.826.0.1.3680043.5876_C1'],\n            \"StudyInstanceUID\": ['1.2.826.0.1.3680043.22327', '1.2.826.0.1.3680043.25399', '1.2.826.0.1.3680043.5876'],\n            \"prediction_type\": [\"C1\", \"C1\", \"patient_overall\"]}\n        )\n    df = pd.DataFrame({\n        'StudyInstanceUID': df['StudyInstanceUID'].unique().tolist()\n    })\n    df['image_folder'] = df['StudyInstanceUID'].apply(lambda x: os.path.join(data_dir, 'test_images', x))\n\ndf.tail()","metadata":{"execution":{"iopub.status.busy":"2023-01-16T16:27:37.13156Z","iopub.execute_input":"2023-01-16T16:27:37.131863Z","iopub.status.idle":"2023-01-16T16:27:37.239726Z","shell.execute_reply.started":"2023-01-16T16:27:37.131837Z","shell.execute_reply":"2023-01-16T16:27:37.238632Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Dataset","metadata":{}},{"cell_type":"code","source":"def load_dicom(path):\n    dicom = pydicom.read_file(path)\n    data = dicom.pixel_array\n    data = cv2.resize(data, (image_size_seg[0], image_size_seg[1]), interpolation = cv2.INTER_AREA)\n    return data\n\n\ndef load_dicom_line_par(path):\n\n    t_paths = sorted(glob(os.path.join(path, \"*\")), key=lambda x: int(x.split('/')[-1].split(\".\")[0]))\n\n    n_scans = len(t_paths)\n#     print(n_scans)\n    indices = np.quantile(list(range(n_scans)), np.linspace(0., 1., image_size_seg[2])).round().astype(int)\n    t_paths = [t_paths[i] for i in indices]\n\n    images = []\n    for filename in t_paths:\n        images.append(load_dicom(filename))\n    images = np.stack(images, -1)\n    \n    images = images - np.min(images)\n    images = images / (np.max(images) + 1e-4)\n    images = (images * 255).astype(np.uint8)\n\n    return images\n\n\nclass SegTestDataset(Dataset):\n\n    def __init__(self, df):\n        self.df = df.reset_index()\n\n    def __len__(self):\n        return self.df.shape[0]\n\n    def __getitem__(self, index):\n        row = self.df.iloc[index]\n\n        image = load_dicom_line_par(row.image_folder)\n        if image.ndim < 4:\n            image = np.expand_dims(image, 0)\n        image = image.astype(np.float32).repeat(3, 0)  # to 3ch\n        image = image / 255.\n        return torch.tensor(image).float()\n","metadata":{"execution":{"iopub.status.busy":"2023-01-16T16:27:37.241374Z","iopub.execute_input":"2023-01-16T16:27:37.241835Z","iopub.status.idle":"2023-01-16T16:27:37.254324Z","shell.execute_reply.started":"2023-01-16T16:27:37.241793Z","shell.execute_reply":"2023-01-16T16:27:37.253144Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"\ndataset_seg = SegTestDataset(df)\nloader_seg = torch.utils.data.DataLoader(dataset_seg, batch_size=batch_size_seg, shuffle=False, num_workers=num_workers)\n","metadata":{"execution":{"iopub.status.busy":"2023-01-16T16:27:37.256135Z","iopub.execute_input":"2023-01-16T16:27:37.256876Z","iopub.status.idle":"2023-01-16T16:27:37.270453Z","shell.execute_reply.started":"2023-01-16T16:27:37.256834Z","shell.execute_reply":"2023-01-16T16:27:37.269373Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"if DEBUG:\n    rcParams['figure.figsize'] = 20,8\n    for i in range(2):\n        f, axarr = plt.subplots(1,4)\n        for p in range(4):\n            idx = i*4+p\n            img = dataset_seg[idx]\n            img = img[:, :, :, 60]\n            axarr[p].imshow(img.transpose(0, 1).transpose(1,2).squeeze())","metadata":{"execution":{"iopub.status.busy":"2023-01-16T16:27:37.271758Z","iopub.execute_input":"2023-01-16T16:27:37.272553Z","iopub.status.idle":"2023-01-16T16:27:37.28089Z","shell.execute_reply.started":"2023-01-16T16:27:37.272517Z","shell.execute_reply":"2023-01-16T16:27:37.27993Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"if DEBUG:\n    rcParams['figure.figsize'] = 20,8\n    for i in range(2):\n        f, axarr = plt.subplots(1,4)\n        for p in range(4):\n            idx = i*4+p\n            img = dataset_seg[idx]\n            img = img[:, :, 60, :]\n            axarr[p].imshow(img.transpose(0, 1).transpose(1,2).squeeze())","metadata":{"execution":{"iopub.status.busy":"2023-01-16T16:27:37.28211Z","iopub.execute_input":"2023-01-16T16:27:37.282408Z","iopub.status.idle":"2023-01-16T16:27:37.292067Z","shell.execute_reply.started":"2023-01-16T16:27:37.282382Z","shell.execute_reply":"2023-01-16T16:27:37.291037Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"if DEBUG:\n    rcParams['figure.figsize'] = 20,8\n    for i in range(2):\n        f, axarr = plt.subplots(1,4)\n        for p in range(4):\n            idx = i*4+p\n            img = dataset_seg[idx]\n            img = img[:, 60, :, :]\n            axarr[p].imshow(img.transpose(0, 1).transpose(1,2).squeeze())","metadata":{"execution":{"iopub.status.busy":"2023-01-16T16:27:37.29324Z","iopub.execute_input":"2023-01-16T16:27:37.294043Z","iopub.status.idle":"2023-01-16T16:27:37.307583Z","shell.execute_reply.started":"2023-01-16T16:27:37.294008Z","shell.execute_reply":"2023-01-16T16:27:37.306216Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Model","metadata":{}},{"cell_type":"code","source":"from timm4smp.models.layers.conv2d_same import Conv2dSame\nfrom conv3d_same import Conv3dSame\n\ndef convert_3d(module):\n\n    module_output = module\n    if isinstance(module, torch.nn.BatchNorm2d):\n        module_output = torch.nn.BatchNorm3d(\n            module.num_features,\n            module.eps,\n            module.momentum,\n            module.affine,\n            module.track_running_stats,\n        )\n        if module.affine:\n            with torch.no_grad():\n                module_output.weight = module.weight\n                module_output.bias = module.bias\n        module_output.running_mean = module.running_mean\n        module_output.running_var = module.running_var\n        module_output.num_batches_tracked = module.num_batches_tracked\n        if hasattr(module, \"qconfig\"):\n            module_output.qconfig = module.qconfig\n            \n    elif isinstance(module, Conv2dSame):\n        module_output = Conv3dSame(\n            in_channels=module.in_channels,\n            out_channels=module.out_channels,\n            kernel_size=module.kernel_size[0],\n            stride=module.stride[0],\n            padding=module.padding[0],\n            dilation=module.dilation[0],\n            groups=module.groups,\n            bias=module.bias is not None,\n        )\n        module_output.weight = torch.nn.Parameter(module.weight.unsqueeze(-1).repeat(1,1,1,1,module.kernel_size[0]))\n\n    elif isinstance(module, torch.nn.Conv2d):\n        module_output = torch.nn.Conv3d(\n            in_channels=module.in_channels,\n            out_channels=module.out_channels,\n            kernel_size=module.kernel_size[0],\n            stride=module.stride[0],\n            padding=module.padding[0],\n            dilation=module.dilation[0],\n            groups=module.groups,\n            bias=module.bias is not None,\n            padding_mode=module.padding_mode\n        )\n        module_output.weight = torch.nn.Parameter(module.weight.unsqueeze(-1).repeat(1,1,1,1,module.kernel_size[0]))\n\n    elif isinstance(module, torch.nn.MaxPool2d):\n        module_output = torch.nn.MaxPool3d(\n            kernel_size=module.kernel_size,\n            stride=module.stride,\n            padding=module.padding,\n            dilation=module.dilation,\n            ceil_mode=module.ceil_mode,\n        )\n    elif isinstance(module, torch.nn.AvgPool2d):\n        module_output = torch.nn.AvgPool3d(\n            kernel_size=module.kernel_size,\n            stride=module.stride,\n            padding=module.padding,\n            ceil_mode=module.ceil_mode,\n        )\n\n    for name, child in module.named_children():\n        module_output.add_module(\n            name, convert_3d(child)\n        )\n    del module\n\n    return module_output\n\n\n\nclass TimmSegModel(nn.Module):\n    def __init__(self, backbone, segtype='unet', pretrained=False):\n        super(TimmSegModel, self).__init__()\n\n        self.encoder = timm4smp.create_model(\n            backbone,\n            in_chans=3,\n            features_only=True,\n            pretrained=pretrained\n        )\n        g = self.encoder(torch.rand(1, 3, 64, 64))\n        encoder_channels = [1] + [_.shape[1] for _ in g]\n        decoder_channels = [256, 128, 64, 32, 16]\n        if segtype == 'unet':\n            self.decoder = smp.unet.decoder.UnetDecoder(\n                encoder_channels=encoder_channels[:n_blocks+1],\n                decoder_channels=decoder_channels[:n_blocks],\n                n_blocks=n_blocks,\n            )\n        self.segmentation_head = nn.Conv2d(decoder_channels[n_blocks-1], 7, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))\n\n    def forward(self,x):\n        global_features = [0] + self.encoder(x)[:n_blocks]\n        seg_features = self.decoder(*global_features)\n        seg_features = self.segmentation_head(seg_features)\n        return seg_features\n    \n    \nclass TimmModel(nn.Module):\n    def __init__(self, backbone, image_size, pretrained=False):\n        super(TimmModel, self).__init__()\n        self.image_size = image_size\n        self.encoder = timm.create_model(\n            backbone,\n            in_chans=in_chans,\n            num_classes=1,\n            features_only=False,\n            drop_rate=0,\n            drop_path_rate=0,\n            pretrained=pretrained\n        )\n\n        if 'efficient' in backbone:\n            hdim = self.encoder.conv_head.out_channels\n            self.encoder.classifier = nn.Identity()\n        elif 'convnext' in backbone or 'nfnet' in backbone:\n            hdim = self.encoder.head.fc.in_features\n            self.encoder.head.fc = nn.Identity()\n\n        self.lstm = nn.LSTM(hdim, 256, num_layers=2, dropout=0, bidirectional=True, batch_first=True)\n        self.head = nn.Sequential(\n            nn.Linear(512, 256),\n            nn.BatchNorm1d(256),\n            nn.Dropout(0),\n            nn.LeakyReLU(0.1),\n            nn.Linear(256, 1),\n        )\n        self.lstm2 = nn.LSTM(hdim, 256, num_layers=2, dropout=0, bidirectional=True, batch_first=True)\n        self.head2 = nn.Sequential(\n            nn.Linear(512, 256),\n            nn.BatchNorm1d(256),\n            nn.Dropout(0),\n            nn.LeakyReLU(0.1),\n            nn.Linear(256, 1),\n        )\n\n\n    def forward(self, x):  # (bs, nc*7, ch, sz, sz)\n        bs = x.shape[0]\n        x = x.view(bs * n_slice_per_c * 7, in_chans, self.image_size, self.image_size)\n        feat = self.encoder(x)\n        feat = feat.view(bs, n_slice_per_c * 7, -1)\n        feat1, _ = self.lstm(feat)\n        feat1 = feat1.contiguous().view(bs * n_slice_per_c * 7, 512)\n        feat2, _ = self.lstm2(feat)\n\n        return self.head(feat1), self.head2(feat2[:, 0])\n    \n    \n    \n    \nclass Timm1BoneModel(nn.Module):\n    def __init__(self, backbone, image_size, pretrained=False):\n        super(Timm1BoneModel, self).__init__()\n        self.image_size = image_size\n\n        self.encoder = timm.create_model(\n            backbone,\n            in_chans=in_chans,\n            num_classes=1,\n            features_only=False,\n            drop_rate=0,\n            drop_path_rate=0,\n            pretrained=pretrained\n        )\n\n        if 'efficient' in backbone:\n            hdim = self.encoder.conv_head.out_channels\n            self.encoder.classifier = nn.Identity()\n        elif 'convnext' in backbone or 'nfnet' in backbone:\n            hdim = self.encoder.head.fc.in_features\n            self.encoder.head.fc = nn.Identity()\n\n        self.lstm = nn.LSTM(hdim, 256, num_layers=2, dropout=0, bidirectional=True, batch_first=True)\n        self.head = nn.Sequential(\n            nn.Linear(512, 256),\n            nn.BatchNorm1d(256),\n            nn.Dropout(0),\n            nn.LeakyReLU(0.1),\n            nn.Linear(256, 1),\n        )\n\n\n    def forward(self, x):  # (bs, nslice, ch, sz, sz)\n        bs = x.shape[0]\n        x = x.view(bs * n_slice_per_c, in_chans, self.image_size, self.image_size)\n        feat = self.encoder(x)\n        feat = feat.view(bs, n_slice_per_c, -1)\n        feat, _ = self.lstm(feat)\n        feat = feat.contiguous().view(bs * n_slice_per_c, -1)\n        feat = self.head(feat)\n        feat = feat.view(bs, n_slice_per_c).contiguous()\n\n        return feat\n","metadata":{"execution":{"iopub.status.busy":"2023-01-16T16:27:37.311227Z","iopub.execute_input":"2023-01-16T16:27:37.311752Z","iopub.status.idle":"2023-01-16T16:27:37.356405Z","shell.execute_reply.started":"2023-01-16T16:27:37.311718Z","shell.execute_reply":"2023-01-16T16:27:37.3554Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Load Models","metadata":{}},{"cell_type":"code","source":"models_seg = []\n\n#kernel_type = '/kaggle/input/seg-v2s-0911/timm3d_v2s_unet4b_128_128_128_dsv2_flip12_shift333p7_gd1p5_mixup1_lr1e3_20x50ep'\nkernel_type='timm3d_res18d_unet4b_128_128_128_dsv2_flip12_shift333p7_gd1p5_bs4_lr3e4_20x50ep'\nbackbone = 'resnet18d'\nmodel_dir_seg = '/kaggle/input/segv2s0911atika'\nn_blocks = 4\nfor fold in range(5):\n    model = TimmSegModel(backbone, pretrained=False)\n    model = convert_3d(model)\n    #model = model.to(device)\n    load_model_file = os.path.join(model_dir_seg, f'{kernel_type}_fold{fold}_best.pth')\n    sd = torch.load(load_model_file, map_location=torch.device('cpu'))\n    if 'model_state_dict' in sd.keys():\n        sd = sd['model_state_dict']\n    sd = {k[7:] if k.startswith('module.') else k: sd[k] for k in sd.keys()}\n    model.load_state_dict(sd, strict=True)\n    model.eval()\n    models_seg.append(model)\n\nlen(models_seg)","metadata":{"execution":{"iopub.status.busy":"2023-01-16T16:27:37.357674Z","iopub.execute_input":"2023-01-16T16:27:37.357993Z","iopub.status.idle":"2023-01-16T16:27:48.126626Z","shell.execute_reply.started":"2023-01-16T16:27:37.357964Z","shell.execute_reply":"2023-01-16T16:27:48.125526Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"kernel_type = '0920_1bonev2_effv2s_224_15_6ch_augv2_mixupp5_drl3_rov1p2_bs8_lr23e5_eta23e6_50ep'\ndel_dir_cls = '/kaggle/input/rsnastage2type1v2s224atika'\nbackbone = 'tf_efficientnetv2_s_in21ft1k'\nin_chans = 6\nmodels_cls1 = []\n\nfor fold in range(5):\n    model = Timm1BoneModel(backbone, image_size=224, pretrained=False)\n    load_model_file = os.path.join(del_dir_cls, f'{kernel_type}_fold{fold}_best.pth')\n    sd = torch.load(load_model_file, map_location=torch.device('cpu'))\n    if 'model_state_dict' in sd.keys():\n        sd = sd['model_state_dict']\n    sd = {k[7:] if k.startswith('module.') else k: sd[k] for k in sd.keys()}\n    model.load_state_dict(sd, strict=True)\n    #model = model.to(device)\n    model.eval()\n    models_cls1.append(model)\n\nlen(models_cls1)","metadata":{"execution":{"iopub.status.busy":"2023-01-16T16:27:48.12798Z","iopub.execute_input":"2023-01-16T16:27:48.1283Z","iopub.status.idle":"2023-01-16T16:27:56.321462Z","shell.execute_reply.started":"2023-01-16T16:27:48.128272Z","shell.execute_reply":"2023-01-16T16:27:56.320359Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"kernel_type = '0920_2d_lstmv22headv2_convnn_224_15_6ch_8flip_augv2_drl3_rov1p2_rov3p2_bs4_lr6e5_eta6e6_lw151_50ep'\nmodel_dir_cls = '../input/rsnastage2type2convnn224atika'\nbackbone = 'convnext_nano'\nin_chans = 6\nmodels_cls2 = []\n\nfor fold in range(5):\n    model = TimmModel(backbone, image_size=224, pretrained=False)\n    #model = model.to(device)\n    load_model_file = os.path.join(model_dir_cls, f'{kernel_type}_fold{fold}_best.pth')\n    sd = torch.load(load_model_file, map_location=torch.device('cpu'))\n    if 'model_state_dict' in sd.keys():\n        sd = sd['model_state_dict']\n    sd = {k[7:] if k.startswith('module.') else k: sd[k] for k in sd.keys()}\n    model.load_state_dict(sd, strict=True)\n    model.eval()\n    models_cls2.append(model)\n\nlen(models_cls2)","metadata":{"execution":{"iopub.status.busy":"2023-01-16T16:27:56.322853Z","iopub.execute_input":"2023-01-16T16:27:56.323633Z","iopub.status.idle":"2023-01-16T16:28:02.996206Z","shell.execute_reply.started":"2023-01-16T16:27:56.323598Z","shell.execute_reply":"2023-01-16T16:28:02.9951Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def load_bone(msk, cid, t_paths, cropped_images):\n    n_scans = len(t_paths)\n    bone = []\n    try:\n        msk_b = msk[cid] > 0.2\n        msk_c = msk[cid] > 0.05\n\n        x = np.where(msk_b.sum(1).sum(1) > 0)[0]\n        y = np.where(msk_b.sum(0).sum(1) > 0)[0]\n        z = np.where(msk_b.sum(0).sum(0) > 0)[0]\n\n        if len(x) == 0 or len(y) == 0 or len(z) == 0:\n            x = np.where(msk_c.sum(1).sum(1) > 0)[0]\n            y = np.where(msk_c.sum(0).sum(1) > 0)[0]\n            z = np.where(msk_c.sum(0).sum(0) > 0)[0]\n\n        x1, x2 = max(0, x[0] - 1), min(msk.shape[1], x[-1] + 1)\n        y1, y2 = max(0, y[0] - 1), min(msk.shape[2], y[-1] + 1)\n        z1, z2 = max(0, z[0] - 1), min(msk.shape[3], z[-1] + 1)\n        zz1, zz2 = int(z1 / msk_size * n_scans), int(z2 / msk_size * n_scans)\n\n        inds = np.linspace(zz1 ,zz2-1 ,n_slice_per_c).astype(int)\n        inds_ = np.linspace(z1 ,z2-1 ,n_slice_per_c).astype(int)\n        for sid, (ind, ind_) in enumerate(zip(inds, inds_)):\n\n            msk_this = msk[cid, :, :, ind_]\n\n            images = []\n            for i in range(-n_ch//2+1, n_ch//2+1):\n                try:\n                    dicom = pydicom.read_file(t_paths[ind+i])\n                    images.append(dicom.pixel_array)\n                except:\n                    images.append(np.zeros((512, 512)))\n\n            data = np.stack(images, -1)\n            data = data - np.min(data)\n            data = data / (np.max(data) + 1e-4)\n            data = (data * 255).astype(np.uint8)\n            msk_this = msk_this[x1:x2, y1:y2]\n            xx1 = int(x1 / msk_size * data.shape[0])\n            xx2 = int(x2 / msk_size * data.shape[0])\n            yy1 = int(y1 / msk_size * data.shape[1])\n            yy2 = int(y2 / msk_size * data.shape[1])\n            data = data[xx1:xx2, yy1:yy2]\n            data = np.stack([cv2.resize(data[:, :, i], (image_size_cls, image_size_cls), interpolation = cv2.INTER_LINEAR) for i in range(n_ch)], -1)\n            msk_this = (msk_this * 255).astype(np.uint8)\n            msk_this = cv2.resize(msk_this, (image_size_cls, image_size_cls), interpolation = cv2.INTER_LINEAR)\n\n            data = np.concatenate([data, msk_this[:, :, np.newaxis]], -1)\n\n            bone.append(torch.tensor(data))\n\n    except:\n        for sid in range(n_slice_per_c):\n            bone.append(torch.ones((image_size_cls, image_size_cls, n_ch+1)).int())\n\n    cropped_images[cid] = torch.stack(bone, 0)\n\n\ndef load_cropped_images(msk, image_folder, n_ch=n_ch):\n\n    t_paths = sorted(glob(os.path.join(image_folder, \"*\")), key=lambda x: int(x.split('/')[-1].split(\".\")[0]))\n    for cid in range(7):\n        threads[cid] = threading.Thread(target=load_bone, args=(msk, cid, t_paths, cropped_images))\n        threads[cid].start()\n    for cid in range(7):\n        threads[cid].join()\n\n    return torch.cat(cropped_images, 0)\n","metadata":{"execution":{"iopub.status.busy":"2023-01-16T16:28:02.999663Z","iopub.execute_input":"2023-01-16T16:28:03.000073Z","iopub.status.idle":"2023-01-16T16:28:03.024394Z","shell.execute_reply.started":"2023-01-16T16:28:03.000043Z","shell.execute_reply":"2023-01-16T16:28:03.023145Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Predict","metadata":{}},{"cell_type":"code","source":"outputs1 = []\noutputs2 = []\n\nbar = tqdm(loader_seg)\nwith torch.no_grad():\n    for batch_id, (images) in enumerate(bar):\n        #images = images.cuda()\n        device=torch.device('cpu')\n        images.to(device)\n\n        # SEG\n        pred_masks = []\n        for model in models_seg:\n            pmask = model(images).sigmoid()\n            pred_masks.append(pmask)\n        pred_masks = torch.stack(pred_masks, 0).mean(0).cpu().numpy()\n\n        # Build cls input\n        cls_inp = []\n        threads = [None] * 7\n        cropped_images = [None] * 7\n\n        for i in range(pred_masks.shape[0]):\n            row = df.iloc[batch_id*batch_size_seg+i]\n            cropped_images = load_cropped_images(pred_masks[i], row.image_folder)\n            cls_inp.append(cropped_images.permute(0, 3, 1, 2).float() / 255.)\n        cls_inp = torch.stack(cls_inp, 0).to(device)  # (1, 105, 6, 224, 224)\n\n        pred_cls1, pred_cls2 = [], []\n        # CLS 2\n        for _, model in enumerate(models_cls2):\n            logits, logits2 = model(cls_inp)\n            pred_cls1.append(logits.sigmoid().view(-1, 7, n_slice_per_c))\n            pred_cls2.append(logits2.sigmoid())\n\n        # CLS 1\n        cls_inp = cls_inp.view(7, 15, 6, image_size_cls, image_size_cls).contiguous()\n        for _, model in enumerate(models_cls1):\n            logits = model(cls_inp)\n            pred_cls1.append(logits.sigmoid().view(-1, 7, n_slice_per_c))\n\n        pred_cls1 = torch.stack(pred_cls1, 0).mean(0)\n        pred_cls2 = torch.stack(pred_cls2, 0).mean(0)\n        outputs1.append(pred_cls1.cpu())\n        outputs2.append(pred_cls2.cpu())\n","metadata":{"execution":{"iopub.status.busy":"2023-01-16T16:28:03.026717Z","iopub.execute_input":"2023-01-16T16:28:03.027099Z","iopub.status.idle":"2023-01-16T16:38:15.184563Z","shell.execute_reply.started":"2023-01-16T16:28:03.027068Z","shell.execute_reply":"2023-01-16T16:38:15.183187Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Output","metadata":{}},{"cell_type":"code","source":"outputs1 = torch.cat(outputs1)\noutputs2 = torch.cat(outputs2)","metadata":{"execution":{"iopub.status.busy":"2023-01-16T16:38:15.186369Z","iopub.execute_input":"2023-01-16T16:38:15.186839Z","iopub.status.idle":"2023-01-16T16:38:15.193192Z","shell.execute_reply.started":"2023-01-16T16:38:15.186799Z","shell.execute_reply":"2023-01-16T16:38:15.192168Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"PRED1 = (outputs1.mean(-1)).clamp(0.0001, 0.9999)\nPRED2 = (outputs2.view(-1)).clamp(0.0001, 0.9999)","metadata":{"execution":{"iopub.status.busy":"2023-01-16T16:38:15.19493Z","iopub.execute_input":"2023-01-16T16:38:15.195523Z","iopub.status.idle":"2023-01-16T16:38:15.205462Z","shell.execute_reply.started":"2023-01-16T16:38:15.195479Z","shell.execute_reply":"2023-01-16T16:38:15.204581Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"row_ids = []\nfor _, row in df.iterrows():\n    for i in range(7):\n        row_ids.append(row.StudyInstanceUID + f'_C{i+1}')\n    row_ids.append(row.StudyInstanceUID + '_patient_overall')","metadata":{"execution":{"iopub.status.busy":"2023-01-16T16:38:15.20692Z","iopub.execute_input":"2023-01-16T16:38:15.207509Z","iopub.status.idle":"2023-01-16T16:38:15.217836Z","shell.execute_reply.started":"2023-01-16T16:38:15.207463Z","shell.execute_reply":"2023-01-16T16:38:15.216863Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"df_sub = pd.DataFrame({\n    'row_id': row_ids,\n    'fractured': torch.cat([PRED1, PRED2.unsqueeze(1)], 1).view(-1),\n})","metadata":{"execution":{"iopub.status.busy":"2023-01-16T16:38:15.219067Z","iopub.execute_input":"2023-01-16T16:38:15.220054Z","iopub.status.idle":"2023-01-16T16:38:15.230938Z","shell.execute_reply.started":"2023-01-16T16:38:15.219989Z","shell.execute_reply":"2023-01-16T16:38:15.230104Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"df_sub.to_csv('submission.csv', index=False)","metadata":{"execution":{"iopub.status.busy":"2023-01-16T16:38:15.232534Z","iopub.execute_input":"2023-01-16T16:38:15.233226Z","iopub.status.idle":"2023-01-16T16:38:15.25269Z","shell.execute_reply.started":"2023-01-16T16:38:15.233176Z","shell.execute_reply":"2023-01-16T16:38:15.251806Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"df_sub","metadata":{"execution":{"iopub.status.busy":"2023-01-16T16:38:15.254313Z","iopub.execute_input":"2023-01-16T16:38:15.254711Z","iopub.status.idle":"2023-01-16T16:38:15.271338Z","shell.execute_reply.started":"2023-01-16T16:38:15.254667Z","shell.execute_reply":"2023-01-16T16:38:15.270243Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"","metadata":{},"execution_count":null,"outputs":[]}]}