Google+ Seguidores

domingo, 12 de agosto de 2018

Procesamiento de Imágenes con Python y Scikit-Image

Introducción:

Hola amigos de Internet, les doy la bienvenida a Mi Diario Python, el mejor blog para Aprender Python. 

En este articulo nos dedicaremos a conocer algunos algoritmos utilizados en el procesamiento de imágenes y los aplicaremos utilizando la librería Scikit-Image.

Procesamiento de Imagenes:

Antes de poder ir a la practica debemos conocer algunos conceptos básicos.

Una imagen es una función bidimensional f(x,y) que representa la intensidad de la luz, donde x e y son coordenadas espaciales y el valor de f es proporcional al brillo de la imagen en un punto cualquiera.

Sabes esto es muy importante ya que nos da la base para poder saber como obtener valores de pixeles. 

En las imágenes, estas pueden ser representadas como una matriz cuyos índices de fila y columna identifican un punto de la imagen el cual es un indicador del nivel de brillo. Estos puntos de la matriz son a los que llamamos pixeles.

 Otro punto a tener en cuenta son las imágenes en escala de grises.

Cuando hablamos de imágenes en escala de grises, nos referimos a una imagen de 8 bits donde cada pixel de la imagen tiene asignada una intensidad con un rango de 0 a 255. 

Para obtener información relevante de una imagen se utiliza el procesamiento digital de señales mediante el cual se busca transformar una imagen en otra mediante procesos algorítmicos con el objetivo de resaltar cierta información de interés o atenuar información irrelevante.

Pre-procesado:

Dentro de la etapa de pre-procesado se tienen diferentes técnicas, las cuales sin aplicadas dependiendo de problema que presenta la imagen.

A continuación realizaremos algunos ejercicios comunes en esta área.

Detección de bordes:

Esto es algo muy común. Cuando hablamos de "detección de bordes", nos referimos a detectar discontinuidades en el nivel de intensidad de la imagen. 

Para realizar este tipo de detección, existen diferentes tipos de filtros, a continuación implementaremos algunos de estos filtros utilizando scikit-imagee y matplotlib:

# Librerias necesarias
from skimage import io
from skimage import filters
from skimage.color import rgb2gray
import matplotlib.pyplot as plt

# Abrimos la imagen
imagen = io.imread("imagen.jpg")
imagen_g = rgb2gray(imagen)

# Filtros: sobel, roberts, prewitt
filtros = [filters.sobel, filters.roberts, filters.prewitt]

for filtro in filtros:
    # Aplicamos cada uno de los filtros
    img_fil = filtro(imagen_g)
    
    # Mostramos los resultados 
    plt.imshow(img_fil)
    plt.show()

png
png
png

Mejoramiento del contraste:

En algunas ocasiones se necesita aumentar el contraste de las imágenes. Al aumentar el contraste incrementa el cambio en la intensidad de la luz entre las zonas más oscuras o más claras.

Scikit-Image nos proporciona diversas funciones para el mejoramiento de contraste:

from skimage import exposure
from skimage import io
import numpy as np
import matplotlib.pyplot as plt

imagen = io.imread("imagen.jpg")

# Estiramiento de contraste
p2, p98 = np.percentile(imagen, (2,98))
img_rescale = exposure.rescale_intensity(imagen, in_range=(p2,p98))

# Ecualización
img_eq = exposure.equalize_hist(imagen)

# Ecualización adaptiva
img_adapteq = exposure.equalize_adapthist(imagen, clip_limit=0.03)

for eq in (img_eq, img_adapteq):
    plt.imshow(eq)
    plt.show()
C:\Users\PILAR\AppData\Local\Programs\Python\Python36-32\lib\site-packages\skimage\exposure\exposure.py:63: UserWarning: This might be a color image. The histogram will be computed on the flattened image. You can instead apply this function to each color channel.
  warn("This might be a color image. The histogram will be "
C:\Users\PILAR\AppData\Local\Programs\Python\Python36-32\lib\site-packages\skimage\util\dtype.py:130: UserWarning: Possible precision loss when converting from float64 to uint16
  .format(dtypeobj_in, dtypeobj_out))
