Visión artificial

Text
0
Kritiken
Leseprobe
Als gelesen kennzeichnen
Wie Sie das Buch nach dem Kauf lesen
Schriftart:Kleiner AaGrößer Aa

5.2 GESTIÓN DE EVENTOS DEL TECLADO

En este apartado aprenderá a gestionar los eventos del teclado, que utilizará básicamente para capturar la pulsación de una tecla. Para ello, OpenCV ofrece la función:

waitKey(milisegundos)

Su único argumento fija el número de milisegundos que estaría bloqueado el programa en este punto hasta que se pulsara cualquier tecla. Si su valor fuera 0, sería de forma indefinida.

El resultado de dicha función es el código Unicode de la tecla pulsada (o -1, si transcurrido el número de milisegundos indicado, no se hubiera pulsado ninguna).


Para llamar a esta función, es necesario que al menos haya una ventana activa.

En el siguiente programa, se amplía el de la pizarra electrónica del apartado anterior para poder salir de la aplicación pulsando las teclas ‘Esc’ o ‘q’. Además, al presionar el retorno de carro, pasará alternativamente del modo dibujo al de borrado. El código es el siguiente:


Solo se explicarán las diferencias con el código del programa anterior, empezando por la declaración inicial de variables, ya que se ha añadido una de tipo booleano (borrado), cuyo valor True indicaría que la aplicación está en modo borrado. Inicialmente tiene el valor False porque se comienza en modo dibujo.

borrado = False

Lo realmente novedoso de este nuevo programa es el bucle while, cuya condición True indica que podría estar ejecutándose indefinidamente.


Dentro, lo primero que se hace es llamar a la función waitKey() para esperar cierto tiempo (se ha puesto 100 ms, aunque podría ser otro diferente) hasta que se pulse una tecla. A continuación, hay una serie de condiciones que detectan si se ha pulsado el retorno de carro para cambiar del modo dibujo al de borrado o viceversa, o las teclas con las que se quiere abandonar la aplicación (‘Esc’ o ‘q’). En este último caso únicamente se ejecutaría la sentencia break, que permite salir del bucle y, en consecuencia, de la aplicación.



El código Unicode del retorno de carro es 13, mientras que el de la tecla ‘Esc’ es 27.

Si se pulsara retorno de carro, lo primero que se haría es cambiar de modo (invertir el valor de la variable borrado) y, en función de este, modificar los valores de las variables que establecen el color del trazo (color) y su grosor (grosor). En el modo borrado, el color del trazo será el mismo que el del fondo (blanco), cuyo efecto será la eliminación de cualquier línea roja que se hubiera dibujado previamente. Además, se amplía el grosor del trazo para facilitar dicho borrado.



Una vez fuera del bucle, lo que se hace es cerrar la ventana con destroyAllWindows().

cv2.destroyAllWindows()

Ya solo queda probarlo. Esta vez, podrá corregir los trazos que no le gusten.

Unidad 6
OPERACIONES BÁSICAS DE MANEJO DE IMÁGENES

En este capítulo aprenderá cómo utilizar el color de los píxeles de una imagen. También estudiará cómo seleccionar áreas de interés, sobre las que podrán aplicarse posteriormente técnicas específicas de análisis de imágenes. La creación de regiones de interés es importante, por ejemplo, para la identificación de la matrícula de un coche aplicando técnicas de OCR (optical character recognition, reconocimiento óptico de caracteres), que requiere previamente extraer de la imagen original el área en la que se encuentra. Lo mismo sucede cuando se pretende localizar los ojos o la sonrisa de una persona, proceso que solo puede llevarse a cabo sobre la imagen de una cara, que previamente deberá haber sido identificada y separada de la principal.


El área de una imagen que tiene información relevante se conoce con el acrónimo ROI (region of interest – región de interés)

También resulta de gran utilidad la operación de escalado, ya que se emplea en procesos de comparación de imágenes. Así, por ejemplo, para saber si un objeto se corresponde con el buscado, antes de confrontar las imágenes de ambos, una de ellas deberá escalarse al tamaño de la otra.

Por último, aprenderá a realizar operaciones de adición y sustracción de imágenes, que ofrecen la posibilidad de mezclarlas u obtener sus diferencias. Además de la edición de imágenes, entre sus múltiples aplicaciones prácticas están, por ejemplo, el desarrollo de sistemas de detección de movimiento o control de calidad.

6.1 OBTENCIÓN DEL COLOR DE UN PÍXEL

En este apartado conocerá cómo extraer y utilizar el color asociado a los píxeles de una imagen. Sus aplicaciones prácticas son muy diversas, desde la modificación del aspecto de la imagen, pasando por la identificación de objetos, hasta el seguimiento de sus movimientos en escena mediante técnicas de color tracking que estudiará más adelante.

