查找和绘制轮廓

findContours 会找到

vector> contours;

vector 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<:point> approxCurve;

double epsilon = 0.002*arcLength(contours[idx], true);

approxPolyDP(contours[idx], approxCurve, epsilon, true);

vector> 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

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.png

https://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<:point> 和 vector。如果用于之前凸包点显示则用point的数组, 如果用于凸缺陷则需要int数组,否则 convexityDefects 无法执行通过。输出的点也包含四个id信息,形状的位置点索引(开始点,结束点,以及凹陷内点)和深度(了解这个值的意义请留言)。另外做凸缺陷处理最好对目标简化,再进行,否则会出现很多数据。凸缺陷并非连续,他只是一些特征,如果是一个完全的圆,那么可能它没有任何缺陷,而矩形的缺陷就是它的四个边的中点。

vector hull2;

convexHull(approxCurve, hull2,false,false);

vector defects;

convexityDefects(approxCurve, hull2, defects);

for (int i=0; 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.png

比较两个轮廓

NSLog(@"match %f",matchShapes(contours[idx], approxCurve, CONTOURS_MATCH_I1, 0));

这里为进行比较了直接输出轮廓 和 简化后的轮廓比较,当两个相互接近时就趋近于0。

轮廓的搜索方式

类型

方式

RETR_LIST

将所有特征都列出来,无层次关系

image.png

RETR_EXTERNAL

只返回外部轮廓

image.png

RETR_CCOMP

只有一级父子, 内部如果有轮廓再生成父子关系

image.png

image.png

RETR_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/h

x,y,w,h = cv2.boundingRect(cnt)

aspect_ratio = float(w)/h

轮廓面积与边界矩形面积比 extent

area = cv2.contourArea(cnt)

x,y,w,h = cv2.boundingRect(cnt)

rect_area = w*h

extent = float(area)/rect_area

轮廓面积与凸包面积比 solidity

area = 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)

Logo

技术共进,成长同行——讯飞AI开发者社区

更多推荐