查找和绘制轮廓
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);
如果在空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 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 );
凸包 hull
将所有点囊括在内
convexHull(contours[idx], approxCurve,false,true);
for (int i=0; i
简化
获得外切矩形 和 最小外切矩形(带旋转
外框
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);
获得外切圆 和 外切椭圆
外切圆
Point2f pot;
float radius;
minEnclosingCircle(approxCurve, pot, radius);
circle(f5, pot, radius, color,5);
外切椭圆
RotatedRect re = fitEllipse(approxCurve);
ellipse(f5, re, color,5);
拟合直线
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);
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);
凸缺陷 / 凹陷特征
这里要注意的是 convexHull 这个函数输出hull特征时可以输出两种类型 vector
vector hull2;
convexHull(approxCurve, hull2,false,false);
vector defects;
convexityDefects(approxCurve, hull2, defects);
for (int i=0; i 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);
}
}
https://stackoverflow.com/questions/31354150/opencv-convexity-defects-drawing
比较两个轮廓
NSLog(@"match %f",matchShapes(contours[idx], approxCurve, CONTOURS_MATCH_I1, 0));
这里为进行比较了直接输出轮廓 和 简化后的轮廓比较,当两个相互接近时就趋近于0。
轮廓的搜索方式
类型 | 方式 | 图 |
---|---|---|
RETR_LIST | 将所有特征都列出来,无层次关系 |
|
RETR_EXTERNAL | 只返回外部轮廓 |
|
RETR_CCOMP | 只有一级父子, 内部如果有轮廓再生成父子关系 |
|
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)