with - OpenCV C++/Obj-C: detección avanzada de cuadrados
pattern recognition opencv (4)
Hace un tiempo, hice una pregunta sobre la detección de cuadrados y karlphillip dio un resultado decente.
Ahora quiero llevar esto un paso más allá y encontrar cuadrados que borde no son completamente visibles. Echale un vistazo a éste ejemplo:
¿Algunas ideas? Estoy trabajando con el código karlphillips:
void find_squares(Mat& image, vector<vector<Point> >& squares)
{
// blur will enhance edge detection
Mat blurred(image);
medianBlur(image, blurred, 9);
Mat gray0(blurred.size(), CV_8U), gray;
vector<vector<Point> > contours;
// find squares in every color plane of the image
for (int c = 0; c < 3; c++)
{
int ch[] = {c, 0};
mixChannels(&blurred, 1, &gray0, 1, ch, 1);
// try several threshold levels
const int threshold_level = 2;
for (int l = 0; l < threshold_level; l++)
{
// Use Canny instead of zero threshold level!
// Canny helps to catch squares with gradient shading
if (l == 0)
{
Canny(gray0, gray, 10, 20, 3); //
// Dilate helps to remove potential holes between edge segments
dilate(gray, gray, Mat(), Point(-1,-1));
}
else
{
gray = gray0 >= (l+1) * 255 / threshold_level;
}
// Find contours and store them in a list
findContours(gray, contours, CV_RETR_LIST, CV_CHAIN_APPROX_SIMPLE);
// Test contours
vector<Point> approx;
for (size_t i = 0; i < contours.size(); i++)
{
// approximate contour with accuracy proportional
// to the contour perimeter
approxPolyDP(Mat(contours[i]), approx, arcLength(Mat(contours[i]), true)*0.02, true);
// Note: absolute value of an area is used because
// area may be positive or negative - in accordance with the
// contour orientation
if (approx.size() == 4 &&
fabs(contourArea(Mat(approx))) > 1000 &&
isContourConvex(Mat(approx)))
{
double maxCosine = 0;
for (int j = 2; j < 5; j++)
{
double cosine = fabs(angle(approx[j%4], approx[j-2], approx[j-1]));
maxCosine = MAX(maxCosine, cosine);
}
if (maxCosine < 0.3)
squares.push_back(approx);
}
}
}
}
}
- convertir a espacio de laboratorio
- use kmeans para 2 clusters
- detectar suqares un clúster interno que resolverá muchas cosas en el espacio rgb
Intenté utilizar el convex hull method
que es bastante simple.
Aquí encontrará el casco convexo del contorno detectado. Elimina los defectos de convexidad en la parte inferior del papel.
A continuación se muestra el código (en OpenCV-Python):
import cv2
import numpy as np
img = cv2.imread(''sof.jpg'')
img = cv2.resize(img,(500,500))
gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
ret,thresh = cv2.threshold(gray,127,255,0)
contours,hier = cv2.findContours(thresh,cv2.RETR_LIST,cv2.CHAIN_APPROX_SIMPLE)
for cnt in contours:
if cv2.contourArea(cnt)>5000: # remove small areas like noise etc
hull = cv2.convexHull(cnt) # find the convex hull of contour
hull = cv2.approxPolyDP(hull,0.1*cv2.arcLength(hull,True),True)
if len(hull)==4:
cv2.drawContours(img,[hull],0,(0,255,0),2)
cv2.imshow(''img'',img)
cv2.waitKey(0)
cv2.destroyAllWindows()
(Aquí, no he encontrado cuadrado en todos los planos. Hágalo usted mismo si lo desea).
Debajo está el resultado que obtuve:
Espero que esto sea lo que necesitabas.
Puede intentar usar HoughLines para detectar los cuatro lados del cuadrado. Luego, ubique las cuatro intersecciones de líneas resultantes para detectar las esquinas. La transformada Hough es bastante robusta al ruido y las oclusiones, por lo que podría ser útil aquí. Además, here hay una demostración interactiva que muestra cómo funciona la transformación Hough (pensé que al menos era genial :). Here es una de mis respuestas anteriores que detecta un centro cruzado láser que muestra la mayor parte de la misma matemática (excepto que solo encuentra una esquina).
Es probable que tenga varias líneas en cada lado, pero la ubicación de las intersecciones debería ayudar a determinar los intrusos frente a los valores atípicos. Una vez que haya localizado las esquinas candidatas, también puede filtrar estos candidatos por área o por la forma "cuadrada" del polígono.
EDITAR: Todas estas respuestas con código e imágenes me hacían pensar que mi respuesta era un poco faltante. Entonces, aquí hay una implementación de cómo podrías hacer esto:
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <iostream>
#include <vector>
using namespace cv;
using namespace std;
Point2f computeIntersect(Vec2f line1, Vec2f line2);
vector<Point2f> lineToPointPair(Vec2f line);
bool acceptLinePair(Vec2f line1, Vec2f line2, float minTheta);
int main(int argc, char* argv[])
{
Mat occludedSquare = imread("Square.jpg");
resize(occludedSquare, occludedSquare, Size(0, 0), 0.25, 0.25);
Mat occludedSquare8u;
cvtColor(occludedSquare, occludedSquare8u, CV_BGR2GRAY);
Mat thresh;
threshold(occludedSquare8u, thresh, 200.0, 255.0, THRESH_BINARY);
GaussianBlur(thresh, thresh, Size(7, 7), 2.0, 2.0);
Mat edges;
Canny(thresh, edges, 66.0, 133.0, 3);
vector<Vec2f> lines;
HoughLines( edges, lines, 1, CV_PI/180, 50, 0, 0 );
cout << "Detected " << lines.size() << " lines." << endl;
// compute the intersection from the lines detected...
vector<Point2f> intersections;
for( size_t i = 0; i < lines.size(); i++ )
{
for(size_t j = 0; j < lines.size(); j++)
{
Vec2f line1 = lines[i];
Vec2f line2 = lines[j];
if(acceptLinePair(line1, line2, CV_PI / 32))
{
Point2f intersection = computeIntersect(line1, line2);
intersections.push_back(intersection);
}
}
}
if(intersections.size() > 0)
{
vector<Point2f>::iterator i;
for(i = intersections.begin(); i != intersections.end(); ++i)
{
cout << "Intersection is " << i->x << ", " << i->y << endl;
circle(occludedSquare, *i, 1, Scalar(0, 255, 0), 3);
}
}
imshow("intersect", occludedSquare);
waitKey();
return 0;
}
bool acceptLinePair(Vec2f line1, Vec2f line2, float minTheta)
{
float theta1 = line1[1], theta2 = line2[1];
if(theta1 < minTheta)
{
theta1 += CV_PI; // dealing with 0 and 180 ambiguities...
}
if(theta2 < minTheta)
{
theta2 += CV_PI; // dealing with 0 and 180 ambiguities...
}
return abs(theta1 - theta2) > minTheta;
}
// the long nasty wikipedia line-intersection equation...bleh...
Point2f computeIntersect(Vec2f line1, Vec2f line2)
{
vector<Point2f> p1 = lineToPointPair(line1);
vector<Point2f> p2 = lineToPointPair(line2);
float denom = (p1[0].x - p1[1].x)*(p2[0].y - p2[1].y) - (p1[0].y - p1[1].y)*(p2[0].x - p2[1].x);
Point2f intersect(((p1[0].x*p1[1].y - p1[0].y*p1[1].x)*(p2[0].x - p2[1].x) -
(p1[0].x - p1[1].x)*(p2[0].x*p2[1].y - p2[0].y*p2[1].x)) / denom,
((p1[0].x*p1[1].y - p1[0].y*p1[1].x)*(p2[0].y - p2[1].y) -
(p1[0].y - p1[1].y)*(p2[0].x*p2[1].y - p2[0].y*p2[1].x)) / denom);
return intersect;
}
vector<Point2f> lineToPointPair(Vec2f line)
{
vector<Point2f> points;
float r = line[0], t = line[1];
double cos_t = cos(t), sin_t = sin(t);
double x0 = r*cos_t, y0 = r*sin_t;
double alpha = 1000;
points.push_back(Point2f(x0 + alpha*(-sin_t), y0 + alpha*cos_t));
points.push_back(Point2f(x0 - alpha*(-sin_t), y0 - alpha*cos_t));
return points;
}
NOTA: La razón principal por la que cambié el tamaño de la imagen fue para poder verla en mi pantalla y acelerar el procesamiento.
Astuto
Utiliza la detección de bordes Canny para ayudar a reducir en gran medida el número de líneas detectadas después del umbral.
Transformada de Hough
Luego, la transformada Hough se usa para detectar los lados del cuadrado.
Intersecciones
Finalmente, calculamos las intersecciones de todos los pares de líneas.
¡Espero que ayude!
Primero : comience a experimentar con técnicas de umbral para aislar la hoja de papel blanco del resto de la imagen. Esta es una manera simple:
Mat new_img = imread(argv[1]);
double thres = 200;
double color = 255;
threshold(new_img, new_img, thres, color, CV_THRESH_BINARY);
imwrite("thres.png", new_img);
pero hay otras alternativas que podrían proporcionar un mejor resultado. Una es investigate inRange()
, y otra es detectar a través del color convirtiendo la imagen al espacio de color HSV.
Este hilo también proporciona una discusión de interés sobre el tema.
2º : después de ejecutar uno de estos procedimientos, puede intentar alimentar el resultado directamente en find_squares()
:
Una alternativa para find_squares()
es implementar la técnica de cuadro delimitador , que tiene el potencial de proporcionar una detección más precisa del área rectangular (siempre que tenga un resultado perfecto de umbral). Lo he usado here y here . Vale la pena señalar que OpenCV tiene su propio tutorial de cuadro delimitador .
Otro enfoque, además de find_squares()
, como señaló Abid en su respuesta, es usar el método convexHull . Consulte el tutorial de C ++ de OpenCV sobre este método para el código.