Vision Maquina Con Opencv: básicos

OpenCv es una biblioteca en C++ la cual es ampliamente utilizada para tareas de visión máquina. Es tan extendida que ya cuenta con traducciones y llamadas en diferentes lenguajes. Uno de los más extendidos es Python, en este artículo voy a detallar un conjunto de operaciones básicas para iniciar el estudio y uso de OpenCV.

Instalación

$ sudo apt-get install python-opencv

Operaciones básicas con imágenes

Para operar con imágenes, lo primero que tenemos que poder hacer es cargarlas en nuestros programas, convertirlas entre diversos formatos, y llevar a cabo operaciones geométricas con ellas, como rotarlas, cambiar el tamaño, invertirlas y hacer transformaciones conformes.

Cargar una imágen

Para cargar una imágen en OpenCV utilizamos la siguiente sintaxis:

#!/usr/bin/env python3

import cv2

# load a test image

image = cv2.imread('lena.png')
cv2.imshow("image", image)
cv2.waitKey(0)

Convertir formatos entre imágenes

#!/usr/bin/env python3

import cv2

# Load png
image = cv2.imread('lena.png')
# Write jpg
outimg = cv2.imwrite('lena.jpg',image)

Cambiar tamaño

#!/usr/bin/env python3

import cv2

img = cv2.imread('lena.png')
# Downsize an image preserving the aspect ratio
print(img.shape)
resized = cv2.resize(img, (256, 256), interpolation = cv2.INTER_AREA)
cv2.imshow("Resized image", resized)
cv2.waitKey(0)
cv2.destroyAllWindows()
# Upsize an image preserving the aspect ratio
resized = cv2.resize(img, (768, 768), interpolation = cv2.INTER_AREA)
cv2.imshow("Resized image", resized)
cv2.waitKey(0)
cv2.destroyAllWindows()

Rotar

