{"cells":[{"metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true},"cell_type":"code","source":"# This Python 3 environment comes with many helpful analytics libraries installed\n# It is defined by the kaggle/python Docker image: https://github.com/kaggle/docker-python\n# For example, here's several helpful packages to load\n\nimport numpy as np # linear algebra\nimport pandas as pd # data processing, CSV file I/O (e.g. pd.read_csv)\n\n# Input data files are available in the read-only \"../input/\" directory\n# For example, running this (by clicking run or pressing Shift+Enter) will list all files under the input directory\nimport collections\nimport os\n#for dirname, _, filenames in os.walk('/kaggle/input'):\n#    for filename in filenames:\n#        print(os.path.join(dirname, filename))\n\n# You can write up to 5GB to the current directory (/kaggle/working/) that gets preserved as output when you create a version using \"Save & Run All\" \n# You can also write temporary files to /kaggle/temp/, but they won't be saved outside of the current session","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"!pip install dicom\n!pip install imageio\nimport pydicom\nimport imageio\nfrom time import sleep\nfrom tqdm.auto import tqdm, trange\nimport cv2","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"os.chdir('/kaggle/working/')","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"!pip3 install -U scikit-image\n!pip3 install -U cython \n!pip3 install \"git+https://github.com/philferriere/cocoapi.git#egg=pycocotools&subdirectory=PythonAPI\"","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"%%bash\ngit clone https://github.com/pytorch/vision.git\ncd vision\ngit checkout v0.3.0\ncp references/detection/utils.py ../\ncp references/detection/transforms.py ../\ncp references/detection/coco_eval.py ../\ncp references/detection/engine.py ../\ncp references/detection/coco_utils.py ../","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"import torch\nfrom PIL import Image\nimport numpy as np\nimport matplotlib.pyplot as plt\nimport torchvision.transforms as T\nfrom PIL import ImageDraw\nfrom torch.utils.data import random_split, DataLoader\nfrom torch import tensor\nfrom torchvision.utils import make_grid\nimport torchvision\nfrom engine import train_one_epoch, evaluate\nfrom torchvision.models.detection.faster_rcnn import FastRCNNPredictor\nimport utils\nimport matplotlib.gridspec as gridspec\n\n\n%matplotlib inline","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"train_path = '/kaggle/input/rsna-pneumonia-detection-challenge/stage_2_train_images/'\ntest_path = '/kaggle/input/rsna-pneumonia-detection-challenge/stage_2_test_images/'\npath = '/kaggle/input/rsna-pneumonia-detection-challenge'\n\nos.mkdir('/kaggle/working/train_pngs')\nos.mkdir('/kaggle/working/test_pngs')\nos.mkdir('/kaggle/working/train_labels')\n\nos.chdir(path)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"labels_csv = pd.read_csv(os.path.join(path,'stage_2_train_labels.csv'))\n\nlabels_csv.x.fillna(0, inplace=True)\nlabels_csv.y.fillna(0, inplace=True)\nlabels_csv.width.fillna(1023, inplace=True)\nlabels_csv.height.fillna(1023, inplace=True)\n\nlabels_csv['x_max'] = labels_csv['x']+labels_csv['width']\nlabels_csv['y_max'] = labels_csv['y']+labels_csv['height']\n\nlabels_csv.head()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"def parse_one_annot(box_coord, filename):\n   boxes_array = box_coord[box_coord[\"patientId\"] == filename][[\"x\", \"y\",        \n   \"x_max\", \"y_max\"]].values\n   \n   return boxes_array\n\ndef dicom_to_png(image_path):\n    dcm_data = pydicom.read_file(image_path)\n    im = dcm_data.pixel_array\n    return im\n\nclass RSNA(torch.utils.data.Dataset):\n    def __init__(self, path, box_coord, transforms=None):\n        self.path = path\n        self.box_coord = box_coord\n        self.transforms = transforms\n        self.imgs = sorted(os.listdir(path))\n\n    def __getitem__(self, idx):\n        # load images and bounding boxes\n        img_path = os.path.join(self.path, self.box_coord['patientId'][idx]+'.dcm')\n        img = dicom_to_png(img_path)\n        #img = Image.open(img_path).convert(\"RGB\")\n        #img = img.resize((1024, 1024))\n        box_list = parse_one_annot(self.box_coord, \n        self.box_coord['patientId'][idx])\n        boxes = torch.as_tensor(box_list, dtype=torch.float32)\n        num_objs = len(box_list)\n        # there is only one class\n        labels = torch.ones((num_objs,), dtype=torch.int64)\n        image_id = torch.tensor([idx])\n        area = (boxes[:, 3] - boxes[:, 1]) * (boxes[:, 2] - boxes[:,\n        0])\n        # suppose all instances are not crowd\n        iscrowd = torch.zeros((num_objs,), dtype=torch.int64)\n        target = {}\n        target[\"boxes\"] = boxes\n        target[\"labels\"] = labels\n        target[\"image_id\"] = image_id\n        target[\"area\"] = area\n        target[\"iscrowd\"] = iscrowd\n        if self.transforms is not None:\n                img = self.transforms(img)\n        return img, target\n    def __len__(self):\n          return len(self.box_coord['patientId'])","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"def train_tfms():\n   transforms = []\n   # converts the image, a PIL image, into a PyTorch Tensor\n   transforms.append(T.ToTensor())\n   #if train:\n      # during training, randomly flip the training images\n      # and ground-truth for data augmentation\n   #   transforms.append(T.RandomHorizontalFlip(0.5))\n   return T.Compose(transforms)\n\ndef val_tfms():\n   transforms = []\n   # converts the image, a PIL image, into a PyTorch Tensor\n   transforms.append(T.ToTensor())\n   #if train:\n      # during training, randomly flip the training images\n      # and ground-truth for data augmentation\n   #   transforms.append(T.RandomHorizontalFlip(0.5))\n   return T.Compose(transforms)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"np.random.seed(42)\nmsk = np.random.rand(len(labels_csv)) < 0.9\n\ntrain_df = labels_csv[msk].reset_index()\nval_df = labels_csv[~msk].reset_index()\nlen(train_df), len(val_df)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"train_ds = RSNA(train_path, train_df, transforms=train_tfms())\nval_ds = RSNA(train_path, val_df, transforms=val_tfms())\n#test_ds = BROID(test_path, test_df, transforms=val_tfms())\nlen(train_ds), len(val_ds)#, len(test_ds)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"train_ds.__getitem__(0)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"batch_size = 8","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"train_dl = DataLoader(train_ds, batch_size, shuffle=True, \n                      num_workers=3, pin_memory=True, collate_fn=utils.collate_fn)\nval_dl = DataLoader(val_ds, batch_size*2, \n                    num_workers=2, pin_memory=True, collate_fn=utils.collate_fn)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"def draw_bounding_box(img, label_boxes):\n  #img = np.squeeze(img, axis=0)\n  #label_boxes = np.squeeze(label_boxes, axis=0)\n  all_imgs = []\n  for i in range(img.shape[0]):        \n      image = img[i,:,:,:]\n      image = image.squeeze(0)\n      im = Image.fromarray(image.mul(255).byte().numpy())\n      draw = ImageDraw.Draw(im)\n      labels = label_boxes[i]['boxes']\n      for elem in range(len(labels)):\n        draw.rectangle([(labels[elem][0], labels[elem][1]),\n        (labels[elem][2], labels[elem][3])], \n        outline =\"white\", width =3)\n      all_imgs.append(np.array(im))\n  all_imgs = np.array(all_imgs)\n  return T.ToTensor()(all_imgs)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"def show_batch(dl):\n    for images, labels in dl:\n        image = draw_bounding_box(torch.stack(images), labels)\n        image = image.permute(1,2,0).mul(255).byte().numpy()\n        fig, ax = plt.subplots(figsize=(16, 16), nrows=2, ncols=3)\n        gs1 = gridspec.GridSpec(3, 4)\n        gs1.update(wspace=0.030, hspace=0.030) # set the spacing between axes. \n        id = 0\n        for i in range(2):\n            for j in range(3):\n                ax[i,j].set_title('Pneumonia:')\n                ax[i,j].imshow(image[id])\n                id = id + 1\n        \n        plt.show()\n        break","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"show_batch(train_dl)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"def get_model(num_classes):\n   # load an object detection model pre-trained on COCO\n   model = torchvision.models.detection.fasterrcnn_resnet50_fpn(pretrained=True)\n   # get the number of input features for the classifier\n   in_features = model.roi_heads.box_predictor.cls_score.in_features\n   # replace the pre-trained head with a new on\n   model.roi_heads.box_predictor = FastRCNNPredictor(in_features, num_classes)\n   \n   return model","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"def get_default_device():\n    \"\"\"Pick GPU if available, else CPU\"\"\"\n    if torch.cuda.is_available():\n        return torch.device('cuda')\n    else:\n        return torch.device('cpu')\n    \ndef to_device(data, device):\n    \"\"\"Move tensor(s) to chosen device\"\"\"\n    if isinstance(data, (list,tuple)):\n        return [to_device(x, device) for x in data]\n    return data.to(device, non_blocking=True)\n\n\nclass DeviceDataLoader():\n    \"\"\"Wrap a dataloader to move data to a device\"\"\"\n    def __init__(self, dl, device):\n        self.dl = dl\n        self.device = device\n        \n    def __iter__(self):\n        \"\"\"Yield a batch of data after moving it to device\"\"\"\n        for b in self.dl: \n            yield to_device(b, self.device)\n\n    def __len__(self):\n        \"\"\"Number of batches\"\"\"\n        return len(self.dl)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"device = get_default_device()\n\ntrain_dl = DeviceDataLoader(train_dl, device)\nval_dl = DeviceDataLoader(val_dl, device)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"device = torch.device('cuda') if torch.cuda.is_available() else torch.device('cpu')\nnum_classes = 2\n# get the model using our helper function\nmodel = get_model(num_classes)\n# move model to the right device\nmodel.to(device)\n# construct an optimizer\nparams = [p for p in model.parameters() if p.requires_grad]\noptimizer = torch.optim.SGD(params, lr=0.005,\n                            momentum=0.9, weight_decay=0.0005)\n# and a learning rate scheduler which decreases the learning rate by # 10x every 3 epochs\nlr_scheduler = torch.optim.lr_scheduler.StepLR(optimizer,\n                                               step_size=3,\n                                               gamma=0.1)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# let's train it for 10 epochs\nnum_epochs = 10\nfor epoch in range(num_epochs):\n   # train for one epoch, printing every 10 iterations\n   train_one_epoch(model, optimizer, train_dl, device, epoch, print_freq=10)\n# update the learning rate\n   lr_scheduler.step()\n   # evaluate on the test dataset\n   evaluate(model, val_dl, device=device)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"d629ff2d2480ee46fbb7e2d37f6b5fab8052498a","_cell_guid":"79c7e3d0-c299-4dcb-8224-4455121ee9b0","trusted":true},"cell_type":"code","source":"def parse_data(df):\n    \"\"\"\n    Method to read a CSV file (Pandas dataframe) and parse the \n    data into the following nested dictionary:\n\n      parsed = {\n        \n        'patientId-00': {\n            'dicom': path/to/dicom/file,\n            'label': either 0 or 1 for normal or pnuemonia, \n            'boxes': list of box(es)\n        },\n        'patientId-01': {\n            'dicom': path/to/dicom/file,\n            'label': either 0 or 1 for normal or pnuemonia, \n            'boxes': list of box(es)\n        }, ...\n\n      }\n\n    \"\"\"\n    parsed = collections.defaultdict(lambda:{'dicom': None,\n                                        'png': None,     \n                                        'label': None,\n                                        'boxes': []})\n    for n, row in df.iterrows():\n        # --- Initialize patient entry into parsed \n        pid = row['patientId']\n        parsed[pid]['dicom'] = str('stage_2_train_images/{id}.dcm').format(id=pid)\n        parsed[pid]['png'] = str('/kaggle/working/train_pngs/{id}.png').format(id=pid)\n        parsed[pid]['label'] = row['Target']\n        parsed[pid]['boxes'].append(hw_bb(row))\n\n    return parsed\n\ndef parse_data_test(test_list):\n    \n    parsed = collections.defaultdict(lambda:{'dicom': None,\n                                        'png': None})\n    for row in test_list:\n        # --- Initialize patient entry into parsed \n        parsed[row]['dicom'] = str('stage_2_test_images/{id}.dcm').format(id=row)\n        parsed[row]['png'] = str('/kaggle/working/test_pngs/{id}.png').format(id=row)\n    \n    return parsed\n\ndef hw_bb(row): return np.array([row['y'], row['x'], row['height']+row['y'], row['width']+row['x']])\n\ndef bb_hw(a): return np.array([a[1],a[0],a[3]-a[1],a[2]-a[0]])","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"parsed = parse_data(labels_csv)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"def from_dicom_to_png(parsed):\n    with tqdm(total=len(parsed)) as pbar:\n        for i in range(len(parsed)):\n            for k, v in parsed.items():\n                dcm_data = pydicom.read_file(v['dicom'])\n                im = dcm_data.pixel_array\n                imageio.imwrite(v['png'], im)\n                pbar.update(1)\n                #sleep(0.01)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"from_dicom_to_png(parsed)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"parsed_test_data = parse_data_test(list(list(i[:-4] for i in os.listdir('stage_2_test_images'))))\nfrom_dicom_to_png(parsed_test_data)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"def save_img_from_dcm(dcm_dir, img_dir, patient_id):\n    img_fp = os.path.join(img_dir, \"{}.jpg\".format(patient_id))\n    if os.path.exists(img_fp):\n        return\n    dcm_fp = os.path.join(dcm_dir, \"{}.dcm\".format(patient_id))\n    img_1ch = pydicom.read_file(dcm_fp).pixel_array\n    img_3ch = np.stack([img_1ch]*3, -1)\n\n    img_fp = os.path.join(img_dir, \"{}.jpg\".format(patient_id))\n    cv2.imwrite(img_fp, img_3ch)\n    \ndef save_label_from_dcm(label_dir, patient_id, row=None):\n    # rsna defualt image size\n    img_size = 1024\n    label_fp = os.path.join(label_dir, \"{}.txt\".format(patient_id))\n    \n    f = open(label_fp, \"a\")\n    if row is None:\n        f.close()\n        return\n\n    top_left_x = row[1]\n    top_left_y = row[2]\n    w = row[3]\n    h = row[4]\n    \n    # 'r' means relative. 'c' means center.\n    rx = top_left_x/img_size\n    ry = top_left_y/img_size\n    rw = w/img_size\n    rh = h/img_size\n    rcx = rx+rw/2\n    rcy = ry+rh/2\n    \n    line = \"{} {} {} {} {}\\n\".format(0, rcx, rcy, rw, rh)\n    \n    f.write(line)\n    f.close()\n        \ndef save_yolov3_data_from_rsna(dcm_dir, img_dir, label_dir, annots):\n    for row in tqdm(annots.values):\n        patient_id = row[0]\n\n        img_fp = os.path.join(img_dir, \"{}.jpg\".format(patient_id))\n        if os.path.exists(img_fp):\n            save_label_from_dcm(label_dir, patient_id, row)\n            continue\n\n        target = row[5]\n        # Since kaggle kernel have samll volume (5GB ?), I didn't contain files with no bbox here.\n        #if target == 0:\n        #    continue\n        save_label_from_dcm(label_dir, patient_id, row)\n        save_img_from_dcm(dcm_dir, img_dir, patient_id)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"save_yolov3_data_from_rsna(train_path, '/kaggle/working/train_pngs', '/kaggle/working/train_labels', labels_csv)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"len(os.listdir('/kaggle/working/train_pngs'))\n","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"len(os.listdir('/kaggle/working/train_labels'))","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}