Explorando la clase cv::Mat de OpenCV
Este tutorial es la continuación de la publicación conociendo la clase cv::Mat, en este curso continuaremos estudiando la clase y explorando las posibilidades que nos brinda la misma, aprenderemos como realizar operaciones aritméticas y lógicas con las matrices OpenCV y veremos cómo copiar una matriz, seleccionar un región de interés, entre otras cosas.
Seleccionar una región de interés
Una región de interés (ROI) es una porción de una matriz que podemos seleccionar para manipularla como si fuese un objeto Mat independiente, aunque los datos de la matriz estarán sincronizados, es decir los cambios en la ROI afectaran también a la matriz original, veamos un ejemplo:
Aplicamos el algoritmo que creamos en el tutorial anterior, solo que lo modificamos para aplicar el algoritmo a una región seleccionada de la imagen original, anteriormente creábamos otro cv::Mat
en el que guardamos la nueva imagen a escala de grises.
#include <iostream>
#include <opencv2/opencv.hpp>
using namespace cv;
using namespace std;
int main(int argc, char** argv)
{
Mat src = imread("../../../opencv-3.2.0/samples/data/lena.jpg");
int x = 100;
int y = 100;
int w = src.cols - (x * 2);
int h = src.rows - (y * 2);
Mat image = src(Rect(x, y, w, h));
for (int i = 0; i < image.rows; i++)
{
Vec3b* imgrow = image.ptr<Vec3b>(i);
for (int j = 0; j < image.cols; j++)
{
uchar B = imgrow[j][0];
uchar G = imgrow[j][1];
uchar R = imgrow[j][2];
uchar gray = (B + G + R) / 3;
imgrow[j][0] = gray;
imgrow[j][1] = gray;
imgrow[j][2] = gray;
}
}
imshow("Display Image", src);
waitKey(0);
return 0;
}
Para crear la ROI del objeto Mat llamado src usamos la siguiente sintaxis, src(cv::Rect)
donde cv::Rect(x, y, w, h)
crea el rectángulo que representa la región deseada, indicamos las coordenadas (x, y) además del ancho y alto (w, h).
Mat src = imread("../../../opencv-3.2.0/samples/data/lena.jpg");
int x = 100;
int y = 100;
int w = src.cols - (x * 2);
int h = src.rows - (y * 2);
Mat roi = src(Rect(x, y, w, h));
Sí hacemos algo como: Mat roi = src;
el objeto roi representa una región completa de la imagen original, es decir, ambos objetos Mat comparten la misma matriz de datos.
Máscaras en OpenCV
Las máscaras son imágenes binarias que nos servirán para seleccionar más detalladamente el área de una imagen en la que deseamos trabajar, usando una ROI seleccionamos una área rectangular, algorítmicamente podemos calcular si una posición (x, y) de un pixel se encuentra dentro de un área determinada, un circulo por ejemplo, pero si el área es más compleja es mejor utilizar una máscara, veamos un ejemplo:
Para a escala de grises el área que se muestra en la imagen superior usamos la siguiente máscara, solo se aplicará el algoritmo a las áreas cuyo color sea blanco, el área negra es omitida.
El código modificado para usar la máscara es el siguiente:
Mat src = imread("../lena.jpg");
Mat mask = imread("../mask.png", CV_LOAD_IMAGE_GRAYSCALE);
Mat roi = src(Rect(100, 100, 312, 312));
for (int i = 0; i < roi.rows; i++)
{
Vec3b* imgrow = roi.ptr<Vec3b>(i);
uchar* mskrow = mask.ptr<uchar>(i);
for (int j = 0; j < roi.cols; j++)
{
// verificar si el pixel esta dentro de la mascara
if (mskrow[j] >= 1) {
uchar B = imgrow[j][0];
uchar G = imgrow[j][1];
uchar R = imgrow[j][2];
uchar gray = (B + G + R) / 3;
imgrow[j][0] = gray;
imgrow[j][1] = gray;
imgrow[j][2] = gray;
}
}
}
Es posible usar una mascara para aplicar distintos niveles de intensidad, por ejemplo, la siguiente máscara:
Aplicando el siguiente algoritmo, en donde multiplicamos el color de los píxeles por la intensidad de la máscara.
#include <iostream>
#include <opencv2/opencv.hpp>
using namespace cv;
using namespace std;
int main(int argc, char** argv)
{
Mat src = imread("../lena.jpg");
Mat mask = imread("../mask.png");
for (int i = 0; i < src.rows; i++)
{
Vec3b* imgrow = src.ptr<Vec3b>(i);
Vec3b* mskrow = mask.ptr<Vec3b>(i);
for (int j = 0; j < src.cols; j++)
{
imgrow[j][0] = imgrow[j][0] * mskrow[j][0] / 256;
imgrow[j][1] = imgrow[j][1] * mskrow[j][1] / 256;
imgrow[j][2] = imgrow[j][2] * mskrow[j][2] / 256;
}
}
imshow("Display Image", src);
imshow("Display Mask", mask);
waitKey(0);
return 0;
}
Obtenemos el siguiente resultado:
Para aplicar el algoritmo correctamente las imagen original y la máscara deben tener el mismo tamaño.
Copiar un cv::Mat
Cuando deseamos tener una copia independiente de una objeto Mat debemos usar los métodos clone()
o copyTo(cv::OutputArray)
, se utilizan del siguiente modo:
Mat copy1 = src.clone();
Mat copy2;
src.copyTo(copy2);
Ambos métodos crean una copia de la matriz original.
Operaciones Lógicas
La clase Mat está preparada para soportar el uso de los operadores lógicos: AND, OR, NOT, XOR, también podemos aplicar estas operaciones usando las funciones: cv::bitwise_and
, cv::bitwise_or
, cv::bitwise_not
, cv::bitwise_xor
, usarlos es simple, por ejemplo:
Mat r1 = roi & mask; // AND
Mat r2 = roi | mask; // OR
Mat r3 = roi ^ mask; // XOR
Mat r4 = ~roi; // NOT
En este ejemplo hemos usado las imágenes anteriores, solo se muestran los resultados de las dos primeras operaciones.
Operaciones aritméticas
Del mismo modo podemos realizar operaciones aritméticas con las matrices, como: suma, resta, multiplicación, etc., estos operadores están sobre-cargados y además disponemos de varias funciones con otras operaciones útiles, como por ejemplo: t()
para calcular la matriz transpuesta, entre otras, visita la documentación de la clase cv::Mat para más información.
Mat a = Mat::eye(4, 4, CV_32F);
Mat b = Mat::ones(4, 4, CV_32F);
Mat r = a + b;
cout << "Matriz A: \n" << format(a, Formatter::FMT_CSV) << endl;
cout << "Matriz B: \n" << format(b, Formatter::FMT_CSV) << endl;
cout << "A + B: \n" << format(r, Formatter::FMT_CSV) << endl;
También podemos realizar las demás operaciones del mismo modo.
Comentarios
Publicar un comentario