OpenCV Búsqueda de Patrones (Template Matching)

La búsqueda de patrones es una técnica de análisis de imágenes que podemos utilizar para buscar una imagen dada (patrón o template) dentro de una imagen de mayor tamaño, no solo buscamos apariciones exactas del patrón también se permite un grado de variación respecto al patrón original.

Template matching (Comparación de plantillas) es uno de los métodos más utilizados en la búsqueda de patrones, con la biblioteca de computación visual OpenCV esta tarea la realiza la función cv::matchTemplate() la cual implementa con distintos métodos de comparación, puedes indicar estos métodos usando la enumeración cv::TemplateMatchModes.

Si la imagen fuente tiene la dimensión (W, H) y la imagen patrón (w, h) el tamaño de la imagen resultado será: (W – w + 1, H – h + 1), esta imagen estará a escala de grises, utilizamos la función cv::minMaxLoc() para obtener los valores máximos y mínimos, esta función nos devuelve también la posición de dichos valores.

messi_face

opencv template matching

Lo valores más claros indican una alta probabilidad de que el patrón se encuentre en esa posición, entre mas oscuro sea el área la probabilidad es menor, esto puede ser al inverso según el modo que estemos utilizando.

Búsqueda de patrones con OpenCV

Implementar la búsqueda de patrones con biblioteca de análisis de imágenes OpenCV es fácil ya que disponemos de todas las funciones necesarias para la tarea, veamos como se hace.

Preparar el cv::Mat para guardar el resultado de la operación.

// obtener las dimensiones para el cv::Mat resultado
int result_cols = img_src.cols - templ.cols + 1;
int result_rows = img_src.rows - templ.rows + 1;

Mat result(result_rows, result_cols, CV_32FC1);

Para aplicar la comparación de plantillas (match template), debemos indicar la imagen fuente (img_src), la imagen template o patrón (templ), el cv::Mat donde se almacenará el resultado (result), además de el modo (match_mode) utilizado por la función.

cv::TemplateMatchModes match_mode = cv::TemplateMatchModes::TM_CCOEFF_NORMED;

// aplicar match template y normalizar el resultado.
matchTemplate(img_src, templ, result, match_mode);
normalize(result, result, 0, 1, NORM_MINMAX, -1, Mat());

Usamos la función cv::minMaxLoc() para determinar los valores máximos o mínimos, junto con la posición de los mismos, según el modo que usemos nos interesarán los valores más claros o los más oscuros.

// obtener max y min junto con sus ubicaciones
minMaxLoc(result, &minVal, &maxVal, &minLoc, &maxLoc, Mat());

if (match_mode == TM_SQDIFF || match_mode == TM_SQDIFF_NORMED)
    matchLoc = minLoc;
else
    matchLoc = maxLoc;

Cuando obtenemos la ubicación del área deseada la marcamos dibujando un rectángulo en dicha posición, esto se hace de la siguiente manera:

// dibujar el rectangulo en la posicion encontrada
rectangle(img_src, matchLoc, Point(matchLoc.x + templ.cols, matchLoc.y + templ.rows), Scalar(0, 255, 0), 2, CV_AA, 0);

// mostrar las imagenes
imshow(result_window, result);
imshow(image_window, img_src);

Ejecutando el código con las siguientes imágenes, la imagen pequeña en la parte superior izquierda es la deseamos localizar: 

match template

Aplicando la función cv::matchTemplate() usando el modo TM_CCOEFF_NORMED:

opencv template matching

Al usar los modos TM_SQDIFF_NORMED o TM_SQDIFF nos interesa el punto mínimo, el contrario de los demás modos.

opencv minmaxloc

Si deseamos ser mas específicos al definir el patrón a buscar podemos utilizar una máscara, esta nos permite establecer de una manera más precisa el patrón que deseamos ubicar, la función cv::matchTemplate() en su último parámetro nos permite indicar la máscara a utilizar.

Debemos tener presente que no todos los modos soportan máscaras, además las imágenes patrón y máscara deben tener el mismo tamaño y tipo.

image

#include <opencv2\opencv.hpp>
#include <iostream>

using namespace std;
using namespace cv;

int main(int, char** argv)
{
    const char* image_window = "Source Image";
    const char* result_window = "Result window";

    Mat img_src = imread("../image.png",  CV_LOAD_IMAGE_COLOR);
    Mat templ = imread("../banana.png", CV_LOAD_IMAGE_COLOR);
    Mat mask = imread("../mascara.png", CV_LOAD_IMAGE_COLOR);

    // obtener las dimensiones para el cv::Mat resultado
    int result_cols = img_src.cols - templ.cols + 1;
    int result_rows = img_src.rows - templ.rows + 1;

    Mat result(result_rows, result_cols, CV_32FC1);

    // establecer el match template mode
    cv::TemplateMatchModes match_mode = cv::TemplateMatchModes::TM_SQDIFF;

    // aplicar match template con el modo indicado y normalizar el resultado
    matchTemplate(img_src, templ, result, match_mode, mask);
    normalize(result, result, 0, 1, NORM_MINMAX, -1, Mat());

    double minVal, maxVal;
    Point minLoc, maxLoc, matchLoc;

    // obtener max y min junto con sus ubicaciones
    minMaxLoc(result, &minVal, &maxVal, &minLoc, &maxLoc, Mat());

    if (match_mode == TM_SQDIFF || match_mode == TM_SQDIFF_NORMED)
        matchLoc = minLoc;
    else
        matchLoc = maxLoc;

    // dibujar el rectangulo en la posicion encontrada
    rectangle(img_src, matchLoc, Point(matchLoc.x + templ.cols, matchLoc.y + templ.rows), Scalar(0, 255, 0), 2, CV_AA, 0);

    imshow(image_window, img_src);
    waitKey(0);

    return 0;
}

image

Comentarios

Entradas populares de este blog

Conectar SQL Server con Java

Entrenar OpenCV en Detección de Objetos

Detección de figuras geométricas