上一篇中的方法存在的問題是矩形框不夠精確,而且效果不能達到要求
這里使用凸包檢測的方法,並將原來膨脹系數由20縮小到5,達到了更好的效果
效果圖:
效果圖:
代碼:
#include <opencv2/highgui/highgui.hpp> #include <opencv2/imgproc/imgproc.hpp> #include <iostream> #include <stdio.h> #include <stdlib.h> using namespace cv; using namespace std; //設置全局參數 Mat srcImage, srcGray; int thresh = 100; int max_thresh = 255; RNG rng(12345); Mat thresh_callback(int, void*) { Mat srcTemp = srcImage.clone(); Mat threMat; //輪廓檢測參數 vector<vector<Point> > contours; vector<Vec4i> hierarchy; //閾值化操作 threshold(srcGray, threMat, thresh, 255, THRESH_BINARY); //輪廓檢測 findContours(threMat, contours, hierarchy, CV_RETR_TREE, CV_CHAIN_APPROX_SIMPLE, Point(0, 0)); //凸包及缺陷檢測參數 vector<vector<Point> > pointHull(contours.size()); vector<vector<int> > intHull(contours.size()); vector<vector<Vec4i> > hullDefect(contours.size()); for (size_t i = 0; i < contours.size(); i++) { //Point類型凸包檢測 convexHull(Mat(contours[i]), pointHull[i], false); //int 類型凸包檢測 convexHull(Mat(contours[i]), intHull[i], false); //凸包缺陷檢測 convexityDefects(Mat(contours[i]), intHull[i], hullDefect[i]); } //繪制凸包及缺陷檢測 Mat drawing = Mat::zeros(threMat.size(), CV_8UC3); for (size_t i = 0; i < contours.size(); i++) { Scalar color = Scalar(rng.uniform(0, 255), rng.uniform(0, 255), rng.uniform(0, 255)); drawContours(drawing, contours, i, Scalar(255,255,255), -1, 8, vector<Vec4i>(), 0, Point()); drawContours(drawing, pointHull, i, Scalar(255,255,255), -1, 8, vector<Vec4i>(), 0, Point()); //繪制缺陷 size_t count = contours[i].size(); if (count < 300) continue; //設置凸包缺陷迭代器 vector<Vec4i>::iterator iterDefects = hullDefect[i].begin(); //遍歷得到4個特征量 while (iterDefects != hullDefect[i].end()) { Vec4i& v = (*iterDefects); //起始位置 int startidx = v[0]; Point ptStart(contours[i][startidx]); //終止位置 int endidx = v[1]; Point ptEnd(contours[i][endidx]); //內凸殼最遠的點缺陷 int faridx = v[2]; Point ptFar(contours[i][faridx]); //凸點之間的最遠點 int depth = v[3] / 256; //繪制相應的線與圓檢測結果 if (depth > 20 && depth < 80) { line(drawing, ptStart, ptFar, CV_RGB(255,255,255), 2); line(drawing, ptEnd, ptFar, CV_RGB(255,255,255), 2); } iterDefects++; } } return drawing; } Mat change(Mat src) { int cPointR,cPointG,cPointB,cPoint; for(int i=0; i<src.rows; i++) { for(int j=0; j<src.cols; j++) { cPointB=src.at<Vec3b>(i,j)[0]=src.at<Vec3b>(i,j)[0]; cPointG=src.at<Vec3b>(i,j)[1]=src.at<Vec3b>(i,j)[1]; cPointR=src.at<Vec3b>(i,j)[2]=src.at<Vec3b>(i,j)[2]; if(cPointR>250||cPointG>250||cPointB>250) { src.at<Vec3b>(i,j)[0]=0; src.at<Vec3b>(i,j)[1]=0; src.at<Vec3b>(i,j)[2]=0; } else { src.at<Vec3b>(i,j)[0]=255; src.at<Vec3b>(i,j)[1]=255; src.at<Vec3b>(i,j)[2]=255; } cPointB=src.at<Vec3b>(i,j)[0]=src.at<Vec3b>(i,j)[0]; cPointG=src.at<Vec3b>(i,j)[1]=src.at<Vec3b>(i,j)[1]; cPointR=src.at<Vec3b>(i,j)[2]=src.at<Vec3b>(i,j)[2]; } } return src; } ///矩形測距 int Distance(Rect rect1,Rect rect2) { // 用於判斷rect1在rect2的第三象限里 用於反轉X軸用 bool isInversion; // 保存兩個比較的點 Point point1; Point point2; // 判斷 rect1 在rect2的上面還是下面 也就是說是在第一、二象限還是在三四象限 if(rect1.y<rect2.y) { // 判斷rect1 在rect2的左邊還是右邊 也就是說是在 一象限還是二象限 isInversion= rect1.x<rect2.x; if(isInversion ) { // 取rect1的右上點 point1 = Point(rect1.x+rect1.width,rect1.y+rect1.height); // 取rect2的左下點 point2 = Point(rect2.x,rect2.y); } else { // 取rect1的左上點 point1 = Point(rect1.x,rect1.y+rect1.height); // 取rect2的右下點 point2 = Point(rect2.x+rect2.width,rect2.y); } } else { // 判斷rect1 在rect2的左邊還是右邊 也就是說是在 三象限還是四象限 isInversion = rect1.x>rect2.x; if(isInversion) { // 取rect2的右上點 point1 = Point(rect2.x+rect2.width,rect2.y+rect2.height); // 取rect1的左下點 point2 = Point(rect1.x,rect1.y); } else { // 取rect2的左上點 point1 = Point(rect2.x,rect2.y+rect2.height); // 取rect1的右下點 point2 = Point(rect1.x+rect1.width,rect1.y); } } // 做向量減法 Point dPoint = point2 -point1; // 如果反轉x軸 dPoint.x = isInversion? dPoint.x:-dPoint.x; // 如果這個向量在第三象限里 那么這兩個矩形相交 返回-1 if(dPoint.x<0&& dPoint.y<0) return -1; // 如果x<0 返回y if(dPoint.x<0) return dPoint.y; // 如果y小於0 返回x if(dPoint.y<0) return dPoint.x; // 返回這個向量的長度 return abs(sqrt((point1.x-point2.x)*(point1.x-point2.x)+(point1.y-point2.y)*(point1.y-point2.y))); } int main() { //freopen("stdout.txt","w",stdout); ///讀圖 //srcImage = imread("C:\\Users\\Administrator\\Desktop\\1-gl300c.png",1); //srcImage = imread("C:\\Users\\Administrator\\Desktop\\2-P330D.png",1); srcImage = imread("C:\\Users\\Administrator\\Desktop\\3-spark.png",1); Mat outImage=srcImage; if (!srcImage.data) return -1; ///腐蝕去噪處理 Mat erosion_dst,temp; int erosion_size=5; Mat element = getStructuringElement( MORPH_RECT,Size( 2*erosion_size + 1, 2*erosion_size+1 ), Point( erosion_size, erosion_size ) ); //腐蝕去噪處理參數 erode( srcImage,erosion_dst, element ); //腐蝕去噪處理 //imshow( "腐蝕去噪處理", erosion_dst ); ///像素變換 Mat change_dst=change(erosion_dst); srcImage=erosion_dst; ///轉灰度圖 cvtColor(srcImage, srcGray, CV_BGR2GRAY); blur(srcGray, srcGray, Size(3, 3)); ///凸包檢測 Mat image=thresh_callback(0, 0); //imwrite("C:\\Users\\Administrator\\Desktop\\image.png", image); ///轉單通道灰度圖 Mat imageSource; cvtColor(image,imageSource,CV_BGR2GRAY); blur(imageSource,image,Size(3,3)); threshold(image,image,0,255,CV_THRESH_OTSU); ///尋找最外層輪廓 vector<vector<Point> > contours0; vector<Vec4i> hierarchy0; findContours(image,contours0,hierarchy0,RETR_EXTERNAL,CHAIN_APPROX_NONE,Point()); cout<<contours0.size()<<endl; ///連接矩形區域 for(int i=0; i<contours0.size(); i++) { RotatedRect rect_i=minAreaRect(contours0[i]); Point2f P_i[4]; rect_i.points(P_i); int lable=0; for(int j=i+1; j<contours0.size(); j++) { RotatedRect rect_j=minAreaRect(contours0[j]); Point2f P_j[4]; rect_j.points(P_j); double recArea_i=contourArea(contours0[i]); double recArea_j=contourArea(contours0[j]); //cout<<"兩矩形坐標:("<<P_i[1].x<<","<<P_i[1].y<<") ("<<P_i[3].x<<","<<P_i[3].y<<") --> ("<<P_j[1].x<<","<<P_j[1].y<<") ("<<P_j[3].x<<","<<P_j[3].y<<") "; Rect r_j = rect_j.boundingRect(); Rect r_i = rect_i.boundingRect(); //cout<<"兩矩形面積:"<<recArea_i<<" -> "<<recArea_j<<" 距離:"<<Distance(r_i,r_j)<<" "; if(Distance(r_i,r_j)<=100&&(recArea_i<2500&&recArea_j<2500)) { lable=1; int minx=min(P_i[2].x,P_j[2].x); int maxx=max(P_i[3].x,P_j[3].x); int miny=min(P_i[2].y,P_j[2].y); int maxy=max(P_i[0].y,P_j[0].y); rectangle(image,Point(minx,miny),Point(maxx,maxy),Scalar(255,255,255),-1,1);//畫實心矩形 //cout<<"yes"; } //cout<<endl; } //cout<<"---------------------------------------------------"<<endl; if(lable==0&&contourArea(contours0[i])<110) rectangle(image,Point(P_i[1].x,P_i[1].y),Point(P_i[3].x,P_i[3].y),Scalar(0,0,0),-1,1); else rectangle(image,Point(P_i[1].x,P_i[1].y),Point(P_i[3].x,P_i[3].y),Scalar(255,255,255),-1,1); } //imwrite("C:\\Users\\Administrator\\Desktop\\image2.png", image); ///繪制輪廓 vector<vector<Point> > contours; vector<Vec4i> hierarchy; findContours(image,contours,hierarchy,RETR_EXTERNAL,CHAIN_APPROX_NONE,Point()); Mat imageContours=Mat::zeros(image.size(),CV_8UC1); //最小外接矩形畫布 for(int i=0; i<contours.size(); i++) { ///繪制輪廓 //drawContours(imageContours,contours,i,Scalar(0,0,0),1,8,hierarchy); ///繪制輪廓的最小外結矩形 RotatedRect rect=minAreaRect(contours[i]); Point2f P[4]; rect.points(P); int minx=min(P[1].x,P[2].x)+3; int maxx=max(P[3].x,P[0].x)-3; int miny=min(P[2].y,P[3].y)+3; int maxy=max(P[1].y,P[0].y)-3; rectangle(outImage,Point(minx,miny),Point(maxx,maxy),Scalar(0,0,0),2,1);//二值圖繪線 } imwrite("C:\\Users\\Administrator\\Desktop\\image.png", outImage); waitKey(0); return 0; }