OpenCV C++ 简单小技巧 - 轮廓 (14

查找和绘制轮廓

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

 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);
    }
}
image.png

https://stackoverflow.com/questions/31354150/opencv-convexity-defects-drawing

比较两个轮廓

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)

你可能感兴趣的:(OpenCV C++ 简单小技巧 - 轮廓 (14)