Cuando dio los primeros pasos en el manejo de imágenes, aprendió cómo acceder a un píxel para obtener o modificar su valor. Incluso hizo una práctica en la que utilizaba estos conocimientos para dibujar una rejilla, tanto en blanco y negro como en color. En dichas prácticas se asignaba un color a un píxel, pero ¿cómo se almacena realmente en una matriz ndarray? Para entenderlo, se recupera la imagen utilizada en dicho capítulo, pero, en vez de mostrar un píxel de color negro, la posición (3, 1) estará ocupada por otro de color rojo.


La sentencia que le permitiría obtener el color de este punto es:

color = img[1, 3]

Podría llegar a pensar que, en la expresión anterior, la variable color tomaría como valor una tupla o una lista con los tres colores del píxel, en concreto:

[0, 0, 255]

Sin embargo, el color de los píxeles se almacena como un array NumPy, es decir, de la siguiente forma:

[0 0 255]


Observe que los tres componentes de color no están separados por comas.

Por lo tanto, para obtener los colores primarios que componen un píxel, previamente deberá convertirlo a una lista con el método:

tolist()

Así, las sentencias que tendría que utilizar para obtener dichos colores serían:



Si la imagen fuera en blanco y negro, el valor del píxel sería el nivel de luz, por lo que no sería necesario utilizar este método.

Con el fin de practicar esta forma de obtener el color de un píxel, desarrollará un programa que, al pulsar el ratón sobre un punto de la imagen, aparecerá un círculo con el color del píxel seleccionado. Su código es el siguiente:


Una vez importada la librería OpenCV, se carga la imagen del cuadro de la niña utilizada en prácticas anteriores. Como novedad, ahora se hace una copia de dicha imagen. Para ello, se utiliza el comando copy() de Python. Esta copia, que tiene la imagen originalmente cargada del archivo, se utilizará para volver a mostrarla una vez que se deje de presionar el ratón. Cuando se pulsó, quedó modificada con un círculo del color del píxel en el que estaba situado.



Si se hubiera utilizado la sentencia de asignación:

img_original = img

La variable img_original no contendría una copia de la imagen contenida en img, sino que ambas variables compartirían la referencia al mismo objeto (imagen). Eso significa que, cuando se modificara una de dichas variables, también cambiaría la otra, ya que ambas comparten el mismo valor (apuntan al mismo objeto).

 

Una vez cargada la imagen, se muestra con la función imshow().

cv2.imshow(‘Cuadro’, img)

Saltando al final de programa, se encuentran las sentencias que asignan a la función color() el manejo de los eventos del ratón.

cv2.setMouseCallback(‘Cuadro’,color)

Por último, se espera a que se pulse cualquier tecla para cerrar la ventana y finalizar la ejecución del programa.


Como habrá supuesto, el código principal está situado en la función color(), dentro de la cual lo primero que se hace es definir la variable img como global, ya que se quiere utilizar fuera de la estructura de control (un if) en la que se modifica. A continuación, se discrimina si se ha pulsado el botón izquierdo del ratón o se ha dejado de pulsar, ya que en cada caso se realizarán tareas diferentes (las verá más adelante). Finalmente, se muestra la imagen con las modificaciones realizadas, que será la incorporación (o desaparición) de un círculo cuyo color de fondo sea el del píxel sobre el que se ha pulsado.


Si se hubiera pulsado el botón izquierdo del ratón, se obtendría el color del píxel en el que estuviera situado, cuyas coordenadas son los argumentos x, y de la función. A continuación, se dibujaría un círculo cuyo centro estuviera en dichas coordenadas, con un radio de 40 píxeles y, como color de fondo, el del píxel seleccionado. Esto último se consigue asignando el valor -1 al último argumento de la función circle().


Cuando se deje de pulsar el botón, lo único que se hace es volver a restaurar de nuevo la imagen original, de manera que desaparece el círculo dibujado.


Ejecute el programa y observe los diferentes tonos de color de cada zona de la imagen. Así, por ejemplo, más abajo puede ver el color de una de las franjas azules del tirante del bañador de la niña.


6.2 RECORTE DE REGIONES

Hay veces que es necesario centrarse en determinadas áreas de una imagen. Por ejemplo, si quisiera saber si una persona está sonriendo, primero tendría que localizar la región en la que está situada su cara para, después de aislarla, aplicar las técnicas de procesamiento y análisis específicas, que solo funcionarían en dichas áreas de interés.

Con el fin de entender cómo se recorta una imagen, observe la siguiente figura, en la que se pretende seleccionar los píxeles componentes del área marcada.


