{"cells":[{"metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true},"cell_type":"markdown","source":"# 1. Import Packages\n"},{"metadata":{"trusted":true},"cell_type":"code","source":"import numpy as np\nimport matplotlib.pyplot as plt\nimport pandas as pd\nimport seaborn as sns\nimport os\nimport pathlib\nfrom pprint import pprint\n\n#img\nimport cv2\n\n#pytorch\nimport torch\n\nfrom torch import nn\nfrom torch import functional as F\nfrom torch import optim\nfrom torch.utils.data import Dataset,DataLoader,random_split\nfrom torchvision import transforms\nfrom torch.nn import Module\nfrom torchvision import models\nfrom PIL import Image\n#dicom\nimport pydicom\n\nfrom torchvision.models.detection import FasterRCNN\nfrom torchvision.models.detection.faster_rcnn import FastRCNNPredictor\nfrom torchvision.models.detection.rpn import AnchorGenerator\nfrom torch.utils.data import DataLoader\nfrom albumentations.pytorch.transforms import ToTensorV2\nfrom tqdm.notebook import tqdm\nimport albumentations as A\n\n#set Device\ndevice = 'cuda' if torch.cuda.is_available() else 'cpu'\nprint(device)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# 2.Path"},{"metadata":{"trusted":true},"cell_type":"code","source":"#-----path-----\n#train csv\ntrain_csv_path=pathlib.\\\n        Path(\"../input/vinbigdata-chest-xray-abnormalities-detection/train.csv\")\nsample_sub_path=pathlib.\\\n        Path(\"../input/vinbigdata-chest-xray-abnormalities-detection/sample_submission.csv\")\n#dicom data\ntrain_data_path=pathlib.\\\n        Path(\"../input/vinbigdata-chest-xray-abnormalities-detection/train\")\ntest_data_path=pathlib.\\\n        Path(\"../input/vinbigdata-chest-xray-abnormalities-detection/test\")\n\n#pathの確認\nprint(pathlib.Path.exists(train_csv_path),\n      pathlib.Path.exists(train_data_path),\n      pathlib.Path.exists(test_data_path)\n     )","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"#trainデータのindexリストを取得する。\nimage_ids=[x for x in train_data_path.iterdir() if x.is_file()]\nlen(image_ids)\n","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# 3. Read DataFrame : Train Data"},{"metadata":{"trusted":true},"cell_type":"code","source":"#train csv\ndf=pd.read_csv(train_csv_path)\ndf.head()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"#image id部分のみを分割\nsample_=pathlib.\\\n    Path('../input/vinbigdata-chest-xray-abnormalities-detection/train/000434271f63a053c4128a0ba6352c7f.dicom')\n#拡張子のみを取得する\nprint(\"拡張子；\",image_ids[0].suffix)\n#拡張子なしのidのみを取得\nprint(\"拡張子なしのファイル名取得:\",image_ids[0].stem)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"#bounding boxを取得することは可能。\ndf[df[\"image_id\"]==sample_.stem]","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# 4. dataframeの欠損値を修正\n\n\n### class Name: No finding はデータなしに該当\n\n>0 - Aortic enlargement\n1 - Atelectasis\n2 - Calcification\n3 - Cardiomegaly\n4 - Consolidation\n5 - ILD\n6 - Infiltration\n7 - Lung Opacity\n8 - Nodule/Mass\n9 - Other lesion\n10 - Pleural effusion\n11 - Pleural thickening\n12 - Pneumothorax\n13 - Pulmonary fibrosis\n\n### No_findingに該当するには15に指定するか\n\n\n#### columns;x_min\ty_min\tx_max\ty_maxはNaNになっているため欠損値補完が必要\n\n\n\n"},{"metadata":{"trusted":true},"cell_type":"code","source":"print(df.isnull().sum())\n#上記意外に欠損値はなさそう。\n#bounding boxがない場合Nanとなっているため、穴埋め \ndf.fillna(0,inplace=True)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"#classid\n\"\"\"\n>0 - Aortic enlargement\n1 - Atelectasis\n2 - Calcification\n3 - Cardiomegaly\n4 - Consolidation\n5 - ILD\n6 - Infiltration\n7 - Lung Opacity\n8 - Nodule/Mass\n9 - Other lesion\n10 - Pleural effusion\n11 - Pleural thickening\n12 - Pneumothorax\n13 - Pulmonary fibrosis\n\"\"\"\nprint(df[\"class_id\"].unique())\nprint(len(df[\"class_id\"].unique()))","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# dicomデータの画像表示例\nsample_ids=image_ids[10]\nprint(sample_ids.stem)\nprint(sample_ids.suffix)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# 5.1 Dicom Example"},{"metadata":{"trusted":true},"cell_type":"code","source":"dicom=pydicom.dcmread(sample_ids)\ndicom","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# 5.2 Normalize Dicom Image\n\n[reference]\nhttps://www.kaggle.com/raddar/popular-x-ray-image-normalization-techniques"},{"metadata":{"trusted":true},"cell_type":"code","source":"import pydicom\nfrom pydicom.pixel_data_handlers.util import apply_voi_lut\nfrom skimage import exposure\n\n\n#[reference]\\\n#https://www.kaggle.com/raddar/popular-x-ray-image-normalization-techniques\n\ndef read_xray(path, voi_lut = True, fix_monochrome = True):\n    dicom = pydicom.read_file(path)\n    \n    # VOI LUT (if available by DICOM device) is used to transform raw DICOM data to \"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        \n    return data","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"### [1] No-Normalization"},{"metadata":{"trusted":true},"cell_type":"code","source":"# 1. Non normalization\n\nimg = read_xray(str(sample_ids))\nplt.figure(figsize=(7,7))\nplt.imshow(img, 'gray')\nplt.show()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"### [2] Histogram normalization\nThe general idea is to make pixel distribution uniform. This makes X-rays appear a little darker. This generates view, which radiologist would not see in his standard workplace.\\\nSuch normalization is used in popular open-source X-ray datasets, such as CheXpert."},{"metadata":{"trusted":true},"cell_type":"code","source":"img = read_xray(str(sample_ids))\nimg = exposure.equalize_hist(img)\nplt.figure(figsize = (7,7))\nplt.imshow(img, 'gray')\nplt.show()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"### [3] CLAHE normalization\nThis method produces sharper images and is quite often used in chest X-ray research. This generates view, which radiologist would not see in his standard workplace. However, it closely resembles the \"bone-enhanced\" view in some X-rays done (usually due to broken ribs).\n\n"},{"metadata":{"trusted":true},"cell_type":"code","source":"img = read_xray(str(sample_ids))\nimg = exposure.equalize_adapthist(img/np.max(img))\nplt.figure(figsize = (7,7))\nplt.imshow(img, 'gray')\nplt.show()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## 6. Data Augmentation and DataSet\n\n>1. multi boudingboxの出力に対応するために train_dataにあるimage_idをまず取得して、そのIDに該当するdataFrameデータを取得する。\n\n>2.targetがマルチ出力になるため、dict形式で出力する\n\nreference\nhttps://www.kaggle.com/pestipeti/vinbigdata-fasterrcnn-pytorch-train"},{"metadata":{"trusted":true},"cell_type":"code","source":"#[reference]\n#https://www.kaggle.com/raddar/popular-x-ray-image-normalization-techniques\n\ndef read_xray(path, voi_lut = True, fix_monochrome = True):\n    dicom = pydicom.read_file(path)\n    \n    # VOI LUT (if available by DICOM device) is used to transform raw DICOM data to \"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        \n    return data","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"### 6.1 Data Augmentation"},{"metadata":{"trusted":true},"cell_type":"code","source":"#Set Image Augumentation\nfrom torchvision import transforms\nimport albumentations\n\n#transforms.Grayscale(3)\n\ntransform=transforms.Compose([\n        transforms.ToPILImage(),\n        transforms.Grayscale(3),\n        transforms.ToTensor(),\n        ]) ","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"###  6.2 Resize Image and BoundingBox"},{"metadata":{"trusted":true},"cell_type":"code","source":"# Resize Bounding Box\n\ndef resize(image, boxes, width, height):\n    # 現在の高さと幅を取得しておく\n    c_height, c_width = image.shape[:2]\n    img = cv2.resize(image, (width, height))\n    \n    # 圧縮する比率(rate)を計算\n    r_width = width / c_width\n    r_height = width / c_height\n    \n    # 比率を使ってBoundingBoxの座標を修正\n    new_boxes = []\n    for box in boxes:\n        x,y,w,h=box\n        x = int(x * r_width)\n        y = int(y * r_height)\n        w = int(w * r_width)\n        h = int(h * r_height)\n        new_box =[x, y, w, h]\n        new_boxes.append(new_box)\n    return img, new_boxes","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"class My_Dataset(Dataset):\n    def __init__(self,df,):\n        \n        #dataframeを格納する\n        self.df = df\n        self.image_ids=df[\"image_id\"].unique()\n        self.image_dir=pathlib.\\\n                    Path(\"../input/vinbigdata-chest-xray-abnormalities-detection/train\")\n        #columnsを設定する\n        self.box_col=[\"y_min\",\"y_min\",\"x_max\",\"y_max\"]\n        #transform\n        self.transform=transforms.Compose(\n            [\n            transforms.ToPILImage(),\n            transforms.Grayscale(3),\n            transforms.ToTensor(),\n            ]) \n\n    def __len__(self):\n        return len(self.image_ids)\n    \n    def __getitem__(self,index,transform=False):\n        \n        #train_data(dicom)よりrandomでdicomデータを取得\n        image_id=self.image_ids[index]\n        #print(image_id)\n        \n        #[dicom_data] #arrayに変換されて出力\n        image=read_xray(str(self.image_dir/image_id)+\".dicom\")\n\n        #Histogram normalization(type:ndarray)\n        image = exposure.equalize_hist(image)\n        \n        \n        #-----bboxが複数の可能性あり、複数のデータを取得する必要あり。-----\n        records = self.df[(self.df['image_id'] == image_id)]\n        records = records.reset_index(drop=True)\n        \n        if records.loc[0, \"class_id\"] == 0:\n            records = records.loc[[0], :]\n        #records = self.df.loc[self.df.image_id == img_path.split('.')[0],:].reset_index(drop = True)\n        \n        #-----bounding box-----\n        boxes = records[self.box_col].values.astype(np.float32)\n        #----area-----\n        #bbox:[x,y,w,h]とすると、(w-x)*(h-y)で出力される。\n        area = (boxes[:,2] - boxes[:,0]) * (boxes[:,3] - boxes[:,1])\n        area = area.astype(np.float32)\n        \n        #----labels-----\n        \"\"\"\n        0 - Aortic enlargement,1 - Atelectasis,2 - Calcification,3 - Cardiomegaly,4 - Consolidation,\n        5 - ILD,6 - Infiltration,7 - Lung Opacity,8 - Nodule/Mass,9 - Other lesion,10 - Pleural effusion,\n        11 - Pleural thickening,12 - Pneumothorax,13 - Pulmonary fibrosis\n        15に該当するのはNoneっぽい\n        \"\"\"\n        \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        #元の画像データの画像サイズを取得する\n        \n        #-----[target]:dict-----\n        target = {}\n        target['boxes'] = torch.tensor(boxes)\n        target['labels'] = labels\n        target['image_id'] = torch.tensor([index])\n        #target['area'] = torch.tensor(area)\n        #target['iscrowd'] = iscrowd\n        target[\"image_row_shape\"]=torch.tensor(image.shape)\n        target[\"dicom_id\"]=image_id\n        \n        #Transoformed Image\n        #transform\n        #image_transformed=self.transform(image.astype(np.float32))\n        \n        #width,height=[512,512]でresizeする\n        width=512\n        height=512\n        image_resized,boxes_resized=resize(image,boxes,width, height)\n        #print(\"boxes_resized:\",boxes_resized)\n        target[\"boxes_resized\"]=torch.tensor(boxes_resized)\n        \n        #transform\n        image_transformed=self.transform(image_resized.astype(np.float32))\n        \n        return image_transformed, target","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"### Dataloaderで出力する場合\n\n各画像ごとにbounding_boxの数が異なるため、collate_fnを変更する必要あり。"},{"metadata":{"trusted":true},"cell_type":"code","source":"def collate_fn(batch):\n    imgs, targets= list(zip(*batch))\n    imgs = torch.stack(imgs)\n    #torch.stackをかけると出力ごとに異なるため、torch.stackできない\n    #list or　tuppleで返せばうまくいく。\n    targets = list(targets)\n    #bc = torch.stack(bc)\n    return imgs,targets","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"train_dataset=My_Dataset(df=df)\ntrain_dataloader=DataLoader(train_dataset,\n                            batch_size=2,shuffle=True, \n                            collate_fn= collate_fn)\n\n# Output Sample\nimage,target =next(iter(train_dataloader))\nprint(\"------image-----\")\nprint(\"image_tensor:\",image.shape)\nprint(\"-----target-----\")\nprint(target[0])\nprint(target[1])","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## 6.3 Sample Image with Bounding Box"},{"metadata":{"trusted":true},"cell_type":"code","source":"def draw_boundingbox(target):\n    \n    #とりあえず上記まで取得しておけば良いか。\n    \n    #-----image-----\n    image_dir=pathlib.\\\n        Path(\"../input/vinbigdata-chest-xray-abnormalities-detection/train\")\n    image_id=target[\"dicom_id\"]\n    img=read_xray(str(image_dir/image_id)+\".dicom\")\n    \n    #-----bounding box-----\n    bboxes=target[\"boxes\"].detach().numpy().astype(int)\n    #print(\"bounding_box:\\n\",bboxes)\n    print(\"bounding box:\",bboxes)\n        \n    #-----label name-----\n    labels=target[\"labels\"].detach().numpy()\n    print(\"label:\",labels)\n        \n    #Plot Image with Bounding Box\n    for bbox,label in zip(bboxes,labels):\n\n        x = int(bbox[0])\n        y = int(bbox[1])\n        w = int(bbox[2])\n        h = int(bbox[3])\n        color = (0,0,255)\n        \n        #labelを付与(stringに変換する必要あり)\n        cv2.putText(img,str(label), (x, y), cv2.FONT_HERSHEY_SIMPLEX, 1.5, color, 3)\n        #cv2.rectangleのtuppleにはintを入力する(floatは不可)\n        cv2.rectangle(img, (x, y), (w, h), (255,0,0), 2)\n    \n    plt.figure(num=None, figsize=(5,5), dpi=80, facecolor='w', edgecolor='k')\n    plt.imshow(img,cmap=\"bone\")\n    plt.show()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"for i in range(len(target)):\n    data=target[i]\n    draw_boundingbox(data)    ","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## 6.4 resized_Image and Bounding Box"},{"metadata":{"trusted":true},"cell_type":"code","source":"def draw_boundingbox_resized(target):\n    \n    #とりあえず上記まで取得しておけば良いか。\n    \n    #-----image-----\n    image_dir=pathlib.\\\n        Path(\"../input/vinbigdata-chest-xray-abnormalities-detection/train\")\n    image_id=target[\"dicom_id\"]\n    img=read_xray(str(image_dir/image_id)+\".dicom\")\n    width,height=512,512\n    img=cv2.resize(img, (width, height))\n    \n    #-----bounding box-----\n    bboxes=target[\"boxes_resized\"].detach().numpy().astype(int)\n    #print(\"bounding_box:\\n\",bboxes)\n    print(\"bounding box:\",bboxes)\n        \n    #-----label name-----\n    labels=target[\"labels\"].detach().numpy()\n    print(\"label:\",labels)\n    #Plot Image with Bounding Box\n    for bbox,label in zip(bboxes,labels):\n\n        x = int(bbox[0])\n        y = int(bbox[1])\n        w = int(bbox[2])\n        h = int(bbox[3])\n        color = (0,0,255)\n        \n        #cv2.rectangleのtuppleにはintを入力する(floatは不可)\n        cv2.rectangle(img, (x, y), (w, h), (255,0,0), 1)\n        \n        #labelを付与(stringに変換する必要あり)\n        cv2.putText(img,str(label), (x, y), cv2.FONT_HERSHEY_SIMPLEX, 1.5, color, 1)\n    \n    plt.figure(num=None, figsize=(5,5), dpi=80, facecolor='w', edgecolor='k')\n    plt.imshow(img,cmap=\"bone\")\n    plt.show()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"for i in range(len(target)):\n    draw_boundingbox_resized(target[i])","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# next: How to Use EfficientDet-pytorch..."},{"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}