{"cells":[{"metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true},"cell_type":"code","source":"import shutil\nfrom pathlib import Path\n!pip install ../input/pytorch-image-models\nif Path('../working/models').exists():\n    shutil.rmtree('../working/models')\nshutil.copytree('../input/rsna2020-module/models', '../working/models')\nif Path('../working/functions').exists():\n    shutil.rmtree('../working/functions')\nshutil.copytree('../input/rsna2020-module/functions', '../working/functions')\nprint(\"OK!\")","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"!cp ../input/gdcm-conda-install/gdcm.tar .\n!tar -xvzf gdcm.tar\n!conda install --offline ./gdcm/gdcm-2.8.9-py37h71b2a6d_0.tar.bz2\nprint(\"OK!\")","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"d629ff2d2480ee46fbb7e2d37f6b5fab8052498a","_cell_guid":"79c7e3d0-c299-4dcb-8224-4455121ee9b0","trusted":true},"cell_type":"code","source":"from pathlib import Path\nfrom typing import List, Optional, Tuple, Union\n\nimport cv2\nimport pydicom\nimport numpy as np\nimport pandas as pd\nfrom torch.utils.data import Dataset\n\n\nclass RSNADataset(Dataset):\n    def __init__(\n        self,\n        csv_path: str,\n        img_dir: str,\n        file_extension: str = 'dcm',\n        mode: str = 'test',\n        transform=None,\n        network_type: str = 'cnn_rnn',\n    ):\n\n        self.transform = transform\n        self.csv_path = Path(csv_path)\n        self.img_dir = Path(img_dir)\n        self.file_extension = file_extension\n        self.mode = mode\n        self.network_type = network_type\n\n        if Path('../input/rsna-str-pulmonary-embolism-detection/train').exists() and not DO_FULL:\n            df = pd.read_csv(self.csv_path).head(200)\n        else:\n            print(\"OK. Test Time Mode.\")\n            df = pd.read_csv(self.csv_path)\n            \n        df[\"file_name\"] = df.SOPInstanceUID + '.' + self.file_extension\n        df[\"image_name\"] = str(self.img_dir) + '/' + \\\n            df.StudyInstanceUID + '/' +  df.SeriesInstanceUID + '/' +  df.file_name\n        self.df = df\n\n        self.df[\"path_to_series_id\"] = str(self.img_dir) + '/' + \\\n            self.df.StudyInstanceUID + '/' + self.df.SeriesInstanceUID\n        self.path_to_series_id = self.df[\"path_to_series_id\"].unique()\n\n    def __len__(self):\n        return len(self.path_to_series_id)\n\n    def __getitem__(self, index):\n        data_path = self.path_to_series_id[index]\n        dicoms, dicom_files = self._load_dicom_array(data_path)\n        imgs = self._get_three_windowing_image(dicoms)\n        if self.transform is not None:\n            imgs = imgs.transpose(0, 2, 3, 1)\n            imgs = [self.transform(image=img).transpose( \n                2, 0, 1) for img in imgs]\n            imgs = np.stack(imgs)\n\n        imgs = imgs.astype('float32')\n\n        exam_level_name, image_level_name = self._get_file_names(dicom_files)\n        \n        return imgs, exam_level_name, image_level_name\n    \n    def _load_dicom_array(self, path_to_series_id):\n        dicom_files = list(Path(path_to_series_id).glob('*.dcm'))\n        dicoms = [pydicom.dcmread(d) for d in dicom_files]\n        slope = float(dicoms[0].RescaleSlope)\n        intercept = float(dicoms[0].RescaleIntercept)\n        # Assume all images are axial\n        z_pos = [float(d.ImagePositionPatient[-1]) for d in dicoms]\n        dicoms = np.asarray([d.pixel_array for d in dicoms])\n        dicoms = dicoms[np.argsort(z_pos)]\n        dicoms = dicoms * slope\n        dicoms = dicoms + intercept\n\n        dicom_files = np.array(dicom_files)[np.argsort(z_pos)]\n\n        return dicoms, dicom_files\n    \n    def _windowing(self, img, window_length, window_width):\n        upper = window_length + window_width // 2\n        lower = window_length - window_width // 2\n        x = np.clip(img.copy(), lower, upper)\n        x = x - np.min(x)\n        x = x / np.max(x)\n        x = (x * 255.0).astype('uint8')\n\n        return x\n    \n    def _get_three_windowing_image(self, dicoms):\n        img_lung = np.expand_dims(\n            self._windowing(dicoms, -600, 1500), axis=1)\n        img_mediastinal = np.expand_dims(\n            self._windowing(dicoms, 40, 400), axis=1)\n        img_pe_specific = np.expand_dims(\n            self._windowing(dicoms, 100, 700), axis=1)\n        \n        return np.concatenate([\n            img_lung, img_pe_specific, img_mediastinal], axis=1)\n    \n    def _get_file_names(self, dicom_files):\n        exam_level_name = str(dicom_files[0].parent.parent.stem)\n        dicom_files = dicom_files.tolist()\n        image_level_name = list(map(lambda x: str(x.stem), dicom_files))\n        \n        return exam_level_name, image_level_name\n        \n","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"import albumentations as A\nimport cv2\n\n\nclass RSNAAugmentation:\n    def __init__(self, size=512):\n\n        self.transform = A.Compose([\n            A.Resize(size, size, p=1.0),\n        ])\n                \n    def __call__(self, **kwargs):\n\n        augmented = self.transform(**kwargs)\n        img = augmented['image']\n\n        return img","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"import argparse\nimport random\nfrom pathlib import Path\n\nimport albumentations as A\nimport numpy as np\nimport pandas as pd\nimport pydicom\nimport torch\nimport yaml\nfrom torch.utils.data import DataLoader\nfrom tqdm.notebook import tqdm\n\nimport models\n\n\ndef inference(config):\n\n    seed = config['seed']\n    torch.backends.cudnn.deterministic = True\n    random.seed(seed)\n    np.random.seed(seed)\n    torch.manual_seed(seed)\n    torch.cuda.manual_seed(seed)\n\n    n_tta = config['n_tta']\n\n    transform = RSNAAugmentation\n\n    dataset_args = {\n        'transform': transform(**config[\"transform\"]),\n        **config[\"dataset\"]\n    }\n    dataset = RSNADataset(**dataset_args)\n    loader = DataLoader(dataset=dataset,\n                        batch_size=config[\"batch_size\"],\n                        shuffle=False,\n                        num_workers=config[\"n_workers\"],\n                        pin_memory=False)\n\n    device = config['gpu'][0]\n\n    checkpoint_list = config['checkpoint']\n\n    net_list = []\n    for ckpt_path_dict in checkpoint_list:\n        model_args = {}\n        ckpt_path_cnn = Path(ckpt_path_dict['cnn'])\n        cfg_path = ckpt_path_cnn.parents[1] / 'train_config.yaml'\n        with open(cfg_path, 'r', encoding='utf-8') as f:\n            cfg = yaml.load(f, Loader=yaml.SafeLoader)\n        model_args['cnn_model'] = cfg[\"model\"][\"name\"]\n        model_args['cnn_pretrained_path'] = ckpt_path_cnn\n        model_args['cnn_param'] = cfg[\"model\"][\"args\"]\n        model_args['cnn_param']['pretrained'] = False\n\n        ckpt_path_cnn = Path(ckpt_path_dict['rnn'])\n        cfg_path = ckpt_path_cnn.parents[1] / 'train_config.yaml'\n        with open(cfg_path, 'r', encoding='utf-8') as f:\n            cfg = yaml.load(f, Loader=yaml.SafeLoader)\n        model_args['rnn_model'] = cfg[\"model\"][\"name\"]\n        model_args['rnn_pretrained_path'] = ckpt_path_cnn\n        model_args['rnn_param'] = cfg[\"model\"][\"args\"]\n        \n        net = getattr(models, 'CNN_RNN')(**model_args)\n        net.to(device)\n        net.eval()\n\n        net_list.append(net)\n    \n    chunk_size_list = config['chunk_size_list']\n    if len(chunk_size_list) == 1:\n        chunk_size_list = chunk_size_list * len(net_list)\n    \n    assert len(chunk_size_list) == len(net_list)\n    \n    tta = None\n\n    exam_names = []\n    image_names = []\n    with torch.no_grad():\n        results_image_level = []\n        results_exam_level = []\n        for x, exam_name, image_name in tqdm(loader):\n            # Note: shape of exam_name & image_name.\n            # exam_name -> ['exam_name']\n            # image_name -> [('image_name_1', ), ..., ('image_name_n', )]\n            exam_names.append(exam_name[0])\n            image_name = list(map(lambda x: x[0], image_name))\n            image_names.extend(image_name)\n            \n            n_sequence = x.size()[1]\n            result_tta_image, result_tta_exam = [], []\n            for tta_cnt in range(n_tta):\n                image = x.clone()\n                result_net_exam, result_net_image = [], []\n                for net_cnt, net in enumerate(net_list):\n                    embeddings = []\n                    for i in range(0, n_sequence, chunk_size_list[net_cnt]):\n                        embedding = net.cnn(image[:, i:i + chunk_size_list[net_cnt], :, :, :].to(device))\n                        embeddings.append(embedding)\n                    embeddings = torch.cat(embeddings, dim=1)\n                    image_level, exam_level = net.rnn(embeddings)\n\n                    image_level = torch.sigmoid(\n                        image_level).cpu().detach().numpy().reshape(-1) # (sequence, )\n                    exam_level = torch.sigmoid(\n                        exam_level).cpu().detach().numpy().reshape(-1) #(9, )\n                    \n                    exam_level = label_consistency(image_level, exam_level)\n                    \n                    result_net_image.append(image_level)\n                    result_net_exam.append(exam_level)\n                result_net_image = np.array(result_net_image) #(len(net_list), sequence)\n                result_tta_image.append(result_net_image)\n                result_net_exam = np.array(result_net_exam) #(len(net_list), 9)\n                result_tta_exam.append(result_net_exam)\n            result_tta_image = np.array(result_tta_image) #(n_tta, len(net_list), sequence)\n            results_image_level.append(result_tta_image)\n            result_tta_exam = np.array(result_tta_exam) #(n_tta, len(net_list), 9)\n            results_exam_level.append(result_tta_exam)\n    \n    results_exam_level = np.array(results_exam_level) #(n_exam, n_tta, len(net_list), 9)\n\n    # Note: shape of results_image_level. \n    # len(results_image_level) = n_exam\n    # results_image_level[i].shape = (n_tta, len(net_list), #image in exam i)\n    \n    results_exam_level = results_exam_level.mean(axis=1).mean(axis=1)\n    results_image_level = list(map(\n        lambda x: x.mean(axis=0).mean(axis=0), results_image_level))\n    results_exam_level = np.stack([label_consistency(image_level, exam_level)\n        for image_level, exam_level in zip(results_image_level, results_exam_level)])\n    results_image_level = np.concatenate(results_image_level)\n\n    results_exam_level = results_exam_level.reshape(-1)\n\n    exam_names = get_exam_names(exam_names)\n    assert len(image_names) == len(results_image_level)\n    assert len(exam_names) == len(results_exam_level)\n\n    names = image_names + exam_names\n    results = np.concatenate([results_image_level, results_exam_level])\n\n    submission = pd.DataFrame([names, results], index=['id', 'label']).T\n    \n    if check_consistency(submission, dataset.df):\n        print(\"Great! Fanstastic! You are genious!!!\" )\n        submission.to_csv('submission.csv', index=False)\n    else:\n        print(\"ERROR! submission file doesn't satisfy concistency!!\")\n\ndef label_consistency(image_level, exam_level):\n    p_negative_exam_for_pe = exam_level[0]\n    p_indeterminate = exam_level[1]\n    p_chronic_pe = exam_level[2]\n    p_acute_and_chronic_pe = exam_level[3]\n    p_central_pe = exam_level[4]\n    p_leftsided_pe = exam_level[5]\n    p_rightsided_pe = exam_level[6]\n    p_rv_lv_ratio_gte_1 = exam_level[7]\n    p_rv_lv_ratio_lt_1 = exam_level[8]\n\n    pe_exist = np.any(image_level > 0.5)\n    if pe_exist:\n        p_negative_exam_for_pe = np.clip(p_negative_exam_for_pe, None, 0.499)\n        p_indeterminate = np.clip(p_indeterminate, None, 0.499)\n\n        if p_chronic_pe > 0.5 and p_acute_and_chronic_pe > 0.5:\n            tmp_list = [p_chronic_pe, p_acute_and_chronic_pe]\n            tmp_list[np.argmin(tmp_list)] = 0.499\n            p_chronic_pe, p_acute_and_chronic_pe = tmp_list\n        \n        if p_central_pe <= 0.5 and p_leftsided_pe <= 0.5 and p_rightsided_pe <= 0.5:\n            tmp_list = [p_central_pe, p_leftsided_pe, p_rightsided_pe]\n            tmp_list[np.argmax(tmp_list)] = 0.501\n            p_central_pe, p_leftsided_pe, p_rightsided_pe = tmp_list\n        \n        if p_rv_lv_ratio_gte_1 <= 0.5 and p_rv_lv_ratio_lt_1 <= 0.5:\n            tmp_list = [p_rv_lv_ratio_gte_1, p_rv_lv_ratio_lt_1]\n            tmp_list[np.argmax(tmp_list)] = 0.501\n            p_rv_lv_ratio_gte_1, p_rv_lv_ratio_lt_1 = tmp_list\n        if p_rv_lv_ratio_gte_1 > 0.5 and p_rv_lv_ratio_lt_1 > 0.5:\n            tmp_list = [p_rv_lv_ratio_gte_1, p_rv_lv_ratio_lt_1]\n            tmp_list[np.argmin(tmp_list)] = 0.499\n            p_rv_lv_ratio_gte_1, p_rv_lv_ratio_lt_1 = tmp_list\n        \n    else:\n        if p_negative_exam_for_pe <= 0.5 and p_indeterminate <= 0.5:\n            tmp_list = [p_negative_exam_for_pe, p_indeterminate]\n            tmp_list[np.argmax(tmp_list)] = 0.501\n            p_negative_exam_for_pe, p_indeterminate = tmp_list\n        if p_negative_exam_for_pe > 0.5 and p_indeterminate > 0.5:\n            tmp_list = [p_negative_exam_for_pe, p_indeterminate]\n            tmp_list[np.argmin(tmp_list)] = 0.499\n            p_negative_exam_for_pe, p_indeterminate = tmp_list\n        \n        p_chronic_pe = np.clip(p_chronic_pe, None, 0.499)\n        p_acute_and_chronic_pe = np.clip(p_acute_and_chronic_pe, None, 0.499)\n\n        p_central_pe = np.clip(p_central_pe, None, 0.499)\n        p_leftsided_pe = np.clip(p_leftsided_pe, None, 0.499)\n        p_rightsided_pe = np.clip(p_rightsided_pe, None, 0.499)\n\n        p_rv_lv_ratio_gte_1 = np.clip(p_rv_lv_ratio_gte_1, None, 0.499)\n        p_rv_lv_ratio_lt_1 = np.clip(p_rv_lv_ratio_lt_1, None, 0.499)\n\n    exam_level = np.array([\n        p_negative_exam_for_pe,\n        p_indeterminate,\n        p_chronic_pe,\n        p_acute_and_chronic_pe,\n        p_central_pe,\n        p_leftsided_pe,\n        p_rightsided_pe,\n        p_rv_lv_ratio_gte_1,\n        p_rv_lv_ratio_lt_1,\n    ])\n\n    return exam_level\n    \ndef get_exam_names(exam_names):\n    target_cols = [\n        'negative_exam_for_pe', \n        'indeterminate',\n        'chronic_pe', 'acute_and_chronic_pe',           # not indeterminate. Only One is true.\n        'central_pe', 'leftsided_pe', 'rightsided_pe',  # not indeterminate. At least One is true.\n        'rv_lv_ratio_gte_1', 'rv_lv_ratio_lt_1',        # not indeterminate. Only One is true.\n    ]\n\n    new_exam_names = []\n    for e in exam_names:\n        for col in target_cols:\n            new_exam_names.append(e + '_' + col)\n    \n    return new_exam_names\n\ndef check_consistency(sub, test_csv):\n    str_split = sub.id.str.split('_', 1, expand=True)\n    str_split.columns = ['StudyInstanceUID', 'label_type']\n    \n    condition = ~str_split.label_type.isnull()\n    new_df = pd.concat([sub[condition], str_split[condition]], axis=1)\n    del new_df['id']\n    df_exam = new_df.pivot(index='StudyInstanceUID', columns='label_type', values='label')\n    \n    condition = str_split.label_type.isnull()\n    df_image = sub[condition]\n    df_image = df_image.merge(test_csv, how='left', left_on='id', right_on='SOPInstanceUID')\n    df_image.rename(columns = {\"label\": \"pe_present_on_image\"}, inplace=True)\n    del df_image['id']\n    \n    df = df_exam.merge(df_image, how='left', on='StudyInstanceUID')\n    ids = [\"StudyInstanceUID\", \"SeriesInstanceUID\", \"SOPInstanceUID\"]\n    labels = [c for c in df.columns if c not in ids]\n    df = df[ids + labels]\n\n    # SPLIT NEGATIVE AND POSITIVE EXAMS\n    df['positive_images_in_exam'] = df['StudyInstanceUID'].map(df.groupby(['StudyInstanceUID']).pe_present_on_image.max())\n\n    df_pos = df.loc[df.positive_images_in_exam >  0.5]\n    df_neg = df.loc[df.positive_images_in_exam <= 0.5]\n\n    \n    # CHECKING CONSISTENCY OF POSITIVE EXAM LABELS\n    rule1a = df_pos.loc[((df_pos.rv_lv_ratio_lt_1  >  0.5)  & \n                         (df_pos.rv_lv_ratio_gte_1 >  0.5)) | \n                        ((df_pos.rv_lv_ratio_lt_1  <= 0.5)  & \n                         (df_pos.rv_lv_ratio_gte_1 <= 0.5))].reset_index(drop = True)\n    rule1a['broken_rule'] = '1a'\n\n    rule1b = df_pos.loc[(df_pos.central_pe    <= 0.5) & \n                        (df_pos.rightsided_pe <= 0.5) & \n                        (df_pos.leftsided_pe  <= 0.5)].reset_index(drop = True)\n    rule1b['broken_rule'] = '1b'\n\n    rule1c = df_pos.loc[(df_pos.acute_and_chronic_pe > 0.5) & \n                        (df_pos.chronic_pe           > 0.5)].reset_index(drop = True)\n    rule1c['broken_rule'] = '1c'\n\n    rule1d = df_pos.loc[(df_pos.indeterminate        > 0.5) | \n                        (df_pos.negative_exam_for_pe > 0.5)].reset_index(drop = True)\n    rule1d['broken_rule'] = '1d'\n    \n    \n    # CHECKING CONSISTENCY OF NEGATIVE EXAM LABELS\n    rule2a = df_neg.loc[((df_neg.indeterminate        >  0.5)  & \n                         (df_neg.negative_exam_for_pe >  0.5)) | \n                        ((df_neg.indeterminate        <= 0.5)  & \n                         (df_neg.negative_exam_for_pe <= 0.5))].reset_index(drop = True)\n    rule2a['broken_rule'] = '2a'\n\n    rule2b = df_neg.loc[(df_neg.rv_lv_ratio_lt_1     > 0.5) | \n                        (df_neg.rv_lv_ratio_gte_1    > 0.5) |\n                        (df_neg.central_pe           > 0.5) | \n                        (df_neg.rightsided_pe        > 0.5) | \n                        (df_neg.leftsided_pe         > 0.5) |\n                        (df_neg.acute_and_chronic_pe > 0.5) | \n                        (df_neg.chronic_pe           > 0.5)].reset_index(drop = True)\n    rule2b['broken_rule'] = '2b'\n    \n    # MERGING INCONSISTENT PREDICTIONS\n    errors = pd.concat([rule1a, rule1b, rule1c, rule1d, rule2a, rule2b], axis = 0)\n    \n    \n    if len(errors) == 0:\n        return True\n    else:\n        return False","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"config = {}\nconfig['checkpoint'] = [\n    {\n        'cnn': '/kaggle/input/rsna2020-models/run-20201020_060628-2cpwvzq3/files/checkpoints/epoch01-val_loss0.095.ckpt',\n        'rnn': '/kaggle/input/rsna2020-models/run-20201021_135533-33iuaotu/files/checkpoints/epoch32-val_loss0.145.ckpt',\n    },\n    {\n        'cnn': '/kaggle/input/rsna2020-models/run-20201020_150752-28onzftp/files/checkpoints/epoch02-val_loss0.095.ckpt',\n        'rnn': '/kaggle/input/rsna2020-models/run-20201021_135644-3kch48tn/files/checkpoints/epoch28-val_loss0.146.ckpt',\n    },\n    {\n        'cnn': '/kaggle/input/rsna2020-models/run-20201020_151143-31kxh5r3/files/checkpoints/epoch02-val_loss0.093.ckpt',\n        'rnn': '/kaggle/input/rsna2020-models/run-20201021_135714-3bjhhbju/files/checkpoints/epoch31-val_loss0.143.ckpt',\n    },\n    {\n        'cnn': '/kaggle/input/rsna2020-models/run-20201022_070959-21nvi91y/files/checkpoints/epoch02-val_loss0.027.ckpt',\n        'rnn': '/kaggle/input/rsna2020-models/run-20201023_041844-1y4pdiyr/files/checkpoints/epoch36-val_loss0.150.ckpt',\n    },\n    {\n        'cnn': '/kaggle/input/rsna2020-models/run-20201022_071013-3iwenrxd/files/checkpoints/epoch02-val_loss0.027.ckpt',\n        'rnn': '/kaggle/input/rsna2020-models/run-20201023_041955-1fmrfriu/files/checkpoints/epoch34-val_loss0.150.ckpt',\n    },\n    {\n        'cnn': '/kaggle/input/rsna2020-models/run-20201022_070525-19vyc6gd/files/checkpoints/epoch02-val_loss0.026.ckpt',\n        'rnn': '/kaggle/input/rsna2020-models/run-20201023_042016-30e7wvxy/files/checkpoints/epoch34-val_loss0.141.ckpt',\n    },\n]\n\nconfig['batch_size'] = 1\nconfig['chunk_size_list'] = [\n    64,\n    64,\n    64,\n    64,\n    64,\n    64,\n]\nconfig['n_workers'] = 1\nconfig['n_tta'] = 1\nconfig['seed'] = 42\nconfig['gpu'] = [0]\n\nconfig['transform'] = {\n    'size': 512\n}\n\nconfig['dataset'] = {\n  \"csv_path\": \"/kaggle/input/rsna-str-pulmonary-embolism-detection/test.csv\",\n  \"img_dir\": \"/kaggle/input/rsna-str-pulmonary-embolism-detection/test\",\n  \"file_extension\": \"dcm\",\n  \"mode\": \"test\",\n  \"network_type\": \"cnn_rnn\",\n}\n\nDO_FULL = False","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"import time\nstart = time.time()\ninference(config)\nprint(f\"elapsed time:{time.time() - start}\")","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"","execution_count":null,"outputs":[]}],"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":4,"nbformat_minor":4}