Professional Documents
Culture Documents
p
TECNOLÓGICO NACIONAL DE MEXICO
Instituto Tecnológico Superior de Pátzcuaro
ertyuiopasdfghjklzxcvbnmqwert
yuiopasdfghjklzxcvbnmqwertyui
opasdfghjklzxcvbnmqwertyuiopa
sdfghjklzxcvbnmqwertyuiopasdf
Manual de Prácticas de Procesamiento
Avanzado de Imágenes
ghjklzxcvbnmqwertyuiopasdfghj
klzxcvbnmqwertyuiopasdfghjklz
Academia de Ingeniería Biomédica
xcvbnmqwertyuiopasdfghjklzxcv
MTI Mario Salvador Castro Zenil
bnmqwertyuiopasdfghjklzxcvbn
mqwertyuiopasdfghjklzxcvbnmq
wertyuiopasdfghjklzxcvbnmqwe
rtyuiopasdfghjklzxcvbnmqwerty
uiopasdfghjklzxcvbnmqwertyuio
pasdfghjklzxcvbnmqwertyuiopas
dfghjklzxcvbnmqwertyuiopasdfg
hjklzxcvbnmqwertyuiopasdfghjk
lzxcvbnmrtyuiopasdfghjklzxcvbn
Carretera Pátzcuaro-Morelia Av. Tecnológico No.1, Zurumutaro
Pátzcuaro, Michoacan, México. C.P. 61615 1
Tel. (434) 542-5049, 5063, 5067, e-mail: direccion@itspa.edu.mx
www.itspa.edu.mx
TECNOLÓGICO NACIONAL DE MEXICO
p Instituto Tecnológico Superior de Pátzcuaro
La tarea más simple y común que realizaremos será cargar imagen a partir de una archivo en
disco, para hacerlo disponemos de la función imread() , mientras que imwrite() nos
permitirá guardar una imagen.
Al cargar una imagen esta es almacenada en memoria usando el formato de color BGR, por
defecto, este es igual al espacio de color RGB pero con los canales invertidos, aquí se
almacena la información para cada uno de los canales azul, verde y rojo.
import cv2
Al guardar la imagen se utilizara el formato definido por la extensión que le demos al nombre
de la imagen, siempre que el mismo este soportado, algunos de estos formatos son: PNG,
BMP, TIFF, JPG, otros.
Cuando hayamos cargado una imagen en memoria es posible mostrarla en una ventana, para
este propósito tenemos la función imshow() , con ella creamos una ventana y desplegamos la
imagen deseada, debemos indicar el nombre y el objeto que deseamos mostrar, ejemplo:
import cv2
Al final vemos una llamada a waitKey(0) , con esto impedimos que la ventana se cierre
inmediatamente, el valor cero indica que se debe esperar indefinidamente hasta que se
presione una tecla, si establecemos un valor mayor que cero este será la cantidad de
milisegundos que se debe esperar.
Esta función devuelve el código ASCII de la tecla presionada, por ejemplo, 27 para la tecla
ESC.
No solo podemos trabajar con imágenes cargadas desde archivos, también podemos leer
imágenes desde la cámara web de nuestro PC, si capturamos una serie de imágenes
sucesivas tendremos un video en tiempo real, veamos como se hace.
import cv2
cap = cv2.VideoCapture(0)
while(True):
ret, frame = cap.read()
if ret:
cv2.imshow('video', frame)
cap.release()
cv2.destroyAllWindows()
Para capturar un cuadro de video usamos cap.read() este devuelve un booleano que indica
si la captura es válida y el objeto imagen que representa la captura, de aquí en adelanta
tratamos la captura como si fuese cualquier otra imagen, al terminar
usaremos cap.release() para liberar recursos.
Los codec que podemos utilizar dependen de cada sistema, es decir si no tienes los codec
para FLV no podrás ver ni escribir en este formato, utilizaremos un código de 4 bytes para
identificar cada uno de ellos, puedes encontrar la lista en, fourcc.org, para indicarlo tenemos la
función: cv2.VideoWriter_fourcc('M','J','P','G') para MJPG.
import cv2
cap = cv2.VideoCapture('video.avi')
while(True):
# capturar el cuadro
ret, frame = cap.read()
if ret:
# procesar la captura, convertir a grises
gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
out.write(gray)
cap.release()
out.release()
cv2.destroyAllWindows()
Veremos cómo aplicar las operaciones básicas sobre imágenes cargadas en memoria
mediante OpenCV, veremos cómo acceder a los pixeles de una imagen y modificarlos,
aprenderemos a seleccionar una región de interés (ROI) y mostraremos como trabajar con los
distintos canales de una imagen.
Para utilizar correctamente OpenCV desde Python se requiere tener un buen conocimiento de
Numpy.
Luego de cargar una imagen podemos acceder a cada uno de los pixeles de la misma, para
una imagen a color obtendremos los canales: azul, verde y rojo, si la imagen es a escala de
grises solo tendremos un canal, el cual indica la intensidad de gris de dicho pixel.
import cv2
image = cv2.imread('lena.jpg')
print('pixel:', b, g, r)
image[100,100] = [255,255,255]
import cv2
image = cv2.imread('lena.jpg')
x = 240
y = 178
b = image.item(y, x, 0)
g = image.item(y, x, 1)
r = image.item(y, x, 2)
print('pixel:', b, g, r)
Con esto modificamos las componente BGR del pixel ubicado en la posición (x, y).
Obtener el numero de filas, columnas y canales que componen una imagen, si la imagen es
ha escala de grises la tupla devuelta solo contiene los dos primeros valores.
>>> image.shape
(512, 512, 3)
>>> image.size
786432
Cuando deseemos obtener el tipo de datos usado para almacenar los pixeles.
>>> image.dtype
dtype('uint8')
En ocasiones deseamos trabajar con una porción de la imagen original, en esos casos
podemos usar el indexing de Numpy para seleccionar y copiar la región rectangular deseada,
ejemplo:
import cv2
image = cv2.imread('lena.jpg')
roi = image[240:375, 215:365]
cv2.imshow('roi', roi)
cv2.imshow('original', image)
cv2.waitKey(0)
cv2.destroyAllWindows()
Manipulación de Pixeles
Práctica De acuerdo a los códigos anteriores carga una imagen y escoge un valor que
1.2 quieras resaltar de la imagen. Reporta el código y las impresiones de pantalla
en el formato de práctica
OpenCV nos proporciona varias funciones que nos permiten dibujas diversas figuras de una
menara rápida y sencilla, para esta práctica haremos uso del trackbar y el ratón para dibujar
figuras como círculos, rectángulos y líneas, además veremos las funciones usadas para pintar
elipses, polígonos y texto.
img: imagen sobre la que se desea dibujar, siempre es el primero que indicamos.
color: color con que se debe pintar la figura.
thickness: grosor del contorno de la figura, -1 rellena la figura, el valor por defecto es 1.
lineType: define el tipo de línea, por defecto es cv2.LINE_8, la opción cv2.LINE_AA
usa anti-aliased por lo que el resultado será mejor, también puedes usar cv2.LINE_4.
Para esta primera demostración crearemos una aplicación que nos permitirá dibujar líneas,
círculos, y rectángulos usando el mouse, por ejemplo, para crear una línea hacemos clic
izquierdo en el punto inicial de la misma y arrastramos hasta el punto final, al soltar el botón
izquierdo la línea será dibujada.
import cv2
import numpy as np
from math import sqrt
mode = 'circle'
color = (0, 0, 255)
pt = None
tk = 1
def on_track(value):
global tk
if event == cv2.EVENT_LBUTTONDOWN:
pt = (x, y)
print('Dibujando ', mode, ', en el punto: ', pt)
if mode == 'circle':
rad = sqrt(pow(pt[0] - x, 2) + pow(pt[1] - y, 2))
cv2.circle(param, pt, int(rad), color, tk, cv2.LINE_AA)
if __name__ == "__main__":
title = 'Drawing'
image = np.zeros((600, 800, 3), np.uint8)
cv2.namedWindow(title)
cv2.createTrackbar('RELLENO', title, 1, 10, on_track)
cv2.setMouseCallback(title, on_mouse, image)
while(1):
cv2.imshow(title, image)
key = cv2.waitKey(20) & 0xFF;
if key == ord('c'):
mode = 'circle'
elif key == ord('r'):
mode = 'rectangle'
elif key == ord('l'):
mode = 'line'
elif key == 27:
break
cv2.destroyAllWindows()
if __name__ == "__main__":
title = 'Drawing'
image = np.zeros((600, 800, 3), np.uint8)
cv2.namedWindow(title)
cv2.createTrackbar('RELLENO', title, 1, 10, on_track)
cv2.setMouseCallback(title, on_mouse, image)
while(1):
cv2.imshow(title, image)
key = cv2.waitKey(20) & 0xFF;
if key == ord('c'):
mode = 'circle'
elif key == ord('r'):
mode = 'rectangle'
elif key == ord('l'):
mode = 'line'
elif key == 27:
break
cv2.destroyAllWindows()
Aquí inicia la aplicación, se crea la imagen sobre la que dibujaremos de tamaño 800x600 de 3
canales de tipo uint8, creamos también la ventana, le agregamos un trackbar y el manejador
de eventos para el mouse.
El bucle inferior nos permite cambiar la figura que deseamos dibujar usando las teclas: c, r, l,
para dibujar círculos, rectángulos y líneas respectivamente, presionando ESC salimos de la
aplicación, al terminar el bucle destruimos la ventana.
def on_track(value):
global tk
Esta es la función invocada cada vez que cambia el valor del trackbar, este valor será usado
para definir el thickness de la figura, si el valor es cero lo cambiamos a -1 para dibujar una
figura rellena, debemos tener en cuenta que una línea no se puede rellenar.
if event == cv2.EVENT_LBUTTONDOWN:
pt = (x, y)
print('Dibujando ', mode, ', en el punto: ', pt)
if mode == 'circle':
rad = sqrt(pow(pt[0] - x, 2) + pow(pt[1] - y, 2))
cv2.circle(param, pt, int(rad), color, tk, cv2.LINE_AA)
Aquí controlamos los eventos del mouse, al hacer clic izquierdo EVENT_LBUTTONDOWN
guardamos la posición del mouse, cunado soltamos dicho botón verificamos cual es la figura a
dibujar, obtenemos la posición actual y dibujamos la figura.
Dibujar un círculo
if mode == 'circle':
rad = sqrt(pow(pt[0] - x, 2) + pow(pt[1] - y, 2))
cv2.circle(param, pt, int(rad), color, tk, cv2.LINE_AA)
Para obtener el radio calculamos la distancia que hay desde el primer punto hasta el punto
actual.
Dibujar un rectángulo
Para esta figura requerimos indicar dos puntos además de los parámetros comunes, el primer
punto corresponde a la esquina superior izquierda y el segundo a la esquina inferior derecha
del rectángulo, la función es cv2.rectangle() .
Dibujar líneas
Con cv2.line() pintamos una líneas solo debemos indicar el punto inicial y final de la
misma.
Existen otras funciones de dibujo que no hemos incluido en esta primera demostración, ellas
son para dibujar: textos, elipses y polígonos, veamos un ejemplo de como utilizarlas.
import cv2
import numpy as np
if __name__ == "__main__":
title = 'Drawing'
img = np.zeros((600, 800, 3), np.uint8)
font = cv2.FONT_HERSHEY_TRIPLEX
cv2.putText(img, 'OpenCV Python', (5, 560), font, 3, (255,255,255),
1, cv2.LINE_AA)
cv2.imshow(title, img)
cv2.waitKey(0)
cv2.destroyAllWindows()
Explicación de cada una de las funciones utilizadas, al igual que las anteriores usamos los
parámetros comunes previamente explicados, el primer parámetro es la imagen en donde se
desea dibujar.
Dibujar Texto
font = cv2.FONT_HERSHEY_TRIPLEX
cv2.putText(img, 'OpenCV Python', (5, 560), font, 3, (255,255,255), 1,
cv2.LINE_AA)
FONT_HERSHEY_SIMPLEX
FONT_HERSHEY_PLAIN
FONT_HERSHEY_DUPLEX
FONT_HERSHEY_COMPLEX
FONT_HERSHEY_TRIPLEX
FONT_HERSHEY_COMPLEX_SMALL
FONT_HERSHEY_SCRIPT_SIMPLEX
FONT_HERSHEY_SCRIPT_COMPLEX
Dibujar un polígono
Usando la función cv2.polylines() podemos dibujar una serie de líneas, debemos crear un
conjunto de puntos que definen la figura, esto puntos serán unidos usando líneas, de este
modo podemos crear figuras complejas, ejemplo, la estrella que vemos en la imagen superior.
pts = np.array([[403, 75], [587, 312], [216, 150], [588, 155], [215,
313]], np.int32)
cv2.polylines(img, [pts], True, (0, 255, 255), 5, cv2.LINE_AA)
La variable pts guarda cada uno de los puntos requeridos para formar la estrella, estos
corresponden a las puntas de la misma, el booleano que en nuestro caso hemos establecido a
True indica si se debe conectar el último punto con el primero.
cv2.ellipse(img, (400, 360), (100, 50), 0, 45, 360, (255, 0, 255), -1,
cv2.LINE_AA)
Esta función nos permite tener mucho control sobre la figura dibujar, para una explicación más
detallada de cada uno de los parámetros visita la documentación oficial.
Dibujar marcadores
OpenCV nos proporciona la función cv2.drawMarker(...) que nos permite dibujar uno de
los marcadores seleccionado, debemos indicar su posición, el marcador, su color y
opcionalmente el tamaño del mismo, por ejemplo:
MARKER_CROSS
MARKER_TILTED_CROSS
MARKER_STAR
MARKER_DIAMOND
MARKER_SQUARE
MARKER_TRIANGLE_UP
MARKER_TRIANGLE_DOWN
Esta es la última función de dibujo que veremos cv2.arrowedLine() nos sirve para dibujar
una flecha, solo será necesario indicar el punto de inicio y final de la misma, ejemplo:
Al dibujar la flecha del medio cambiamos el porcentaje que ocupa la punta de la fecha a 0.3.
Dibujo de formas
Práctica
Ejecuta los códigos anteriores, cambia algunos parámetros de color y tamaño.
1.3
Reporta el código y las impresiones de pantalla en el formato de práctica
El mouse y el trackbar son componentes de la GUI que nos ayudarán a interactuar con la
aplicación OpenCV que estemos desarrollando, esta la biblioteca nos permite agregar
manejadores de eventos para responder a eventos del ratón, de igual manera podemos
agregar una barra desplazable a una ventana y obtener el valor de la misma.
title = 'Drawing'
cv2.namedWindow(title)
cv2.createTrackbar('Grosor', title, 5, 50, on_trackbar)
def on_trackbar(value):
print(value)
Este pequeño ejemplo imprime el valor actual del control, otra forma de obtener el valor es
usando la función cv2.getTrackbarPos('Grosor', 'Drawing') primero indicamos el
nombre del trackbar y luego la ventana en la que se encuentra, el valor devuelto por esta
función corresponde al valor del control indicado.
title = 'Drawing'
image = np.zeros((600, 800, 3), np.uint8)
cv2.namedWindow(title)
cv2.setMouseCallback(title, on_mouse, image)
La función on_mouse es invocada cada vez que se produce un evento del mouse, esta
función tiene la siguiente forma:
global start
pt = (x, y)
if event == cv2.EVENT_LBUTTONDOWN:
start = True
elif event == cv2.EVENT_LBUTTONUP:
start = False
elif start and event == cv2.EVENT_MOUSEMOVE:
ventana = 'Drawing'
grosor = cv2.getTrackbarPos('Grosor', ventana)
En esta función x, y contienen la posición actual del mouse, param hace referencia al
parámetro opcional enviado desde la función principal.
Para la demostración de la teoría explicada crearemos una aplicación que nos permite pintar
usando los movimiento del mouse, usaremos el trackbar para definir el grosor del pincel.
Para dibujar debemos mantener presionado el botón izquierdo y movernos sobre el área que
deseamos pintar, para hacer esto simplemente dibujamos un circulo del grosor indicado en el
trackbar y la posición definida por el movimiento del mouse.
import cv2
import numpy as np
start = False
def on_trackbar(value):
pass
global start
pt = (x, y)
if event == cv2.EVENT_LBUTTONDOWN:
start = True
elif event == cv2.EVENT_LBUTTONUP:
start = False
elif start and event == cv2.EVENT_MOUSEMOVE:
ventana = 'Drawing'
grosor = cv2.getTrackbarPos('Grosor', ventana)
if __name__ == "__main__":
title = 'Drawing'
image = np.zeros((600, 800, 3), np.uint8)
cv2.namedWindow(title)
cv2.createTrackbar('Grosor', title, 5, 50, on_trackbar)
cv2.setMouseCallback(title, on_mouse, image)
while(1):
cv2.imshow(title, image)
if cv2.waitKey(20) & 0xFF == 27:
break
cv2.destroyAllWindows()
El proceso de filtrado puede llevarse a cabo sobre los dominios de frecuencia y/o espacio. De aquí
en adelante nos centraremos en filtros en el dominio del espacio.
g(x,y)=h(x,y)*f(x,y)
OpenCV proporciona la función cv2.filter2D() para realizar la convolución de una imagen con
un kernel determinado. Por ejemplo, un kernel de tamaño 3x3 para un filtro de promedio se puede
definir de la siguiente forma:
El procedimiento de filtrado es el siguiente: para cada píxel de la imagen, una ventana de tamaño
3x3 es centrada en él, realizándose el sumatorio de la multiplicación del kernel por los píxeles
contiguos, dividiéndose entre 9. Esto equivale a realizar la media de los valores de los píxeles de
dentro de la ventana. Esta operación se realiza para cada uno de los píxeles de la imagen.
image_file = 'examples/images/rabbit.jpg'
img = cv2.imread(image_file)
# Show
cv2.imshow('Original', img)
cv2.imshow('Filtered', dst)
cv2.waitKey(0)
cv2.destroyAllWindows()
Filtrado 2D
Práctica
Descargar una imagen y aplicar un filtrado 2D con un kernel de tamaño 5x5 ponderado.
2.1
Probar a cambiar los distintos valores del kernel.
Suavizado de imágenes
El suavizado de imágenes se logra realizando la convolución de la imagen con un filtro paso-bajo,
y suele utilizarse para la reducción y/o eliminación del ruido, ya que se elimina el contenido de
altas frecuencias. OpenCV proporciona varias técnicas para el suavizado de imágenes. A
continuación veremos varias de ellas.
Promedio
El filtro de la media es el más simple, intuitivo y fácil de implementar para suavizar imágenes. Esta
operación se realiza mediante la convolución de la imagen con un filtro normalizado. Simplemente
calcula la media de los píxeles que están bajo el área del kernel y reemplaza el valor del elemento
central.
import cv2
# Load image
image_path = 'examples/images/sunset.jpg'
image = cv2.imread(image_path)
# Blur
k = 5
blur = cv2.blur(image, (k, k))
# Show
cv2.imshow('Original', image)
cv2.imshow('Filtered', blur)
cv2.waitKey(0)
cv2.destroyAllWindows()
Promedio
Práctica
Descargar una imagen con muchos colores. Cambiar el tamaño del kernel y comprobar
2.2
qué ocurre.
En cuanto a las desventajas del filtro promedio o de la media, encontramos que es bastante sensible a
cambios locales. Además, puede crear nuevas intensidades de color que no aparecían en la imagen
original.
Filtro Gaussiano
El filtro gaussiano se usa para emborronar imágenes y eliminar ruido. Es similar al filtro de media
pero se usa una máscara diferente, modelizando la función gaussiana.
número impar positivo. Además, también debe especificarse la desviación estándar en las
direcciones X e Y, a través de los parámetros sigmaX y sigmaY respectivamente. Si sólo se
especifica el valor para el parámetro sigmaX, la función asigna el mismo valor para el
parámetro sigmaY.
import cv2
# Load image
image_path = 'examples/images/sunset.jpg'
image = cv2.imread(image_path)
# Gaussian blur
k = 5
sigma = 0
blur = cv2.GaussianBlur(image, (k, k), sigma)
# Show
cv2.imshow('Original', image)
cv2.imshow('Filtered', blur)
cv2.waitKey(0)
cv2.destroyAllWindows()
Práctica Gaussiano
2.3 Cambiar el tamaño del kernel y el valor del parámetro sigma y comprobar qué ocurre.
Una de las razones por las que los filtros de tipo Gaussiano son tan importantes es que son muy
efectivos para eliminar ruido Gaussiano de la imagen. Este filtro produce un suavizado más uniforme
que el filtro promedio.
Ejemplo 9. Filtrado de imagen: filtro Gaussiano sobre imagen con mucho ruido.
import cv2
# Load image
image_path = 'images/taj-noise.jpg'
image = cv2.imread(image_path)
# Gaussian blur
k = 5
sigma = 1
blur = cv2.GaussianBlur(image, (k, k), sigma)
# Show
cv2.imshow('Original with noise', image)
cv2.imshow('Filtered', blur)
cv2.waitKey(0)
cv2.destroyAllWindows()
Mediana
El filtro mediana se aplica mediante la función cv2.medianBlur(). En él, se calcula la mediana de
todos los píxeles que están bajo el área del kernel, y el elemento central se sustituye por el valor
de la mediana. El valor para el tamaño del kernel debe ser un número impar positivo.
Este filtro es muy efectivo para eliminar el ruido impulsional, llamado "sal y pimienta".
https://en.wikipedia.org/wiki/Salt-and-pepper_noise
Ejemplo. Filtrado de imagen: filtro mediana sobre imagen con ruido de tipo "sal y pimienta".
import cv2
# Load image
image_path = 'examples/images/salt_pepper_noise.png'
image = cv2.imread(image_path)
# Gaussian blur
k = 5
blur = cv2.medianBlur(image, k)
# Show
cv2.imshow('Original', image)
cv2.imshow('Filtered', blur)
cv2.waitKey(0)
cv2.destroyAllWindows()
Gradientes
En este apartado vamos a mostrar varios filtros paso-alto que nos permitirán filtrar las imágenes
para extraer bordes y gradientes.
Sobel
El operador Sobel, también conocido como el operador Sobel–Feldman, realiza un gradiente
espacial 2D sobre una imagen y de esta forma enfatiza las regiones con altas frecuencias
espaciales, que se corresponden con bordes.
El operador Sobel calcula el gradiente de la intensidad de una imagen en cada punto (píxel). Así,
para cada punto, este operador da la magnitud del mayor cambio posible, la dirección de éste y el
sentido desde oscuro a claro. El resultado muestra cómo de abruptamente o suavemente cambia
una imagen en cada punto analizado y, en consecuencia, cuán probable es que éste represente un
borde en la imagen y, también, la orientación a la que tiende ese borde. En la práctica, el cálculo
de la magnitud -probabilidad de un borde- es más fiable y sencillo de interpretar que el cálculo de
la dirección y sentido.
El operador utiliza dos máscaras o kernels de tamaño 3x3 los cuales son convolucionados con la
imagen original para calcular aproximaciones de las derivativas, una para cambios en el eje
horizontal y otra para cambios en el eje vertical.
Kernels de convolución Sobel: Estos kernels están diseñados para responder ante ejes
verticales y horizontales. Pueden ser aplicados de forma separada a la imagen de entrada para
producir mediciones separadas, o por el contrario, pueden ser combinados para encontrar y
delimitar ejes en ambas direcciones.
La función disponible en OpenCV que realiza este filtrado es cv2.Sobel(). Se puede especificar la
dirección de los gradientes, vertical u horizontal, mediante los
argumentos xorder/dx e yorder/dy respectivamente. Además, también se puede especificar el
tamaño del kernel mediante el argumento ksize.
import cv2
import numpy as np
# Load image
image = cv2.imread('examples/images/sudoku.jpg', cv2.IMREAD_GRAYSCALE)
# Sobel
k = 3
sobelx = cv2.Sobel(image, ddepth=cv2.CV_64F, dx=1, dy=0, ksize=k)
sobely = cv2.Sobel(image, ddepth=cv2.CV_64F, dx=0, dy=1, ksize=k)
# Show
cv2.imshow('Original', image)
cv2.imshow('Sobel X', sobelx)
cv2.imshow('Sobel Y', sobely)
cv2.waitKey(0)
cv2.destroyAllWindows()
Laplacian
El operador Laplacian es una medida isotrópica 2D de la segunda derivada espacial de una
imagen. Aplicar un filtro Laplacian sobre una imagen consigue resaltar las regiones en las que se
producen cambios bruscos de intensidad, y es por tanto utilizada para detección de bordes.
Normalmente, el filtro Laplacian es aplicado sobre una imagen que ha sido previamente filtrada
con un filtro Gaussiano para poder reducir la sensibilidad ante ruido.
La función que proporciona OpenCV para aplicar un filtro Laplacian es cv2.Laplacian(). Los
argumentos obligatorios son la imagen de entrada y la profundidad de la imagen de salida.
4. Umbrales. En esta última etapa del algoritmo, se decide sobre todos los ejes obtenidos
cuáles de ellos son realmente ejes y cuáles de ellos no. Para ello, se establecen dos
valores umbral, minVal y maxVal. Todos los ejes con una intensidad de gradiente mayor
que el valor umbral maxVal se consideran de forma segura como ejes. Todos los ejes
con una intensidad de gradiente menor que el valor umbral minVal se consideran de
forma segura como no ejes, y por tanto, son descartados. Aquellos que se encuentran
entre los valores minVal y maxVal son clasificados dependiendo de su conexión. Si están
conectados a los píxeles con valor mayor a maxVal son considerados parte de los ejes.
En cualquier otro caso son descartados.
5.
import cv2
import numpy as np
webcam_id = 0
cap = cv2.VideoCapture(webcam_id)
while(cap.isOpened()):
# Capture frame-by-frame
ret, frame = cap.read()
if ret == True:
# Operations on the frame
edges = cv2.Canny(frame, threshold_one, threshold_two, aperture_size)
# Display
cv2.imshow("Original", frame)
cv2.imshow("Canny edge detection", edges)
# Exit?
if cv2.waitKey(1) & 0xFF == ord('q'):
break
else:
break
Histogramas
Un histograma es un gráfico que nos da una idea general sobre la distribución de la intensidad de
una imagen. Generalmente, es un gráfico en el que en el eje X se establece el rango [0, 255] y en
el eje Y se muestra la cantidad de píxeles con dicha intensidad.
Es otra forma de entender una imagen. Mirando el histograma de una imagen, podemos intuir el
contraste, el brillo y la distribución de la intensidad de una imagen.
Ejemplo. Calcular el histograma sobre una imagen en escala de grises y mostrar el gráfico.
import cv2
import numpy as np
from matplotlib import pyplot as plt
# Load
image_path = 'examples/images/rabbit.jpg'
image = cv2.imread(image_path, cv2.IMREAD_GRAYSCALE)
# Show image
cv2.imshow('Image', image)
cv2.waitKey(0)
# Histogram
hist = cv2.calcHist([image], channels=[0], mask=None, histSize=[256], ranges=[0,
255])
# Plot
plt.plot(hist)
plt.xlim([0, 255])
plt.show()
cv2.destroyAllWindows()
import cv2
from matplotlib import pyplot as plt
# Load
image_path = 'examples/images/rabbit.jpg'
image = cv2.imread(image_path, cv2.IMREAD_GRAYSCALE)
Template matching
Template matching es un método de procesamiento digital de imágenes para buscar y encontrar la
localización/posición de una imagen template dentro de otra imagen. Es una técnica para
encontrar pequeñas partes de una imagen que se ajusten a una imagen template.
OpenCV viene con una función para este propósito: cv2.matchTemplate. De forma resumida, el
algoritmo símplemente desliza la imagen template sobre la imagen general, como si se tratara de
una convolución 2D, y compara la plantilla con esa zona de la imagen. El resultado es una imagen
en escala de grises donde cada píxel denota cuánto se aproxima a la imagen templateatendiendo
a su vecindario. Mediante la función cv2.minMaxLoc() se puede buscar posteriormente dónde
está el máximo/mínimo para poder localizar la imagen.
http://docs.opencv.org/3.1.0/df/dfb/group__imgproc__object.html#ga3a7850640f1fe1f58fe91a
2d7583695d
import cv2
import numpy as np
from matplotlib import pyplot as plt
img = cv2.imread('examples/images/hobbiton.jpg', 0)
img2 = img.copy()
template = cv2.imread('examples/images/template.png', 0)
w, h = template.shape[::-1]
plt.subplot(121),plt.imshow(res,cmap = 'gray')
plt.title('Matching Result'), plt.xticks([]), plt.yticks([])
plt.subplot(122),plt.imshow(img,cmap = 'gray')
Detección de caras
La detección de objetos mediante el uso de clasificadores en cascada basados en la extracción de
características con filtros de base Haar, es un método efectivo de detección de objetos propuesto
por Paul Viola y Michael Jones en 2001. Es un método basado en las técnicas de aprendizaje
automático donde una función cascada es entrenada mediante una gran cantidad de imágenes
positivas y negativas, posibilitando la detección de objetos en otras imágenes. Las imágenes
deben tener un tamaño prefijado.
Tras ser entrenado el clasificador, puede ser aplicado a una región de interés en una imagen de
entrada. El clasificador generará como salida "1" si la región es como el objeto buscado o "0" en
otro caso. Para buscar el objeto en la imagen completa, la ventana de búsqueda se moverá a
traves de la imagen. Además, el clasificador está diseñado para poder cambiar su tamaño, para
buscar objetos de diferentes tamaños.
Se denomina "en cascada" porque el clasificador está formado por varios clasificadores simples en
diferentes etapas, los cuales son aplicados de forma sucesiva hasta que en una etapa se rechaza
o por el contrario se avanza hasta el final.
OpenCV viene tanto con un entrenador como con un detector. Si se desea entrenar un clasificador
propio para cualquier tipo de objeto (trenes, coches, perros, etc.), se puede utilizar OpenCV para
crearlo. Además, OpenCV también viene muchos clasificadores previamente entrenados para
caras, ojos, sonrisas, etc. Esos ficheros se encuentran almacenados en la siguiente ruta:
opencv/data/haarcascades/
import cv2
import sys
# Webcam
webcam_id = 0
video_capture = cv2.VideoCapture(webcam_id)
dmf_flag = cv2.cv.CV_HAAR_SCALE_IMAGE
else:
dmf_flag = cv2.CASCADE_SCALE_IMAGE
while True:
# Capture frame-by-frame
ret, frame = video_capture.read()
# Gray image
gray_image = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
# Detect faces
faces = face_cascade.detectMultiScale(
gray_image,
scaleFactor=1.1,
minNeighbors=5,
minSize=(30, 30),
flags=dmf_flag
)
# Webcam
webcam_id = 0
video_capture = cv2.VideoCapture(webcam_id)
while True:
# Capture frame-by-frame
ret, frame = video_capture.read()
# Gray image
gray_image = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
# Detect faces
faces = face_cascade.detectMultiScale(
gray_image,
scaleFactor=1.1,
minNeighbors=5,
minSize=(30, 30),
flags=dmf_flag
)
# Detect eyes
roi_gray = frame[y:y+h, x:x+w]
Reconocimiento de caras
Práctica
Realizar un programa que identifique caras en una imagen donde al menos aparezcan 8
3.1
personas
Seguimiento de objetos
Ahora que conocemos cómo convertir una imagen RGB a HSV, ya estamos en disposición de
extraer objetos de un determinado color. En HSV, es más fácil representar un color que en el
espacio de color RGB. El método para la extracción de objetos de un cierto color es el siguiente:
Obtención de cada frame del vídeo
Conversión del espacio de color: de RGB a HSV
Creación de una máscara de color
Extracción de la imagen aquellos objetos del color definido
# Webcam
webcam_id = 0
cap = cv2.VideoCapture(webcam_id)
while True:
_, frame = cap.read()
# Show
cv2.imshow('Frame', frame)
cv2.imshow('Mask', mask)
cv2.imshow('Color', res)
# Exit?
if cv2.waitKey(1) & 0xFF == ord('q'):
break
cv2.destroyAllWindows()
cap.release()
Filtrado de colores
Práctica
Descargar un vídeo y filtrar los objetos de un color determinado
3.3
(https://videos.pexels.com/video-license).
Features
La mayoría de nosotros alguna vez ha jugado a montar puzles. En ellos, partiendo de un gran
número de pequeñas piezas de una imagen, se debe reconstruir la imagen original la cual tiene un
tamaño mayor. La pregunta es ¿cómo eres capaz de resolverlo?
capaces de señalarla. Esta es la razón por la cual los niños pequeños pueden simplemente jugar a
montar puzles.
No existe una definición universal o exacta de qué constituye una característica, y la definición
concreta a menudo depende del problema o del tipo de aplicación que se esté analizando. Dicho
esto, una característica es definida como una parte interesante de una imagen, y suelen utilizarse
como punto de partida para muchos algoritmos de visión por computador. Por tanto, al ser el punto
de partida, la calidad global del algoritmo dependerá de lo bueno que sea su detector de
características. Consecuentemente, la propiedad deseable para cualquier detector de
características es la repetibilidad/reproducibilidad, es decir, si es capaz o no de detectar la misma
características en dos o más imágenes diferentes de la misma escena.
Las imágenes C y D son mucho más simples, ya que son bordes del edificio y es más
fácil encontrar una posición aproximada, pero la posición exacta sigue siendo difícil de
determinar. Esto es porque el patrón es el mismo a lo largo de todo el borde.
Finalmente, las imágenes E y F son diferentes esquinas del edificio y como se observa,
es fácil determinar su posición. Por tanto, pueden ser consideradas como
buenas features.
import cv2
import numpy as np
filename = 'images/taj-noise.jpg'
img = cv2.imread(filename)
# Corner detection
dst = cv2.cornerHarris(gray, blockSize=2, ksize=3, k=0.04)
# Show
cv2.imshow('dst', img)
# Exit?
if cv2.waitKey(0) & 0xFF == ord('q'):
cv2.destroyAllWindows()
Harris
Práctica
Descargar una imagen con un abundante número de corners y aplicar el algoritmo de
4.1
Harris.
Para crear un objeto de tipo ORB, OpenCV proporciona la función cv2.ORB(). Una vez creado,
para detectar los keypoints y descriptors tendremos que utilizar el
método orb.detectAndCompute(). Toda la información viene descrita en el siguiente enlace:
http://docs.opencv.org/3.1.0/d1/d89/tutorial_py_orb.html
Ejemplo. Uso del algoritmo ORB para la detección de esquinas sobre una fotografía del castillo de
Bratislava.
In [ ]:
import numpy as np
import cv2
from matplotlib import pyplot as plt
# Load image
path = 'examples/images/bratislava_castle.jpg'
img = cv2.imread(path, cv2.IMREAD_GRAYSCALE)
Práctica ORB
4.2 Aplicar el algoritmo ORB sobre la imagen descargada para el ejercicio anterior.
ORB II
Práctica
Aplicar los algoritmos ORB, SIFT y FAST y comparar visualmente los resultados que se
4.3
obtienen.
El resultado del método match es una lista de objetos de tipo DMatch. Este objeto tiene los
siguientes atributos:
DMatch.distance - Distancia entre descriptores. Mejor cuanto menor sea.
DMatch.trainIdx - Índice del descriptor en la imagen train.
DMatch.queryIdx - Índice del descriptor en la imagen query
DMatch.imgIdx - Índice de la imágen train.
import numpy as np
import cv2
import matplotlib.pyplot as plt
# Create a new output image that concatenates the two images together
# (a.k.a) a montage
rows1 = img1.shape[0]
cols1 = img1.shape[1]
rows2 = img2.shape[0]
cols2 = img2.shape[1]
# x - columns
# y - rows
(x1,y1) = kp1[img1_idx].pt
(x2,y2) = kp2[img2_idx].pt
# Query image
img1 = cv2.imread('examples/images/r2d2.jpg', 0)
# Train image
img2 = cv2.imread('examples/images/starwars.jpg', 0)
# Match descriptors.
matches = bf.match(des1, des2)
Feature Matching
Práctica
Descargar un par de fotos de internet en la que la imagen query aparezca dentro de la
4.4
imagen train. Aplicar el algoritmo Brute-Force matcher.
Aprendizaje automático
k-Nearest Neighbour
El método kNN (k-nearest neighbour o k-vecinos más cercanos) es uno de los algoritmos de
clasificación más simples dentro de los métodos de clasificación supervisada. Este es un método
de clasificación no paramétrico, que estima el valor de la función de densidad de probabilidad o
En la imagen, vemos dos tipos de objetos, los cuadrados azules y los triángulos rojos. A cada tipo
de objeto distinto lo llamamos clase. Los objetos son mostrados en un plano al que
llamaremos feature space.
Ahora imaginemos que queremos clasificar un nuevo objeto (círculo verde), teniendo que elegir si
pertenece al grupo de objetos azules o rojos. A este proceso lo llamamos clasificación. El algoritmo
va a intentar clasificar al objeto nuevo dependiendo de cuáles sean los vecinos más cercanos, que
dependerá del parámetro k. En cuanto a la elección de este parámetro, dependerá
fundamentalmente de los datos; generalmente, valores grandes de k reducen el efecto de ruido en
la clasificación, pero crean límites entre clases parecidas.
El método kNN está disponible en OpenCV. Lo primero que tenemos que hacer es obtener una
instancia mediante el método knn = cv2.KNearest(). Tras esto, deberemos realizar el proceso de
entrenamiento mediante el método knn.train(). Por último, para realizar el proceso de clasificación
haremos uso del método knn.find_nearest().
import cv2
import numpy as np
import matplotlib.pyplot as plt
# New object
newcomer = np.random.randint(0, 100, (1, 2)).astype(np.float32)
plt.scatter(newcomer[:,0], newcomer[:,1], 80, 'g', 'o')
# Classification
k = 5
if cv2.__version__.startswith('2.4'):
knn = cv2.KNearest()
knn.train(trainData, responses)
ret, results, neighbours, dist = knn.find_nearest(newcomer, k)
else:
knn = cv2.ml.KNearest_create()
knn.train(trainData, cv2.ml.ROW_SAMPLE, responses)
ret, results, neighbours, dist = knn.findNearest(newcomer, k)
plt.show()
K-means
El algoritmo de las K-medias (K-means en inglés), presentado por MacQueen en 1967, es uno de
los algoritmos de aprendizaje no supervisado más simples para resolver el problema de la
clusterización. El procedimiento aproxima por etapas sucesivas un cierto número (prefijado) de
clusters haciendo uso de los centroides de los puntos que deben representar.
¿Cómo funciona?
Considera el conjunto de datos de la siguiente imagen. Se requiere clasificar estos datos en dos
grupos diferentes.
En el primer paso, el algoritmo escoge dos centroides. Tras esto, en el segundo paso, el
algoritmo calculará la distancia desde cada punto a cada uno de los centroides. Si uno de los datos
está más cerca, entonces ese dato se le asignará la etiqueta del centroide. Si por el contrario está
más cerca del centroide , en ese caso se le asignará la etiqueta del centroide .
En el tercer paso del algoritmo, para cada uno de los centroides, se realiza el cálculo de la media
de todos los puntos asignados a ese centroide, siendo ésta la nueva posición a la que se
desplazará el centroide (centro de masas).
Por último, los pasos dos y tres serán iterados hasta que todos los centroides converjan a puntos
fijos. Además, también pueden aplicarse otros criterios de parada, como son el número máximo de
iteraciones o la obtención de una precisión determinada.
import numpy as np
import cv2
from matplotlib import pyplot as plt
# Convert to np.float32
Z = np.float32(Z)
# Define criteria
criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 10, 1.0)
# Attempts
attempts = 10
# Apply kmeans
if cv2.__version__.startswith('2.4'):
ret, label, center = cv2.kmeans(Z, K, criteria, attempts, cv2.KMEANS_RANDOM_
CENTERS)
else:
ret, label, center = cv2.kmeans(Z, K, None, criteria, attempts, cv2.KMEANS_R
ANDOM_CENTERS)
plt.subplot(211)
plt.scatter(A[:,0], A[:,1], c = 'gray')
plt.scatter(B[:,0], B[:,1], c = 'gray')
plt.xlabel('Height')
plt.ylabel('Weight')
plt.subplot(212)
plt.scatter(A[:,0], A[:,1])
plt.scatter(B[:,0], B[:,1], c = 'r')
plt.scatter(center[:,0], center[:,1], s = 80, c = 'y', marker = 's')
plt.xlabel('Height')
plt.ylabel('Weight')
plt.show()
Color Quantization
import numpy as np
import cv2
img = cv2.imread('images/machu_picchu.jpg')
Z = img.reshape((-1,3))
# Convert to np.float32
Z = np.float32(Z)
# Define criteria
criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 10, 1.0)
# Attempts
attempts = 10
# Apply kmeans
if cv2.__version__.startswith('2.4'):
ret, label, center = cv2.kmeans(Z, K, criteria, attempts, cv2.KMEANS_RANDOM_
CENTERS)
else:
ret, label, center = cv2.kmeans(Z, K, None, criteria, attempts, cv2.KMEANS_R
ANDOM_CENTERS)