png
png

Eliminación de ruido:

Existen diferentes tipos de ruidos que pueden reducir la calidad de la imagen. Uno de los más comunes es el ruido conocido como sal y pimienta, el cual es producido por perturbaciones agudas y repentinas en la imagen causadas por el mismo equipo de medición o el ambiente.

Para minimizar el ruido, podemos utilizar el filtro de medianas:

from skimage import io
from skimage.filters.rank import median
from skimage.morphology import disk
from skimage.color import rgb2gray
import matplotlib.pyplot as plt

imagen = io.imread("charlie.jpg")
img_gray = rgb2gray(imagen)

med = median(img_gray)

plt.imshow(med)
plt.show()
C:\Users\PILAR\AppData\Local\Programs\Python\Python36-32\lib\site-packages\skimage\util\dtype.py:130: UserWarning: Possible precision loss when converting from float64 to uint8
  .format(dtypeobj_in, dtypeobj_out))
png

Restauración:

Al restaurar una imagen se busca normalmente eliminar el ruido, mejorar el brillo, el color y los detalles de la misma, recuperando de esta forma aquellas partes que han sido deterioradas

Para la restauración, utilizaremos el filtro Wiener:

from skimage import restoration
from scipy.signal import convolve2d
from skimage import io
from skimage.color import rgb2gray
import numpy as np
import matplotlib.pyplot as plt

imagen = io.imread("imagen_ruido.jpg")

img = rgb2gray(imagen)

psf = np.ones((5,5)) / 25

img = convolve2d(img, psf, 'same')
img += 0.1 * img.std() * np.random.standard_normal((img.shape))
deconvolved_img = restoration.wiener(img, psf, 100)

plt.imshow(deconvolved_img)
plt.show()
png

Suavizar y resaltar contornos:

En algunos casos no basta con detectar los bordes en la imagen, sino que se necesita detectarlos y suavizarlos. Para esto se utiliza el filtro Canny, el cual se compone de tres etapas: la primera dirigida a la reducción del ruido, obteniendo una imagen suavizada y la segunda donde se encuentra los gradientes en la intensidad de los píxeles.

from skimage import io
from skimage import feature
from skimage.color import rgb2gray
import matplotlib.pyplot as plt

imagen = io.imread("mario.jpg")
img = rgb2gray(imagen)
edge = feature.canny(img)
plt.imshow(edge)
plt.show()
png


Estos algunos ejemplos básicos que nos servirán mucho.

Puedes ver y descargar todos los ejemplo en formato ipynb: https://github.com/LuisAlejandroSalcedo/Procesamiento-de-Imagenes-con-Python-y-Scikit-Image.

¿Alguna duda ? No dudes en dejar tu comentario.

Mi nombre es Luis y fue un placer compartir mis conocimientos con todos ustedes :D. 

Seguir Leyendo

jueves, 9 de agosto de 2018

Introducción a las Redes Neuronales: Parte #2 - Aprendiendo matemáticas usando Redes Neuronales

Introducción:

Nuestro objetivo es construir y entrenar una red  neuronal, que pueda predecir la salida de una expresión matemática sin conocer su formula exacta. Considerando la siguiente expresión: (a+b)*2.

Lo que único que utilizaremos sera la librería numpy.

Resultado de imagen para inteligencia artificial

Matemáticas con redes neuronales:

Sera un ejemplo muy sencillo, que nos permitirá reforzar nuestras habilidades. La neurona tendrá 2 entradas y una salida.

El conjunto de entrenamiento constara de las siguientes entradas y salidas:

Inputs  Output
2, 3         10
1, 1         4
5, 2         14
12, 3       30

Tomaremos las entradas para el entrenamiento y las usaremos para ajustar los pesos y mediante una formula poder calcular la salida de la neurona.

Luego calcularemos el error comparando la salida esperada con la de la neurona. Dependiendo del valor del error calculado se ajustaran los pesos.

