{"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":"code","source":"DEBUG=False","metadata":{"execution":{"iopub.status.busy":"2023-02-27T14:34:36.477011Z","iopub.execute_input":"2023-02-27T14:34:36.477443Z","iopub.status.idle":"2023-02-27T14:34:36.482961Z","shell.execute_reply.started":"2023-02-27T14:34:36.477408Z","shell.execute_reply":"2023-02-27T14:34:36.481422Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Install Libs","metadata":{}},{"cell_type":"markdown","source":"## PyTorch 1.12.1\nhttps://github.com/pytorch/pytorch","metadata":{}},{"cell_type":"code","source":"!pip install --quiet /kaggle/input/pytorch-offline/torch-1.12.1+cu113-cp37-cp37m-linux_x86_64.whl\n!pip install --quiet /kaggle/input/pytorch-offline/torchvision-0.13.1+cu113-cp37-cp37m-linux_x86_64.whl","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","_kg_hide-output":true,"execution":{"iopub.status.busy":"2023-02-27T14:34:40.126425Z","iopub.execute_input":"2023-02-27T14:34:40.127548Z","iopub.status.idle":"2023-02-27T14:37:24.253291Z","shell.execute_reply.started":"2023-02-27T14:34:40.127507Z","shell.execute_reply":"2023-02-27T14:37:24.25184Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## TensorRT\nhttps://github.com/NVIDIA/TensorRT","metadata":{}},{"cell_type":"code","source":"!pip install --quiet /kaggle/input/tensorrt-offline-installer/nvidia_cuda_runtime_cu11-11.8.89-py3-none-manylinux1_x86_64.whl\n!pip install --quiet /kaggle/input/tensorrt-offline-installer/nvidia_cublas_cu11-11.11.3.6-py3-none-manylinux1_x86_64.whl\n!pip install --quiet /kaggle/input/tensorrt-offline-installer/nvidia_cudnn_cu11-8.7.0.84-py3-none-manylinux1_x86_64.whl\n!pip install --quiet /kaggle/input/tensorrt-offline-installer/tensorrt-8.5.2.2-cp37-none-manylinux_2_17_x86_64.whl","metadata":{"_kg_hide-output":true,"execution":{"iopub.status.busy":"2023-02-27T14:37:24.256421Z","iopub.execute_input":"2023-02-27T14:37:24.257198Z","iopub.status.idle":"2023-02-27T14:40:21.642577Z","shell.execute_reply.started":"2023-02-27T14:37:24.257142Z","shell.execute_reply":"2023-02-27T14:40:21.641195Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## torch2trt\nhttps://github.com/NVIDIA-AI-IOT/torch2trt","metadata":{}},{"cell_type":"code","source":"!pip install --quiet /kaggle/input/torch2trt-offline-installer/torch2trt-0.4.0-py3-none-any.whl","metadata":{"_kg_hide-output":true,"execution":{"iopub.status.busy":"2023-02-27T14:40:21.644402Z","iopub.execute_input":"2023-02-27T14:40:21.644836Z","iopub.status.idle":"2023-02-27T14:40:53.883538Z","shell.execute_reply.started":"2023-02-27T14:40:21.644788Z","shell.execute_reply":"2023-02-27T14:40:53.882268Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## PyTorch Image Models\nhttps://github.com/rwightman/pytorch-image-models","metadata":{}},{"cell_type":"code","source":"!pip install --quiet /kaggle/input/timm-offline-installer/timm-0.6.12-py3-none-any.whl","metadata":{"_kg_hide-output":true,"execution":{"iopub.status.busy":"2023-02-27T14:40:53.887391Z","iopub.execute_input":"2023-02-27T14:40:53.888272Z","iopub.status.idle":"2023-02-27T14:41:25.840306Z","shell.execute_reply.started":"2023-02-27T14:40:53.888216Z","shell.execute_reply":"2023-02-27T14:41:25.838946Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# YOLOX\nhttps://github.com/Megvii-BaseDetection/YOLOX","metadata":{}},{"cell_type":"code","source":"!pip install --quiet /kaggle/input/yolox-offline/loguru-0.6.0-py3-none-any.whl\n!pip install --quiet /kaggle/input/yolox-offline/protobuf-3.20.3-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.whl\n!pip install --quiet /kaggle/input/yolox-offline/onnx-1.13.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl\n!pip install --quiet /kaggle/input/yolox-offline/onnx_simplifier-0.4.10-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl\n!pip install --quiet /kaggle/input/yolox-offline/thop-0.1.1.post2209072238-py3-none-any.whl\n!pip install --quiet /kaggle/input/yolox-offline/psutil-5.9.4-cp36-abi3-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl\n!pip install --quiet /kaggle/input/yolox-offline/pycocotools-2.0.6-cp37-cp37m-linux_x86_64.whl\n!pip install --quiet /kaggle/input/yolox-offline/yolox-0.3.0-cp37-cp37m-linux_x86_64.whl","metadata":{"_kg_hide-output":true,"execution":{"iopub.status.busy":"2023-02-27T14:41:25.842412Z","iopub.execute_input":"2023-02-27T14:41:25.842861Z","iopub.status.idle":"2023-02-27T14:46:20.016193Z","shell.execute_reply.started":"2023-02-27T14:41:25.84281Z","shell.execute_reply":"2023-02-27T14:46:20.014795Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## pydicom\nhttps://github.com/pydicom/pydicom","metadata":{}},{"cell_type":"code","source":"!pip install --quiet /kaggle/input/pydicom-offline-installer/pydicom-2.3.1-py3-none-any.whl\n!pip install --quiet /kaggle/input/pydicom-offline-installer/pylibjpeg-1.4.0-py3-none-any.whl\n!pip install --quiet /kaggle/input/pydicom-offline-installer/pylibjpeg_libjpeg-1.3.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl\n!pip install --quiet /kaggle/input/pydicom-offline-installer/pylibjpeg_openjpeg-1.2.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl\n!pip install --quiet /kaggle/input/pydicom-offline-installer/pylibjpeg_rle-1.3.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl\n!pip install --quiet /kaggle/input/pydicom-offline-installer/python_gdcm-3.0.20-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl","metadata":{"_kg_hide-output":true,"execution":{"iopub.status.busy":"2023-02-27T14:46:20.019995Z","iopub.execute_input":"2023-02-27T14:46:20.021109Z","iopub.status.idle":"2023-02-27T14:49:28.467443Z","shell.execute_reply.started":"2023-02-27T14:46:20.021037Z","shell.execute_reply":"2023-02-27T14:49:28.465848Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## DICOMSDL\nhttps://github.com/tsangel/dicomsdl","metadata":{}},{"cell_type":"code","source":"!pip install --quiet /kaggle/input/dicomsdl-offline-installer/dicomsdl-0.109.1-cp37-cp37m-manylinux_2_12_x86_64.manylinux2010_x86_64.whl","metadata":{"_kg_hide-output":true,"execution":{"iopub.status.busy":"2023-02-27T14:49:28.469609Z","iopub.execute_input":"2023-02-27T14:49:28.470132Z","iopub.status.idle":"2023-02-27T14:49:59.722399Z","shell.execute_reply.started":"2023-02-27T14:49:28.470074Z","shell.execute_reply":"2023-02-27T14:49:59.720989Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Import dependencies","metadata":{}},{"cell_type":"code","source":"import cv2\nimport dicomsdl\nimport lightgbm as lgb\nimport math\nimport numpy as np\nimport os\nimport pandas as pd\nimport psutil\nimport pydicom\nimport random\nimport time\nimport timm\nimport torch\nimport torch.nn.functional as F\nimport torchvision.transforms as transforms\n\nfrom pydicom.pixel_data_handlers import apply_voi_lut\nfrom torch2trt import TRTModule\n\nimport sys\nsys.path.append(\"/kaggle/input/omegaconf\")\nfrom omegaconf import DictConfig\nsys.path.append(\"/kaggle/input/nvjpeg2k\")\nimport nvjpeg2k","metadata":{"execution":{"iopub.status.busy":"2023-02-27T14:49:59.7268Z","iopub.execute_input":"2023-02-27T14:49:59.727238Z","iopub.status.idle":"2023-02-27T14:50:03.476258Z","shell.execute_reply.started":"2023-02-27T14:49:59.727198Z","shell.execute_reply":"2023-02-27T14:50:03.474823Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Preprocess","metadata":{}},{"cell_type":"code","source":"def dicom_postprocess(\n    dicom: pydicom.dataset.FileDataset,\n    pixel_array: np.array,\n    voi_lut: bool,\n):\n    if voi_lut:\n        data = apply_voi_lut(pixel_array, dicom).astype(np.float32)\n    else:\n        data = pixel_array.astype(np.float32)\n\n    data = data - np.min(data)\n    data = data / np.max(data)\n\n    if dicom.PhotometricInterpretation == \"MONOCHROME1\":\n        data = 1 - data\n\n    return data","metadata":{"execution":{"iopub.status.busy":"2023-02-27T14:50:03.48205Z","iopub.execute_input":"2023-02-27T14:50:03.482834Z","iopub.status.idle":"2023-02-27T14:50:03.495734Z","shell.execute_reply.started":"2023-02-27T14:50:03.482785Z","shell.execute_reply":"2023-02-27T14:50:03.494582Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def crop_roi(\n    dicom: pydicom.dataset.FileDataset,\n    pixel_array: np.array,\n    resize_ratio: int=20,\n    fg_thr: float=0.6,\n    crop_min_thr: int=400,\n    return_offset: bool=False,\n):\n    # convert\n    tmp_img = dicom_postprocess(dicom=dicom, pixel_array=pixel_array, voi_lut=False)\n    # resize\n    tmp_img = tmp_img[::resize_ratio, ::resize_ratio]\n    tmp_img = tmp_img[1:-1, 1:-1] # offset\n    # smooth\n    tmp_img = cv2.GaussianBlur(tmp_img, (3,3), 0)\n    tmp_img = (tmp_img*255).astype(np.uint8)\n\n    # thresholding\n    fg_ratio = np.count_nonzero(tmp_img > tmp_img.min()) / tmp_img.size\n    if fg_ratio < fg_thr:\n        thr = tmp_img.min()\n        binary_image = np.zeros_like(tmp_img)\n        binary_image[tmp_img > thr] = 255\n    else:\n        _, binary_image = cv2.threshold(tmp_img, 0, 255, cv2.THRESH_BINARY+cv2.THRESH_OTSU)\n\n    # closing\n    binary_image = cv2.morphologyEx(binary_image, cv2.MORPH_CLOSE, kernel = np.ones((3,3), np.uint8), iterations = 3)\n\n    # Turn all images to the right\n    flip_flag = False\n    h, w = binary_image.shape\n    left_count = np.count_nonzero(binary_image[:, :w//2])\n    right_count = np.count_nonzero(binary_image[:, w//2:])\n    if right_count > left_count:\n        flip_flag = True\n\n    # connected component analysis\n    nlabels, labeled_image, stats, _ = cv2.connectedComponentsWithStats(binary_image)\n    if nlabels < 2:\n        return pixel_array\n\n    # get the largest connected element\n    sizes = stats[:, -1]\n    max_label = 1\n    max_size = sizes[1]\n    for i in range(2, nlabels):\n        if sizes[i] > max_size:\n            max_label = i\n            max_size = sizes[i]\n    mask = np.zeros(labeled_image.shape, dtype=np.uint8)\n    mask[labeled_image == max_label] = 255\n\n    # get bounding box\n    mask_indexes = np.where(mask)\n    height, width = pixel_array.shape[:2]\n    y_min = max(np.min(mask_indexes[0]+1) * resize_ratio - 1, 0)\n    y_max = min(np.max(mask_indexes[0]+1) * resize_ratio + 1, height)\n    x_min = max(np.min(mask_indexes[1]+1) * resize_ratio - 1, 0)\n    x_max = min(np.max(mask_indexes[1]+1) * resize_ratio + 1, width)\n\n    if x_max - x_min < crop_min_thr or y_max - y_min < crop_min_thr:\n        x_min = 0\n        x_max = width\n        y_min = 0\n        y_max = height\n\n    cropped_img = pixel_array[y_min:y_max, x_min:x_max]\n    if flip_flag:\n        cropped_img = cv2.flip(cropped_img, 1)\n\n    if return_offset:\n        return cropped_img, (x_min, y_min), flip_flag\n    else:\n        return cropped_img","metadata":{"execution":{"iopub.status.busy":"2023-02-27T14:50:03.501951Z","iopub.execute_input":"2023-02-27T14:50:03.505001Z","iopub.status.idle":"2023-02-27T14:50:03.531612Z","shell.execute_reply.started":"2023-02-27T14:50:03.504963Z","shell.execute_reply":"2023-02-27T14:50:03.530456Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## DICOMSDL","metadata":{}},{"cell_type":"code","source":"def read_dicomsdl(path: str):\n    image_id = path.split(\"/\")[-1][:-4]\n    dicom = pydicom.dcmread(path, stop_before_pixels=True)\n    ds = dicomsdl.open(path)\n    info = ds.getPixelDataInfo()\n    pixel_array = np.empty(shape = [info['Rows'], info['Cols']], dtype=info['dtype'])\n    ds.copyFrameData(0, pixel_array)\n    return dicom, pixel_array, image_id","metadata":{"execution":{"iopub.status.busy":"2023-02-27T14:50:03.537141Z","iopub.execute_input":"2023-02-27T14:50:03.539686Z","iopub.status.idle":"2023-02-27T14:50:03.548952Z","shell.execute_reply.started":"2023-02-27T14:50:03.539638Z","shell.execute_reply":"2023-02-27T14:50:03.547935Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## nvjpeg2k\nhttps://www.kaggle.com/competitions/rsna-breast-cancer-detection/discussion/372275\n\nhttps://www.kaggle.com/code/snaker/easy-?-the-image-with-nvjpeg2000/\n\nhttps://github.com/louis-she/nvjpeg2k-python","metadata":{}},{"cell_type":"code","source":"j2k_decoder = nvjpeg2k.Decoder()\n\ndef read_j2k(path: str):\n    image_id = path.split(\"/\")[-1][:-4]\n    dicom = pydicom.dcmread(path)\n    offset = dicom.PixelData.find(b\"\\x00\\x00\\x00\\x0C\")\n    jpeg_stream = bytearray(dicom.PixelData[offset:])\n    pixel_array = j2k_decoder.decode(jpeg_stream)\n\n    return dicom, pixel_array, image_id","metadata":{"execution":{"iopub.status.busy":"2023-02-27T14:50:03.554102Z","iopub.execute_input":"2023-02-27T14:50:03.557072Z","iopub.status.idle":"2023-02-27T14:50:04.246186Z","shell.execute_reply.started":"2023-02-27T14:50:03.557036Z","shell.execute_reply":"2023-02-27T14:50:04.24476Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def load_dcm_list(\n    input_dir: str,\n    df: pd.DataFrame,\n):\n    TransferSyntaxUID = df[\"TransferSyntaxUID\"].unique()\n    assert len(TransferSyntaxUID)==1\n    TransferSyntaxUID = TransferSyntaxUID[0]\n\n    dcm_files = input_dir + \"/\" + df.patient_id.astype(str) + \"/\"  + df.image_id.astype(str) + \".dcm\"\n\n    dicom_list = []\n    pixel_array_list = []\n    image_id_list = []\n    for dcm_file in dcm_files:\n        if not os.path.exists(dcm_file):\n            dcm_file = dcm_file[:-4] + \".dicom\"\n\n        if TransferSyntaxUID == \"1.2.840.10008.1.2.4.90\":\n            dicom, pixel_array, image_id = read_j2k(dcm_file)\n        else:\n            dicom, pixel_array, image_id = read_dicomsdl(dcm_file)\n\n        dicom_list.append(dicom)\n        pixel_array_list.append(pixel_array)\n        image_id_list.append(image_id)\n\n    return dicom_list, pixel_array_list, image_id_list","metadata":{"execution":{"iopub.status.busy":"2023-02-27T14:50:04.247655Z","iopub.execute_input":"2023-02-27T14:50:04.249337Z","iopub.status.idle":"2023-02-27T14:50:04.260153Z","shell.execute_reply.started":"2023-02-27T14:50:04.249265Z","shell.execute_reply":"2023-02-27T14:50:04.259053Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# TensorRT","metadata":{}},{"cell_type":"code","source":"def load_model(model_path, fold, mode, gpu_id):\n    weight_file = os.path.join(\n            model_path,\n            f\"fold{fold}\",\n            f\"{mode}_device{gpu_id}.trt\"\n        )\n    if not os.path.exists(weight_file):\n        weight_file = os.path.join(\n            model_path,\n            f\"fold{fold}\",\n            f\"{mode}.trt\"\n        )\n\n    with torch.cuda.device(gpu_id):\n        model = TRTModule()\n        weight = torch.load(weight_file)\n        model.load_state_dict(weight)\n    return model\n\ndef load_model_list(model_path, num_folds, mode, gpu_id):\n    models = []\n    for fold in range(num_folds):\n        if mode == \"all\":\n            for _mode in [\"val_loss\", \"val_auc\"]:\n                model = load_model(model_path, fold, _mode, gpu_id)\n                models.append(model)\n        else:\n            model = load_model(model_path, fold, mode, gpu_id)\n            models.append(model)\n    return models\n\ndef predict_core(model, images):\n    return model(images).squeeze(dim=1).cpu().detach().numpy()\n\ndef predict(cfg, model, images):\n    predictions = []\n    predictions.append(predict_core(model, images))\n\n    # test time augmentation\n    if cfg['lrflip']:\n        predictions.append(predict_core(model, torch.flip(images, dims=[-1])))\n    if cfg['udflip']:\n        predictions.append(predict_core(model, torch.flip(images, dims=[-2])))\n    if cfg['rot180']:\n        predictions.append(predict_core(model, torch.flip(images, dims=[-2, -1])))\n\n    return np.concatenate(predictions)","metadata":{"execution":{"iopub.status.busy":"2023-02-27T14:50:04.261955Z","iopub.execute_input":"2023-02-27T14:50:04.262665Z","iopub.status.idle":"2023-02-27T14:50:04.279769Z","shell.execute_reply.started":"2023-02-27T14:50:04.262622Z","shell.execute_reply":"2023-02-27T14:50:04.278368Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Detector","metadata":{}},{"cell_type":"markdown","source":"## YOLOX","metadata":{}},{"cell_type":"code","source":"from yolox.exp import get_exp\nfrom yolox.data.data_augment import ValTransform\nfrom yolox.utils import postprocess","metadata":{"execution":{"iopub.status.busy":"2023-02-27T14:50:04.282173Z","iopub.execute_input":"2023-02-27T14:50:04.282698Z","iopub.status.idle":"2023-02-27T14:50:04.338082Z","shell.execute_reply.started":"2023-02-27T14:50:04.282651Z","shell.execute_reply":"2023-02-27T14:50:04.336953Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def load_model_yolox(pth_path, trt_path, config_path, mode, gpu_id):\n\n    with torch.cuda.device(gpu_id):\n\n        pth_path=os.path.join(pth_path, f\"{mode}.pth\")\n        trt_path=os.path.join(trt_path, f\"{mode}_device0.trt\")\n\n        exp = get_exp(exp_file=config_path)\n        model = exp.get_model()\n        model.half().cuda().eval()\n        ckpt = torch.load(pth_path, map_location=\"cpu\")\n        model.load_state_dict(ckpt[\"model\"])\n        model.head.decode_in_inference = False\n\n        decoder = model.head.decode_outputs\n\n        trt_model = TRTModule()\n        trt_model.load_state_dict(torch.load(trt_path))\n        model(torch.ones(1, 3, exp.test_size[0], exp.test_size[1]).half().cuda())\n        model = trt_model\n\n        preprocess = ValTransform(legacy=False)\n\n    return {\n        \"model\": model,\n        \"decoder\": decoder,\n        \"preprocess\": preprocess,\n        \"test_size\": exp.test_size,\n    }","metadata":{"execution":{"iopub.status.busy":"2023-02-27T14:50:04.339731Z","iopub.execute_input":"2023-02-27T14:50:04.340189Z","iopub.status.idle":"2023-02-27T14:50:04.349054Z","shell.execute_reply.started":"2023-02-27T14:50:04.34013Z","shell.execute_reply":"2023-02-27T14:50:04.347856Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def inference_yolox(yolox_model, img):\n\n    model = yolox_model[\"model\"]\n    decoder = yolox_model[\"decoder\"]\n    preprocess_yolox = yolox_model[\"preprocess\"]\n    test_size = yolox_model[\"test_size\"]\n\n    img_info = {\"id\": 0}\n    height, width = img.shape[:2]\n    img_info[\"height\"] = height\n    img_info[\"width\"] = width\n    img_info[\"raw_img\"] = img\n\n    ratio = min(test_size[0] / img.shape[0], test_size[1] / img.shape[1])\n    img_info[\"ratio\"] = ratio\n\n    img = cv2.cvtColor(img*255, cv2.COLOR_GRAY2BGR)\n    img, _ = preprocess_yolox(img, None, test_size)\n    img = torch.from_numpy(img).unsqueeze(0)\n    img = img.float().cuda().half()\n\n    with torch.no_grad():\n        outputs = model(img)\n        outputs = decoder(outputs, dtype=outputs.type())\n        outputs = postprocess(\n            prediction=outputs,\n            num_classes=1,\n            conf_thre=0.0,\n            nms_thre=0.0,\n            class_agnostic=True,\n        )\n\n    # postprocess\n    if outputs[0] is None:\n        return None, None\n\n    output = outputs[0].detach().cpu().numpy()\n    bboxes = output[:, 0:4]\n    bboxes /= ratio\n    scores = output[:, 4] * output[:, 5]\n\n    num_box_thr = 8\n    box_list = []\n    score_list = []\n    for box, score in zip(bboxes[:num_box_thr], scores[:num_box_thr]):\n        box_list.append(np.array([int(box[0]), int(box[1]), int(box[2]), int(box[3])], dtype=np.int32))\n        score_list.append(float(score))\n\n    return box_list, score_list","metadata":{"execution":{"iopub.status.busy":"2023-02-27T14:50:04.350984Z","iopub.execute_input":"2023-02-27T14:50:04.351511Z","iopub.status.idle":"2023-02-27T14:50:04.374156Z","shell.execute_reply.started":"2023-02-27T14:50:04.351467Z","shell.execute_reply":"2023-02-27T14:50:04.372918Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def calc_roi_size(bbox, image, roi_size):\n\n    xmin, ymin, xmax, ymax = bbox\n    height, width = image.shape[:2]\n\n    new_roi_size = min(max(xmax - xmin, ymax - ymin), width, height)\n    if new_roi_size < roi_size:\n        new_roi_size = roi_size\n\n    center_x = (xmax + xmin) / 2\n    if center_x - new_roi_size / 2 < 0:\n        new_xmin = 0\n    elif center_x + new_roi_size / 2 > width:\n        new_xmin = width - new_roi_size\n    else:\n        new_xmin = center_x - new_roi_size / 2\n    new_xmax = new_xmin + new_roi_size\n\n    center_y = (ymax + ymin) / 2\n    if center_y - new_roi_size / 2 < 0:\n        new_ymin = 0\n    elif center_y + new_roi_size / 2 > image.shape[0]:\n        new_ymin = height - new_roi_size\n    else:\n        new_ymin = center_y - new_roi_size / 2\n    new_ymax = new_ymin + new_roi_size\n\n    return [int(new_xmin), int(new_ymin), int(new_xmax), int(new_ymax)]\n\ndef crop_roi_yolobox(_box_list, _score_list, img, roi_size):\n\n    cropped_roi_list = []\n    score_list = []\n    for bbox, score in zip(_box_list, _score_list):\n\n        # TODO: 効果確認してから導入\n        #if bbox[2] - bbox[0] > roi_size or bbox[3] - bbox[1] > roi_size:\n        #    continue\n\n        new_bbox = calc_roi_size(bbox, img, roi_size)\n        xmin, ymin, xmax, ymax = new_bbox\n\n        cropped_roi = img[ymin:ymax, xmin:xmax]\n        # TODO: 効果確認してから導入\n        #assert cropped_roi.shape[0] == cropped_roi.shape[1] == roi_size, f\"{cropped_roi.shape}, {bbox}\"\n        cropped_roi = cv2.resize(cropped_roi, (roi_size, roi_size), interpolation=cv2.INTER_AREA)\n\n        cropped_roi_list.append(cropped_roi)\n        score_list.append(score)\n\n    return cropped_roi_list, score_list","metadata":{"execution":{"iopub.status.busy":"2023-02-27T14:50:04.380949Z","iopub.execute_input":"2023-02-27T14:50:04.381324Z","iopub.status.idle":"2023-02-27T14:50:04.395963Z","shell.execute_reply.started":"2023-02-27T14:50:04.381291Z","shell.execute_reply":"2023-02-27T14:50:04.395064Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def get_image_tiles(cfg, images):\n\n    model = cfg['model']\n    roi_size = cfg['dataset']['roi_size']\n    num_instances = cfg['dataset']['num_instances']\n\n    cropped_roi_list_all = []\n    score_list_all = []\n    for img in images:\n\n        _box_list, _score_list = inference_yolox(model, img)\n\n        if _box_list is None:\n            continue\n\n        cropped_roi_list, score_list = crop_roi_yolobox(_box_list, _score_list, img, roi_size)\n\n        cropped_roi_list_all.extend(cropped_roi_list)\n        score_list_all.extend(score_list)\n\n    score_list_all = np.array(score_list_all)\n    sort_idx = np.argsort(score_list_all)[::-1][:num_instances]\n\n    image_tiles = []\n    for idx in sort_idx:\n        img = cropped_roi_list_all[idx]\n        img = transforms.ToTensor()(img).type(torch.FloatTensor)\n        image_tiles.append(img)\n\n    # padding\n    if len(image_tiles) < num_instances:\n        for i in range(num_instances - len(image_tiles)):\n            image_tiles.append(image_tiles[i])\n\n    return torch.stack(image_tiles, dim=0)","metadata":{"execution":{"iopub.status.busy":"2023-02-27T14:50:04.397949Z","iopub.execute_input":"2023-02-27T14:50:04.399014Z","iopub.status.idle":"2023-02-27T14:50:04.416669Z","shell.execute_reply.started":"2023-02-27T14:50:04.398964Z","shell.execute_reply":"2023-02-27T14:50:04.415273Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Multiple Instance Learning","metadata":{}},{"cell_type":"code","source":"def inference_mil(image_tiles, cfg):\n\n    models = cfg['models']\n    cfg_tta = cfg['tta']\n\n    image_tiles = image_tiles.half().cuda().repeat(1, 1, 3, 1, 1) # 1ch -> 3ch\n    predictions = []\n    predictions.append([predict(cfg_tta, model, image_tiles) for model in models])\n\n    return np.mean(np.stack(predictions))","metadata":{"execution":{"iopub.status.busy":"2023-02-27T14:50:04.418549Z","iopub.execute_input":"2023-02-27T14:50:04.419079Z","iopub.status.idle":"2023-02-27T14:50:04.436733Z","shell.execute_reply.started":"2023-02-27T14:50:04.419031Z","shell.execute_reply":"2023-02-27T14:50:04.435196Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Classifier","metadata":{}},{"cell_type":"code","source":"def calc_pad_size(org_size, dst_size):\n    if org_size > dst_size:\n        return 0, 0\n    pad0 = int(math.floor(0.5*(dst_size - org_size)))\n    pad1 = dst_size - org_size - pad0\n    return pad0, pad1\n\ndef resize_with_padding(\n    input_img: np.array,\n    image_width: int,\n    image_height: int,\n):\n    # resize\n    height, width = input_img.shape[:2]\n    scale = min(image_width / width, image_height / height)\n    w = int(width * scale + 0.5)\n    h = int(height * scale + 0.5)\n    if scale > 1.0:\n        interpolation=cv2.INTER_CUBIC\n    else:\n        interpolation=cv2.INTER_AREA\n    resized_img = cv2.resize(input_img, (w, h), interpolation=interpolation)\n\n    # padding\n    h, w = resized_img.shape\n    pad_x0, pad_x1 = calc_pad_size(w, image_width)\n    pad_y0, pad_y1 = calc_pad_size(h, image_height)\n    resized_img = np.pad(resized_img, [(pad_y0, pad_y1), (pad_x0, pad_x1)], mode=\"constant\")\n\n    return resized_img\n\ndef resize_without_padding(\n    input_img: np.array,\n    image_width: int,\n    image_height: int,\n):\n    # resize\n    height, width = input_img.shape[:2]\n    scale = min(image_width / width, image_height / height)\n    if scale > 1.0:\n        interpolation=cv2.INTER_CUBIC\n    else:\n        interpolation=cv2.INTER_AREA\n    resized_img = cv2.resize(input_img, (image_width, image_height), interpolation=interpolation)\n\n    return resized_img\n\ndef preprocess_img(\n    input_img: np.array,\n    image_width: int,\n    image_height: int,\n    padding: bool,\n):\n    if input_img.ndim == 3:\n        im_gray = cv2.cvtColor(input_img, cv2.COLOR_BGR2GRAY)\n    else:\n        im_gray = input_img\n\n    if padding:\n        resized_img = resize_with_padding(im_gray, image_width, image_height)\n    else:\n        resized_img = resize_without_padding(im_gray, image_width, image_height)\n\n    return resized_img","metadata":{"execution":{"iopub.status.busy":"2023-02-27T14:50:04.439259Z","iopub.execute_input":"2023-02-27T14:50:04.439833Z","iopub.status.idle":"2023-02-27T14:50:04.457434Z","shell.execute_reply.started":"2023-02-27T14:50:04.439781Z","shell.execute_reply":"2023-02-27T14:50:04.456137Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def classify(cfg, image_list):\n\n    models = cfg['models']\n    cfg_tta = cfg['tta']\n\n    cancer_list = []\n    for img in image_list:\n\n        img = preprocess_img(\n            img,\n            cfg['dataset'][\"image_width\"],\n            cfg['dataset'][\"image_height\"],\n            cfg['dataset'][\"padding\"],\n        )\n        img = transforms.ToTensor()(img).type(torch.FloatTensor)\n\n        img = img.half().cuda().repeat(1, 3, 1, 1) # 1ch -> 3ch\n        predictions = []\n        predictions.append([predict(cfg_tta, model, img) for model in models])\n        pred = np.mean(np.stack(predictions))\n\n        cancer_list.append(pred)\n\n    cancer_list.sort(reverse=True)\n\n    return cancer_list[0], cancer_list[1]","metadata":{"execution":{"iopub.status.busy":"2023-02-27T14:50:04.459558Z","iopub.execute_input":"2023-02-27T14:50:04.461063Z","iopub.status.idle":"2023-02-27T14:50:04.476141Z","shell.execute_reply.started":"2023-02-27T14:50:04.461011Z","shell.execute_reply":"2023-02-27T14:50:04.47483Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## LightGBM","metadata":{}},{"cell_type":"code","source":"def load_lgbm_model(model_path, num_folds):\n    models = []\n    for fold in range(num_folds):\n        models.append(lgb.Booster(model_file=os.path.join(model_path, f\"model_fold{fold}.txt\")))\n\n    return models\n\ndef predict_lgbm(models, features):\n    predictions = []\n    predictions.append([model.predict(features, num_iteration=model.best_iteration) for model in models])\n    return np.mean(np.stack(predictions))","metadata":{"execution":{"iopub.status.busy":"2023-02-27T14:50:04.478028Z","iopub.execute_input":"2023-02-27T14:50:04.478906Z","iopub.status.idle":"2023-02-27T14:50:04.494116Z","shell.execute_reply.started":"2023-02-27T14:50:04.478823Z","shell.execute_reply":"2023-02-27T14:50:04.492934Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Pipeline implementation","metadata":{}},{"cell_type":"code","source":"import threading\nimport queue","metadata":{"execution":{"iopub.status.busy":"2023-02-27T14:50:04.496139Z","iopub.execute_input":"2023-02-27T14:50:04.496608Z","iopub.status.idle":"2023-02-27T14:50:04.508987Z","shell.execute_reply.started":"2023-02-27T14:50:04.496563Z","shell.execute_reply":"2023-02-27T14:50:04.50795Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def preprocess(args, params):\n\n    prediction_id = args[\"prediction_id\"]\n    df = params[\"dataframe\"]\n    input_dir = params[\"input_dir\"]\n\n    records = df[(df[\"prediction_id\"] == prediction_id)]\n    assert len(records) >= 2, records\n\n    # Load image\n    with torch.cuda.device(0):\n        dicom_list, pixel_array_list, _ = load_dcm_list(\n            input_dir=input_dir,\n            df=records,\n        )\n\n    # preprocess\n    image_list = []\n    image_list_vl = []\n    for dicom, pixel_array in zip(dicom_list, pixel_array_list):\n        pixel_array = crop_roi(dicom=dicom, pixel_array=pixel_array)\n        image_list.append(dicom_postprocess(dicom=dicom, pixel_array=pixel_array, voi_lut=False))\n        image_list_vl.append(dicom_postprocess(dicom=dicom, pixel_array=pixel_array, voi_lut=True))\n\n    result = {\n        \"prediction_id\": prediction_id,\n        \"image_list\": image_list,\n        \"image_list_vl\": image_list_vl,\n    }\n    return result","metadata":{"execution":{"iopub.status.busy":"2023-02-27T14:50:04.510743Z","iopub.execute_input":"2023-02-27T14:50:04.511573Z","iopub.status.idle":"2023-02-27T14:50:04.52681Z","shell.execute_reply.started":"2023-02-27T14:50:04.511484Z","shell.execute_reply":"2023-02-27T14:50:04.525923Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def detection(args, params):\n\n    prediction_id = args[\"prediction_id\"]\n    image_list = args[\"image_list\"]\n    image_list_vl = args[\"image_list_vl\"]\n\n    yolox_cfg = params[\"detector\"][\"yolox\"]\n    mil_cfg = params[\"detector\"][\"mil\"]\n\n    with torch.cuda.device(0):\n\n        # stage1: yolox\n        image_tiles = get_image_tiles(\n            cfg=yolox_cfg,\n            images=image_list_vl,\n        )\n        if len(image_tiles) != yolox_cfg['dataset']['num_instances']:\n            pred = 0.0\n        else:\n            # stage2: MIL\n            pred = inference_mil(image_tiles, mil_cfg)\n\n    result = {\n        \"prediction_id\": prediction_id,\n        \"image_list\": image_list,\n        \"image_list_vl\": image_list_vl,\n        \"pred_detector\": pred,\n    }\n    return result","metadata":{"execution":{"iopub.status.busy":"2023-02-27T14:50:04.530618Z","iopub.execute_input":"2023-02-27T14:50:04.531009Z","iopub.status.idle":"2023-02-27T14:50:04.546057Z","shell.execute_reply.started":"2023-02-27T14:50:04.530978Z","shell.execute_reply":"2023-02-27T14:50:04.544813Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def classification(args, params):\n\n    prediction_id = args[\"prediction_id\"]\n    image_list = args[\"image_list\"]\n    image_list_vl = args[\"image_list_vl\"]\n    pred_detector = args[\"pred_detector\"]\n\n    cfg = params['classifier']\n\n    if cfg['dataset']['apply_voi_lut']:\n        input_image_list = image_list_vl\n    else:\n        input_image_list = image_list\n\n    with torch.cuda.device(1):\n        pred0, pred1 = classify(cfg, input_image_list)\n\n    # postprocess: LightGBM\n    features = np.array([pred_detector, pred0, pred1]).reshape(1, -1)\n    pred = predict_lgbm(params['lgbm']['model'], features)\n\n    result = {\n        \"prediction_id\": prediction_id,\n        \"cancer\": pred,\n    }\n    return result","metadata":{"execution":{"iopub.status.busy":"2023-02-27T14:50:04.548003Z","iopub.execute_input":"2023-02-27T14:50:04.548544Z","iopub.status.idle":"2023-02-27T14:50:04.560272Z","shell.execute_reply.started":"2023-02-27T14:50:04.548502Z","shell.execute_reply":"2023-02-27T14:50:04.558915Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def wrap_func_for_mt(func, params):\n    def wrap_func(queue_input, queue_output):\n        while True:\n            input = queue_input.get()\n            if input is None:\n                queue_output.put(None)\n                continue\n\n            result = func(input, params)\n\n            queue_output.put(result)\n\n    return wrap_func","metadata":{"execution":{"iopub.status.busy":"2023-02-27T14:50:04.56202Z","iopub.execute_input":"2023-02-27T14:50:04.563196Z","iopub.status.idle":"2023-02-27T14:50:04.576935Z","shell.execute_reply.started":"2023-02-27T14:50:04.563132Z","shell.execute_reply":"2023-02-27T14:50:04.575914Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def loop_proc(queues_input, queues_output, inputs):\n    for queue_input, input in zip(queues_input, inputs):\n        queue_input.put(input)\n\n    outputs = []\n    for queue_output in queues_output:\n        output = queue_output.get()\n        outputs.append(output)\n\n    return outputs","metadata":{"execution":{"iopub.status.busy":"2023-02-27T14:50:04.580671Z","iopub.execute_input":"2023-02-27T14:50:04.581011Z","iopub.status.idle":"2023-02-27T14:50:04.591085Z","shell.execute_reply.started":"2023-02-27T14:50:04.580983Z","shell.execute_reply":"2023-02-27T14:50:04.590026Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def prepare_multithreading(params):\n\n    # funcs to proc in pipeline\n    func_params = [\n        (preprocess, (params)),\n        (detection, (params)),\n        (classification, (params)),\n    ]\n    wrap_funcs = list(map(lambda func_param: wrap_func_for_mt(func_param[0], func_param[1]), func_params))\n\n    # prepare queues\n    queues_input = [queue.Queue() for _ in range(len(wrap_funcs))]\n    queues_output = [queue.Queue() for _ in range(len(wrap_funcs))]\n\n    # create Threads\n    threads = []\n    for wrap_func, queue_input, queue_output in zip(wrap_funcs, queues_input, queues_output):\n        t = threading.Thread(target=wrap_func, args=(queue_input, queue_output), daemon=True)\n        threads.append(t)\n\n    for t in threads:\n        t.start()\n\n    return queues_input, queues_output, len(wrap_funcs)","metadata":{"execution":{"iopub.status.busy":"2023-02-27T14:50:04.592295Z","iopub.execute_input":"2023-02-27T14:50:04.592673Z","iopub.status.idle":"2023-02-27T14:50:04.605663Z","shell.execute_reply.started":"2023-02-27T14:50:04.592643Z","shell.execute_reply":"2023-02-27T14:50:04.604687Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Inference","metadata":{}},{"cell_type":"code","source":"def make_transfer_syntax_uid(\n    df: pd.DataFrame,\n    input_dir: str,\n):\n    machine_id_to_transfer = {}\n    machine_id = df.machine_id.unique()\n    for i in machine_id:\n        d = df[df.machine_id==i].iloc[0]\n        f = os.path.join(input_dir, d.patient_id.astype(str), f\"{d.image_id.astype(str)}.dcm\")\n        dicom = pydicom.dcmread(f)\n        machine_id_to_transfer[i] = dicom.file_meta.TransferSyntaxUID\n    return machine_id_to_transfer","metadata":{"execution":{"iopub.status.busy":"2023-02-27T14:50:04.609306Z","iopub.execute_input":"2023-02-27T14:50:04.609624Z","iopub.status.idle":"2023-02-27T14:50:04.622951Z","shell.execute_reply.started":"2023-02-27T14:50:04.609597Z","shell.execute_reply":"2023-02-27T14:50:04.621928Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def seed_everything(seed):\n    random.seed(seed)\n    os.environ['PYTHONHASHSEED'] = str(seed)\n    np.random.seed(seed)\n    torch.manual_seed(seed)\n    torch.cuda.manual_seed(seed)\n    torch.backends.cudnn.deterministic = True\n    torch.backends.cudnn.benchmark = False","metadata":{"execution":{"iopub.status.busy":"2023-02-27T14:50:04.624443Z","iopub.execute_input":"2023-02-27T14:50:04.625082Z","iopub.status.idle":"2023-02-27T14:50:04.634727Z","shell.execute_reply.started":"2023-02-27T14:50:04.625018Z","shell.execute_reply":"2023-02-27T14:50:04.633712Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"input_dir = \"/kaggle/input/rsna-breast-cancer-detection\"\n\nif DEBUG:\n    data_mode = \"train\"\nelse:\n    data_mode = \"test\"\n\nseed_everything(seed=42)","metadata":{"execution":{"iopub.status.busy":"2023-02-27T14:50:04.636425Z","iopub.execute_input":"2023-02-27T14:50:04.636803Z","iopub.status.idle":"2023-02-27T14:50:04.647628Z","shell.execute_reply.started":"2023-02-27T14:50:04.636768Z","shell.execute_reply":"2023-02-27T14:50:04.646649Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"df = pd.read_csv(os.path.join(input_dir, f\"{data_mode}.csv\"))\n# Add TransferSyntaxUID\nmachine_id_to_transfer = make_transfer_syntax_uid(df, os.path.join(input_dir, f\"{data_mode}_images\"))\ndf.loc[:,'TransferSyntaxUID'] = df.machine_id.map(machine_id_to_transfer)\n# Add prediction_id\ndf['prediction_id'] = df['patient_id'].astype(str) + \"_\" + df['laterality']","metadata":{"execution":{"iopub.status.busy":"2023-02-27T14:50:04.648899Z","iopub.execute_input":"2023-02-27T14:50:04.649626Z","iopub.status.idle":"2023-02-27T14:50:04.811738Z","shell.execute_reply.started":"2023-02-27T14:50:04.649586Z","shell.execute_reply":"2023-02-27T14:50:04.810659Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Config","metadata":{}},{"cell_type":"code","source":"detector_param = {\n    \"yolox\": {\n        \"model\": load_model_yolox(\n            pth_path=\"/kaggle/input/yolox-230216-train-all\",\n            trt_path=\"/kaggle/input/tensorrt-conversion-yolox\",\n            config_path=\"/kaggle/input/yolox-230216-train-all/vindr_config_yolox_l\",\n            mode=\"last_epoch_ckpt\",\n            gpu_id=0,\n        ),\n        \"dataset\": {\n            \"roi_size\": 384,\n            \"num_instances\": 8,\n        }\n    },\n    \"mil\": {\n        \"models\":load_model_list(\n            model_path=\"/kaggle/input/trt-conversion-mil-nextvit-remove-label-noise\",\n            num_folds=4,\n            mode=\"val_loss\",\n            gpu_id=0,\n        ),\n        \"tta\": {\n            \"lrflip\": True,\n            \"udflip\": True,\n            \"rot180\": True,\n        },\n    },\n}\nclassifier_param = {\n    \"models\": load_model_list(\n        model_path=\"/kaggle/input/tensorrt-conversion-tf-efficientnetv2-s\",\n        num_folds=4,\n        mode=\"val_auc\",\n        gpu_id=1,\n    ),\n    \"dataset\": {\n        \"image_width\": 1024,\n        \"image_height\": 2048,\n        \"padding\": False,\n        \"apply_voi_lut\": False,\n    },\n    \"tta\": {\n        \"lrflip\": True,\n        \"udflip\": True,\n        \"rot180\": True,\n    },\n}\nlgbm_param ={\n    \"model\": load_lgbm_model(\n        model_path=\"/kaggle/input/lgbm-230227-model\",\n        num_folds=4,\n    ),\n}","metadata":{"_kg_hide-output":true,"execution":{"iopub.status.busy":"2023-02-27T14:50:04.813547Z","iopub.execute_input":"2023-02-27T14:50:04.814004Z","iopub.status.idle":"2023-02-27T14:50:42.890365Z","shell.execute_reply.started":"2023-02-27T14:50:04.813962Z","shell.execute_reply":"2023-02-27T14:50:42.889378Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"params = {\n    \"dataframe\": df,\n    \"input_dir\": os.path.join(input_dir, f\"{data_mode}_images\"),\n    \"detector\": detector_param,\n    \"classifier\": classifier_param,\n    \"lgbm\": lgbm_param,\n}\nbinary_threshold = 0.17","metadata":{"_kg_hide-output":true,"execution":{"iopub.status.busy":"2023-02-27T14:50:42.894849Z","iopub.execute_input":"2023-02-27T14:50:42.897473Z","iopub.status.idle":"2023-02-27T14:50:42.903978Z","shell.execute_reply.started":"2023-02-27T14:50:42.897431Z","shell.execute_reply":"2023-02-27T14:50:42.902936Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"prediction_ids = df[\"prediction_id\"].unique()\nif DEBUG:\n    prediction_ids = prediction_ids[:50]\n\nqueues_input, queues_output, len_wrap_funcs = prepare_multithreading(params)\n\nstart = time.time()\nidx = 0\nprediction_id_list = []\ncancer_list = []\nwhile len(prediction_id_list) < len(prediction_ids):\n\n    if idx >= len(prediction_ids):\n        args = None\n    else:\n        args = {\n            \"prediction_id\": prediction_ids[idx],\n        }\n\n    if idx == 0:\n        init_inputs = [args] + [None]*(len_wrap_funcs - 1)  # [[], None, None, ...]\n        inputs = init_inputs\n    else:\n        inputs = [args] + outputs[:-1]\n\n    outputs = loop_proc(queues_input, queues_output, inputs)\n    result = outputs[-1]\n\n    if result is not None:\n        prediction_id_list.append(result[\"prediction_id\"])\n        cancer_list.append(result[\"cancer\"])\n\n    idx = idx + 1\n\ndf_sub = pd.DataFrame({\n    \"prediction_id\": prediction_id_list,\n    \"cancer\": cancer_list,\n})\n\nend = time.time()\nprint(\"elapsed time: \", end - start, \" s\")\nif len(df) == 4:\n    print(df_sub)\n\n# postprocess\ndf_sub['cancer'] = (df_sub['cancer'] > binary_threshold).astype(int)\n\n# submission\ndf_sub.to_csv(\"submission.csv\", index=False)","metadata":{"execution":{"iopub.status.busy":"2023-02-27T14:50:42.905749Z","iopub.execute_input":"2023-02-27T14:50:42.906673Z","iopub.status.idle":"2023-02-27T14:50:48.822415Z","shell.execute_reply.started":"2023-02-27T14:50:42.906628Z","shell.execute_reply":"2023-02-27T14:50:48.82114Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"","metadata":{},"execution_count":null,"outputs":[]}]}