Data Augmentation


Este notebook esta destinado para tareas de data augmentation. Sin transformaciones de los ficheros que contienen las coordenadas. Por lo tanto, solo esta destinado para los recortes de los fitolitos (imágenes que se encuentran en las carpetas con el nombre de los distintos tipos).

Si desea ver el notebook que realiza tareas de data augmentation y a su vez transforma las coordenadas, véase "Data_augmentation.ipynb"


In [132]:
%matplotlib inline
from __future__ import print_function
from ipywidgets import interact_manual, fixed

import os.path

from math import ceil

import numpy as np
import random
from random import randint

from skimage import io
from skimage.transform import rotate, rescale, resize

import matplotlib.pyplot as plt

import json

1. Cambios de tamaño en la imagen


In [133]:
import warnings
warnings.filterwarnings('ignore')

original_img = io.imread("../../../doc/general_doc/img/Bilobate/2.jpg")

# Factores de rescalado
factors = [0.6, 0.8, 1.2]

1.1. Rescalado en los dos ejes


In [134]:
r_imgs = []
r_imgs.append(rescale(original_img, factors[0]))
r_imgs.append(rescale(original_img, factors[1]))
r_imgs.append(rescale(original_img, factors[2]))

In [135]:
# Mostramos las imagenes resultantes
# Las imágenes se muestran con el mismo 
# tamaño, aunque sean de distinto tamaño

fig, axes = plt.subplots(ncols=3, figsize=(16, 16), subplot_kw={'adjustable':'box-forced'})

i=0
for ax in axes:

    ax.imshow(r_imgs[i])
    ax.axis('off')
    i += 1


1.2. Rescalado en el eje horizontal


In [136]:
def rescale_one_image_axis(image, factor, axis=0):
    h, w = image.shape[:2]
    
    if axis == 0:
        w = round(w * factor)
    elif axis == 1:
        h = round(h * factor)
    else:
        raise "Axis should be equalts to 0 o 1"
    
    return resize(image,(h,w))

In [137]:
r_h_imgs = []
r_h_imgs.append(rescale_one_image_axis(original_img, factors[0]))
r_h_imgs.append(rescale_one_image_axis(original_img, factors[1]))
r_h_imgs.append(rescale_one_image_axis(original_img, factors[2]))

In [138]:
# Mostramos las imagenes resultantes

fig, axes = plt.subplots(ncols=3, figsize=(16, 16), subplot_kw={'adjustable':'box-forced'})

i=0
for ax in axes:

    ax.imshow(r_h_imgs[i])
    ax.axis('off')
    i += 1


1.3. Rescalado en el eje vertical


In [139]:
r_v_imgs = []
r_v_imgs.append(rescale_one_image_axis(original_img, factors[0],1))
r_v_imgs.append(rescale_one_image_axis(original_img, factors[1],1))
r_v_imgs.append(rescale_one_image_axis(original_img, factors[2],1))

In [140]:
# Mostramos las imagenes resultantes

fig, axes = plt.subplots(ncols=3, figsize=(16, 16), subplot_kw={'adjustable':'box-forced'})

i=0
for ax in axes:

    ax.imshow(r_v_imgs[i])
    ax.axis('off')
    i += 1


2. Espejar imágenes


In [141]:
flipped_imgs = []

# Damos la vuelta a la imagen en el eje vertical
flipped_imgs.append(np.flipud(original_img))

# Damos la vuelta a la imagen en el eje horizontal
flipped_imgs.append(np.fliplr(original_img))

In [142]:
# Mostramos las imagenes resultantes

fig, axes = plt.subplots(ncols=2, figsize=(16, 16), subplot_kw={'adjustable':'box-forced'})

i=0
for ax in axes:

    ax.imshow(flipped_imgs[i])
    ax.axis('off')
    i += 1


3. Ruido en las imágenes


In [143]:
from skimage.util import random_noise

In [144]:
noise_imgs = []

# Valores por defecto mode='gaussian'
# mean = 0, var = 0.01
# amount = 0.05, salt_vs_pepper = 0.5