Todo esto se repetirá 10,000 de esta manera la neurona sera más eficaz. Este proceso es conocido como propagación hacia atrás.

Implementación:

from numpy import array, dot, random

# Declaramos la clase
class neural_network:
    def __init__(self):
        random.seed(1)
        # Pesos aleatorios. 2 entradas y una salida
        self.weights = 2 * random.random((2,1)) - 1
        
    def train(self, inputs, outputs, num):
        for iteration in range(num):
            output = self.predict(inputs) # Salida de la neurona
            error = outputs - output # Calculamos el error
            # Formula Ajuste: 0.01 * entradas * array(entradas) 
            ajuste = 0.01*dot(inputs.T, error)
            self.weights += ajuste
        
    def predict(self, inputs):
        # Calcular la salida de la neurona
        return dot(inputs, self.weights)
    
# Entradas
x = array([[2,3],[1,1],[5,2],[12,3]])

# Salidas
y = array([[10,4,14,30]]).T

# Entrenamiento de la red
red = neural_network()
red.train(x,y,10000)

# Dato de prueba
test1 = array([15,2])

# Mostramos la salida de la red
print("Resultado: %s" % red.predict(test1))

Resultado: [34.]

Efectivamente, (15+2)*2 es igual a 34.

Como pueden observar, es un ejemplo muy sencillo y practico. 

¿Alguna duda? No dudes en dejar tu comentario.
Seguir Leyendo

miércoles, 8 de agosto de 2018

Introducción a las Redes Neuronales: Parte #2 - Red Neurona para el reconocimiento de patrones

Introducción:

Ya hemos adquirido los conocimientos suficientes para crear nuestro primer perceptrón, y lo hicimos de manera fácil y comprensible.

Lo siguiente que haremos, sera programar una red neuronal para el reconocimiento de patrones. 

Utilizaremos la librería numpy, de la cual utilizaremos algunos métodos para ahorrar algo de tiempo. Aunque te darás cuenta que son métodos que se pueden implementar de manera rápida en una función, todo depende de ti.

Por ultimo quiero advertirles que utilizaremos la función de activación sigmoide. Ya hemos hablado de las funciones de activación y de la función sigmoide en la parte #1 de la guía.


Red Neuronal para el reconocimiento de patrones:

¿A que me refiero con "Reconocimiento de Patrones"? 

111 = 1
101 = 1
011 = 0
010 = 0

El cerebro humano tiene la capacidad de aprender de su entorno gracias a que podemos reconocer patrones de manera rápida. 

En secuencia mostrada anteriormente, podemos ver un patrón muy sencillo. Si la cadena comienza o termina con el uno (1), esta cadena o datos, se clasificara como "1". Si la cadena empieza o termina con un cero (0) se clasificara como "0".

Nuestro objetivo es programar una red neuronal que pueda hacer lo que nosotros hemos hecho, reconocer este patrón.

# Importamos los métodos de numpy que utilizaremos
from numpy import exp, array, random, dot

# exp: Calcular la exponencialidad de los elementos de la matriz de entrada
# dot: Método para realizar producto de matrices

class neural_network_patterns():
    def __init__(self):
        random.seed(1)
        # Pesos iniciales
        # Red neuronal con 3 entradas y una salida
        self.weights = 2 * random.random((3,1)) - 1
        
    def sigmoide(self, x):
        return 1 / (1 + exp(-x))
    
    def train(self, inputs, outputs, num):
        for i in range(num): # Iteración
            output = self.prediction(inputs) # Salida 
            error = outputs - output # Calculo de error
            # Ajustamos los pesos
            # Formula: error * input * output * (1-output)
            ajuste = dot(inputs.T, error * output * (1-output)) 
            self.weights += ajuste
            
    def prediction(self, inputs):
        # Función sigmoide
        resultado = self.sigmoide(dot(inputs, self.weights))
        return resultado

# Instancia de nuestra clase
red = neural_network_patterns()