La forma de extraer los píxeles contenidos en la región sombreada es mediante la siguiente expresión, cuyas coordenadas se corresponden con las esquinas inferior derecha y superior izquierda:

imagen[y1:y2, x1:x2]

Por lo tanto, si la imagen de más arriba estuviera almacenada en la variable img_original, la forma de recortar la zona señalada sería:

img_recortada = img_original[2:4, 1:3]

Con el objeto de practicar esta técnica, va a desarrollar un programa que le permita fijar el área de una imagen. Al mover el ratón con el botón izquierdo pulsado, aparecerá un rectángulo de color rojo que delimitará la zona en cuestión. Cuando deje de pulsarlo, se extraerán los píxeles encerrados dentro de dicho rectángulo para crear una nueva imagen, que se mostrará en otra ventana. El código es el siguiente:


Después de importar la librería OpenCV, se declaran las variables que fijan el color (color) y el grosor (grosor) del rectángulo con el que se va a delimitar la zona que hay que recortar.


La siguiente variable establece el ancho mínimo que deberá tener esta área (ancho_min). La necesidad de esta última variable se debe a que, en Windows, la cabecera de una ventana tiene tres iconos: el de la aplicación, otro para maximizarla y el aspa que permite cerrarla. Situados, uno al lado del otro, ocupan (aproximadamente) 125 píxeles, lo que hace que este sea el ancho mínimo de una ventana. Si el área de la imagen seleccionada fuera menor, el resto de la ventana mostraría información basura. Para evitarlo, dicha variable se utilizará más adelante en una condición que solo permitirá recortes con ese ancho mínimo.

min_ancho = 125

A continuación, se carga la conocida imagen del cuadro de la niña, de la que se hace una copia (img_original). El motivo es porque, cada vez que se modifique el área que hay que recortar (enmarcada por un rectángulo de color rojo), se tendrá que borrar el rectángulo que limitaba la seleccionada anteriormente. La forma de hacerlo es restaurando la imagen original a partir de esta copia.


Después, se muestra la imagen.

cv2.imshow(‘Cuadro’, img)

Ahora, vaya al final del programa. Allí encontrará la sentencia que asigna la gestión de eventos del ratón a la función region().

cv2.setMouseCallback(‘Cuadro’,region)

Las últimas líneas de código esperan que se pulse cualquier tecla para cerrar todas las ventanas (puede haber dos, la original y la del recorte) y salir del programa.


Como cabía esperar, el código principal del programa está en la función region(), encargada de la gestión de eventos del ratón. En dicha función, se definen como globales las variables que deberán compartirse cada vez que se llame. Luego, se comprueba si el evento es el producido al pulsar el botón izquierdo del ratón (EVENT_LBUTTONDOWN), su movimiento mientras sigue pulsado (EVENT_MOUSEMOVE) o su liberación (EVENT_LBUTTONUP). Por último, se muestra la imagen modificada (aquella en la que aparece la nueva región seleccionada en ese instante) con la función imshow().



Si lo que se ha detectado es la pulsación del botón izquierdo del ratón, la posición del píxel en el que se ha producido (contenida en los argumentos x, y) se convierte en la esquina superior izquierda del área que hay que recortar (x1 e y1).


La siguiente condición detecta si se mueve el ratón mientras se mantiene pulsado el botón izquierdo. Por eso, se comprueba que, además de que el evento producido sea el de dicho movimiento (EVENT_MOUSEMOVE), el argumento flags tenga el valor EVENT_FLAG_LBUTTON, lo que indica que el botón izquierdo sigue pulsado. Si se cumpliera dicha condición, se cargaría una copia de la imagen original. Con esto se consigue (de forma indirecta) borrar el perímetro del área dibujado anteriormente, de manera que se deja la imagen preparada para trazar el nuevo. La esquina superior izquierda del nuevo rectángulo seguiría siendo la misma en la que se pulsó el ratón, mientras que la inferior derecha corresponderá a las coordenadas en las que se encuentra en cada momento (contenidas en los argumentos x, y).


Cuando se deje de pulsar el botón izquierdo del ratón (se genera el evento EVENT_LBUTTONUP), se obtendrán los píxeles contenidos dentro de los límites del rectángulo dibujado, que se utilizarán para crear una nueva imagen que se mostrará en otra ventana (img_recortada). Pero antes de hacerlo, se comprueba que las coordenadas de la esquina inferior derecha sean mayores que las de las de la superior izquierda (los píxeles de una imagen se recorren en orden creciente) y que el ancho de la región sea superior al de la ventana más pequeña que se puede crear en Windows. Si se cumplieran todas estas condiciones, se recortaría la imagen tal como se ha explicado anteriormente y se mostraría en otra ventana, cuyo título es “Recorte”.


