{"cells":[{"metadata":{},"cell_type":"markdown","source":"# Images augmentation with torchvision.transforms\n\nDeep learning models usually require a lot of data for training. In general, the more the data, the better the performance of the model. But acquiring massive amounts of data comes with its own challenges. Instead of spending days manually collecting data, we can make use of Image augmentation techniques. Image augmentation helps spruce up existing images without having to put manual time taking efforts.\n\nImage Augmentation is the process of generating new images for the training CNN model. These new images are generated from the existing training images and hence we don’t have to do them manually.\n\nIn this notebook, we will implement  some of image augmentation methods using torchvision.transforms for the dataset of the competition: Cassava Leaf Disease Classification"},{"metadata":{},"cell_type":"markdown","source":"# Loading the Libraries "},{"metadata":{"trusted":true},"cell_type":"code","source":"import torch\nimport torchvision\nimport torchvision.transforms as transforms\nimport matplotlib.pyplot as plt\nimport glob\nfrom torch.utils.data import DataLoader,Dataset\nfrom PIL import Image\nimport numpy as np\nimport pandas as pd\nimport os","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Preparing the Dataset class"},{"metadata":{"trusted":true},"cell_type":"code","source":"class LeafDiseaseDataset(Dataset):\n    def __init__(self, root_dir, annotation_file, transform=None):\n        self.root_dir = root_dir\n        self.annotations = pd.read_csv(annotation_file)\n        self.transform = transform\n\n    def __len__(self):\n        return len(self.annotations)\n\n    def __getitem__(self, index):\n        img_id = self.annotations.iloc[index, 0]\n        img = Image.open(os.path.join(self.root_dir, img_id)).convert(\"RGB\")\n\n        if self.transform is not None:\n            img = self.transform(img)\n\n        return torch.tensor(img,dtype=torch.float)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# The function used to see images with the selected transformations\ndef show_img(img):\n  plt.figure(figsize=(40,38))\n  npimg=img.numpy()\n  plt.imshow(np.transpose(npimg,(1,2,0)))\n  plt.show()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"images_path = \"../input/cassava-leaf-disease-classification/train_images\"\ntraining_csv_file = \"../input/cassava-leaf-disease-classification/train.csv\"","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Visualizing some images without any transformation"},{"metadata":{"trusted":true},"cell_type":"code","source":"transform = transforms.Compose(\n        [\n            transforms.ToTensor(),\n        ]\n    )","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"dataloader=DataLoader(LeafDiseaseDataset(images_path,training_csv_file, transform= transform),batch_size=8,shuffle=True)\ndata=iter(dataloader)\nshow_img(torchvision.utils.make_grid(data.next(), nrow=4))","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Adding some images augmentation techniques"},{"metadata":{},"cell_type":"markdown","source":"1. **Rotation**\n\nImage rotation helps our model to become more robust to the changes in the orientation of objects. The information of the image remains the same. \nLet’s see how we can rotate it. we will use the RandomRotation function of the torchvision.transforms to rotate the image."},{"metadata":{"trusted":true},"cell_type":"code","source":"# Add rotation\nrot_transform=transforms.Compose([\n                              transforms.RandomRotation(45),  \n                              transforms.ToTensor(),\n                              ])","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"dataloader=DataLoader(LeafDiseaseDataset(images_path,training_csv_file, transform= rot_transform),batch_size=8,shuffle=True)\ndata=iter(dataloader)\nshow_img(torchvision.utils.make_grid(data.next(), nrow=4))","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"2. **Random Cropping**\n\nThe differently cropped image is the most important aspect of image diversity. When your network is used by the real users, the object in the image can be a different position."},{"metadata":{"trusted":true},"cell_type":"code","source":"crop_transform=transforms.Compose([\n                              transforms.RandomCrop((240,240)),        \n                              transforms.ToTensor(),\n                              ])","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"dataloader=DataLoader(LeafDiseaseDataset(images_path,training_csv_file, transform= crop_transform),batch_size=8,shuffle=True)\ndata=iter(dataloader)\nshow_img(torchvision.utils.make_grid(data.next(), nrow=4))","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"3. **Flipping images**\n\nYour network will be trained on batches of images which are randomly flipped from the original dataset, and which are sometimes flipped probability = 0.5.\nFlipping the images make more generalized models that will learn the patterns of the original as well as the flipped images. "},{"metadata":{"trusted":true},"cell_type":"code","source":"flip_transform=transforms.Compose([\n                              transforms.RandomVerticalFlip(0.5), \n                              transforms.RandomHorizontalFlip(0.5),        \n                              transforms.ToTensor(),\n                              ])","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"dataloader=DataLoader(LeafDiseaseDataset(images_path,training_csv_file, transform= flip_transform),batch_size=8,shuffle=True)\ndata=iter(dataloader)\nshow_img(torchvision.utils.make_grid(data.next(), nrow=4))","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"4. **Brightness, Contrast, Saturation, Hue**\n\nThe quality of the images will not be the same from each source. Some images might be of very high quality while others might be just plain bad. In such scenarios, we can blur the image. This helps make our deep learning model more robust. Transforms provide a class for randomly change the brightness, contrast, and saturation of an image."},{"metadata":{"trusted":true},"cell_type":"code","source":"specific_transform=transforms.Compose([\n                              transforms.ColorJitter(brightness=0.1, contrast=0.2, saturation=0, hue=0),\n                              transforms.ToTensor(),\n                              ])","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"dataloader=DataLoader(LeafDiseaseDataset(images_path,training_csv_file, transform= specific_transform),batch_size=8,shuffle=True)\ndata=iter(dataloader)\nshow_img(torchvision.utils.make_grid(data.next(), nrow=4))","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"5. **Gaussian Noise to Images**\n\nAdding random noise to the images is also an image augmentation technique. It can be done with a Gaussian filter for blurring the image. "},{"metadata":{"trusted":true},"cell_type":"code","source":"class AddGaussianNoise(object):\n    def __init__(self, mean=0., std=1.):\n        self.std = std\n        self.mean = mean\n        \n    def __call__(self, tensor):\n        return tensor + torch.randn(tensor.size()) * self.std + self.mean\n    \n    def __repr__(self):\n        return self.__class__.__name__ + '(mean={0}, std={1})'.format(self.mean, self.std)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Transforms give us fine-grained control of the transformation pipeline. we can use a functional transform to build transform classes with custom behavior."},{"metadata":{"trusted":true},"cell_type":"code","source":"gauss_transform=transforms.Compose([\n                              transforms.ToTensor(),\n                              AddGaussianNoise(0.1, 0.08)\n                              ])","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"dataloader=DataLoader(LeafDiseaseDataset(images_path,training_csv_file, transform= gauss_transform),batch_size=8,shuffle=True)\ndata=iter(dataloader)\nshow_img(torchvision.utils.make_grid(data.next(), nrow=4))","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"6. **Random Erasing**\n\nRandomly selects a rectangle region in an image and erases its pixels with random values. In this process, training images with various levels of occlusion are generated, which reduces the risk of over-fitting and makes the model robust to occlusion."},{"metadata":{"trusted":true},"cell_type":"code","source":"erase_transform=transforms.Compose([  \n                              transforms.ToTensor(),\n                              transforms.RandomErasing(),  \n                              ])","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"dataloader=DataLoader(LeafDiseaseDataset(images_path,training_csv_file, transform= erase_transform),batch_size=8,shuffle=True)\ndata=iter(dataloader)\nshow_img(torchvision.utils.make_grid(data.next(), nrow=4))","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Conclusion\n\nThese are many image augmentation techniques which help to make our deep learning model robust and generalizable. This also helps increase the size of the training set for small datasets.\n\nAll transformations somehow change the image. They leave the original untouched, just returning a changed copy. Given the same input image, some methods will always apply the same changes(e.g., converting it to Tensor, resizing to a fixed shape, etc.). Other methods will apply transformations with random parameters, returning different results each time (e.g., randomly cropping the images, randomly changing their brightness or saturation, etc.).So that means that upon every epoch you get a different version of the dataset, You can apply many data augmentation techniques together via  transforms.Compose().\n\nThe purpose of data augmentation is to try to get an upper bound of the data distribution of unseen data.\nhere's the link for the resources used : \n* https://towardsdatascience.com/image-augmentation-mastering-15-techniques-and-useful-functions-with-python-codes-44c3f8c1ea1f\n* https://androidkt.com/pytorch-image-augmentation-using-transforms/#:~:text=techniques%20using%20torchvision.-,transforms.,have%20to%20do%20them%20manually."},{"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}