# Datos de entrenamiento
inputs = array([[1,1,1],[1,0,1],[0,1,1]])
outputs = array([[1,1,0]]).T # Salidas de los datos de entrenamiento
        
# Entrenamiento de la red neuronal
# inputs = datos de entrada
# outputs = salida de cada entrada
# 10000 = cuantas veces se repetira el entrenamiento de la red
red.train(inputs, outputs, 10000)

# Prueba 1
pre_1 = red.prediction(array([1,1,1]))[0]

# Prueba 2
pre_2 = red.prediction(array([0,1,0]))[0]

# round nos permitira redondear el numero
# y saber con más precisión la predicción
print("[1,1,1] = %s" % round(pre_1))
print("[0,1,0] = %s" % round(pre_2))


[1,1,1] = 1.0
[0,1,0] = 0.0


¿Alguna duda? No dudes en dejar tu comentario.
Seguir Leyendo

domingo, 5 de agosto de 2018

Introducción a las Redes Neuronales - Parte #2: Nuestra primera red neuronal

Introducción:

Ya hemos terminado la primera parte de este proyecto. Vimos cuales eran las características y conceptos básicos de una red neuronal. Ahora es momento de emplear todo lo aprendido para crear nuestras propias redes neuronales.

En esta segunda parte, nos dedicaremos a la creación de las distintas redes neuronales. En este primer articulo practico, construiremos un Perceptrón el cual ya hemos visto y analizado anteriormente. 

Por supuesto, si no has visto la primera parte, te recomiendo que lo hagas:


Creación de nuestra propia red neuronal:

Existen miles de herramientas que nos ofrecen modelos y redes neuronales pre entrenadas. Incluso ya hemos visto algunas de esas librerías en este blog.

Pero no hay nada como crear nuestras propias herramientas. Esto nos permite conocer con exactitud con funciona el programa y sus procedimientos. Podemos acceder a ellas y alterarlas de la manera que creamos correcta.

Por ello en esta segunda parte de este proyecto, realizaremos redes neuronales desde cero, es decir que las programaremos nosotros mismos sin necesidad de una librería externa. 

En este caso realizaremos un Perceptrón , ya que es uno de los más sencillo y necesarios de conocer. Ya hemos hablado de el perceptrón y de sus características.

Sabemos que la forma de entrenar a un Perceptrón es la siguiente:

w = w + N(d(k)-y) x(k)

Donde:

W = El peso actual asociado a la sinapsis que une la neurona i de la capa de entrada y la neurona j de la capa de salida.
N = Es una constante entre 0 y 1 que indica cuanto aprende la red.
d(k) = El estado de la neurona de la capa de salida j.
y = El valor deseado para esa neurona.
x(k) = El estado de la neurona de la capa de entrada i.

Teniendo esto en cuenta, podemos entrenar a nuestra red neuronal.

Nuestro Primer Perceptrón:

Pateamiento del problema:
Nuestro objetivo es entrenar a un Perceptrón que pueda clasificar un conjuntos de datos sin clasificar.

Lo que haremos, sera crear un clase llamada Perceptron, de esta manera podremos crear todos los perceptrones que necesitemos y emplearlos para diferentes objetivos.

Tendrá dos entradas y una salida (p1, p2). 

Cada neurona de la red es una unidad de procesamiento de información; es decir, recibe información a través de las conexiones con las neuronas de la capa anterior, procesa la información, y emite el resultado a través de sus conexiones con las neuronas de la capa siguiente, siempre y cuando dicho resultado supere un valor "umbral".


En una red neuronal ya entrenada, las conexiones entre neuronas tienen un determinado peso ("peso sináptico").

El procesamiento de la información llevado a cabo por cada neurona Y, consiste en una función (F) que opera con los valores recibidos desde las neuronas de la capa anterior (Xi, generalmente 0 o 1), y que tiene en cuenta el peso sináptico de la conexión por la que se recibieron dichos valores (Wi). Así, una neurona dará mas importancia a la información que le llegue por una conexión de peso mayor que no a aquella que le llegue por una conexión de menor peso sináptico.

