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:

ROI (región de interés) de una imagen en OpenCV

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:

Máscara en OpenCV

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.

imagen binaria máscara

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:

mask-lena

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:

Máscara con nivel de intensidad variable

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:

operaciones lógicas en OpenCV

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;

operaciones matemáticas con matrices en opencv

También podemos realizar las demás operaciones del mismo modo.

Comentarios

Entradas populares de este blog

Conectar SQL Server con Java

Gauss Seidel y Jacobi

Entrenar OpenCV en Detección de Objetos

Procesamiento de imágenes en OpenCV

Acceso a la webcam con OpenCV