OpenCV C++ 简单小技巧
查找和绘制轮廓findContours 会找到vector<vector<cv::Point>> contours;vector<Vec4i> hierarchy;f4 = Mat::zeros(frame.rows, frame.cols, CV_8UC3);f5 = Mat::zeros(frame.rows, frame.cols, CV_8UC3);findContours(f2, contours, hierarchy, RETR_TREE, CHAIN_APPROX_SIMPLE, cv::Point());int idx=0;for( ; idx >= 0; idx = hierarchy[idx][0] ){ Scalar color( arc4random()&255, arc4random()&255, arc4random()&255 ); drawContours( f4, contours, idx, color, CV_FILLED, LINE_8, hierarchy ); drawContours( f5, contours, idx, color, 3, LINE_8, hierarchy );}// drawContours(f5, contours, -1, {0,255,0}, CV_FILLED, LINE_8);
threshold
morphologyEx+adaptiveThreshold如果在空mat上绘制,先把mat的区域设置好,填充满黑色。获得轮廓的矩形区域, 优化轮廓获得轮廓Moments m = moments(contours[idx]);质心int cx = int(m.m10/m.m00);int cy = int(m.m01/m.m00);面积 (面积小可以踢掉m.m00曲线优化 (epsilon百分比越低,越贴近原轮廓vector<cv::Point> approxCurve;double epsilon = 0.002*arcLength(contours[idx], true);approxPolyDP(contours[idx], approxCurve, epsilon, true);vector<vector<cv::Point>> poolApproxCurve;poolApproxCurve.push_back(approxCurve);drawContours( f5 , poolApproxCurve, 0, color, CV_FILLED, LINE_8 );
image.png凸包 hull将所有点囊括在内convexHull(contours[idx], approxCurve,false,true);for (int i=0; i<approxCurve.size(); i++) { circle(f3, approxCurve[i], 2, Scalar(255),30);}
image.png
image.png简化
image.png
image.png
image.png获得外切矩形 和 最小外切矩形(带旋转外框cv::Rect r = boundingRect(approxCurve);rectangle(f5, {r.x,r.y}, {r.x+r.width,r.y+r.height}, color,5);旋转外框cv::RotatedRect rr = minAreaRect(approxCurve);Mat boxPoints2f,boxPointsCov;boxPoints(rr, boxPoints2f);boxPoints2f.assignTo(boxPointsCov,CV_32S);polylines(f5, boxPointsCov, true,color,5);
image.png获得外切圆 和 外切椭圆外切圆Point2f pot;float radius;minEnclosingCircle(approxCurve, pot, radius);circle(f5, pot, radius, color,5);外切椭圆RotatedRect re = fitEllipse(approxCurve);ellipse(f5, re, color,5);
image.png拟合直线Vec4f lineData;fitLine(approxCurve, lineData, DIST_L2, 0, 0.01, 0.01);int lefty = (-lineData[2]*lineData[1]/lineData[0])+lineData[3];int righty = ((f5.cols-lineData[2])*lineData[1]/lineData[0])+lineData[3];line(f5, cv::Point(f5.cols-1,righty),cv::Point(0,lefty), color, 10);
image.pnghttps://stackoverflow.com/questions/14184147/detect-lines-opencv-in-object生成垂线double nx = 1;double ny = -lineData[0]/lineData[1];double mag = sqrt(1+ny*ny);lineData[0] = nx/mag;lineData[1] = ny/mag;lefty = (-lineData[2]*lineData[1]/lineData[0])+lineData[3];righty = ((f5.cols-lineData[2])*lineData[1]/lineData[0])+lineData[3];line(f5, cv::Point(f5.cols-1,righty),cv::Point(0,lefty), color, 10);
image.png凸缺陷 / 凹陷特征这里要注意的是 convexHull 这个函数输出hull特征时可以输出两种类型 vector<cv::Point> 和 vector<int>。如果用于之前凸包点显示则用point的数组, 如果用于凸缺陷则需要int数组,否则 convexityDefects 无法执行通过。输出的点也包含四个id信息,形状的位置点索引(开始点,结束点,以及凹陷内点)和深度(了解这个值的意义请留言)。另外做凸缺陷处理最好对目标简化,再进行,否则会出现很多数据。凸缺陷并非连续,他只是一些特征,如果是一个完全的圆,那么可能它没有任何缺陷,而矩形的缺陷就是它的四个边的中点。vector<int> hull2;convexHull(approxCurve, hull2,false,false);vector<Vec4i> defects;convexityDefects(approxCurve, hull2, defects);for (int i=0; i<defects.size(); i++) { Vec4i v = defects[i]; float depth = v[3]; if (depth > 10) { int startidx = v[0]; cv::Point ptStart(approxCurve[startidx]); int endidx = v[1]; cv::Point ptEnd(approxCurve[endidx]); int faridx = v[2]; cv::Point ptFar(approxCurve[faridx]); line(f3, ptStart, ptEnd, color, 5); circle(f3, ptFar, 30, color, -1); }}
image.pnghttps://stackoverflow.com/questions/31354150/opencv-convexity-defects-drawing比较两个轮廓NSLog(@"match %f",matchShapes(contours[idx], approxCurve, CONTOURS_MATCH_I1, 0));这里为进行比较了直接输出轮廓 和 简化后的轮廓比较,当两个相互接近时就趋近于0。轮廓的搜索方式类型方式图RETR_LIST将所有特征都列出来,无层次关系
image.pngRETR_EXTERNAL只返回外部轮廓
image.pngRETR_CCOMP只有一级父子, 内部如果有轮廓再生成父子关系
image.png
image.pngRETR_TREE所有等级关系[图片上传中...(image.png-3c7b3-1533915625653-0)]只遍历第一层 for(int idx=0 ; idx >= 0; idx = hierarchy[idx][0] )遍历所有轮廓 for (int idx=0; idx< contours.size(); idx++)扩展 (python代码,未进行验证)长宽比 w/hx,y,w,h = cv2.boundingRect(cnt)aspect_ratio = float(w)/h轮廓面积与边界矩形面积比 extentarea = cv2.contourArea(cnt)x,y,w,h = cv2.boundingRect(cnt)rect_area = w*hextent = float(area)/rect_area轮廓面积与凸包面积比 solidityarea = cv2.contourArea(cnt)hull = cv2.convexHull(cnt)hull_area = cv2.contourArea(hull)solidity = float(area)/hull_area获得轮廓面积相等的圆的直径area = cv2.contourArea(cnt)equi_diameter = np.sqrt(4*area/np.pi)方向(x,y),(MA,ma),angle = cv2.fitEllipse(cnt)最大最小值和它们的位置min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(imgray,mask = mask)平均颜色及平均灰度min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(imgray,mask = mask)极点leftmost = tuple(cnt[cnt[:,:,0].argmin()][0])rightmost = tuple(cnt[cnt[:,:,0].argmax()][0])topmost = tuple(cnt[cnt[:,:,1].argmin()][0])bottommost = tuple(cnt[cnt[:,:,1].argmax()][0]点到轮廓的最小距离第三个参数设置为true进行距离计算,false则判断返回+/-1和0这样的模糊量。dist = cv2.pointPolygonTest(cnt,(50,50),True)