{"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":"!pip install python-gdcm pylibjpeg --no-index --find-links \"../input/rsna-wheels/\"\n","metadata":{"execution":{"iopub.status.busy":"2022-12-08T19:14:48.713716Z","iopub.execute_input":"2022-12-08T19:14:48.714252Z","iopub.status.idle":"2022-12-08T19:15:02.972629Z","shell.execute_reply.started":"2022-12-08T19:14:48.714138Z","shell.execute_reply":"2022-12-08T19:15:02.971094Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"I want to mention about the datasets i included in this notebook.\n- **rsna-breast-cancer-detection** : this is the competition dataset\n- **rsna-breast-cancer-detection-roi-model** : this is the roi extractor yolo model weights\n- **yolov5-repo** : this is the roi extractor yolo model source repository\n- **rsna-wheels** : this dataset contains wheels for the external packages to install them during offline submission.","metadata":{}},{"cell_type":"code","source":"\nimport pandas as pd\nimport torch\nimport torch.nn as nn\nfrom torchvision import transforms\nimport numpy as np\nimport os\nimport pydicom\nfrom pydicom.pixel_data_handlers import apply_windowing\n","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","execution":{"iopub.status.busy":"2022-12-08T19:15:02.975318Z","iopub.execute_input":"2022-12-08T19:15:02.976261Z","iopub.status.idle":"2022-12-08T19:15:05.588551Z","shell.execute_reply.started":"2022-12-08T19:15:02.976197Z","shell.execute_reply":"2022-12-08T19:15:05.587288Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"roi_extractor_model = torch.hub.load('/kaggle/input/yolov5-repo', 'custom', path='/kaggle/input/rsna-breast-cancer-detection-roi-model/rsna-roi-003.pt', source='local')","metadata":{"execution":{"iopub.status.busy":"2022-12-08T19:15:05.589956Z","iopub.execute_input":"2022-12-08T19:15:05.590599Z","iopub.status.idle":"2022-12-08T19:15:07.459515Z","shell.execute_reply.started":"2022-12-08T19:15:05.590561Z","shell.execute_reply":"2022-12-08T19:15:07.45822Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Some notes\n\nI am going to serve every breast in their own example because there are patients that has one breast with cancer and one breast without cancer.\nAnd every breast have at least one MLO and one CC views.\nAnd i know most people are using already preprocessed datasets but this class may help for inference time.\n\n# Steps\nNo matter what we do in our preprocessed datasets. We would be needing to replicate it for inference. \n- Read dicom with windowing\n- Cropping (ROI extraction using a yolo model)\n- Resizing (1024x1024)\n- Saving as png\n\nAlso your augmentations should come after above steps.","metadata":{}},{"cell_type":"code","source":"# helper functions\ndef read_dicom_with_windowing(dcm_file):\n    # from: https://www.kaggle.com/code/davidbroberts/mammography-apply-windowing/\n    im = pydicom.dcmread(dcm_file)\n    data = im.pixel_array\n    \n    # This line is the only difference in the two functions\n    data = apply_windowing(data, im)\n    \n    if im.PhotometricInterpretation == \"MONOCHROME1\":\n        data = np.amax(data) - data\n    else:\n        data = data - np.min(data)\n        \n    if np.max(data) != 0:\n        data = data / np.max(data)\n    data=(data * 255).astype(np.uint8)\n\n    return data","metadata":{"execution":{"iopub.status.busy":"2022-12-08T19:15:07.461246Z","iopub.execute_input":"2022-12-08T19:15:07.461745Z","iopub.status.idle":"2022-12-08T19:15:07.470396Z","shell.execute_reply.started":"2022-12-08T19:15:07.461702Z","shell.execute_reply":"2022-12-08T19:15:07.468395Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def crop_yolo_ROI(image):\n    # crop according to yolo breast detection\n    # https://www.kaggle.com/code/remekkinas/breast-cancer-roi-brest-extractor/notebook\n    detections = roi_extractor_model(image)\n    \n    bbox_df = detections.pandas().xyxy[0]\n    bbox_df.drop([\"name\"], axis=1, inplace=True)\n    bboxes = bbox_df.astype(int).to_dict(orient=\"records\")\n    \n    if len(bboxes) == 0:\n        return image\n    \n    if len(bboxes) != 1:\n        print(bboxes)\n        print(\"More than one ROI detected\")\n\n    bbox = bboxes[0]\n    \n    image = image[bbox[\"ymin\"]:bbox[\"ymax\"], bbox[\"xmin\"]:bbox[\"xmax\"]]\n    return image\n","metadata":{"execution":{"iopub.status.busy":"2022-12-08T19:15:07.475162Z","iopub.execute_input":"2022-12-08T19:15:07.475732Z","iopub.status.idle":"2022-12-08T19:15:07.484802Z","shell.execute_reply.started":"2022-12-08T19:15:07.475685Z","shell.execute_reply":"2022-12-08T19:15:07.483225Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"class BreastCancerDataset(torch.utils.data.Dataset):\n    # image to cancer\n    def __init__(self, df, dataset_folder, transform=None):\n        self.dataset_folder = dataset_folder\n        self.transform = transform\n        \n        if \"breast_id\" not in df.columns:\n            df[\"breast_id\"] = df[\"patient_id\"].astype(str) + \"_\" + df[\"laterality\"]\n\n        if \"image_path\" not in df.columns:\n            df[\"image_path\"] = df[\"patient_id\"].astype(str) + \"/\" + df[\"image_id\"].astype(str) + \".dcm\"\n\n        self.df = df\n        if 'cancer' in self.df.columns:\n            self.subset = 'train'\n        else:\n            self.subset = 'test'\n\n    def __len__(self):\n        return len(self.df.groupby(\"breast_id\"))\n\n    def __getitem__(self, idx):\n        # return MLO and CC images from given breast\n        breast_id = self.df[\"breast_id\"].unique()[idx]\n        breast_df = self.df[self.df[\"breast_id\"] == breast_id]\n        \n        MLO = breast_df[breast_df[\"view\"] == \"MLO\"]\n        CC = breast_df[breast_df[\"view\"] == \"CC\"]\n\n        MLO_image_path = os.path.join(self.dataset_folder, self.subset+ \"_images\", MLO[\"image_path\"].values[0])\n        CC_image_path = os.path.join(self.dataset_folder, self.subset+ \"_images\", CC[\"image_path\"].values[0])\n\n        MLO_image = read_dicom_with_windowing(MLO_image_path)\n        CC_image = read_dicom_with_windowing(CC_image_path)\n        \n        MLO_image = crop_yolo_ROI(MLO_image)\n        CC_image = crop_yolo_ROI(CC_image)\n\n        print(type(MLO_image))\n        MLO_image = torch.from_numpy(MLO_image)\n        CC_image = torch.from_numpy(CC_image)\n        \n        if self.transform:  # normalization and augmentation are in here\n            MLO_image = self.transform(MLO_image)\n            CC_image = self.transform(CC_image)\n        \n        # add these images into a 2 channel image\n        image = torch.cat([MLO_image, CC_image], dim=0)\n\n        if self.subset == 'train':\n            label = breast_df['cancer'].values[0]\n            return image, label\n        \n        \n        return image\n","metadata":{"execution":{"iopub.status.busy":"2022-12-08T19:15:07.486717Z","iopub.execute_input":"2022-12-08T19:15:07.487182Z","iopub.status.idle":"2022-12-08T19:15:07.505031Z","shell.execute_reply.started":"2022-12-08T19:15:07.487136Z","shell.execute_reply":"2022-12-08T19:15:07.503674Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"train_df = pd.read_csv('../input/rsna-breast-cancer-detection/train.csv')\ntest_df = pd.read_csv('../input/rsna-breast-cancer-detection/test.csv')\n\ndataset_folder = '../input/rsna-breast-cancer-detection'\n\ntransform = transforms.Compose([ # also add augmentation\n    transforms.ToPILImage(),\n    transforms.Resize((1024, 1024)),\n    transforms.ToTensor(),\n])\n\ntrain_dataset = BreastCancerDataset(train_df, dataset_folder, transform=transform)\n","metadata":{"execution":{"iopub.status.busy":"2022-12-08T19:15:07.506845Z","iopub.execute_input":"2022-12-08T19:15:07.507465Z","iopub.status.idle":"2022-12-08T19:15:07.775277Z","shell.execute_reply.started":"2022-12-08T19:15:07.507417Z","shell.execute_reply":"2022-12-08T19:15:07.77371Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# plot first batch\nbatch_size = 1\ntrain_loader = torch.utils.data.DataLoader(train_dataset, batch_size=batch_size, shuffle=True)\n\nimages, labels = next(iter(train_loader))\n\nprint(images.shape, labels.shape)\n","metadata":{"execution":{"iopub.status.busy":"2022-12-08T19:15:07.777745Z","iopub.execute_input":"2022-12-08T19:15:07.778149Z","iopub.status.idle":"2022-12-08T19:15:10.808333Z","shell.execute_reply.started":"2022-12-08T19:15:07.778112Z","shell.execute_reply":"2022-12-08T19:15:10.807007Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import matplotlib.pyplot as plt\n%matplotlib inline\nfig, ax = plt.subplots(1, 2, figsize=(10, 10))\nax[0].imshow(images[0, 0, :, :])\nax[1].imshow(images[0, 1, :, :])\n","metadata":{"execution":{"iopub.status.busy":"2022-12-08T19:15:10.810106Z","iopub.execute_input":"2022-12-08T19:15:10.811555Z","iopub.status.idle":"2022-12-08T19:15:11.595773Z","shell.execute_reply.started":"2022-12-08T19:15:10.811503Z","shell.execute_reply":"2022-12-08T19:15:11.594773Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"print(labels[0])\n","metadata":{"execution":{"iopub.status.busy":"2022-12-08T19:15:11.597112Z","iopub.execute_input":"2022-12-08T19:15:11.598187Z","iopub.status.idle":"2022-12-08T19:15:11.604885Z","shell.execute_reply.started":"2022-12-08T19:15:11.598138Z","shell.execute_reply":"2022-12-08T19:15:11.603897Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# lets preprocess all the data and serve the files as a dataset","metadata":{}},{"cell_type":"code","source":"\"\"\"\nimport cv2\nfrom PIL import Image\nfrom tqdm import tqdm\n\n\nfor path in tqdm(df.image_path.values):\n    path = os.path.join(\"./train_images\", path)\n    image_path = path.replace(\"_images\", \"_images_1024\")\n    image_path = image_path.replace(\".dcm\", \".png\")\n    if os.path.exists(image_path):\n        continue\n\n    image = read_dicom_with_windowing(path)\n    image = crop_yolo_ROI(image)\n    image = cv2.resize(image, (1024, 1024))\n    image_path = path.replace(\"_images\", \"_images_1024\")\n    image_path = image_path.replace(\".dcm\", \".png\")\n\n    # create folder if it doesnt exist\n    os.makedirs(os.path.dirname(image_path), exist_ok=True)\n    \n    im = Image.fromarray(image)\n    im.save(image_path)\n\n    print(image_path)\n\n\"\"\"","metadata":{"execution":{"iopub.status.busy":"2022-12-08T19:15:11.606371Z","iopub.execute_input":"2022-12-08T19:15:11.607138Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"","metadata":{},"execution_count":null,"outputs":[]}]}