<jupyter_start><jupyter_text>Solution: Image Data Augmentation[![Open In Colab <](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/ShawnHymel/computer-vision-with-embedded-machine-learning/blob/master/2.3.5%20-%20Project%20-%20Data%20Augmentation/solution_image_data_augmentation.ipynb)This is an example for creating an augmented dataset. It will transform input images to create a series of augmented samples that are saved in a new output directory.Create a folder named "dataset" in the /content directory and upload your images there. The images should be divided into their respective classes, where each class has its own folder with the name of the class. For example:/content    |- dataset        |- background        |- capacitor        |- diode        |- led        |- resistorThe original images along with their transforms will be saved in the output directory. Each output file will be the original filename appended with "_{num}" where {num} is some incrementing value based on the total number of transforms performed per image.For example, if you have a file named "0.png" in /content/dataset/resistor, it will become "0_0.png" in /content/output/resistor. The first transform will be "0_1.png", the second transform will be "0_2.png" and so on.Run each of the cells paying attention to their contents and output. Fill out the necessary parts of the functions where you find the following comment:``` >>> ENTER YOUR CODE HERE <<<```Author: EdgeImpulse, Inc.Date: August 3, 2021License: [Apache-2.0](apache.org/licenses/LICENSE-2.0)<jupyter_code>import numpy as np
import matplotlib.pyplot as plt
import random
import os
import PIL

import skimage.transform
import skimage.util
### Settings

# Location of dataset and output folder
DATASET_PATH = "/content/dataset"
OUT_PATH = "/content/output"
OUT_ZIP = "augmented_dataset.zip"

# File format to use for new dataset
IMG_EXT = ".png"

# You are welcome to change the seed to get different augmentation effects
SEED = 42
random.seed(SEED)
### Create output directory
try:
  os.makedirs(OUT_PATH)
except FileExistsError:
  print("WARNING: Output directory already exists. Check to make sure it is empty.")<jupyter_output><empty_output><jupyter_text>Transform FunctionsCreate one or more functions that transform an input image.<jupyter_code>### Example: Function to create 3 new flipped images of the input
def create_flipped(img):

  # Create a list of flipped images
  flipped = []
  flipped.append(np.fliplr(img))
  flipped.append(np.flipud(img))
  flipped.append(np.flipud(np.fliplr(img)))

  return flipped
# >>> ENTER YOUR CODE HERE <<<
# Create one or more functions that create transforms of your images
### Function to create new rotated images of the input
def create_rotated(img, rotations):

  # Create list of rotated images (keep 8-bit values)
  rotated = []
  for rot in rotations:
    img_rot = skimage.transform.rotate(img, angle=rot, mode='edge', preserve_range=True)
    img_rot = img_rot.astype(np.uint8)
    rotated.append(img_rot)

  return rotated
### Function to create random scale/crop (zoom) images
def create_random_zooms(img, scale_factor, num_crops):

  # Get height and width of original image
  height = img.shape[0]
  width = img.shape[1]

  # Create scaled images (e.g. make the image bigger) and keep 8-bit values
  img_scaled = skimage.transform.rescale(img, 
                                        scale=scale_factor, 
                                        anti_aliasing=True, 
                                        multichannel=True,
                                        preserve_range=True)
  img_scaled = img_scaled.astype(np.uint8)

  # Get height and width of scaled image
  s_h = img_scaled.shape[0]
  s_w = img_scaled.shape[1]

  # Create list of random zooms
  zooms = []
  for i in range(num_crops):
    
    # Randomly choose start of crop point
    crop_y = round(random.random() * (s_h - height))
    crop_x = round(random.random() * (s_w - width))

    # Crop scaled image
    zoom = img_scaled[crop_y:(crop_y + height), crop_x:(crop_x + width), :]

    # Append zoomed image to list
    zooms.append(zoom)

  return zooms
### Function to create a random set of translated images (no more than 1/4 of width or height away)
def create_random_translations(img, num_translations):

  # Get height and width of original image
  height = img.shape[0]
  width = img.shape[1]

  # Create list of random translations
  translations = []
  for i in range(num_translations):
  
    # Choose random amount to translate (up to 1/4 image width, height) in either direction
    tr_y = round((0.5 - random.random()) * (height / 2))
    tr_x = round((0.5 - random.random()) * (width / 2))

    # Perform translation to create new image
    translation = skimage.transform.AffineTransform(translation=(tr_y, tr_x))
    img_tr = skimage.transform.warp(img, translation, mode='edge', preserve_range=True)
    img_tr = img_tr.astype(np.uint8)

    # Append translated image to list
    translations.append(img_tr)

  return translations
### Function to add random noise to images
def create_noisy(img, types, seed=None):

  # Add noise of different types
  noisy_imgs = []
  for t in types:
    noise = skimage.util.random_noise(img, mode=t, seed=seed)
    noise = (noise * 255).astype(np.uint8)
    noisy_imgs.append(noise)

  return noisy_imgs<jupyter_output><empty_output><jupyter_text>Perform TransformsCall your functions to create a set of augmented data.<jupyter_code>### Function to open image and create a list of new transforms
# NOTE: You will need to call your functions here!
def create_transforms(file_path):

  # Open the image
  img = PIL.Image.open(file_path)

  # Convert the image to a Numpy array (keep all color channels)
  img_array = np.asarray(img)

  # Add original image to front of list
  img_tfs = []
  img_tfs.append([img_array])

  # Perform transforms (call your functions)
  img_tfs.append(create_flipped(img_array))
  # >>> ENTER YOUR CODE HERE <<<
  # e.g. img_tfs.append(create_translations(img_array, 2))
  img_tfs.append(create_flipped(img_array))
  img_tfs.append(create_rotated(img_array, [45, 90, 135]))
  img_tfs.append(create_random_zooms(img_array, 1.3, 2))
  img_tfs.append(create_random_translations(img_array, 2))
  img_tfs.append(create_noisy(img_array, ['gaussian', 's&p'], SEED))

  # Flatten list of lists (to create one long list of images)
  img_tfs = [img for img_list in img_tfs for img in img_list]

  return img_tfs
### Load all images, create transforms, and save in output directory

# Find the directories in the dataset folder (skip the Jupyter Notebook checkpoints hidden folder)
for label in os.listdir(DATASET_PATH):
  class_dir = os.path.join(DATASET_PATH, label)
  if os.path.isdir(class_dir) and label != ".ipynb_checkpoints":

    # Create output directory
    out_path = os.path.join(OUT_PATH, label)
    os.makedirs(out_path, exist_ok=True)

    # Go through each image in the subfolder
    for i, filename in enumerate(os.listdir(class_dir)):

      # Skip the Jupyter Notebook checkpoints folder that sometimes gets added
      if filename != ".ipynb_checkpoints":

        # Get the root of the filename before the extension
        file_root = os.path.splitext(filename)[0]

        # Do all transforms for that one image
        file_path = os.path.join(DATASET_PATH, label, filename)
        img_tfs = create_transforms(file_path)

        # Save images to new files in output directory
        for i, img in enumerate(img_tfs):

          # Create a Pillow image from the Numpy array
          img_pil = PIL.Image.fromarray(img)

          # Construct filename (<orignal>_<transform_num>.<EXT>)
          out_file_path = os.path.join(out_path, file_root + "_" + str(i) + IMG_EXT)

          # Convert Numpy array to image and save as a file
          img_pil = PIL.Image.fromarray(img)
          img_pil.save(out_file_path)
### Zip our new dataset (use '!' to call Linux commands)
!zip -r -q "{OUT_ZIP}" "{OUT_PATH}"<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.09263157894736843, 'fraction_numerical': 0.0086687306501548, 'mean_word_length': 3.6844547563805103, 'pattern_counts': {'":': 2, '<': 26, '<?xml version=': 0, '>': 25, 'https://': 2, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 4, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '3', 'dir': 'jupyter-structured-clean-dedup', 'id': '460472', 'license': 'Apache-2.0', 'n_tokens_mistral': 2573, 'n_tokens_neox': 2448, 'n_words': 817, 'path': '2.3.5 - Project - Data Augmentation/solution_image_data_augmentation.ipynb', 'repo_name': 'neubrom/computer-vision-with-embedded-machine-learning', 'size': '8075'}