#include "opencv2/opencv.hpp" #include <iostream> #include <math.h> #include <string.h> using namespace cv; using namespace std; int thresh = 50, N = 11; const char* wndname = "Square Detection Demo"; int calcdistance(Point_<int> &point1, Point_<int> &point2) { int x1 = point1.x; int y1 = point1.y; int x2 = point2.x; int y2 = point2.y; int dist = sqrt(pow(x2-x1,2)+pow(y2-x1,2)); return dist; } // 查找向量之間的角度余弦 // 從pt0->pt1和從pt0->pt2 static double angle( Point pt1, Point pt2, Point pt0 ) { double dx1 = pt1.x - pt0.x; double dy1 = pt1.y - pt0.y; double dx2 = pt2.x - pt0.x; double dy2 = pt2.y - pt0.y; return (dx1*dx2 + dy1*dy2)/sqrt((dx1*dx1 + dy1*dy1)*(dx2*dx2 + dy2*dy2) + 1e-10); //cos<a,b>=(ab的內積)/(|a||b|) } // 返回圖像上檢測到的正方形序列 // 序列存儲在指定的內存存儲器中 static void findSquares( const Mat& image, vector<vector<Point> >& squares ) { int h = image.rows; int w = image.cols; squares.clear(); Mat pyr, timg, gray0(image.size(), CV_8U), gray; // 縮小和放大圖像以濾除噪音 pyrDown(image, pyr, Size(image.cols/2, image.rows/2));//高斯降噪,並只取奇數行列縮小圖片 pyrUp(pyr, timg, image.size());//插入偶數行列,再次高斯降噪 vector<vector<Point> > contours; // 在圖像的每個顏色平面中查找正方形 for( int c = 0; c < 3; c++ ) { int ch[] = {c, 0}; mixChannels(&timg, 1, &gray0, 1, ch, 1); // 嘗試幾個閾值級別 for( int l = 0; l < N; l++ ) { if( l == 0 ) { Canny(gray0, gray, 0, thresh, 5); dilate(gray, gray, Mat(), Point(-1,-1)); } else { // apply threshold if l!=0: // tgray(x,y) = gray(x,y) < (l+1)*255/N ? 255 : 0 gray = gray0 >= (l+1)*255/N; } //注意_RETR_LIST參數指明最后只存了角點位置 findContours(gray, contours, RETR_LIST, CHAIN_APPROX_SIMPLE); vector<Point> approx; // test each contour //一次取一個輪廓判斷下是不是矩形 for( size_t i = 0; i < contours.size(); i++ ) { //approx存儲了近似后的輪廓 approxPolyDP(Mat(contours[i]), approx, arcLength(Mat(contours[i]), true)*0.02, true); if( approx.size() == 4 && //矩形必須是四個點 fabs(contourArea(Mat(approx))) > 5000 && isContourConvex(Mat(approx)) )//必須是凸的,咋理解???? { double maxCosine = 0; for( int j = 2; j < 5; j++ ) { // find the maximum cosine of the angle between joint edges double cosine = fabs(angle(approx[j%4], approx[j-2], approx[j-1])); maxCosine = MAX(maxCosine, cosine); } //依次計算1,2,3頂點的角余弦,最大的余弦值,對應三個角中的最小角,也就是三個角中,最不像直角的 //為什么是三個呢?三個中最不像直角的都接近直角了,剩下的自然也是直角 if( maxCosine < 0.3 ) if (calcdistance(approx[0],approx[3]) < h && calcdistance(approx[0],approx[1]) < w){ squares.push_back(approx); } } } } } } // 函數繪制圖像中的所有四邊形 static void drawSquares(Mat& image, const vector<vector<Point> >& squares) { for( size_t i = 0; i < squares.size(); i++ ) { const Point* p = &squares[i][0]; int n = (int)squares[i].size(); circle(image,squares[i][0],3,Scalar(0,0,255),1); circle(image,squares[i][1],3,Scalar(0,0,255),1); circle(image,squares[i][2],3,Scalar(0,0,255),1); circle(image,squares[i][3],3,Scalar(0,0,255),1); // polylines(image, &p, &n, 1, true, Scalar(0,255,0), 1, LINE_AA); // break; Rect rr = boundingRect(squares[i]); //通過最小包圍盒來獲取主要函數區域 vector<vector<Point>> main; main[0][0] = squares[0][0]; main[0][1] = squares[0][1]; main[0][2] = squares[1][2]; main[0][3] = squares[1][3]; circle(image,main[0][0],1,Scalar(0,255,0),-1); circle(image,main[0][1],1,Scalar(0,255,0),-1); imshow("main",image); RotatedRect minRect = minAreaRect(Mat(main[0])); Point2f vertex[4];//用於存放最小矩形的四個頂點 minRect.points(vertex);//返回矩形的四個頂點給vertex //繪制最小面積包圍矩形 vector<Point>min_rectangle; for (int i = 0; i < 4; i++) { line(image, vertex[i], vertex[(i + 1) % 4], Scalar(0, 255, 255), 1, 8);//非常巧妙的表達式 min_rectangle.push_back(vertex[i]);//將最小矩形的四個頂點以Point的形式放置在vector容器中 } // rectangle(image,Point(rr.x, rr.y), Point(rr.x + rr.width, rr.y + rr.height), 1); // float area = contourArea(squares[i], false); // cout << "area==" << area << endl; // break; } imshow(wndname, image); } int main(int /*argc*/, char** /*argv*/) { Mat image = imread("/home/leoxae/KeekoRobot/TestPic/大班/1.png"); vector<vector<Point>> squares; findSquares(image, squares); for (auto itx = squares.begin(); itx != squares.end(); itx++){ vector<Point> points = *itx; cout << "Pts=" << points << endl; } /* Point b_tl = squares[0][0]; Point b_tr = squares[0][1]; Point b_bl = squares[0][2]; Point b_br = squares[0][3]; Point s_tl = squares[1][0]; Point s_tr = squares[1][1]; Point s_bl = squares[1][2]; Point s_br = squares[1][3]; cout << "b_tl==" << b_tl << endl; cout << "b_tr==" << b_tr << endl; cout << "b_bl==" << b_bl << endl; cout << "b_br==" << b_br << endl; cout << "s_tl==" << s_tl << endl; cout << "s_tr==" << s_tr << endl; cout << "s_bl==" << s_bl << endl; cout << "s_br==" << s_br << endl; circle(image,b_bl,3,Scalar(0,0,255),-1); circle(image,b_tr,3,Scalar(0,0,255),-1); circle(image,b_bl,3,Scalar(0,0,255),-1); circle(image,b_br,3,Scalar(0,0,255),-1); circle(image,s_tl,3,Scalar(0,255,0),-1); circle(image,s_tr,3,Scalar(0,255,0),-1); circle(image,s_bl,3,Scalar(0,255,0),-1); circle(image,s_br,3,Scalar(0,255,0),-1);*/ // imshow("drawcircle",image); drawSquares(image, squares); waitKey(); return 0; }