Acceso directo a píxeles
Cuando se trabaja en procesado de imágenes, más tarde o más temprano llega el momento en que las funciones que proporciona la librería (en este caso, OpenCV) se revelan insuficientes para hacer lo que el programador quiere hacer. No queda más remedio, en estos casos, que acceder directamente a los píxeles de la imagen y modificarlos a mano.
El acceso directo a los píxeles no tiene nada de malo. Sin embargo, la mejor recomendación que se puede hacer es la siguiente: merece la pena perder un poco de tiempo buscando alguna función en OpenCV que sirva para lo que se quiere hacer. ¿Por qué? (1) Porque es más fácil equivocarse al acceder a los píxeles que al usar una función, y (2) porque la función de OpenCV, normalmente, será más eficiente que el código que nosotros podamos generar.
Sólo si:
No se encuentra ninguna función que sirva, tras un tiempo de búsqueda razonable.
Se encuentra una función que parece que sirve, pero requiere una preparación de las imágenes (preprocesado y postprocesado) demasiado complicada.
Se encuentra una función que sirve, se utiliza... pero resulta poco eficiente (por ejemplo, porque produce resultados adicionales que a nosotros no nos interesen).
Sólo en estos casos será razonable plantearse el acceso directo a los píxeles.
ACCESO A UN PÍXEL DE UNA IMAGEN
Llegados al punto en que hay que tocar los píxeles de la imagen directamente, hay que tener siempre muy presente lo siguiente:
Los píxeles se almacenan en el atributo uchar *data de la imagen. Esto es un puntero simple, que apunta a un array de bytes, al que habrá que acceder teniendo en cuenta las fórmulas que se han dado en un apartado anterior.
Hay que tener mucho cuidado con los límites de la imagen. Salirse de esos límites cuando se recorre el array data producirá errores: violaciones de segmento en el mejor de los casos; en el peor, se escribirá en zonas indeseadas o se leerá basura sin que el programa se dé cuenta.
En este curso, supondremos siempre que la profundidad de canal es de un byte. Es el caso más normal, y evitará complicaciones adicionales en el acceso a píxeles.
Dada una imagen, imageA, con los siguientes parámetros:
Altura: height.
Anchura: width.
Número de canales: k.
Profundidad de canal: ya lo hemos dicho... siempre la consideramos de un byte en este curso.
Para acceder al canal 0 del píxel en la fila i, columna j, se utilizará la siguiente instrucción:
imageA.data[ width*k*i + k*j ]; // accede al canal 0 del píxel
Para acceder al canal 2 del píxel en la fila i, columna j, se utilizará la siguiente instrucción:
imageA.data[ width*k*i + k*j + 2 ]; // accede al canal 2 del píxel
ACCESO A VARIOS PÍXELES DE UNA IMAGEN
Puede ser necesario acceder a un cierto grupo de píxeles en la imagen... o a toda la imagen. En este caso, se suele utilizar una estructura de bucles for anidados, que van recorriendo las filas y las columnas. Se define así una estructura rectangular en la imagen, cuyos píxeles se van recorriendo ordenadamente.
El siguiente fragmento de código muestra un ejemplo de uno de estos bucles, en el que se va recorriendo una imagen completa de tamaño 800x600, con 3 canales, y poniendo a 0 los canales de los píxeles cuyo valor original fuese de menos de 50:
// i recorre las filas, j las columnas, en una imagen, imageA, de 3 canales de 800x600
for (int i=0; i<600; i++)
{
for (int j=0; j<800; j++)
{
for (int k=0; k<3; k++)
{
if ( imageA.data[i*800*3 + j*3 + k] < 50 )
{
imageA.data[i*800*3 + j*3 + k] = 0; // si el canal vale menos de 50, se pone a 0
}
}
}
}
Otro ejemplo de acceso a píxeles de la imagen puede encontrarse en el código C++ que puede descargarse pinchando aquí. Este código, basado en el código de ejemplo de la Unidad Práctica 1, recorre los píxeles de la imagen, y va invirtiendo los valores de todos sus canales. El resultado de esa operación se muestra en la siguiente figura: