索引地址:系列索引
一、准备
OpenCV 4.1.0 mingw 7.3 自编译版
Qt 5.12.4
二、前提
公司给出题目提取下面图片中中间的部分,并绘出拟合曲线。
三、开发
3.1 灰度化图像
代码:
1 2 3 4 5 6 7 8 9 cv::Mat grayImage (Mat srcImage) { Mat grayImage, tempImage; cvtColor (srcImage, tempImage, COLOR_RGB2GRAY); tempImage.copyTo (grayImage); tempImage.release (); imwrite ("gray.png" ,grayImage); return grayImage; }
3.2 采用canny算法提取轮廓
1 2 3 cv::Mat contours; cv::Canny (gray,contours,100 ,150 ); cv::imshow ("canny" ,contours);
其中参数是我以50为间隔,测试出来的
3.3 结果处理
可以看出中间的部分就是我需要的,但是左右两边和上方包含噪音,需要去除。
考虑到直线,那么就先查找所有的直线,然后从原图中删除直线,顺便把x直线周围的两个像素一起删除
消除方法是,按照像素对比两张图片,如果直线图中的像素原图中存在就赋值为0,否则保持原图值不变。
查找直线部分:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 cv::Mat linesImg = cv::Mat::zeros (contours.rows,contours.cols,contours.type ()); std::vector<cv::Vec2f> lines; cv::HoughLines (contours,lines,1 ,PI/180 ,60 ); std::vector<cv::Vec2f>::const_iterator it = lines.begin ();while (it!=lines.end ()){ float rho = (*it)[0 ]; float theta = (*it)[1 ]; if (theta<PI / 4.0 || theta>3.0 *PI / 4.0 ) { cv::Point pt1 (static_cast <int >(rho / cos(theta)), 0.0 ) ; cv::Point pt2 (rho / cos(theta) - contours.rows*sin(theta) / cos(theta), contours.rows) ; cv::line (linesImg, pt1, pt2, cv::Scalar (255 ,255 ,255 ), 1 ); } else { cv::Point pt1 (0 , rho / sin (theta)); cv::Point pt2 (contours.cols, rho / sin(theta) - contours.cols*cos(theta) / sin(theta)) ; cv::line (linesImg, pt1, pt2, cv::Scalar (255 ,255 ,255 ), 1 ); } it++; }imshow ("lines" ,linesImg);
消除噪音部分
1 2 3 4 5 6 7 8 9 10 11 12 uchar *pcontour,*plines;for (int i=0 ;i<contours.rows;i++){ pcontour = contours.ptr <uchar>(i); plines = linesImg.ptr <uchar>(i); for (int j=0 ;j<contours.cols;j++){ if (pcontour[j]==plines[j]){ pcontour[j]=0 ; pcontour[j-1 ]=0 ; } } } cv::imshow ("new result" ,contours);
然后图片就剩下两部分,下面的那一部分是我需要的
处理部分是,生成y轴像素投影图
投影部分
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 vector<Mat> GetHorizontalProjection (Mat src,Mat &dst) { vector<Mat>rois; dst = Mat::zeros (src.size (), CV_8UC1); vector<int >vectorH; vector<int >HUp; vector<int >HDown; for (int j = 0 ;j < src.rows;j++) { Mat data = src.row (j); int iCount = countNonZero (data); vectorH.push_back (iCount); } for (int k = 1 ;k < vectorH.size ();k++) { if (vectorH[k - 1 ] == 0 && vectorH[k] > 0 ) HUp.push_back (k); if (vectorH[k - 1 ] > 0 && vectorH[k] == 0 ) HDown.push_back (k); } for (int i = 0 ;i < vectorH.size ();i++) { if (vectorH[i] != 0 ) line (dst, Point (dst.cols,i ), Point (dst.cols - vectorH[i],i), Scalar (255 )); } for (int i = 0 ;i < HUp.size ();i++) { Mat roi = src (Rect (Point (0 , HUp[i]), Size (src.cols, HDown[i] - HUp[i]))); rois.push_back (roi); } return rois; }
可以看出中间部分有一段空白区域,以此为分界线,从下往上读取水平投影图,如果读取到20行空白值则把空白值以上部分的像素全部置为0
查找中间位置
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 bool status=false ;int count =0 ;int position =0 ;for (int i=temp.rows-1 ;i>=0 ;i--){ Mat data = temp.row (i); int iCould = cv::countNonZero (data); if (iCould){ if (count>=20 ){ position=i; break ; } else { count=0 ; status=true ; } } else if (iCould == 0 && status){ count++; } }
消除上面部分像素
1 2 3 4 5 6 7 8 uchar *p;for (int i=0 ;i<position;i++){ p = contours.ptr <uchar>(i); for (int j=0 ;j<contours.cols;j++){ p[j]=0 ; } }imshow ("canny result" ,contours);
3.4 拟合图像
可以看出,提取的图像左右对称,为了方便计算,我们取其中一半来处理。
1 2 3 4 5 Mat half = img (Rect (0 ,0 ,img.cols/2 ,img.rows));qDebug ()<<"half width is: " <<half.cols<<" height is: " <<half.rows; Mat t = half.clone ();imshow ("t" ,t);
拟合图像需要拟合点,我采用的是随机取点的方式,随机从图片中取25个有效点
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 int pCount=0 ; RNG rng; uchar *p; vector<Point> key_point;do { int x = rng.uniform (0 ,half.cols); qDebug ()<<"x=" <<x; p = half.ptr <uchar>(x); for (int y=0 ;y<half.rows;y++){ if (p[y]!=0 ){ qDebug ()<<"y=" <<y; key_point.push_back (Point (y,x)); circle (half,Point (y,x),3 ,Scalar (255 ,255 ,255 ),3 ,8 ,0 ); pCount++; y=half.rows-2 ; } } }while (pCount<=25 );imshow ("half" ,half);for (int i=0 ;i<key_point.size ();i++){ std::cout<<key_point.at (i)<<std::endl; }
拟合图像
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 cv::Mat A; detect->polynomial_curve_fit (key_point,3 ,A); std::vector<cv::Point> points_fitted; std::cout<<A<<std::endl;for (int x = 0 ; x < 400 ; x++) { double y = A.at <double >(0 , 0 ) + A.at <double >(1 , 0 ) * x + A.at <double >(2 , 0 )*std::pow (x, 2 ) + A.at <double >(3 , 0 )*std::pow (x, 3 ); points_fitted.push_back (cv::Point (x, y)); } cv::polylines (t, points_fitted, false , cv::Scalar (255 , 255 , 255 ), 1 , 8 , 0 );imshow ("t" ,t);
可以看出虽然左侧有点不符合,但是近似,可能是点的位置有点问题
四、测试
公司给了三张图片,我们用另外两张来测试
可以看出第二张图片因为拟合点的位置,曲线与原图有差异待改进。
参考资料太多,就不写了,基本上都是CSDN上面的,主要是整合。