{"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":"markdown","source":"![](https://storage.googleapis.com/kaggle-competitions/kaggle/36073/logos/header.png?t=2022-04-12-20-53-30)","metadata":{"execution":{"iopub.status.busy":"2022-04-21T18:39:54.474251Z","iopub.execute_input":"2022-04-21T18:39:54.474986Z","iopub.status.idle":"2022-04-21T18:39:55.157888Z","shell.execute_reply.started":"2022-04-21T18:39:54.474943Z","shell.execute_reply":"2022-04-21T18:39:55.157081Z"}}},{"cell_type":"markdown","source":"# The task is to denoise “shabby” pages, reversing the effects imposed on them by Augraphy","metadata":{}},{"cell_type":"markdown","source":"### Importing some basic libraries required to play with the images","metadata":{}},{"cell_type":"code","source":"import os\nimport cv2\nimport glob\nimport numpy as np\nimport pandas as pd\nimport matplotlib.pyplot as plt","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","execution":{"iopub.status.busy":"2022-05-31T12:44:22.175324Z","iopub.execute_input":"2022-05-31T12:44:22.175746Z","iopub.status.idle":"2022-05-31T12:44:22.520498Z","shell.execute_reply.started":"2022-05-31T12:44:22.175661Z","shell.execute_reply":"2022-05-31T12:44:22.519659Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"BASE_DIR = \"/kaggle/input/denoising-shabby-pages\"","metadata":{"execution":{"iopub.status.busy":"2022-05-31T12:44:22.522214Z","iopub.execute_input":"2022-05-31T12:44:22.522488Z","iopub.status.idle":"2022-05-31T12:44:22.526878Z","shell.execute_reply.started":"2022-05-31T12:44:22.52245Z","shell.execute_reply":"2022-05-31T12:44:22.526224Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"glob.glob(BASE_DIR+'/*')","metadata":{"execution":{"iopub.status.busy":"2022-05-31T12:44:22.528292Z","iopub.execute_input":"2022-05-31T12:44:22.528793Z","iopub.status.idle":"2022-05-31T12:44:22.543757Z","shell.execute_reply.started":"2022-05-31T12:44:22.528736Z","shell.execute_reply":"2022-05-31T12:44:22.542781Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"cleaned_img_paths = sorted(glob.glob(BASE_DIR+\"/train_cleaned/train_cleaned/*.png\"))\ntrain_shabby_paths = sorted(glob.glob(BASE_DIR+\"/train/train/*.png\"))","metadata":{"execution":{"iopub.status.busy":"2022-05-31T12:44:22.546099Z","iopub.execute_input":"2022-05-31T12:44:22.54653Z","iopub.status.idle":"2022-05-31T12:44:22.830088Z","shell.execute_reply.started":"2022-05-31T12:44:22.546494Z","shell.execute_reply":"2022-05-31T12:44:22.829349Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"len(cleaned_img_paths),len(train_shabby_paths)","metadata":{"execution":{"iopub.status.busy":"2022-05-31T12:44:22.833126Z","iopub.execute_input":"2022-05-31T12:44:22.833341Z","iopub.status.idle":"2022-05-31T12:44:22.841194Z","shell.execute_reply.started":"2022-05-31T12:44:22.833315Z","shell.execute_reply":"2022-05-31T12:44:22.840467Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### Display some of the shabby images given for training","metadata":{}},{"cell_type":"code","source":"plt.figure(figsize=(20, 5))\nprint(\"Input samples:\")\nfor i,pth in enumerate(train_shabby_paths[0:20]):\n    im = cv2.imread(pth)\n    plt.subplot(2, 10, i+1)\n    plt.imshow(im, cmap='gray')\nplt.show()","metadata":{"execution":{"iopub.status.busy":"2022-05-31T12:44:22.842209Z","iopub.execute_input":"2022-05-31T12:44:22.842485Z","iopub.status.idle":"2022-05-31T12:44:25.03094Z","shell.execute_reply.started":"2022-05-31T12:44:22.842448Z","shell.execute_reply":"2022-05-31T12:44:25.029228Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"plt.figure(figsize=(20, 5))\nprint(\"Target samples:\")\nfor i,pth in enumerate(cleaned_img_paths[0:20]):\n    im = cv2.imread(pth)\n    plt.subplot(2, 10, i+1)\n    plt.imshow(im, cmap='gray')\nplt.show()","metadata":{"execution":{"iopub.status.busy":"2022-05-31T12:44:25.03204Z","iopub.execute_input":"2022-05-31T12:44:25.032255Z","iopub.status.idle":"2022-05-31T12:44:27.172793Z","shell.execute_reply.started":"2022-05-31T12:44:25.032227Z","shell.execute_reply":"2022-05-31T12:44:27.171392Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"X_train = []\nfor i,pth in enumerate(train_shabby_paths):\n    im = cv2.imread(pth,0)\n    X_train.append(im)\nX_train = np.array(X_train)","metadata":{"execution":{"iopub.status.busy":"2022-05-31T12:44:27.174001Z","iopub.execute_input":"2022-05-31T12:44:27.176518Z","iopub.status.idle":"2022-05-31T12:44:35.533474Z","shell.execute_reply.started":"2022-05-31T12:44:27.176478Z","shell.execute_reply":"2022-05-31T12:44:35.532722Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"y_train = []\nfor i,pth in enumerate(cleaned_img_paths):\n    im = cv2.imread(pth,0)\n    y_train.append(im)\ny_train = np.array(y_train)","metadata":{"execution":{"iopub.status.busy":"2022-05-31T12:44:35.534599Z","iopub.execute_input":"2022-05-31T12:44:35.53663Z","iopub.status.idle":"2022-05-31T12:44:42.344279Z","shell.execute_reply.started":"2022-05-31T12:44:35.536598Z","shell.execute_reply":"2022-05-31T12:44:42.343562Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"X_train.shape, y_train.shape","metadata":{"execution":{"iopub.status.busy":"2022-05-31T12:44:42.347282Z","iopub.execute_input":"2022-05-31T12:44:42.347541Z","iopub.status.idle":"2022-05-31T12:44:42.352447Z","shell.execute_reply.started":"2022-05-31T12:44:42.347507Z","shell.execute_reply":"2022-05-31T12:44:42.351798Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"We have 700 shabby pages and corresponding cleaned images with the size of 400x400","metadata":{}},{"cell_type":"markdown","source":"Various approaches are available to denoise shabby images.\n\nFeel free to experiment and find best suitable algo/model","metadata":{}},{"cell_type":"code","source":"#TODO: Do your stuff here","metadata":{"execution":{"iopub.status.busy":"2022-05-31T12:44:42.353719Z","iopub.execute_input":"2022-05-31T12:44:42.354153Z","iopub.status.idle":"2022-05-31T12:44:42.36585Z","shell.execute_reply.started":"2022-05-31T12:44:42.354118Z","shell.execute_reply":"2022-05-31T12:44:42.36516Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"We can use this notebook for DnCNN pytorch model\nhttps://www.kaggle.com/code/sarahxue/kaggleday-denoising/notebook","metadata":{}},{"cell_type":"code","source":"import os\nimport time\nfrom IPython.display import clear_output\n\nimport numpy as np\nimport random\nimport torch\nimport torch.nn as nn\n\nfrom matplotlib.pyplot import imshow, show, figure\nfrom PIL import Image\nfrom torch.optim import Adam\nfrom torch.utils.data import Dataset, DataLoader\nfrom torchvision.transforms import RandomCrop, ToTensor, Compose, RandomHorizontalFlip, RandomVerticalFlip, ToPILImage","metadata":{"execution":{"iopub.status.busy":"2022-05-31T12:44:42.367141Z","iopub.execute_input":"2022-05-31T12:44:42.367416Z","iopub.status.idle":"2022-05-31T12:44:44.089633Z","shell.execute_reply.started":"2022-05-31T12:44:42.367379Z","shell.execute_reply":"2022-05-31T12:44:44.088881Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"NUM_EPOCHS = 20\nBATCH_SIZE = 64\n\nPATCH_SIZE = 256\nCROPS_PER_IMAGE = 2\n\nTRAIN_DATA_PATH = \"/kaggle/input/kaggle-days-denoise/data/train/\"\nVALIDATION_DATA_PATH = \"/kaggle/input/kaggle-days-denoise/data/val/\"","metadata":{"execution":{"iopub.status.busy":"2022-05-31T12:44:44.091158Z","iopub.execute_input":"2022-05-31T12:44:44.091421Z","iopub.status.idle":"2022-05-31T12:44:44.095733Z","shell.execute_reply.started":"2022-05-31T12:44:44.091386Z","shell.execute_reply":"2022-05-31T12:44:44.095089Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"class DenoisingDataset(Dataset):\n    def __init__(self, base_dir):\n        self.cleaned_img_paths = sorted(glob.glob(base_dir+\"/train_cleaned/train_cleaned/*.png\"))\n        self.train_shabby_paths = sorted(glob.glob(base_dir+\"/train/train/*.png\"))\n        self.total_len = len(self.train_shabby_paths)\n        \n        self._transforms = Compose([\n            ToTensor()\n        ])\n\n    def __len__(self):\n        return len(self.train_shabby_paths) * CROPS_PER_IMAGE\n\n    def __getitem__(self, index):\n        idx = index % self.total_len\n        image = cv2.imread(self.cleaned_img_paths[idx])\n        h, w, c = image.shape\n        if h < PATCH_SIZE and w < PATCH_SIZE:\n            print(\"Invalid Image Shape.\")\n            sys.exit(1)\n        \n        px = random.randint(0, h - 257)\n        py = random.randint(0, h - 257)\n        \n        image = image[py:py+256, px:px+256]\n        image = self._transforms(Image.fromarray(image))\n        \n        noise = cv2.imread(self.train_shabby_paths[idx])\n        h, w, c = noise.shape\n        if h < PATCH_SIZE and w < PATCH_SIZE:\n            print(\"Invalid Image Shape.\")\n            sys.exit(1)\n        \n        noise = noise[py:py+256, px:px+256]\n        noise = self._transforms(Image.fromarray(noise))\n        \n        return noise, image","metadata":{"execution":{"iopub.status.busy":"2022-05-31T12:44:44.096961Z","iopub.execute_input":"2022-05-31T12:44:44.097441Z","iopub.status.idle":"2022-05-31T12:44:44.106963Z","shell.execute_reply.started":"2022-05-31T12:44:44.097406Z","shell.execute_reply":"2022-05-31T12:44:44.106191Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"class DnCNN(nn.Module):\n    def __init__(self, depth=7, n_channels=16, image_channels=3, use_bnorm=True, kernel_size=3):\n        super(DnCNN, self).__init__()\n        kernel_size = 3\n        padding = 1\n        layers = []\n\n        layers.append(nn.Conv2d(in_channels=image_channels, out_channels=n_channels, kernel_size=kernel_size, padding=padding, bias=True))\n        layers.append(nn.ReLU(inplace=True))\n        for _ in range(depth-2):\n            layers.append(nn.Conv2d(in_channels=n_channels, out_channels=n_channels, kernel_size=kernel_size, padding=padding, bias=False))\n            layers.append(nn.BatchNorm2d(n_channels, eps=0.0001, momentum = 0.95))\n            layers.append(nn.ReLU(inplace=True))\n        layers.append(nn.Conv2d(in_channels=n_channels, out_channels=image_channels, kernel_size=kernel_size, padding=padding, bias=False))\n        self.dncnn = nn.Sequential(*layers)\n        self._initialize_weights()\n\n    def forward(self, x):\n        out = self.dncnn(x)\n        return x - out\n\n    def _initialize_weights(self):\n        for m in self.modules():\n            if isinstance(m, nn.Conv2d):\n                nn.init.orthogonal_(m.weight)\n                if m.bias is not None:\n                    nn.init.constant_(m.bias, 0)\n            elif isinstance(m, nn.BatchNorm2d):\n                nn.init.constant_(m.weight, 1)\n                nn.init.constant_(m.bias, 0)","metadata":{"execution":{"iopub.status.busy":"2022-05-31T12:44:44.108418Z","iopub.execute_input":"2022-05-31T12:44:44.108669Z","iopub.status.idle":"2022-05-31T12:44:44.121245Z","shell.execute_reply.started":"2022-05-31T12:44:44.108635Z","shell.execute_reply":"2022-05-31T12:44:44.1205Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"model = DnCNN()\ndataset = DenoisingDataset(BASE_DIR)\ncriterion = nn.MSELoss(reduction=\"sum\")\noptimizer = Adam(model.parameters(), lr=0.001)","metadata":{"execution":{"iopub.status.busy":"2022-05-31T12:44:44.122525Z","iopub.execute_input":"2022-05-31T12:44:44.123237Z","iopub.status.idle":"2022-05-31T12:44:44.268868Z","shell.execute_reply.started":"2022-05-31T12:44:44.123202Z","shell.execute_reply":"2022-05-31T12:44:44.268217Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def visualize_validation(model):\n    model.eval()\n    plt.figure(figsize=(20, 5))\n    \n    transforms = Compose([\n            ToTensor()\n        ])\n    \n    for idx, path in enumerate(train_shabby_paths[0:10]):\n        input_image = cv2.imread(path)\n        image = transforms(Image.fromarray(input_image))\n        model_input = image.unsqueeze(0)\n\n        with torch.no_grad():\n            result = model(model_input)\n\n        result_image = result[0].clamp(0, 1).permute(1, 2, 0).numpy()\n        result_image = (result_image * 255).astype(\"uint8\")\n\n        plt.subplot(2, 10, idx+1)\n        plt.imshow(input_image, cmap='gray')\n        plt.subplot(2, 10, idx+11)\n        plt.imshow(result_image, cmap='gray')\n\n\n    plt.show()","metadata":{"execution":{"iopub.status.busy":"2022-05-31T12:44:44.270132Z","iopub.execute_input":"2022-05-31T12:44:44.2704Z","iopub.status.idle":"2022-05-31T12:44:44.278042Z","shell.execute_reply.started":"2022-05-31T12:44:44.27035Z","shell.execute_reply":"2022-05-31T12:44:44.277251Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"for epoch_id in range(NUM_EPOCHS):\n    # First, we will use current model to predict a validation image\n    visualize_validation(model)\n\n    # Let's optimize parameters of our model\n    model.train()\n    \n    data_loader = DataLoader(dataset, batch_size=BATCH_SIZE, shuffle=True, drop_last=True, num_workers=4)\n\n    for iter_id, (input_images, target_images) in enumerate(data_loader):\n        predicted_images = model(input_images)\n        loss = criterion(predicted_images, target_images)\n\n        optimizer.zero_grad()\n        loss.backward()\n        optimizer.step()\n\n        print(\"\\rEpoch {} Iteration {} Loss {}\".format(epoch_id, iter_id, loss.item() / BATCH_SIZE), end=\"\")","metadata":{"execution":{"iopub.status.busy":"2022-05-31T12:44:44.279408Z","iopub.execute_input":"2022-05-31T12:44:44.279763Z","iopub.status.idle":"2022-05-31T12:48:58.40026Z","shell.execute_reply.started":"2022-05-31T12:44:44.27973Z","shell.execute_reply":"2022-05-31T12:48:58.39915Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Result preparation:","metadata":{}},{"cell_type":"markdown","source":"### Once the solution is ready, make the submission with the following steps:","metadata":{}},{"cell_type":"code","source":"TEST_DIR = BASE_DIR+\"/test/test/\"\nOUTPUT_DIR = \"/kaggle/working/results\"\n!rm -rf \"results\"\n\ntry:\n    os.mkdir(OUTPUT_DIR)\nexcept Exception:\n    pass","metadata":{"execution":{"iopub.status.busy":"2022-05-31T12:48:58.401449Z","iopub.status.idle":"2022-05-31T12:48:58.402077Z","shell.execute_reply.started":"2022-05-31T12:48:58.40184Z","shell.execute_reply":"2022-05-31T12:48:58.401865Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"final_img_paths = sorted(glob.glob(TEST_DIR+\"*.png\"))","metadata":{"execution":{"iopub.status.busy":"2022-05-31T12:48:58.403834Z","iopub.status.idle":"2022-05-31T12:48:58.404231Z","shell.execute_reply.started":"2022-05-31T12:48:58.404014Z","shell.execute_reply":"2022-05-31T12:48:58.404036Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"len(final_img_paths)","metadata":{"execution":{"iopub.status.busy":"2022-05-31T12:48:58.405547Z","iopub.status.idle":"2022-05-31T12:48:58.405946Z","shell.execute_reply.started":"2022-05-31T12:48:58.405732Z","shell.execute_reply":"2022-05-31T12:48:58.405754Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### We have to predict/recreate 300 cleaned images from the given test set to make submissions","metadata":{}},{"cell_type":"code","source":"model.eval()\ntransforms = Compose([ToTensor()])\n\nfor i,full_pth in enumerate(final_img_paths):\n    pth = full_pth.split('/')[-1]\n    img = cv2.imread(TEST_DIR+pth.strip())\n    \n    image = transforms(Image.fromarray(img))\n    model_input = image.unsqueeze(0)\n\n    with torch.no_grad():\n        result = model(model_input)\n\n    result_image = result[0].clamp(0, 1).permute(1, 2, 0).numpy()\n    result_image = (result_image * 255).astype(\"uint8\")\n\n    cv2.imwrite(f'{OUTPUT_DIR}/{pth.strip()}',result_image)","metadata":{"execution":{"iopub.status.busy":"2022-05-31T12:48:58.407223Z","iopub.status.idle":"2022-05-31T12:48:58.407629Z","shell.execute_reply.started":"2022-05-31T12:48:58.407414Z","shell.execute_reply":"2022-05-31T12:48:58.407436Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### Visualize some of the test set and results:","metadata":{}},{"cell_type":"code","source":"plt.figure(figsize=(20, 5))\nfor i,pth in enumerate(final_img_paths[0:10]):\n    filename = pth.split('/')[-1]\n    im = cv2.imread(pth,0)\n    res = cv2.imread(os.path.join(OUTPUT_DIR, filename), 0)\n    plt.subplot(2, 10, i+1)\n    plt.imshow(im, cmap='gray')\n    plt.subplot(2, 10, i+11)\n    plt.imshow(res, cmap='gray')\n\nplt.show()","metadata":{"execution":{"iopub.status.busy":"2022-05-31T12:48:58.40893Z","iopub.status.idle":"2022-05-31T12:48:58.409324Z","shell.execute_reply.started":"2022-05-31T12:48:58.409108Z","shell.execute_reply":"2022-05-31T12:48:58.409129Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### Make a submission:","metadata":{}},{"cell_type":"markdown","source":"To create submission.csv file, run the following code block...","metadata":{}},{"cell_type":"code","source":"# Make_submission.py:\nimport random\nimport os\nimport cv2\n\n################################################################################\n# CHANGE THIS\n################################################################################\ncleaned_images_dir = OUTPUT_DIR\n\n################################################################################\n# DON'T CHANGE ANYTHING BELOW HERE\n################################################################################\ndef select_pixels(img):\n    y,x = img.shape\n\n    pixels = list()\n\n    for i in range(10000):\n        pixel = (random.randrange(y), random.randrange(x))\n\n        if pixel not in pixels:\n            pixels.append(pixel)\n\n    return pixels\n\nrandom.seed(0)\ncleaned_images = sorted(os.listdir(cleaned_images_dir))\n\nwith open(\"submission.csv\", \"w\") as submission_file:\n    submission_file.write(\"id,predicted\\n\")\n\n    filenum = 1\n    for image in cleaned_images:\n\n        img = cv2.imread(\"{}/{}\".format(cleaned_images_dir, image), cv2.IMREAD_GRAYSCALE)\n        pixels = select_pixels(img)\n\n        for pixel in pixels:\n            y,x = pixel\n            submission_file.write(\"{}_{}_{},{}\\n\".format(filenum, y, x, img[y][x]/255.0))\n\n        filenum += 1","metadata":{"execution":{"iopub.status.busy":"2022-05-31T12:48:58.410643Z","iopub.status.idle":"2022-05-31T12:48:58.41105Z","shell.execute_reply.started":"2022-05-31T12:48:58.410835Z","shell.execute_reply":"2022-05-31T12:48:58.410856Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# The world belongs to those who READ! Let's make shabby pages readable, those are not to be thrown in the trash! :D\n# Thanks!","metadata":{}},{"cell_type":"markdown","source":"> ","metadata":{}}]}