{"cells":[{"metadata":{},"cell_type":"markdown","source":"As discussed at [here](https://www.kaggle.com/c/ranzcr-clip-catheter-line-classification/discussion/210064) the dataset has inverted images.  \nLet's check them."},{"metadata":{"trusted":true},"cell_type":"code","source":"import cv2\nimport matplotlib.pyplot as plt\nimport numpy as np\nimport glob\nimport os\nimport pandas as pd","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"train_folder = \"/kaggle/input/ranzcr-clip-catheter-line-classification/train/\"\ninverted_img_uid_l = [\n    '1.2.826.0.1.3680043.8.498.93053605340693492468203536922883055634',\n    '1.2.826.0.1.3680043.8.498.89369371707034087254309411362762932453',\n    '1.2.826.0.1.3680043.8.498.40874263518848015471042617691509326469',\n    '1.2.826.0.1.3680043.8.498.60784708544708592859086705347710043758',\n    '1.2.826.0.1.3680043.8.498.85400081110981214468425786540292202327',\n    '1.2.826.0.1.3680043.8.498.46805490620878014733000033680286522306',\n    '1.2.826.0.1.3680043.8.498.12426606037326639593164801231383702795',\n    '1.2.826.0.1.3680043.8.498.44981676356222715641792310487558318854',\n    '1.2.826.0.1.3680043.8.498.92223955132523718945948760352349399544',\n    '1.2.826.0.1.3680043.8.498.45667033584171921001890022815707001978',\n    '1.2.826.0.1.3680043.8.498.62409687695240227890004324406035594441'\n]","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"len(inverted_img_uid_l)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"inverted_img_path_l = [train_folder+uid+\".jpg\" for uid in inverted_img_uid_l]","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"fig = plt.figure()\nfor i,uid in enumerate(inverted_img_uid_l):\n    ax = fig.add_subplot(2, 6, i+1)\n    path = train_folder + uid + \".jpg\"\n    img = cv2.imread(path)\n    assert(img is not None)\n    ax.imshow(img)\nplt.show()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Yeah, there are inverted. But are these the ALL of inverted images? I don't know...  \nLet's do ML classification!\n\nThe number of known inverted images are small. So why don't we increase the inverted images by invert known normal images by ourself?\n\nFirst. Pick up images which not inverted."},{"metadata":{"trusted":true},"cell_type":"code","source":"train_path_l = glob.glob(train_folder + \"*.jpg\")\nnormal_path_l = [path for path in train_path_l if os.path.splitext(os.path.basename(path))[0] not in inverted_img_uid_l]\nprint(len(train_path_l))\nprint(len(normal_path_l))","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"image_num = 100\ntarget_img_path_l = normal_path_l[:image_num]","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"plt_col_num = 10\nfig = plt.figure()\nfor i,path in enumerate(target_img_path_l):\n    ax = fig.add_subplot(len(target_img_path_l)/plt_col_num, plt_col_num, i+1)\n    img = cv2.imread(path)\n    assert(img is not None)\n    ax.imshow(img)\nplt.show()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"All of above images looks not inverted.  \nNext, pick up half of these images paths to invert. I'm going to invert when data loading time."},{"metadata":{"trusted":true},"cell_type":"code","source":"import random\ninverted_path_l = random.sample(target_img_path_l, len(target_img_path_l)//2)\nnormal_path_l = list(set(target_img_path_l) - set(inverted_path_l))","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Make labels."},{"metadata":{"trusted":true},"cell_type":"code","source":"df_label_normal = pd.DataFrame({\"path\": normal_path_l, \"inverted\":False})\ndf_label_inverted = pd.DataFrame({\"path\": inverted_path_l, \"inverted\":True})\ndf_label = pd.concat([df_label_normal, df_label_inverted])\ndf_label = df_label.sample(frac=1).reset_index(drop=True)\ndf_label.head()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Check the label balance."},{"metadata":{"trusted":true},"cell_type":"code","source":"df_label[\"inverted\"].value_counts()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Now, ML time!"},{"metadata":{"trusted":true},"cell_type":"code","source":"import torch\nfrom torch.utils.data import Dataset, DataLoader\nimport pandas as pd\nimport glob\nimport os\nimport json\n# from skimage import io, transform\nimport cv2 as cv\nimport numpy as np\nimport re\nimport matplotlib.pyplot as plt\nfrom torchvision import transforms","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Create dataset."},{"metadata":{"trusted":true},"cell_type":"code","source":"class MyDataset(Dataset):\n\n    def __init__(self, label_df, img_size):\n        self.label_df = label_df\n        self.img_size = img_size\n\n    def __len__(self):\n        return self.label_df.shape[0]\n\n    def __getitem__(self, idx):\n        if torch.is_tensor(idx):\n            idx = idx.tolist()\n\n        row = self.label_df.loc[idx, :]\n        \n        img = cv.imread(row[\"path\"])\n        assert(img is not None)\n        img = cv2.resize(img, (self.img_size, self.img_size))\n        \n        if row[\"inverted\"]:\n            img = cv2.bitwise_not(img)\n        \n        img = img.transpose(2, 0, 1).astype('float32')\n        \n        label_ans = torch.Tensor([0, 0])\n        label_ans[int(row[\"inverted\"])] = 1\n\n        return img, row[\"inverted\"]","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Test dataset."},{"metadata":{"trusted":true},"cell_type":"code","source":"dataset = MyDataset(df_label, 64)\ndataloader = torch.utils.data.DataLoader(\n    dataset, batch_size=10, shuffle=True\n)\nimage_l, label_ans = next(iter(dataloader))","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"print(label_ans)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"plt_col_num = 10\nfig = plt.figure()\nfor i,img in enumerate(image_l):\n#     print(img.shape)\n    ax = fig.add_subplot(2,5, i+1)\n    img_np = img.to('cpu').detach().numpy().copy()\n    img_np = img_np.transpose(1, 2, 0).astype(\"uint8\")\n    ax.imshow(img_np)\nplt.show()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Define net."},{"metadata":{"trusted":true},"cell_type":"code","source":"import torch\nimport torch.nn as nn\nimport torch.nn.functional as F\n\n\nclass MyNet(nn.Module):\n    def __init__(self):\n        super(MyNet, self).__init__()\n        self.block1 = self.conv_block(c_in=3, c_out=64, dropout=0.1, kernel_size=5, stride=1, padding=2)\n        self.block2 = self.conv_block(c_in=64, c_out=32, dropout=0.1, kernel_size=3, stride=1, padding=1)\n        self.block3 = self.conv_block(c_in=32, c_out=32, dropout=0.1, kernel_size=3, stride=1, padding=1)\n        self.lastcnn = nn.Conv2d(in_channels=32, out_channels=2, kernel_size=16, stride=1, padding=0)\n        self.maxpool = nn.MaxPool2d(kernel_size=2, stride=2)\n\n    def forward(self, x):\n        x = self.block1(x)\n        x = self.maxpool(x)\n        x = self.block2(x)\n        x = self.block3(x)\n        x = self.maxpool(x)\n        x = self.lastcnn(x)\n        return x\n\n    def conv_block(self, c_in, c_out, dropout,  **kwargs):\n        seq_block = nn.Sequential(\n            nn.Conv2d(in_channels=c_in, out_channels=c_out, **kwargs),\n            nn.BatchNorm2d(num_features=c_out),\n            nn.ReLU(),\n            nn.Dropout2d(p=dropout)\n        )\n        return seq_block","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Prepare for learning."},{"metadata":{"trusted":true},"cell_type":"code","source":"import torch.optim as optim\n\nmodel = MyNet()\nmodel = model.cuda()\ninput_img_size = 64\n\ndataset = MyDataset(df_label, input_img_size)\n\ntrain_length = int(0.7 * len(dataset))\ntest_length = len(dataset) - train_length\nlengths = [train_length, test_length]\n\n\ntrain_dataset, valid_dataset = torch.utils.data.random_split(\n    dataset, lengths, generator=torch.Generator().manual_seed(42))\n\ntrain_batch_num  = 8\ntrain_loader = DataLoader(train_dataset, batch_size=train_batch_num, shuffle=True, num_workers=12)\nvalid_loader = DataLoader(valid_dataset, batch_size=1, shuffle=False, num_workers=4)\n\ncriterion = nn.CrossEntropyLoss()\n# criterion = nn.BCELoss()\noptimizer = optim.SGD(model.parameters(), lr=0.001, momentum=0.9)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Learning."},{"metadata":{"trusted":true},"cell_type":"code","source":"epochs = 20\nfor epoch in range(epochs):\n    for i, (inputs, labels) in enumerate(train_loader, 0):\n        inputs = inputs.cuda()\n        labels = labels.long().cuda()\n        # zero the parameter gradients\n        optimizer.zero_grad()\n\n        # forward + backward + optimize\n        outputs = model(inputs).squeeze()\n#         print(outputs, outputs.shape)\n#         print(labels, labels.shape)\n        loss = criterion(outputs, labels)\n        loss.backward()\n        optimizer.step()\n\n        # print statistics\n    print(i, loss.item())\n\nprint('Finished Training')\ntorch.save(model, \"./invert_detect_model.h5\")","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Predict with the model."},{"metadata":{"trusted":true},"cell_type":"code","source":"model = torch.load(\"./invert_detect_model.h5\")\nmodel = model.cuda()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"df_train = pd.DataFrame({\"path\": train_path_l, \"inverted\":False}) # Dummy for inverted column.\ndataset = MyDataset(df_train, input_img_size)\n\ndataloader = torch.utils.data.DataLoader(\n    dataset, batch_size=64, shuffle=False\n)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"df_train.shape","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"df_train_result = pd.DataFrame({\"path\": train_path_l, \"false_score\":np.nan, \"true_score\":np.nan})","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"predicted_inverted_img_l = []\nfor i, (inputs, labels) in enumerate(dataloader, 0):\n    inputs = inputs.cuda()\n    outputs = model(inputs).squeeze()\n    start_index = i * dataloader.batch_size\n    df_train_result.loc[\n        start_index: start_index+len(inputs)-1, [\"false_score\", \"true_score\"]\n    ] = outputs.to('cpu').detach().numpy().copy()\n    \n    if i%10 == 0:\n        print(f\"{i}/{len(dataloader)} done\")","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"df_train_result_inverted = df_train_result[df_train_result[\"false_score\"]<df_train_result[\"true_score\"]]","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"fig = plt.figure()\ncount = 0\nfor _,row in df_train_result_inverted.iterrows():\n    count += 1\n    ax = fig.add_subplot(6, 5, count)\n    print(count, path)\n    path = row[\"path\"]\n    img = cv2.imread(path)\n    assert(img is not None)\n    ax.imshow(img)\nplt.show()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"print(len(inverted_img_path_l))\nprint(df_train_result_inverted[\"path\"].isin(inverted_img_path_l).sum())","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"All of the image which mentioned by at [here](https://www.kaggle.com/c/ranzcr-clip-catheter-line-classification/discussion/210064#1149491) found by prediction.  \n\nBelow images are not mentioned it the discussion."},{"metadata":{"trusted":true},"cell_type":"code","source":"df_not_mentioned = df_train_result_inverted[\"path\"][~df_train_result_inverted[\"path\"].isin(inverted_img_path_l)]\n\nplt_col_num = 10\nfig = plt.figure()\ni = 0\nfor path in df_not_mentioned:\n    i += 1\n    ax = fig.add_subplot(4, 5, i)\n    print(i, path)\n    img = cv2.imread(path)\n    assert(img is not None)\n    ax.imshow(img)\nplt.show()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"\nBut several normal image were mis-labeled.  \nAccidentary, some upside-down image found."},{"metadata":{"trusted":true},"cell_type":"code","source":"up_side_down_image = [\n    \"/kaggle/input/ranzcr-clip-catheter-line-classification/train/1.2.826.0.1.3680043.8.498.56964749951381900643748134536978560792.jpg\",\n    \"/kaggle/input/ranzcr-clip-catheter-line-classification/train/1.2.826.0.1.3680043.8.498.76129162274163220380041920805275862370.jpg\",\n    \"/kaggle/input/ranzcr-clip-catheter-line-classification/train/1.2.826.0.1.3680043.8.498.11582767971938057384592968535311883741.jpg\",\n    \"/kaggle/input/ranzcr-clip-catheter-line-classification/train/1.2.826.0.1.3680043.8.498.29092351703254040179552572484602410700.jpg\",\n    \"/kaggle/input/ranzcr-clip-catheter-line-classification/train/1.2.826.0.1.3680043.8.498.11620053814932996350746126485322079242.jpg\",\n]","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"plt_col_num = 10\nfig = plt.figure()\nfor i,path in enumerate(up_side_down_image):\n    ax = fig.add_subplot(2, 3, i+1)\n    img = cv2.imread(path)\n    assert(img is not None)\n    ax.imshow(img)\nplt.show()","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}