Opencv 改进的外接矩形合并拼接方法
上一篇中的方法存在的问题是矩形框不够精确,而且效果不能达到要求
这里使用凸包检测的方法,并将原来膨胀系数由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; }