{"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":"#Attaullah\n#attaullahshafiq10@gmail.com\n","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Import libs\n!conda install gdcm -c conda-forge -y\nimport numpy as np # linear algebra\nimport pandas as pd # data processing, CSV file I/O (e.g. pd.read_csv)\nimport os\nimport pydicom\nimport glob\nfrom tqdm.notebook import tqdm\nfrom pydicom.pixel_data_handlers.util import apply_voi_lut\nimport matplotlib.pyplot as plt\nfrom skimage import exposure","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"#import gdcm\nimport cv2\nimport warnings\nfrom fastai.vision.all import *\nfrom fastai.medical.imaging import *\nwarnings.filterwarnings('ignore')\ndataset_path = Path('../input/siim-covid19-detection')\nimport vtk\n\nfrom vtk.util import numpy_support\n\nreader = vtk.vtkDICOMImageReader()","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"#HTML view\n\nfrom IPython.display import HTML\nHTML('<iframe src=https://arxiv.org/pdf/1506.01497.pdf width=600 height=650></iframe>')","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# from https://www.kaggle.com/tanlikesmath/siim-covid-19-detection-a-simple-eda\ndef dicom2array(path, voi_lut=True, fix_monochrome=True):\n    # Original from: https://www.kaggle.com/raddar/convert-dicom-to-np-array-the-correct-way\n    dicom = pydicom.read_file(path)\n    \n    # VOI LUT (if available by DICOM device) is used to transform raw DICOM data to \n    # \"human-friendly\" view\n    if voi_lut:\n        data = apply_voi_lut(dicom.pixel_array, dicom)\n    else:\n        data = dicom.pixel_array\n               \n    # depending on this value, X-ray may look inverted - fix that:\n    if fix_monochrome and dicom.PhotometricInterpretation == \"MONOCHROME1\":\n        data = np.amax(data) - data\n        \n    data = data - np.min(data)\n    data = data / np.max(data)\n    data = (data * 255).astype(np.uint8)\n        \n    return data","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def plot_img(img, size=(7, 7), is_rgb=True, title=\"\", cmap='gray'):\n    plt.figure(figsize=size)\n    plt.imshow(img, cmap=cmap)\n    plt.suptitle(title)\n    plt.show()\n\n\ndef plot_imgs(imgs, cols=4, size=7, is_rgb=True, title=\"\", cmap='gray', img_size=(500,500)):\n    rows = len(imgs)//cols + 1\n    fig = plt.figure(figsize=(cols*size, rows*size))\n    for i, img in enumerate(imgs):\n        if img_size is not None:\n            img = cv2.resize(img, img_size)\n        fig.add_subplot(rows, cols, i+1)\n        plt.imshow(img, cmap=cmap)\n    plt.suptitle(title)\n    plt.show()","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"dicom_paths = get_dicom_files(dataset_path/'train')\nprint([str(path) for path in dicom_paths[:4]])\nimgs = [dicom2array(str(path)) for path in dicom_paths[:4]]\nplot_imgs(imgs)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"## training","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"#training\ntrain_image_df = pd.read_csv(dataset_path/'train_image_level.csv')\ntrain_image_df.head()","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## AxesSubplot","metadata":{}},{"cell_type":"code","source":"# Reference: https://www.kaggle.com/tanlikesmath/siim-covid-19-detection-a-simple-eda\ntrain_image_df['class'] = train_image_df.label.apply(lambda x: x.split()[0])\n\n\ntrain_image_df['x_min'] = train_image_df.label.apply(lambda x: float(x.split()[2]))\ntrain_image_df['y_min'] = train_image_df.label.apply(lambda x: float(x.split()[3]))\ntrain_image_df['x_max'] = train_image_df.label.apply(lambda x: float(x.split()[4]))\ntrain_image_df['y_max'] = train_image_df.label.apply(lambda x: float(x.split()[5]))\n\n\n\ndef get_bbox_area(row):\n    return (row['x_max']-row['x_min'])*(row['y_max']-row['y_min'])\n\n\ntrain_image_df['bbox_area'] = train_image_df.apply(get_bbox_area, axis=1)\ntrain_image_df['bbox_area'].hist()","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def image_path(row):\n    study_path = dataset_path/'train'/row.StudyInstanceUID\n    for i in get_dicom_files(study_path):\n        if row.id.split('_')[0] == i.stem: return i \n        \ntrain_image_df['image_path'] = train_image_df.apply(image_path, axis=1)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Now Ref: https://www.kaggle.com/chekoduadarsh/starter-code-faster-rcnn-covid-19-detection","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"imgs = []\nimage_paths = train_image_df['image_path'].values\nclass_ids = train_image_df['class']\n\n# map label_id to specify color\nlabel2color = {class_id:[random.randint(0,255) for i in range(3)] for class_id in class_ids}\nthickness = 3\nscale = 5\n\n\nfor i in range(8):\n    image_path = random.choice(image_paths)\n    print(image_path)\n    img = dicom2array(str(image_path))\n    img = cv2.resize(img, None, fx=1/scale, fy=1/scale)\n    img = np.stack([img, img, img], axis=-1)\n    \n    box = train_image_df.loc[train_image_df['image_path'] == image_path, ['x_min', 'y_min', 'x_max', 'y_max']].values[0]/scale\n    label = train_image_df.loc[train_image_df['image_path'] == image_path, ['class']].values[0][0]\n    \n    color = label2color[label]\n    img = cv2.rectangle(\n        img,\n        (int(box[0]), int(box[1])),\n        (int(box[2]), int(box[3])),\n        color, thickness)\n    \n    img = cv2.resize(img, (500,500))\n    imgs.append(img)\n    \nplot_imgs(imgs, cmap=None)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Model","metadata":{}},{"cell_type":"code","source":"import pandas as pd\nimport numpy as np\nimport cv2\nimport os\nimport re\nimport pydicom\nimport warnings\n\nfrom PIL import Image\n\nimport albumentations as A\nfrom albumentations.pytorch.transforms import ToTensorV2\nfrom albumentations.core.transforms_interface import ImageOnlyTransform\n\nimport torch\nimport torchvision\n\nfrom torchvision.models.detection.faster_rcnn import FastRCNNPredictor\nfrom torchvision.models.detection import FasterRCNN\nfrom torchvision.models.detection.rpn import AnchorGenerator\n\nfrom torch.utils.data import DataLoader, Dataset\nfrom torch.utils.data.sampler import SequentialSampler\nfrom pydicom import dcmread\n\nfrom matplotlib import pyplot as plt","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import random\npaddingSize= 0","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"num_classes = 2 # 1 Classes + 1 background\n\ndef label_to_name(id):\n    id = int(id)\n    id = id-1 \n    if id == 0:\n        return \"COVID\"\n    else:\n        return \"INVALID\"","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"image_ids = train_image_df['id'].unique()\nvalid_ids = image_ids[-6000:]# Tran and Validation Split \ntrain_ids = image_ids[:-6000]\n\n\nvalid_df = train_image_df[train_image_df['id'].isin(valid_ids)]\ntrain_df = train_image_df[train_image_df['id'].isin(train_ids)]\n\ntrain_df[\"class_id\"] = [1]*len(train_df)\nvalid_df[\"class_id\"] = [1]*len(valid_df)\nprint(len(train_image_df))\nprint(train_df.shape)\ntrain_df.head()","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"#Ref: https://www.kaggle.com/chekoduadarsh/starter-code-faster-rcnn-covid-19-detection\nclass COVIDTrainDataLoader(Dataset): #Class to load Training Data\n    \n    def __init__(self, dataframe, transforms=None,stat = 'Train'):\n        super().__init__()\n        \n        self.image_ids = dataframe[\"id\"].unique()\n        \n        self.df = dataframe\n        self.transforms = transforms\n        self.stat = stat\n        \n    def __getitem__(self, index):\n        if self.stat == 'Train':\n            \n            image_id = self.image_ids[index]\n            \n            records = self.df[(self.df['id'] == image_id)]\n            records = records.reset_index(drop=True)\n            image = dicom2array(self.df[(self.df['id'] == image_id)]['image_path'].values[0])#dcmread\n\n            #image = ds.pixel_array\n           \n            '''if \"PhotometricInterpretation\" in dicom:\n                if dicom.PhotometricInterpretation == \"MONOCHROME1\":\n                    image = np.amax(image) - image'''\n\n            intercept =  0.0\n            slope =1.0\n\n            if slope != 1:\n                image = slope * image.astype(np.float64)\n                image = image.astype(np.int16)\n\n            \n            image += np.int16(intercept)        \n\n            image = np.stack([image, image, image])\n            image = image.astype('float32')\n            image = image - image.min()\n            image = image / image.max()\n            image = image * 255.0\n            image = image.transpose(1,2,0)\n\n            if records.loc[0, \"class_id\"] == 0:\n                records = records.loc[[0], :]\n\n            boxes = records[['x_min', 'y_min', 'x_max', 'y_max']].values\n            area = (boxes[:, 3] - boxes[:, 1]) * (boxes[:, 2] - boxes[:, 0])\n            area = torch.as_tensor(area, dtype=torch.float32)\n            labels = torch.tensor(records[\"class_id\"].values, dtype=torch.int64)\n\n            # suppose all instances are not crowd\n            iscrowd = torch.zeros((records.shape[0],), dtype=torch.int64)\n\n            target = {}\n            target['boxes'] = boxes\n            target['labels'] = labels\n            target['id'] = torch.tensor([index])\n            target['area'] = area\n            target['iscrowd'] = iscrowd\n\n            if self.transforms:\n                sample = {\n                    'image': image,\n                    'bboxes': target['boxes'],\n                    'labels': labels\n                }\n                sample = self.transforms(**sample)\n                image = sample['image']\n\n                target['boxes'] = torch.tensor(sample['bboxes'])\n\n            if target[\"boxes\"].shape[0] == 0:\n                # Albumentation cuts the target (class 14, 1x1px in the corner)\n                target[\"boxes\"] = torch.from_numpy(np.array([[0.0, 0.0, 1.0, 1.0]]))\n                target[\"area\"] = torch.tensor([1.0], dtype=torch.float32)\n                target[\"labels\"] = torch.tensor([0], dtype=torch.int64)\n            \n            return image, target, image_ids\n        \n        else:\n                   \n            image_id = self.image_ids[index]\n            records = self.df[(self.df['id'] == image_id)]\n            records = records.reset_index(drop=True)\n\n            image = dicom2array(self.df[(self.df['id'] == image_id)]['image_path'].values[0])#dcmread\n\n            #image = ds.pixel_array\n\n            intercept =  0.0\n            slope = 1.0\n\n            if slope != 1:\n                image = slope * image.astype(np.float64)\n                image = image.astype(np.int16)\n\n            image += np.int16(intercept)        \n\n            image = np.stack([image, image, image])\n            image = image.astype('float32')\n            image = image - image.min()\n            image = image / image.max()\n            image = image * 255.0\n            image = image.transpose(1,2,0)\n\n            if self.transforms:\n                sample = {\n                    'image': image,\n                }\n                sample = self.transforms(**sample)\n                image = sample['image']\n\n            return image, image_id\n    \n    def __len__(self):\n        return self.image_ids.shape[0]","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Albumentations\ndef get_train_transform():\n    return A.Compose([\n        A.ShiftScaleRotate(scale_limit=0.1, rotate_limit=45, p=0.25),\n        A.LongestMaxSize(max_size=800, p=1.0),\n        A.Normalize(mean=(0, 0, 0), std=(1, 1, 1), max_pixel_value=255.0, p=1.0),\n        ToTensorV2(p=1.0)\n    ], bbox_params={'format': 'pascal_voc', 'label_fields': ['labels']})\n\ndef get_valid_transform():\n    return A.Compose([\n        A.Normalize(mean=(0, 0, 0), std=(1, 1, 1), max_pixel_value=255.0, p=1.0),\n        ToTensorV2(p=1.0)\n    ], bbox_params={'format': 'pascal_voc', 'label_fields': ['labels']})\n\ndef get_test_transform():\n    return A.Compose([\n        A.Normalize(mean=(0, 0, 0), std=(1, 1, 1), max_pixel_value=255.0, p=1.0),\n        ToTensorV2(p=1.0)\n    ])","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"model = torchvision.models.detection.fasterrcnn_resnet50_fpn(pretrained=True)\n# get number of input features for the classifier\nin_features = model.roi_heads.box_predictor.cls_score.in_features\n\n# replace the pre-trained head with a new one\nmodel.roi_heads.box_predictor = FastRCNNPredictor(in_features, num_classes)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"device = torch.device('cuda') if torch.cuda.is_available() else torch.device('cpu')\n\ndef collate_fn(batch):\n    return tuple(zip(*batch))\n\ntrain_dataset = COVIDTrainDataLoader(train_df, get_train_transform())\nvalid_dataset = COVIDTrainDataLoader(valid_df, get_valid_transform())\n\n\n# split the dataset in train and test set\nindices = torch.randperm(len(train_dataset)).tolist()\n# Create train and validate data loader\ntrain_data_loader = DataLoader(\n    train_dataset,\n    batch_size=2,\n    shuffle=True,\n    num_workers=4,\n    collate_fn=collate_fn\n)\n\nvalid_data_loader = DataLoader(\n    valid_dataset,\n    batch_size=2,\n    shuffle=False,\n    num_workers=4,\n    collate_fn=collate_fn\n)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"class Averager:\n    def __init__(self):\n        self.current_total = 0.0\n        self.iterations = 0.0\n\n    def send(self, value):\n        self.current_total += value\n        self.iterations += 1\n\n    @property\n    def value(self):\n        if self.iterations == 0:\n            return 0\n        else:\n            return 1.0 * self.current_total / self.iterations\n\n    def reset(self):\n        self.current_total = 0.0\n        self.iterations = 0.0","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"model.to(device)\nparams = [p for p in model.parameters() if p.requires_grad]\noptimizer = torch.optim.SGD(params, lr=0.005, momentum=0.9, weight_decay=0.0005)\nlr_scheduler = torch.optim.lr_scheduler.StepLR(optimizer, step_size=4, gamma=0.1)\n\nnum_epochs =  2 #Low epoch to save GPU time\n\nloss_hist = Averager()\nitr = 1\nlossHistoryiter = []\nlossHistoryepoch = []\n\nimport time\nstart = time.time()\n\nfor epoch in range(num_epochs):\n    loss_hist.reset()\n    \n    for images, targets, image_ids in train_data_loader:\n        \n        images = list(image.to(device) for image in images)\n        targets = [{k: v.to(device) for k, v in t.items()} for t in targets]\n\n        loss_dict = model(images, targets)  \n        \n        losses = sum(loss for loss in loss_dict.values())\n        loss_value = losses.item()\n\n        loss_hist.send(loss_value)\n        lossHistoryiter.append(loss_value)\n        optimizer.zero_grad()\n        losses.backward()\n        optimizer.step()\n\n        if itr % 50 == 0:\n            print(f\"Iteration #{itr} loss: {loss_value}\")\n\n        itr += 1\n    \n    # update the learning rate\n    if lr_scheduler is not None:\n        lr_scheduler.step()\n    lossHistoryepoch.append(loss_hist.value)\n    print(f\"Epoch #{epoch} loss: {loss_hist.value}\")   \n    \nend = time.time()\nhours, rem = divmod(end-start, 3600)\nminutes, seconds = divmod(rem, 60)\nprint(\"Time taken to Train the model :{:0>2}:{:0>2}:{:05.2f}\".format(int(hours),int(minutes),seconds))","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import plotly.graph_objects as go\n\nx = [i for i in range(num_epochs)]\ny = lossHistoryepoch\nfig = go.Figure()\nfig.add_trace(go.Scatter(x=x,y=y,\n                    mode='lines',\n                    name='lines'))\n\nfig.update_layout(title='Loss vs Epochs',\n                   xaxis_title='Epochs',\n                   yaxis_title='Loss')\nfig.show()","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"test_df = pd.read_csv('../input/siim-covid19-detection/sample_submission.csv')\n\ndef image_path(row):\n    study_path = dataset_path/'test'/row.id.split(\"_\")[0]\n    for i in get_dicom_files(study_path):\n        return i \n        \ntest_df['image_path'] = test_df.apply(image_path, axis=1)\ntest_df.head()","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"cpu_device = torch.device(\"cpu\")\nlabels =  targets[1]['labels'].cpu().numpy()","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"test_dataset = COVIDTrainDataLoader(test_df, get_test_transform(),\"Test\")\n\ntest_data_loader = DataLoader(\n    test_dataset,\n    batch_size=8,\n    shuffle=False,\n    num_workers=1,\n    drop_last=False,\n    collate_fn=collate_fn\n)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def format_prediction_string(labels, boxes, scores):\n    pred_strings = []\n    for j in zip(labels, scores, boxes):\n        pred_strings.append(\"{0} {1:.4f} {2} {3} {4} {5}\".format(\n            j[0], j[1], j[2][0], j[2][1], j[2][2], j[2][3]))\n\n    return \" \".join(pred_strings)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"#data testing","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"images, image_ids = next(iter(test_data_loader))\nimages = list(image.to(device) for image in images)\n\nfor number in random.sample([1,2,3],3):\n  img = images[number].permute(1,2,0).cpu().numpy()\n  #labels= targets[number]['labels'].cpu().numpy().astype(np.int32)\n  fig, ax = plt.subplots(1, 1, figsize=(16, 8))\n  ax.set_axis_off()\n  ax.imshow(img)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"#sample prediction","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"\"\"\"\nimages = list(img.to(device) for img in images)\n\noutputs = model(images)\noutputs = [{k: v.to(cpu_device) for k, v in t.items()} for t in outputs]\n\n\nboxes = outputs[2]['boxes'].cpu().detach().numpy().astype(np.int32)\nimg = images[2].permute(1,2,0).cpu().detach().numpy()\nlabels= outputs[2]['labels'].cpu().detach().numpy().astype(np.int32)\nscore = outputs[2]['scores']\n\nfig, ax = plt.subplots(1, 1, figsize=(16, 8))\n\nimg = cv2.cvtColor(np.float32(img), cv2.COLOR_RGB2BGR)\nfor i in range(len(boxes)):\n  img = cv2.rectangle(img,(boxes[i][0]+paddingSize,boxes[i][1]+paddingSize),(boxes[i][2]+paddingSize,boxes[i][3]+paddingSize),(255,0,0),20)\n  #print(le.inverse_transform([labels[i]-1])[0])\n  #print(label_to_name(labels[i]), (boxes[i][0]+paddingSize,boxes[i][1]+paddingSize),(boxes[i][2]+paddingSize,boxes[i][3]+paddingSize))\n  img = cv2.putText(img, label_to_name(labels[i]), (int(boxes[i][0]), int(boxes[i][1])), cv2.FONT_HERSHEY_TRIPLEX,3, (255,0,0), 3, cv2.LINE_AA)\n\nax.set_axis_off()\nax.imshow(img)\n\"\"\"","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def image_path(row):\n    study_path = dataset_path/'train'/row.StudyInstanceUID\n    for i in get_dicom_files(study_path):\n        if row.id.split('_')[0] == i.stem: return i \n        \ntrain_image_df['image_path'] = train_image_df.apply(image_path, axis=1)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"\"\"\"\nimgs = []\nimage_paths = train_image_df['image_path'].values\n\n# map label_id to specify color\nthickness = 10\nscale = 5\n\n\nfor i in range(8):\n    image_path = random.choice(image_paths)\n    print(image_path)\n    img = dicom2array(path=image_path)\n    img = cv2.resize(img, None, fx=1/scale, fy=1/scale)\n    img = np.stack([img, img, img], axis=-1)\n    for i in train_image_df.loc[train_image_df['image_path'] == image_path].split_label.values[0]:\n        if i[0] == 'opacity':\n            img = cv2.rectangle(img,\n                                (int(float(i[2])/scale), int(float(i[3])/scale)),\n                                (int(float(i[4])/scale), int(float(i[5])/scale)),\n                                [255,0,0], thickness)\n    \n    img = cv2.resize(img, (500,500))\n    imgs.append(img)\n    \nplot_imgs(imgs, cmap=None)\n\"\"\"","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"test_df = pd.DataFrame(results, columns=['id', 'PredictionString'])\ntest_df.to_csv('mysubmission.csv', index=False)\ntest_df.head()","metadata":{"trusted":true},"execution_count":null,"outputs":[]}]}