OpenCV-Python系列八:提取图像轮廓

当你完成图像分割之后,图像轮廓检测往往可以进一步筛选你要的目标,OpenCV中可以使用cv2.findContours来得到轮廓。

1. 基本使用方法如下:
轮廓检测
import cv2
import numpy as np

img = cv2.imread('black_rect1.png', 0)

ret, th = cv2.threshold(img, 127, 255, cv2.THRESH_BINARY_INV)
contours, hierarchy = cv2.findContours(th,cv2.RETR_TREE,cv2.CHAIN_APPROX_SIMPLE)

color_img = cv2.cvtColor(img, cv2.COLOR_GRAY2BGR)

img = cv2.drawContours(color_img, contours, -1, (255, 0, 0), 2)
# bitwise_not对二值图像取反
cv2.imshow('th_img', cv2.bitwise_not(th))
cv2.imshow('contours_img', img)
cv2.waitKey(0)
cv2.destroyAllWindows()

补充
再不少场景中,找轮廓的最小外接矩形是基本需求,opencv中minAreaRect得到的是一个带有旋转角度信息的rect,可以使用cv2.boxPoints(rect)来将其转为矩形的四个顶点坐标(浮点类型).你也可以使用cv2.polylines来绘制这样的轮廓信息

最小外接矩形轮廓绘制

import cv2
import numpy as np

img = cv2.imread('rotate_rect.png', 0)

ret, th = cv2.threshold(img, 127, 255, cv2.THRESH_BINARY_INV)
contours, hierarchy = cv2.findContours(th,cv2.RETR_TREE,cv2.CHAIN_APPROX_SIMPLE)
# 利用cv2.boxPoints用来计算minAreaRect所得到的最小面积外接圆的四个顶点,不过得到的是浮点型
rect = cv2.minAreaRect(contours[0])
# 浮点类型转换
box = np.int64(cv2.boxPoints(rect))
# 为了绘制彩色,将图像转为三通道
color_img = cv2.cvtColor(img, cv2.COLOR_GRAY2BGR)
# 你可以用polylines来绘制点集数据,第三个参数为绘制时是否将轮廓首尾相连
cv2.polylines(color_img, [box], True, (25, 25, 255), 3)

cv2.imshow('rotate_rect_contour_img', color_img)
cv2.waitKey(0)
cv2.destroyAllWindows()
2. 轮廓函数的解释

cv2.findContours(img, mode, method[, contours[, hierarchy[, offset]]])
img:支持8bit单通道的图像,对于非0的像素点会当作1来处理,所以基本上你需要先二值化图像;
mode: 提取轮廓的方式,常用的参数为cv2.RETR_TREE, cv2.RETR_EXTERNAL,前者按照外层轮廓和里层孔来建立等级结构,后者则只求取最外层的轮廓;
method:cv2.CHAIN_APPROX_SIMPLE最常用,压缩水平、垂直、倾斜部分,只会保留最后的一个点,某些情况下,会减少大量不必要的点,比如你需要找矩形,这种方法只会保留四个端点,但对你来说,这样的轮廓就满足需求了;其次cv2.CHAIN_APPROX_NONE则会保留所有找到的轮廓点,会增大消耗;
offset:表示你需要的偏移量,当你是在ROI中进行查找轮廓,那在原图中绘制轮廓信息时,则offset会非常有用,tuple(x_offset, y_offset)
对于hierarchy的信息,其结构形式为[next, previous, first_child, parent],分别代表同级的下一条轮廓,同级的上一条轮廓,当前轮廓的第一个条轮廓,当前轮廓的父轮廓。
参考博客:图像轮廓检索方式详解https://www.cnblogs.com/wojianxin/p/12602490.html

注意findContours参数的变化,在opencv4中,返回值只有contours和hierarchy ,这一点与opencv3中不同。对与轮廓的层级结构,比较难用,虽然可以通过轮廓的层级结构来进行索引你需要的轮廓,不过对于大部分机器视觉应用场景,二值化的结果有时候很难预料,单单通过这种层级关系索引,非常容易出错。所以,只找最外部结构的cv2.RETR_EXTERNAL是不是真香呢?

绘制轮廓:cv2.drawContours(image, contours, contourIdx, color[, thickness[, lineType[, hierarchy[, maxLevel[, offset]]]])
contourIdx:可以绘制指定的某条轮廓,为-1代表绘制全部轮廓
thickness:线条的宽度,为-1时代表填充轮廓内部区域(看你的需要)

3. 轮廓的近似

approx = cv2.approxPolyDP(cnt, epsilon, closed)
epsilon:决定了近似的准确性,代表原始的轮廓与拟合轮廓的最大距离,你可以自己尝试不同的精度来拟合轮廓信息
closed:bool, 轮廓是否闭合

使用approxPolyDP来近似表达轮廓
# approxPolyDP
import cv2
import numpy as np

img = cv2.imread('approx_star.png', -1)

gray_img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
ret, th = cv2.threshold(gray_img, 127, 255, cv2.THRESH_BINARY)
contours, hierarchy = cv2.findContours(th,cv2.RETR_TREE,cv2.CHAIN_APPROX_SIMPLE)

img_temp = img.copy()
for contour in contours:
    # 求轮廓的周长,传递轮廓参数和轮廓是否闭合
    epsilon = 0.06 * cv2.arcLength(contour, True)
    approx_cnt = cv2.approxPolyDP(contour, epsilon, True)

    # 绘制轮廓
    cv2.polylines(img_temp, [contour], True, (25, 25, 255), 3)
    cv2.polylines(img, [approx_cnt], True, (255, 25, 25), 3)

cv2.imshow('contours', img_temp)
cv2.imshow('approx_lines', img)
cv2.waitKey(0)
cv2.destroyAllWindows()

处理cv2.approxPolyDP()外,你也可以使用cv2.convexHull来求轮廓的近似凸包,其中凸形状内部--任意两点连线都在该形状内部。

cv2.convexHull(cnt, clockwise, returnPoints)

clockwise:默认为False,即轮廓为逆时针方向进行排列;
returnPoints:设置为False会返回与凸包上对应的轮廓的点索引值,设置为True,则会返回凸包上的点坐标集,默认为True

对于opencv-python的提取图像轮廓部分有问题欢迎留言, Have Fun With OpenCV-Python, 下期见。

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

推荐阅读更多精彩内容