En la siguiente imagen puede ver el resultado obtenido tras seleccionar la cara de la niña, que, una vez recortada, se muestra en otra ventana a su derecha.


6.3 ESCALADO

El escalado permite obtener una imagen con un tamaño diferente al original. Esto es especialmente importante cuando se aplican técnicas de comparación de imágenes, las cuales requieren que sean de las mismas dimensiones.

La función utilizada para el escalado de una imagen es:

resize(imagen, tamaño)

El primer argumento es la imagen que se va a escalar, mientras que el segundo es una tupla que fija el ancho y alto de la imagen escalada.

Esta función admite un tercer argumento opcional, que determina el método de interpolación que se va a utilizar. Su valor podrá ser una de las siguientes constantes:

• INTER_AREA. Úselo cuando quiera reducir el tamaño de la imagen.

• INTER_LINEAR. Recomendado para hacer zoom. Es el método de interpolación predeterminado en OpenCV.

• INTER_CUBIC. Es el más eficiente (aunque el más lento).

Para practicar con esta nueva función, va a reducir a la mitad la imagen de Notre Dame con el siguiente código:


Tras importar la librería OpenCV y cargar la imagen, se declaran las variables utilizadas en el programa. En concreto, el alto y ancho de la imagen (alto y ancho), que se obtiene del atributo shape; el nivel de escala (escala), que, como se pretende reducir a la mitad, tomará el valor 0.5; así como el ancho y alto que tendrá la imagen escalada (ancho_escalado y alto_escalado), que será el resultado de multiplicar el de la imagen original por la escala. Puesto que el número de píxeles de una imagen tiene que ser entero, deberá realizarse la operación de casting correspondiente con la función int():


La sentencia principal del programa es la que realiza el escalado con la función resize().

imagen_escalada = cv2.resize(img, (ancho_escalado, alto_escalado))

Las siguientes sentencias muestran la imagen original y la escalada en ventanas independientes.


Como ya sabe, las últimas sentencias esperan que se pulse una tecla para cerrar dichas ventanas y finalizar el programa.

 

A continuación, puede ver el resultado obtenido tras la ejecución de este programa. Experimente con él, modificando el valor de la variable escala, o añada un método de interpolación diferente al utilizado por defecto, para analizar así la calidad de la imagen escalada.


6.4 ADICIÓN

Si dispone de dos imágenes, es posible crear otra nueva combinando ambas con la función:

add(imagen1, imagen2)

La imagen devuelta por esta función es aquella en la que el valor de cada píxel es la suma de los valores del correspondiente en las imágenes pasadas como argumento (si la imagen fuera en color, de cada uno de los componentes de color por separado). Esto podría expresarse como:

píxel imagen compuesta = píxel imagen1 + píxel imagen2

Otra forma de sumar imágenes es aplicando un peso a cada una de ellas, de forma que, cuanto menor sea este, menos influencia tendrá en la composición final. Es como si fuera más transparente. Para ello, la función utilizada sería:

addWeighted(imagen1, peso1, imagen2, peso2, valor gamma)

En este caso, el valor de cada uno de los píxeles de la imagen compuesta sería el resultado de aplicar la fórmula:

píxel imagen compuesta = píxel imagen1*peso1 + píxel imagen2*peso2 + valor gamma

Dicha fórmula determina una suma ponderada que establece la relevancia de cada una de las imágenes en la compuesta, a la que se añade un valor gamma que aumenta el nivel de luminosidad (su valor está en el rango 0-255).


Puesto que la operación de suma se realiza píxel a píxel, ambas imágenes deberán tener las mismas dimensiones y el mismo número de canales.

Para comprobar el resultado de esta función, observe las siguientes imágenes:


¿Quiere saber cuál sería el resultado de sumarlas? Escriba el siguiente código:


En este programa, después de importar la librería OpenCV, se cargan ambas imágenes para, posteriormente, sumarlas de forma ponderada con la función addWeighted(). Como puede observar, se fija el doble de peso a la imagen de la tierra (0.8) que a la de las estrellas (0.4), por lo que destacará más.

img = cv2.addWeighted(img1, 0.4, img2, 0.8, 0)

Una vez creada la imagen compuesta, se muestra, como viene siendo habitual, con la función imshow().

El resultado obtenido tras la ejecución de este programa se muestra a continuación. Pruebe a realizar distintas combinaciones de pesos. Los resultados que puede llegar a obtener son impactantes.


Sie haben die kostenlose Leseprobe beendet. Möchten Sie mehr lesen?