En el caso de las víctimas el color no nos interesa, dado que siempre son letras negras (H, S o U) sobre un cartel blanco. Por lo tanto, podemos convertir esa imagen a un modelo de escala de grises, para acentuar las diferencias y trabajar con menos datos. Para ello tenemos que utilizar el método cvtColor de opencv.
gris=cv2.cvtColor(imagen, cv2.COLOR_BGR2GRAY)
Donde pasamos como primer parámetro la imagen a convertir, y en el segundo caso el modelo de color al cual queremos llevarla. Este método nos devuelve la imagen en ese modelo. Por lo tanto, en gris tenemos imagen, pero convertida a escala de grises.
Aún convertida a grises, tengo un degradé bastante amplio (256 valores). Si quiero binarizar la imagen, es decir, convertirla a dos valores, uno para oscuro y otro para claro, podemos ejecutar el método threshold de opencv. Para ello, debo definir un umbral. Aquellos pixeles que están por debajo de ese umbral, se convertirán en 0 (negro), y aquellos que no, pasarán a tener un valor determinado que paso como parámetro. La sintaxis es así:
ret, thresh=cv2.threshold(gris, 140, 255, cv2.THRESH_BINARY)
En el primer parámetro indicamos la imagen a convertir. En el segundo, el valor del umbral. El tercero es el valor al cual quiero llevar los pixeles que superen los 140. Y el último me indica el tipo de conversión. Como dijimos anteriormente, nuestra conversión es binaria.
Hay diferentes formas de realizar el threshold. Por ejemplo, para manejar diferencias de luz en la imagen, podríamos tener un umbral dinámico según el contexto de los pixeles. Nosotros no tenemos ese problema, con lo cual usamos un mecanismo de umbral simple. Es decir, para cada pixel se aplica el mismo umbral.
Podemos ver como la imagen quedó completamente clara. El objetivo ahora es poder recortar el cartel, dejando de lado lo demás. De esta manera podremos detectar en forma sencilla la letra que representa. Para ello, vamos a pedirle a nuestro buen amigo opencv que detecte los contornos con la función findContours:
contornos, jerarquia = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
Me devuelve dos valores: en primer lugar, una lista de contornos, donde cada contorno a su vez es una lista de puntitos. En segundo lugar obtenemos un árbol de jerarquías, donde podemos saber qué contorno se encuentra dentro de cuál otro, o cuáles están al mismo nivel. Como no lo usaremos, no nos detendremos en analizarlo.
Con respecto a los parámetros, en el primer caso indicamos la imagen a procesar. El segundo parámetro indica cómo se elabora la jerarquía de contornos (árbol, todos iguales, sólo dos niveles, etc). En nuestro caso, nos interesan sólo los contornos externos, dado que queremos reconocer el cartel. El último parámetro nos permite definir el grado de aproximación al contorno: ¿guardo todos los puntos o tomo sólo los más representativos? Aquí hemos elegido la segunda opción. De todos modos, invitamos a seguir investigando en el siguiente link: Contornos y cómo dibujarlos en OpenCV.
Si una vez detectados, queremos dibujarlos sobre una imagen, utilizamos el siguiente método de openCV:
cv2.drawContours(imagen, contornos, -1, (0, 0, 255), 2)
En el primer parámetro indico sobre qué imagen se dibujarán. El segundo define la variable que tiene la lista de los contornos a dibujar. En el tercero puede indicar qué contorno dibujar. Si pongo -1, expreso que quiero todos. El cuarto parámetro es una tupla de 3 valores con la cantidad de azul, de verde y de rojo que tendrá mi línea. Y por último indico el grosor de la línea en pixeles.
¡Ya lo tenemos en nuestras manos! ¡Traigan la tijera!
Ejercicio 1: hacer una función que dada una imagen me devuelva los contornos si considera que es un cartel, o None si considera que no.