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

查找和绘制轮廓

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.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<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.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)
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 218,036评论 6 506
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,046评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,411评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,622评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,661评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,521评论 1 304
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,288评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,200评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,644评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,837评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,953评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,673评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,281评论 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,889评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,011评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,119评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,901评论 2 355

推荐阅读更多精彩内容

  • 1.1什么是轮廓 cv2.findContours() 轮廓可以简单认为成连续的点(连着边界)连在一起的曲线,具有...
    Zoe_C阅读 6,876评论 1 8
  • 1图像矩 帮你计算一些属性,比如重心,面积等。 函数cv2.moments()会给你一个字典,包含所有矩值 imp...
    xxxss阅读 19,678评论 0 59
  • 原文链接 背景 识别二维码的项目数不胜数,每次都是开箱即用,方便得很。这次想用 OpenCV 从零识别二维码,主要...
    粗识名姓阅读 4,929评论 1 22
  • 这本书是西藏金刚比丘尼佩玛 丘卓对弟子的开示记录,她是创巴仁波切最优秀的大弟子之一,因为得知丈夫外遇而开始修行之旅...
    石川河女神阅读 2,257评论 0 1
  • 你累了吗? 折腾了快一年了,你累了吗?你认识了自己吗?你还有什么想法吗?你还会快乐吗?你还有追求吗?你还认识自己吗...
    三小妹阅读 246评论 0 0