{"cells":[{"metadata":{},"cell_type":"markdown","source":"## Introduction\nI has labeled 100 masks that randomly sample from all of lung slices and used Unet to segment lung.Unet model performs well although data only has 100. I hasn't enough time to label it.Fortunately,model performed better than I expected. It may have little overfitting, but predicting the lung is enough."},{"metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true},"cell_type":"code","source":"import matplotlib.pyplot as plt\nimport numpy as np\nimport random\nimport os\nimport glob\nimport pandas as pd \nfrom tqdm import tqdm\nimport sys\nimport glob\nimport cv2\n\nimport pydicom\nfrom sklearn.utils import shuffle\n\nimport albumentations as A\n\nimport torch\nimport torch.nn as nn\nfrom torch import optim\nfrom torch.utils.data import DataLoader\nfrom torch.utils.data.dataset import Dataset\nfrom torch.optim.lr_scheduler import  ReduceLROnPlateau","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"d629ff2d2480ee46fbb7e2d37f6b5fab8052498a","_cell_guid":"79c7e3d0-c299-4dcb-8224-4455121ee9b0","trusted":true},"cell_type":"code","source":"sys.path.append('../input/efficientnet-pytorch/EfficientNet-PyTorch-master')\nsys.path.append('../input/pretrainedmodels/pretrainedmodels-0.7.4/')\nsys.path.append('../input/segmentation-models-pytorch/')\nimport segmentation_models_pytorch as smp","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"root_path = '../input/osiclungmask100'\nimg_path = sorted(glob.glob(root_path+'/*/img.png'))\nmask_path = sorted(glob.glob(root_path+'/*/post_label.png'))\n\nimgpaths,maskpaths = shuffle(img_path,mask_path, random_state=0)\n\ntrain_images_path = imgpaths[:int(len(imgpaths)*0.8)]\ntrain_masks_path = maskpaths[:int(len(imgpaths)*0.8)]\nval_images_path = imgpaths[int(len(imgpaths)*0.8):]\nval_masks_path = maskpaths[int(len(maskpaths)*0.8):]\n\ntransform = A.Compose([\n    A.Rotate(p=0.2,limit=30),\n    A.HorizontalFlip(p=0.2),\n    A.OneOf([\n        A.GridDistortion(p=0.1,distort_limit=0.2),\n        A.ElasticTransform(sigma=10, alpha=1,  p=0.1)\n    ]),\n])\n","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"batch = 8\nlr = 0.0003\nwd = 5e-4\nepochs = 80\noutput_path = './'\ndevice =  torch.device('cuda:0')\nexperiment_name = 'lung_Unet_densenet121'","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"class Data_Generate(Dataset):\n    def __init__(self,img_paths,seg_paths=None,transform=None):\n        self.img_paths = img_paths\n        self.seg_paths = seg_paths\n        self.transform = transform\n        \n    def __getitem__(self,index):\n        if self.seg_paths is not None:\n            img_path = self.img_paths[index]\n            mask_path = self.seg_paths[index]\n            \n            mask = cv2.imread(mask_path,0)/255\n            img = cv2.imread(img_path,0)/255\n\n            if self.transform != None:\n                aug = transform(image=img,mask=mask)\n                img = aug['image']\n                mask = aug['mask']\n                \n            img = img[None,:,:]\n            img = img.astype(np.float32)\n            mask = mask[None,:,:]\n            mask = mask.astype(np.float32)\n            \n            return img,mask\n        \n        else:\n            img = cv2.imread(self.img_paths[index],0)/255\n            img = img[None,:,:]\n            img = img.astype(np.float32)\n            return img\n        \n    def __len__(self):\n        return len(self.img_paths)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"train_db = Data_Generate(train_images_path,train_masks_path,transform=transform)\ntrain_loader = DataLoader(train_db, batch_size=batch, shuffle=True, num_workers=4)\nval_db = Data_Generate(val_images_path,val_masks_path,transform=None)\nval_loader = DataLoader(val_db, batch_size=batch, shuffle=False, num_workers=4)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"f,ax = plt.subplots(4,4,figsize=(16,16))\nfor i in range(16):\n    img = train_db[i][0]\n    ax[i//4,i%4].imshow(img[0])","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"class EarlyStopping:\n    \"\"\"Early stops the training if validation loss doesn't improve after a given patience.\"\"\"\n    def __init__(self, patience=7, verbose=False, delta=0, path='checkpoint.pt'):\n        \"\"\"\n        Args:\n            patience (int): How long to wait after last time validation loss improved.\n                            Default: 7\n            verbose (bool): If True, prints a message for each validation loss improvement. \n                            Default: False\n            delta (float): Minimum change in the monitored quantity to qualify as an improvement.\n                            Default: 0\n            path (str): Path for the checkpoint to be saved to.\n                            Default: 'checkpoint.pt'\n        \"\"\"\n        self.patience = patience\n        self.verbose = verbose\n        self.counter = 0\n        self.best_score = None\n        self.early_stop = False\n        self.val_loss_min = np.Inf\n        self.delta = delta\n        self.path = path\n\n    def __call__(self, val_loss, model):\n\n        score = -val_loss\n\n        if self.best_score is None:\n            self.best_score = score\n            self.save_checkpoint(val_loss, model)\n        elif score < self.best_score + self.delta:\n            self.counter += 1\n            print(f'EarlyStopping counter: {self.counter} out of {self.patience}')\n            if self.counter >= self.patience:\n                self.early_stop = True\n        else:\n            self.best_score = score\n            self.save_checkpoint(val_loss, model)\n            self.counter = 0\n\n    def save_checkpoint(self, val_loss, model):\n        '''Saves model when validation loss decrease.'''\n        if self.verbose:\n            print(f'Validation loss decreased ({self.val_loss_min:.6f} --> {val_loss:.6f}).  Saving model ...')\n        torch.save(model.state_dict(), self.path)\n        self.val_loss_min = val_loss","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"model = smp.Unet('densenet121', classes=1, in_channels=1,activation='sigmoid',encoder_weights='imagenet').to(device)\n    \noptimizer = optim.Adam(filter(lambda p: p.requires_grad, model.parameters()), lr=lr, weight_decay=wd)\nscheduler = ReduceLROnPlateau(optimizer, mode='min', factor=0.5, patience=3, min_lr=1e-8, verbose=True)\n\ncriterion = smp.utils.losses.DiceLoss(eps=1.)\niou = smp.utils.metrics.IoU()\nearly_stopping = EarlyStopping(patience=6, verbose=True,path=os.path.join(output_path, f'best_{experiment_name}.pth'))","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"num_train_loader = len(train_loader)\nnum_val_loader = len(val_loader)\nfor epoch in range(epochs):\n    train_losses,train_score,val_losses,val_score = 0,0,0,0\n    model.train()\n\n    for idx, sample in enumerate(train_loader):\n        image, label = sample\n        image, label = image.to(device), label.to(device)\n        out = model(image)\n        loss = criterion(out, label)\n        optimizer.zero_grad()\n        loss.backward()\n        optimizer.step()\n        train_losses += loss/num_train_loader\n        train_score += iou(out,label)/num_train_loader\n    \n    model.eval()\n    for idx, sample in enumerate(val_loader):\n        image, label = sample\n        image, label = image.to(device), label.to(device)\n        with torch.no_grad():\n            out = model(image)\n        loss = criterion(out, label)\n        val_losses += loss/num_val_loader\n        val_score += iou(out,label)/num_val_loader\n    print('epoch {}/{}\\t LR:{}\\t train_loss:{}\\t train_score:{}\\t val_loss:{}\\t val_score:{}' \\\n          .format(epoch+1, epochs, optimizer.param_groups[0]['lr'], train_losses, train_score, val_losses, val_score))\n    scheduler.step(val_losses)\n    \n    early_stopping(val_losses, model)\n    if early_stopping.early_stop:\n        print(\"Early stopping\")\n        break","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"class Test_Generate(Dataset):\n    def __init__(self,img_paths):\n        self.img_paths = img_paths\n        \n    def __getitem__(self,index):\n        dicom = pydicom.dcmread(self.img_paths[index])\n        slice_img = dicom.pixel_array\n        slice_img = (slice_img-slice_img.min())/(slice_img.max()-slice_img.min())\n        slice_img = (slice_img*255).astype(np.uint8)\n        if slice_img.shape[0] != 512:\n            slice_img = cv2.resize(slice_img,(512,512))\n            \n        slice_img = slice_img[None,:,:]\n        slice_img = (slice_img/255).astype(np.float32)\n        return slice_img\n        \n    def __len__(self):\n        return len(self.img_paths)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"dicom_root_path = '../input/osic-pulmonary-fibrosis-progression/train/*/*'\ndicom_paths = glob.glob(dicom_root_path)\ndicom_paths = random.sample(dicom_paths,16)\n\ntest_db = Test_Generate(dicom_paths)\ntest_loader = DataLoader(test_db, batch_size=batch, shuffle=False, num_workers=0)\n\nmodel.load_state_dict(torch.load('./best_lung_Unet_densenet121.pth'))\nmodel.eval()\n\nouts = []\nfor idx, sample in enumerate(test_loader):\n    image = sample\n    image = image.to(device)\n    with torch.no_grad():\n        out = model(image)\n    out = out.cpu().data.numpy()\n    out = np.where(out>0.5,1,0)\n    out = np.squeeze(out)\n    outs.append(out)\n    \nouts = np.concatenate(outs)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"f,ax = plt.subplots(4,4,figsize=(16,16))\naxes = ax.flatten()\nfor idx in range(len(outs)//2):\n    axes[idx*2].imshow(test_db[idx][0])\n    axes[idx*2+1].imshow(outs[idx])","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## Conclusion\nUnet perform well to segment lung,and beyond the previous traditional CV methods.\n\nps:I spent several hours on labeling 100 lung masks,hope it can help us to caculate some metrics."}],"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}