noise_imgs.append(original_img)
noise_imgs.append(random_noise(original_img,mode="gaussian", mean=0, var=0.01))
noise_imgs.append(random_noise(original_img,mode="pepper", amount=0.1))
# Se puede utilizar el parametro local_vars a modo de matriz
# para indicar la varianza local en cada pixel, para el modo "localvar"
noise_imgs.append(random_noise(original_img,mode="localvar"))
#noise_imgs.append(random_noise(original_img,mode="poisson"))
noise_imgs.append(random_noise(original_img,mode="salt", amount=0.1))
noise_imgs.append(random_noise(original_img,mode="s&p", amount=0.05, salt_vs_pepper=0.5))
noise_imgs.append(random_noise(original_img,mode="speckle", mean=0, var=0.1))

In [145]:
# Mostramos las imagenes resultantes

fig, axes = plt.subplots(nrows= 7,
                         figsize=(16, 16), 
                         subplot_kw={'adjustable':'box-forced'})
axes = axes.ravel()
i=0
for ax in axes:

    ax.imshow(noise_imgs[i])
    ax.axis('off')
    i += 1


4. Rotaciones de 90, 180 y 270 grados

Rotamos las imágenes.


In [146]:
rot_imgs = []

rot_imgs.append(rotate(original_img,90,resize=True))
rot_imgs.append(rotate(original_img,180,resize=True))
rot_imgs.append(rotate(original_img,270,resize=True))

In [147]:
# Mostramos las imagenes resultantes

fig, axes = plt.subplots(ncols=3, figsize=(16, 16), subplot_kw={'adjustable':'box-forced'})

i=0
for ax in axes:

    ax.imshow(rot_imgs[i])
    ax.axis('off')
    i += 1


5. Filtrar imagen


In [148]:
from skimage import exposure

fils_imgs = []

fils_imgs.append(original_img)
# 10 es un valor alto para el mínimo
p2, p98 = np.percentile(original_img, (5, 100))
fils_imgs.append(exposure.rescale_intensity(original_img, in_range=(p2, p98)))

# 90 es un valor bajo para el máximo
p2, p98 = np.percentile(original_img, (0, 90))
fils_imgs.append(exposure.rescale_intensity(original_img, in_range=(p2, p98)))

In [149]:
# Mostramos las imagenes resultantes

fig, axes = plt.subplots(ncols=3, figsize=(16, 16), subplot_kw={'adjustable':'box-forced'})

i=0
for ax in axes:

    ax.imshow(fils_imgs[i])
    ax.axis('off')
    i += 1


Generador de imágenes

Tras la definición de todos los métodos necesarios para la generación aleatoría de imágenes modificadas (abajo del todo), tenemos un slider y un botón que nos permite generar las nuevas imágenes en las subcarpetas de cada tipo de fitolito dentro de la carpeta, por defecto, imgaum, en la ruta code/rsc/img.


In [150]:
# Contamos el número de imágenes que tenemos en
# el directorio de las imágenes etiquetadas
import os, os.path
import re

path="../../rsc/img/"
dest_path = "../../rsc/img/imgaum"

pattern = re.compile("^.*\.jpg$", re.IGNORECASE)

def list_images(path='../rsc/img/Default'):
    images_list = []
    for name in os.listdir(path):
        if pattern.match(name):
            images_list.append(path + "/" + name)
    return images_list

In [151]:
probs_technic = 0.30
probs_especific_technic = 0.76
probs_subespecific_technic = [0.76, 0.84, 0.92]

In [152]:
def random_resize(image):
    # Cambio de tamaño de imagen
    
    image = np.array(image)
    
    # 1. Ambos ejes
    rands_for_resize = [random.random() for j in range(3)]
    
    if rands_for_resize[0] > probs_especific_technic:
        rand = round(random.uniform(0.6, 1.2),1)
        image = rescale(image, rand)
        
    # 2. Eje horizontal
    if rands_for_resize[1] > probs_especific_technic:
        rand = round(random.uniform(0.6, 1.2),1)
        image = rescale_one_image_axis(image, rand)
    
    # 3. Eje vertical
    if rands_for_resize[2] > probs_especific_technic:
        rand = round(random.uniform(0.6, 1.2),1)
        image = rescale_one_image_axis(image, rand, 1)
    return image

In [153]:
def random_flip(image):
    
    image = np.array(image)
    
    rands_for_flip = [random.random() for j in range(2)]
    if rands_for_flip[0] > probs_especific_technic:
        image = np.flipud(image)
    if rands_for_flip[1] > probs_especific_technic:
        image = np.fliplr(image)
    return image