Para simplificar el sistema de entrenamiento, el valor umbral (U) pasa a expresarse como un peso sináptico más (-W0), pero asociado a una neurona siempre activa (X0). Esta neurona siempre activa, se denomina "bias", y se sitúa en la capa anterior a la neurona Y.


Neurona bias y su peso sináptico asociado (-W0), en substitución del valor umbral (U).


Teniendo todo esto en cuenta, podemos empezar a implementarlo todo en el lenguaje de programación Python.

Perceptron.py

Empezaremos declarando nuestra clase con sus respectivos atributos:

 # Importamos la librerias necesarias
import random

# Declaramos la clase
class Perceptron:
    def __iniit__(self, sample, exit, learn_rate=0.01, epoch_number=1000, bias=-1):
        # Atributos de la clase
        self.sample = sample # Datos de entrenamiento
        self.exit = exit # Salida esperada para cada dato
        self.learn_rate = learn_rate # Que tanto aprendera la red
        self.epoch_number = epoch_number
        self.bias = bias # Bias de la red
        self.number_sample = len(sample) # Numero de ejemplos
        self.col_sample = len(sample[0]) # Columnas de los datos
        self.weight = [] # Lista de pesos

Estos serán los atributos que utilizara la red. Cada parámetro determinara la configuración de la red así como su eficiencia. Es recomendable ir cambiando los valored de los parametros para conseguir los resultados más eficientes. 



def trannig(self): # Metodo de entrenamiento
    
        for sample in self.sample: # Se recorren los datos de entrenamiento
            sample.insert(0, self.bias) # Se inserta el bias en la primera pocisión
        
        for i in range(self.col_sample):
            self.weight.append(random.random()) # Asignamos pesos aleatorios
            
        self.weight.insert(0, self.bias) # Insertamos el bias en los pesos
        
        epoch_count = 0
        
        while True:
            erro = False
            for i in range(self.number_sample):
                u = 0
                for j in range(self.col_sample + 1):
                    # Función de activación
u = u + self.weight[j] * self.sample[i][j] y = self.sign(u) # Comprobar el valor del umbral if y != self.exit[i]: for j in range(self.col_sample+1): # Función de entrenamiento # w = w + N(d(k)-y) x(k) self.weight[j] = self.weight[j] + self.learn_rate * (self.ex it[i]-y) * self.sample[i][j] erro = True epoch_count = epoch_count+1 # Se aumenta el numero de epoch if erro == False: print(('\nEpoch: \n', epoch_count)) # Mostramos el valor de epoch print('-'*20) print("\n") break

Aquí hay otro tema a tener en cuenta. Epoch: Cada presentación completa al perceptrón multicapa del set de entrenamiento se denomina epoch. Así, el proceso de aprendizaje se repite epoch tras epoch hasta que los pesos sinápticos se estabilizan y la performance de la red converge a un valor aceptable.


def sort(self, sample):
        """
        Se inserta el bias, ya que como discutimos antes,
        sera una neurona que siempre estara activada.
        """
        sample.insert(0, self.bias)
        u = 0
        for i in range(self.col_sample + 1):
            # Función de activación
            u = u + self.weight[i] * sample[i]
        
        # Comprobamos el valor de la función de activación
        y = self.sign(u) 
    
        # Si y es igual a -1, la clasificación corresponde a P1
        if  y == -1:
            print(('Ejemplo: ', sample))
            print('Clasificación: P1')
        # Si y es igual a 1, la clasificación corresponde a P1
        elif y == 1:
            print(('Ejemplo: ', sample))
            print('Clasificación: P2')
            
def sign(self, u):
    return 1 if u >= 0 else -1

El método sort utilizara la función de activación. Luego se comprobara la salida y se le asignara una clasificación a cada uno de los datos. Si y = -1 se asignara la clasificación P1 de lo contrario, se le asignara la clasificación P2.

# Datos de entrenamiento
samples = [
    [0, 2],
    [-2, 2],
    [0, -2],
    [2, 0],
    [-2,2],
    [-2,-2],
    [2,-2],
    [2,2],
]