(h, w) = img.shape[:2] # Dimensiones de la imagen
(cX, cY) = (w // 2, h // 2) # Centro de la imagen
# grab the rotation matrix (applying the negative of the
# angle to rotate clockwise), then grab the sine and cosine
# (i.e., the rotation components of the matrix)
M = cv2.getRotationMatrix2D((cX, cY), -37, 1.0) 
cos = np.abs(M[0, 0])
sin = np.abs(M[0, 1])
# compute the new bounding dimensions of the image
nW = int((h * sin) + (w * cos))
nH = int((h * cos) + (w * sin))

# adjust the rotation matrix to take into account translation
M[0, 2] += (nW / 2) - cX
M[1, 2] += (nH / 2) - cY

# perform the actual rotation and return the image
rt_img = cv2.warpAffine(img, M, (nW, nH))
cv2.imshow('Rot 37', rt_img)
cv2.waitKey()
cv2.destroyAllWindows()

Traslación

# translation
# La traslacion se hara con la matriz 
# |1 0 nw|
# |0 1 nh|

h, w = img.shape[:2]
nh = h / 5
nh = 0
nw = h / 5

Tm = np.float32([[1, 0 , nw], [0, 1, nh]])
th = int(math.ceil(h + nh))
tw = int(math.ceil(w + nw))
tr_image = cv2.warpAffine(img, Tm, (tw, th))
# nueva El tamano de la imagen es importante controlarlo con la tupla final
cv2.imshow('Translated', tr_image)
cv2.waitKey()
cv2.destroyAllWindows()

Reflexión

# reflection (flipping)

fv_img = cv2.flip(img, 0)
cv2.imshow('Flip vertical', fv_img)
cv2.waitKey()
fh_img = cv2.flip(img, 1)
cv2.imshow('Flip horizontal', fh_img)
cv2.waitKey()
cv2.destroyAllWindows()

Shear (estirar)

# shear
Sm = np.float32([[1, 0.3, 0 ], [0, 1, 0]])
sh_image = cv2.warpAffine(img, Sm, (200, 200))
cv2.imshow('Sheared', sh_image)
cv2.waitKey()
cv2.destroyAllWindows()

Perspective Warp

import cv2
import numpy as np

image = cv2.imread("perspective-tutorial.png")
#rectangle = cv2.rectangle(image, (204, 422), (502, 84), (255, 0, 0), 3)
cv2.imshow("Original", image)
cv2.waitKey()
orig = np.array([[138, 245],[502, 84],[204, 422],[573, 265]], dtype="float32")
dest = np.array([[100, 100], [401,100], [100, 289],[401, 289]], dtype = "float32")

Pm = cv2.getPerspectiveTransform(orig, dest)
warped = cv2.warpPerspective(image, Pm, (max_width, max_height))
cv2.imshow("Corrected", warped)

Pasar a grayscale

gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
cv2.imshow('Gray Scale', gray)
cv2.waitKey()
ret, binary1 = cv2.threshold(gray,127,255,cv2.THRESH_BINARY)
cv2.imshow(' Simple Binary', binary1)
cv2.waitKey()
binary2 =  cv2.adaptiveThreshold(gray,255,cv2.ADAPTIVE_THRESH_MEAN_C, cv2.THRESH_BINARY,9,2)
cv2.imshow(' Mean Adaptive Binary', binary2)
cv2.waitKey()
cv2.destroyAllWindows()

Cuantización de colores

Para la cuantización de colores se utiliza el algoritmo de K-means el cual genera clusters de cada pixel y los convierte al color de su centroide. La filosofía aquí es limitar el número de colores de un número grande a un número menor, para facilitar el procesamiento posterior o el uso de memoria.

from sklearn.cluster import MiniBatchKMeans
import cv2
import numpy as np

image = cv2.imread("lena.jpg")
cv2.imshow("image", image)
cv2.waitKey()

# image = cv2.cvtColor(image, cv2.COLOR_BGR2LAB)
im_vec = image.reshape((image.shape[0]*image.shape[1], 3))

model = MiniBatchKMeans(n_clusters = 8)
labels = model.fit_predict(im_vec)
quant = model.cluster_centers_.astype("uint8")[labels]

quant_img = quant.reshape((image.shape[0], image.shape[1], 3))
# quant_img = cv2.cvtColor(quant_img, cv2.COLOR_LAB2BGR)

cv2.imshow("quantized image", quant_img)
cv2.waitKey()

Como entrenar el OCR de Tesseract

Tesseract es el estándar de facto para hacer OCR con software de código libre y abierto. Es un software bastante bueno y su entrenamiento en general da buenos resultados así como viene al descargarlo para documentos de uso general. De todas maneras, es importante que una vez que se va a utilizar en aplicaciones dedicadas de visión máquina podamos mejorar nuestros resultados.

Cómo mejorar los resultados de Tesseract?

Lo primero que dice Tesseract que debemos hacer es mejorar la calidad de la imágen y el tamaño de fuente. De su foro

Detección de objetos con Haar Cascade

  1. Colectar imágenes negativas
  2. Colectar imágenes positivas
  3. Crear un vector de imágenes positivas en OpenCV
  4. Entrenar cascada

Para los negativos tomar imágenes de fondo de cosas y lugares, estas regiones serán clasificadas como negativos en las fotos y deben ser recortadas. Para los positivos, si es una sola imágen se debe rotar, se le deben poner varios objetos, y se deben tener las coordenadas de donde se puso el objeto. Es mejor usar imágenes de fondo grandes y objetos pequeños. Para generar las imágenes lo mejor es greyscale, de 100x100, con diferentes fondos, las imágenes de entrenamiento se ponen en una carpeta

Para crear el vector de imágenes de entrenamiento

$ opencv_createsamples -info info/infor.txt -num <num_imgs> -w <ancho> -h <alto>
-vec positives.vec

Para entrenar la cascada

$ opencv_traincascade -data data -vec positives.vec -bg bg.txt -numPos <numpos> 
-numNeg <numneg> -numStages 10 -w <ancho> -h <alto>

Para usarlo:

cascade = cv2.CascadeClassifier('cascade.xml')
objeto = cascade.detectMultiScale(img, params)