In [154]:
def my_random_noise(image):
    rands_for_noise = [random.random() for j in range(6)]
    if rands_for_noise[0] > probs_especific_technic:
        image = random_noise(image,mode="gaussian", mean=0, var=0.01)
    if rands_for_noise[1] > probs_especific_technic:
        image = random_noise(image,mode="pepper", amount=0.1)
    if rands_for_noise[2] > probs_especific_technic:
        image = random_noise(image,mode="localvar")
    if rands_for_noise[3] > probs_especific_technic:
        image = random_noise(image,mode="salt", amount=0.1)
    if rands_for_noise[4] > probs_especific_technic:
        image = random_noise(image,mode="s&p", amount=0.05, salt_vs_pepper=0.5)
    if rands_for_noise[5] > probs_especific_technic:
        image = random_noise(image,mode="speckle", mean=0, var=0.1)
    return image

In [155]:
def my_filter(image):
    rands_for_filter = random.random()
    if rands_for_filter > 0.5:
        # 10 es un valor alto para el mínimo
        p2, p98 = np.percentile(image, (2, 100))
        image = exposure.rescale_intensity(image, in_range=(p2, p98))
    else:
        # 90 es un valor bajo para el máximo
        p2, p98 = np.percentile(image, (0, 90))
        image = exposure.rescale_intensity(image, in_range=(p2, p98))
    return image

In [156]:
def random_rotate(image):
    
    mod_image = np.array(image)
    angle = randint(30, 330)
    mod_image = rotate(mod_image, angle, resize=True, mode='edge')
        
    return mod_image

In [157]:
import copy

phytoliths_types = {'Rondel': None, 'Bulliform': None, 'Bilobate': None, 'Trichomas': None,
                     'Saddle': None, 'Spherical': None, 'Cyperaceae': None}

for k, v in phytoliths_types.items():
    phytoliths_types[k] = list_images(path + k)

initial_value = 100
end_value = 6000

def data_augmentation_generator(number_of_images, dest_path):
    
    # Calculamos el número de imágenes que hay
    # que generar por cada imagen hasta llegar
    # al número especificado por el usuario
    
    images_per_image = []
    for k, v in phytoliths_types.items():
        images_per_image.append(ceil(number_of_images / len(v)))
    
    # Generamos imágenes con filtros aleatorios
    # hasta llegar a completar el número deseado
    class_num= 0
    for k, v in phytoliths_types.items():
        count = 0
        for name in v:
            if count == number_of_images:
                break
            
            original_image = rescale(io.imread(name), 0.5)
            image_name = os.path.split(name)[1].split(".")[0]
            image_with_format = image_name + ".jpg"
            
            for i in range(images_per_image[class_num]):
                # Paramos si hemos llegado al número
                # de imágenes que deseamos
                if count == number_of_images:
                    break

                mod_image = original_image
                
                # Descomentar para generar mucha 
                # variablidad en las imágenes
                rands = [random.random() for j in range(5)]

                # Cambios de tamaño en la imagen
                if rands[0] > probs_technic:
                    mod_image = random_resize(mod_image)

                # Espejados de imagen
                # 1. Espejado vertical
                # 2.  Espejado horizontal
                if rands[1] > probs_technic:
                    mod_image = random_flip(mod_image)

                # Ruido en las imágenes
                # 7 tipos de ruido
                if rands[2] > probs_technic:
                    mod_image = my_random_noise(mod_image)

                # Rotaciones
                # En 90, 180 o 270 grados
                if rands[3] > 0.15:
                    mod_image = random_rotate(mod_image)

                # Filtrado de las imágenes
                if rands[4] > probs_technic:
                    mod_image = my_filter(mod_image)
                
                # Comentar si se descomenta el código anterior 
                #mod_image = random_rotate(mod_image)
                # Guardamos imagen
                io.imsave(dest_path+ "/" + k + "/" + image_name
                          + str(count) + ".jpg", mod_image,
                          quality=30)
                count += 1
        class_num += 1
            
interact_manual(data_augmentation_generator, 
                number_of_images=(initial_value,end_value,10),
                dest_path=fixed(dest_path))


Out[157]:
<function __main__.data_augmentation_generator>