# Clasificación de los datos de entrenamiento (salidas que esperamos para cada conjunto de dato)
"""
[0,2] = 1
[-2,-2] = 1
[0,-2] = 0
...
"""
exit = [1, 1, 0, 0, 1, 1, 0, 1]

#Intancia de nuestra neurona
network = Perceptron(sample=samples, exit = exit, learn_rate=0.01, epoch_number=1000, bias=-1)
 
# Entrenamos a la neurona
network.trannig()

"""
Le pedimos al usuario datos para entrenar.
Luego mostramos el resultados
"""
while True:
    sample = []
    for i in range(2):
        sample.insert(i, float(input('Valor: ')))
    network.sort(sample) # Clasificacipon de nuevos datos
    print("\n")

Ahora es momentos de utilizar la clase y empezar a entrenar a la neurona.

Todo el código completo nos quedaría así:

'''
 Perceptron
 w = w + N * (d(k) - y) * x(k)

 p1 = -1
 p2 = 1
'''

import random

class Perceptron:
    def __init__(self, sample, exit, learn_rate=0.01, epoch_number=1000, bias=-1):
        self.sample = sample
        self.exit = exit
        self.learn_rate = learn_rate
        self.epoch_number = epoch_number
        self.bias = bias
        self.number_sample = len(sample)
        self.col_sample = len(sample[0])
        self.weight = []

    def trannig(self):
        for sample in self.sample:
            sample.insert(0, self.bias)

        for i in range(self.col_sample):
           self.weight.append(random.random())

        self.weight.insert(0, self.bias)

        epoch_count = 0

        while True:
            erro = False
            for i in range(self.number_sample):
                u = 0
                for j in range(self.col_sample + 1):
                    u = u + self.weight[j] * self.sample[i][j]
                y = self.sign(u)
                if y != self.exit[i]:

                    for j in range(self.col_sample + 1):

                        self.weight[j] = self.weight[j] + self.learn_rate * (self.exit[i] - y) * self.sample[i][j]
                    erro = True
            epoch_count = epoch_count + 1
            if erro == False:
                break

    def sort(self, sample):
        sample.insert(0, self.bias)
        u = 0
        for i in range(self.col_sample + 1):
            u = u + self.weight[i] * sample[i]

        y = self.sign(u)

        if  y == -1:
            print('Clasificación: P1 (-1)')
        else:
            print('Clasificación: P2 (1)')

    def sign(self, u):
        return 1 if u >= 0 else -1
        
# Datos de entrenamiento
samples = [
    [0, 2],
    [-2, 2],
    [0, -2],
    [2, 0],
    [-2,2],
    [-2,-2],
    [2,-2],
    [2,2],
]

# Clasificación de los datos de entrenamiento (salidas que esperamos para cada conjunto de dato)
"""
[0,2] = 1
[-2,-2] = 1
[0,-2] = 0
...
"""
exit = [1, 1, -1, -1, 1, 1, -1, 1]

network = Perceptron(sample=samples, exit = exit, learn_rate=0.01, epoch_number=1000, bias=-1)

network.trannig()

while True:
    sample = []
    for i in range(2):
        sample.insert(i, float(input('Valor: ')))
    network.sort(sample)
    print("\n")

Luego de realizar too esto, ya estamos aptos de ejecutar el algoritmos.

Valor: 1
Valor: -2
Clasificación: P1 (-1)


Valor: 1
Valor: 2
Clasificación: P2 (1)

El programa continuara pidiendo datos para clasificar hasta detener el programa.

Siempre lo digo, y lo volveré a decir. "Entre más datos, mejor", entrena a la red con todos los datos posibles. En la mayoría de los casos, los datos de entrenamiento son los que más influyen en la eficacia de la red neuronal.

Eso ha sido todo por hoy. ¿Alguna duda? No dudes en dejar tu comentario.

Mi nombre es Luis, y fue un placer compartir mis conocimientos con todos ustedes :D.
Seguir Leyendo
Powered by Blogger .