Detección de figuras geométricas

En tutoriales anteriores aprendimos a detectar líneas y círculos en una imagen, esta vez avanzaremos un poco mas y detectaremos las figuras geométricas básicas como: triángulos, cuadrados, círculos y pentágonos, haciendo uso de la librería opencv.

Previo a la detección de figuras geométricas aprenderemos a usar las funciones opencv que nos ayudaran a conseguir lo que deseamos.
 

Detección de contornos de una imagen

 
La función findContours devuelve un conjunto de puntos que representan el contorno de una imagen, esta imagen debe ser binaria, además findContours modifica la imagen por lo que debemos tener cuidado. Esta función nos servirá de mucho para la detección y reconocimiento de figuras geométricas y de otros objetos.
 
Una vez tengamos los contornos podemos mostrarlos usando la función drawContours esta se encargara de unir con líneas los puntos obtenidos por la función anterior, podemos indicar un grosor de línea en pixeles o indicar –1 para rellenar toda la figura.
 
Antes de buscar los contornos aplicaremos la función canny para buscar los bordes de la imagen. Le pasamos como parámetros la imagen de entrada, la de salida, un umbral máximo y uno mínimo.
 
Mat src = imread("star.png", 1);

// Mostrar la imagen original
imshow("Original", src);

Mat canny_output;
vector<vector<Point> > contours;
vector<Vec4i> hierarchy;

int thresh = 100;

// Detectar los bordes con un umbral min = 100 y max = 200 
Canny(src, canny_output, thresh, thresh * 2);

// Mostrar los bordes detectados con Canny
imshow("Bordes", canny_output);

// Buscar los contornos de la imagen, se almacenan en contours
findContours(canny_output, contours, hierarchy, CV_RETR_TREE, CV_CHAIN_APPROX_SIMPLE, Point(0, 0));

// Mostrar la Imagen modificada por la funcion findContours
imshow("Modificada", canny_output);

// Dibujar los contornos encontrados
Mat drawing = Mat::zeros(canny_output.size(), CV_8UC3);
for (size_t i = 0; i< contours.size(); i++)
{
 Scalar color = CV_RGB(0, 255, 0);
 drawContours(drawing, contours, (int)i, color, 2, 8, hierarchy, 0, Point());
}

// Mostrar la imagen final
imshow("Contours", drawing);

find draw contours

Detección de figuras simples

 
Para reconocer una figura geométrica haremos uso de la función approxPolyDP la cual usa el algoritmo de Douglas-Peucker para reducir el número de puntos que se aproxima a una curva. 
 
Si tiene tres vértices de salida sabemos que es un triangulo.
Para saber si es un rectángulo se deben cumplir los siguientes parámetros:
  • La función isContourConvex debe devolver true.
  • Debe tener 4 vértices.
  • Los ángulos deben ser de 90 grados.  
Para un hexágono el número de vértices debe ser 6 y los ángulos de 120 grados.
Para un circulo tenemos las siguientes condiciones:
  • Mas de 6 vértices.
  • Tener diámetro de igual dimensión en todas direcciones.
for (size_t i = 0; i < contours.size(); i++)
{
 // reducir el nuemro de puntos usando del algoritmo de Douglas-Peucker
 cv::approxPolyDP(cv::Mat(contours[i]), approx, cv::arcLength(cv::Mat(contours[i]), true)*0.02, true);

 // Si no cumple con estas condiciones es un objeto invalido
 if (std::fabs(cv::contourArea(contours[i])) < 100 || !cv::isContourConvex(approx))
  continue;

 // Es un triangulo
 if (approx.size() == 3)
 {
  setLabel(dst, "TRI", contours[i]); 
 }
 else if (approx.size() >= 4 && approx.size() <= 6)
 {
  // Obtener el numero de vertices
  int vtc = approx.size();

  // Obtener todos los angulos (cosenos)
  std::vector<double> cos;
  for (int j = 2; j < vtc + 1; j++)
   cos.push_back(angle(approx[j%vtc], approx[j - 2], approx[j - 1]));

  //Ordenar los angulos obtenidos
  std::sort(cos.begin(), cos.end());

  // Obtener los angulos maximos y minimos
  double mincos = cos.front();
  double maxcos = cos.back();

  //Obtener los angulos y determinar el tipo de figura segun los 
  //angulos obtenidos y el nuemero de vertices
  if (vtc == 4 && mincos >= -0.1 && maxcos <= 0.3)
   setLabel(dst, "RECT", contours[i]);
  else if (vtc == 5 && mincos >= -0.34 && maxcos <= -0.27)
   setLabel(dst, "PENTA", contours[i]);
  else if (vtc == 6 && mincos >= -0.55 && maxcos <= -0.45)
   setLabel(dst, "HEXA", contours[i]);
 }
 else
 {
  // Determinar si es un circulo
  double area = cv::contourArea(contours[i]);
  cv::Rect r = cv::boundingRect(contours[i]);
  int radius = r.width / 2;

  if (std::abs(1 - ((double)r.width / r.height)) <= 0.2 &&
   std::abs(1 - (area / (CV_PI * std::pow(radius, 2)))) <= 0.2)
   setLabel(dst, "CIR", contours[i]);
 }
}

detectar figuras geometricas simples
Descargar: detector de figuras.zip

Comentarios

  1. hola que tal oye ando haciendo algo parecido para detectar las placas de los carros, cuando usas lo de approxpolyDP, como obtuviste el valor: cv::arcLength(cv::Mat(contours[i]), true)*0.02 , como llegaste a eso?
    saludos

    ResponderEliminar
  2. en alguna parte se explica como iniciarse en opencv?

    ResponderEliminar
    Respuestas
    1. Creo que este es un buen link :p
      http://acodigo.blogspot.mx/2013/05/introduccion-opencv.html

      Eliminar
  3. Hola, estoy buscando como detectar los contornos de un defecto de una fruta y seleccionarlo ahi el contorno es irregular. utilizando libreria Opencv solo quiero ver que funcion utilizar ?

    ResponderEliminar
  4. Hola,en que software esta implementando la libreria opencv

    ResponderEliminar
  5. Hola,en que software esta implementando la libreria opencv

    ResponderEliminar
  6. amigo tengo un error en approx no me sale esa funcion

    ResponderEliminar
    Respuestas
    1. que includes tienes? agrega este include #include "opencv2/imgproc.hpp" es donde esta definida

      Eliminar
  7. tengo una imagen de 100 x 100. Bien mi imagen es una matriz, ahora a esta matriz de mi imagen, quiero sumarle otra matriz de 100 x 100

    ResponderEliminar
  8. La variable approx que tipo de variable es o donde esta declarada?? o como se declara??

    ResponderEliminar
    Respuestas
    1. std::vector approx;

      Esta es una variable usada para almacenar cada uno de los puntos que corresponden a las esquinas de la figura, descarga eco código completo para ver donde está declarada.

      https://www.dropbox.com/sh/ycgh9u8rjxb49jt/AAABbPgE5iuW2mfFe1b7K7nWa?dl=0&preview=Deteccion_de_figuras.rar

      Eliminar
  9. Hola, como podría hacer esto para solo dibujar los circulos?

    ResponderEliminar
    Respuestas
    1. Para buscar cículos usas cv::HoughCircle() puedes ver un ejemplo de esto en:
      http://acodigo.blogspot.com/2017/09/deteccion-de-lineas-y-circulos-usando.html

      Eliminar

Publicar un comentario

Temas relacionados

Entradas populares de este blog

tkinter Grid

Conectar SQL Server con Java

JavaFX Uso de ComboBox

tkinter Canvas

JavaFX Reproducción de Audio y Video