jueves, 8 de mayo de 2014

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
Descarga detector de figuras

Ver Categoria:

6 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