在之前的教程中,我们谈到了轮廓的一些基本特征,包括有图像的矩、轮廓区域的面积、轮廓的周长、轮廓的外接图形等等。今天我们仍然讨论几种轮廓的特征,它们将很具有实战意义,我们将从综合方面讲述。

轮廓近似

通常在某些特定场合,我们并不需要太过精确的轮廓信息,而只需要大概的信息,这个时候我们就需要对轮廓进行近似处理,实际上也称之为多边形拟合。

我们接下来将会做一个综合性实验,从而完善对轮廓近似的实战学习。先来看相关的函数原型:

cv2.approxPolyDP(curve, epsilon, closed)

curve

需要进行近似的轮廓

epsilon

指定近似精度的参数ε,这是原始曲线与其近似值之间的最大距离。参数越小,两直线越接近

closed

若为true,曲线第一个点与最后一个点连接形成闭合曲线,若为false,曲线不闭合。

我们先来看一幅图像:

这个实际上是一个综合性的实战项目——OCR文档识别,但是在这里我们只做一些前期的操作,我们现在的目标就是将这幅图像进行校正,这可以通过OpenCV的****变换进行完成,但是在前面我们讲述过,OpenCV的****变化需要四个坐标点,所以我们需要将文档的四个顶点的坐标提取出来,那么现在,我们有了明确的目标,就是将图像的四个顶点找出来。

我们先来看代码:import cv2

import numpy as np

def resize(image, width=None, height=None, inter=cv2.INTER_AREA):

dim = None

(h, w) = image.shape[:2]

if width is None and height is None:

return image

if width is None:

r = height / float(h)

dim = (int(w * r), height)

else:

r = width / float(w)

dim = (width, int(h * r))

resized = cv2.resize(image, dim, interpolation=inter)

return resized

def show(img):

cv2.imshow("res", img)

cv2.waitKey(0)

cv2.destroyAllWindows()

img = cv2.imread("page.jpg")

ratio = img.shape[0] / 500.0

orig = img.copy()

img = resize(img, height=500)

show(img)

gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

show(gray)

gray = cv2.GaussianBlur(gray, (5, 5), 0)

show(gray)

edged = cv2.Canny(gray, 45, 230)

show(edged)

contours = cv2.findContours(edged, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)[0]

print(len(contours[0]))

cv2.drawContours(img, contours, -1, (0, 0, 255), 2)

show(img)

我们会进行一步步的分析,首先我们需要对图像进行尺寸统一,在这里我们自定义函数加上OpenCV的函数进行的尺寸自适应的统一,这些我们在前面都已经讲过,现在都综合起来。

这是重新定义尺寸之后的图像,在这里定义为高度为500,宽度等比例缩小:

接下来是对图像进行灰度化,方便后期的处理:

高斯模糊,消除噪声的影响,方便后面的边缘检测:

使用Canny边缘检测算法对高斯模糊后的图像进行边缘检测:

于此同时,我们检测最外层的轮廓,然后将轮廓绘制出来:

打印出来轮廓的所有的坐标点:

可以看到,坐标点总共有185个,这样根本没有办法进行****变换,****变换只允许四个坐标点,这个时候我们本次教程介绍的轮廓近似的作用就已经显现出来了,我们来看代码:import cv2

import numpy as np

def resize(image, width=None, height=None, inter=cv2.INTER_AREA):

dim = None

(h, w) = image.shape[:2]

if width is None and height is None:

return image

if width is None:

r = height / float(h)

dim = (int(w * r), height)

else:

r = width / float(w)

dim = (width, int(h * r))

resized = cv2.resize(image, dim, interpolation=inter)

return resized

def show(img):

cv2.imshow("res", img)

cv2.waitKey(0)

cv2.destroyAllWindows()

img = cv2.imread("page.jpg")

ratio = img.shape[0] / 500.0

orig = img.copy()

img = resize(img, height=500)

gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

gray = cv2.GaussianBlur(gray, (5, 5), 0)

edged = cv2.Canny(gray, 45, 230)

contours = cv2.findContours(edged, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)[0]

print(len(contours[0]))

epsilon = 0.1 * cv2.arcLength(contours[0], True)

approx = cv2.approxPolyDP(contours[0], epsilon, True)

cv2.drawContours(img, [approx], -1, (0, 0, 255), 2)

print(len(approx))

show(img)

我们可以看到,在进行了轮廓近似之后,坐标点由185变为了四个,这极大的方便了之后的****变换:

通过这个实战,我们既熟练了之前所讲述的内容,同时也体会到了新内容的实用性,现在我们来看看轮廓的其他特征。

凸包检测

事实上,OpenCV轮廓的凸包检测也是十分实用的,凸包(Convex Hull)是一个计算几何(图形学)中的概念,它的严格的数学定义为:在一个向量空间V中,对于给定集合X,所有包含X的凸集的交集S被称为X的凸包。

凸包检测原理:

思路:Graham扫描的思想和Jarris步进法类似,也是先找到凸包上的一个点,然后从那个点开始按逆时针方向逐个找凸包上的点。

操作步骤大概可分为两步

第一步:排序

第二步:扫描

步骤:

1.把所有点放在二维自然直角坐标系中(注意与图片像素坐标系区分),则纵坐标最小的点一定是凸包上的点,如图中的P0。

2.那么以P0作为坐标参考点,如上图所示:

计算各个点相对于 P0 的幅角 α ,按从小到大的顺序对各个点排序。当 α 相同时,距离 P0 比较近的排在前面。例如上图得到的结果为 P1,P2,P3,P4,P5,P6,P7,P8。我们由定义可以知道,结果中第一个点 P1和最后一个点 P8 一定是凸包上的点。

3.以上,我们已经知道了凸包上的第一个点 P0 和第二个点 P1,我们把它们放在栈里面,并令P2为当前点.

4.根据栈顶的前两个点作出有向直线,记PX-1->PX ,看当前点是在有向直线 L 的右边还是左边。如果在直线的右边就执行步骤5;如果在直线上,或者在直线的左边就执行步骤6。

5.栈顶的那个元素不是凸包上的点,把栈顶元素出栈,当前点不变,执行步骤4。

6.当前点是凸包上的点,把它压入栈,执行步骤7。

7.检查当前的点是不是步骤3那个结果的最后一个元素:当前点是最后一个元素的话就结束,如果不是的话就将下一个点作为新的当前点,返回步骤4。

静态求解过程如下:

实战

  在图像处理过程中,我们常常需要寻找图像中包围某个物体的凸包。凸包跟多边形逼近很像,只不过它是包围物体最外层的一个凸集,这个凸集是所有能包围这个物体的凸集的交集。如下图所示:

在上图中,绿色线条所包围的凸集即为白色图形的凸包。

在OpenCV中,通过函数convexHulll能很容易的得到一系列点的凸包,比如由点组成的轮廓,通过convexHull函数,我们就能得到轮廓的凸包。寻找图像的凸包,能够让我们做一些有意思的事情,比如手势识别等。

函数原型:

hull       =     cv.convexHull(   points[, hull[, clockwise[, returnPoints]]]  )

第一个参数:输入的点集,可以是矩阵。

第二个参数:可以为vector,此时返回的是每个凸包点在原轮廓点容器中的索引,也可以为vector,此时存放的是凸包点的位置,即点(x,y)坐标。

第三个参数:bool变量,表示输出的凸包是顺时针方向还是逆时针方向,true是顺时针方向,false为逆时针方向,默认值是true,即顺时针方向输出凸包。

第四个参数:bool型变量returnPoints,表示第二个参数的输出类型,默认为true,即返回凸包的各个点,设置为false时,表示返回凸包各个点的索引。当第二个参数类型为std::vector,则该标志位被忽略,就是以第二个参数为准,即为vector,此时返回的是每个凸包点在原轮廓点容器中的索引,为vector时,此时存放的是凸包点的位置,即点(x,y)坐标。

下面将会通过两个简单例子来展示如何用OpenCV来寻找图像的凸包。

首先,我们用以下的Python代码来自己绘制一张简单的多边形的图片(tubao.png),代码如下:import cv2

import numpy as np

# 新建512*512的空白图片

img = np.zeros((512,512,3), np.uint8)

# 平面点集

pts = np.array([[200,250], [250,300], [300, 270], [270,200], [120, 240]], np.int32)

pts = pts.reshape((-1,1,2))

# 绘制填充的多边形

cv2.fillPoly(img, [pts], (255,255,255))

# 保存图片

cv2.imshow("res",img)

cv2.imwrite('tubao.png', img)

cv2.waitKey(0)

接着我们需要寻找这个多边形的凸包,利用OpenCV的convexHull函数,然后再将这个凸包绘制出来,得到直观的展示结果。处理的Python代码如下:import cv2

# 读取图片并转至灰度模式

img = cv2.imread("tubao.png", 1)

gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

# 二值化

ret, thresh = cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY)

# 图片轮廓

contours, hierarchy = cv2.findContours(thresh, 2, 1)

cnt = contours[0]

# 寻找凸包并绘制凸包(轮廓)

hull = cv2.convexHull(cnt)

print(hull)

length = len(hull)

for i in range(len(hull)):

cv2.line(img, tuple(hull[i][0]), tuple(hull[(i+1)%length][0]), (0,255,0), 2)

# 显示图片

cv2.imshow('line', img)

cv2.waitKey()

这是凸包所在的轮廓的点集集合,有了它,我们就能绘制出凸包的轮廓了,如下:

接下来,我们将介绍一张稍微难一点的图片——手势图片(finger.jpg),如下所示:

我们将会来寻找这个手势的凸包。基本的处理思路还是和之前的一致,只是要在二值化以及凸包点集集合的大小上做一些处理,取二值化的阈值为235,凸包点集中的点个数大于5,完整的Python代码如下:import cv2

# 读取图片并转至灰度模式

imagepath = 'F://finger.jpg'

img = cv2.imread(imagepath, 1)

gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

# 二值化,取阈值为235

ret, thresh = cv2.threshold(gray, 235, 255, cv2.THRESH_BINARY)

# 寻找图像中的轮廓

image, contours, hierarchy = cv2.findContours(thresh, 2, 1)

# 寻找物体的凸包并绘制凸包的轮廓

for cnt in contours:

hull = cv2.convexHull(cnt)

length = len(hull)

# 如果凸包点集中的点个数大于5

if length > 5:

# 绘制图像凸包的轮廓

for i in range(length):

cv2.line(img, tuple(hull[i][0]), tuple(hull[(i+1)%length][0]), (0,0,255), 2)

cv2.imshow('finger', img)

cv2.waitKey()

可以发现,一共检测到2个凸包,一个是整个手势外围的凸包,正好包围整个手,另一个是两个手指形成的内部的图形,类似于O的凸包,这符合我们的预期。

至此,OpenCV轮廓的特征就全部介绍完毕,它们的用途极为广泛,其重要性不言而喻。

python二值化特征_OpenCV-Python系列之轮廓特征高阶相关推荐

  1. python二值化特征_R与Python手牵手:特征工程(数值型变换)

    原标题:R与Python手牵手:特征工程(数值型变换) 作者:黄天元,复旦大学博士在读,目前研究涉及文本挖掘.社交网络分析和机器学习等.希望与大家分享学习经验,推广并加深R语言在业界的应用. 邮箱:h ...

  2. 基于旋转轮廓的点云局部浮点型和二值化特征描述(RCS)

    点击上方"3D视觉工坊",选择"星标" 干货第一时间送达 本次介绍一个发表于Computer Vision and Image Understanding的经典 ...

  3. python对参数二值化处理_OpenCV自适应阀值二值化表格检测方法(python版)

    OCR主要分为三个步骤:检测.分割.文字识别.其中文字识别无论是英文还是中文相对比较成熟.只要检测到位,标准的印刷体识别率还是非常高的. 文书OCR检测主要有文字检测和表格检测.文本段落基于行的检测通 ...

  4. 用python怎么样实现图像二值化_使用Python+OpenCV如何实现图像二值化

    使用Python+OpenCV如何实现图像二值化 发布时间:2020-10-26 14:15:52 来源:亿速云 阅读:77 作者:蛋片鸡 这篇文章运用简单易懂的例子给大家介绍使用Python+Ope ...

  5. 计算机视觉基础-图像处理(图像分割/二值化)cpp+python

    5.1 简介 该部分的学习内容是对经典的阈值分割算法进行回顾,图像阈值化分割是一种传统的最常用的图像分割方法,因其实现简单.计算量小.性能较稳定而成为图像分割中最基本和应用最广泛的分割技术.它特别适用 ...

  6. python二值化 感兴趣区域_Python+OpenCV感兴趣区域ROI提取方法

    方法一:使用轮廓 步骤1 """src为原图""" ROI = np.zeros(src.shape, np.uint8) #感兴趣区域RO ...

  7. opencv(python)------二值化阙值(threshold)、自适应阙值(adaptiveThreshold)

    1. 简单阈值 对于每个像素,应用相同的阈值.如果像素值小于阈值,则将其设置为0,否则将其设置为最大值.函数cv.threshold用于应用阈值.第一个参数是源图像,它应该是灰度图像.第二个参数是阈值 ...

  8. python + opencv + 二值化处理图片数据集(三种方法)

    二值化处理图片数据集 首先导入相应的包 cv2即opencv-python包 if __name__ == "__main__": 上面这一句话的含义: 自己的 .py 可以作为自 ...

  9. 二值化每个特征,微软用1350亿参数稀疏神经网络改进搜索结果

    GPT-3 强大,但不是很「聪明」,微软提出了一种大规模稀疏模型,改进了生产型 Transformer 模型,在自家搜索引擎Bing上改进并测试,性能大幅提升. 近来 GPT-3 等基于 Transf ...

最新文章

  1. 用C语言实现扫雷小游戏(附上思路+项目展示+源代码)
  2. C++ explicit关键字详解(转载)
  3. struts+hibernate+oracle+easyui实现lazyout组件的简单案例——hibernate的config文件(hibernate.cfg.xml)...
  4. 从QQ进程内存中搜索出QQ号码
  5. EtherCAT 网站链接
  6. 2008至今,Chrome如何成长为霸主
  7. 解决“A problem has been encountered while loading the setup components. Canceling setup.”的问题...
  8. 国家自科基金人工智能项目比较:西电第一 清华第二 电子科大第三
  9. Bootstrap 折叠插件Collapse 事件
  10. Javascript 已被弃用的或删除的特性(V客学院知识分享)
  11. 微信公号“架构师之路”学习笔记(五)-数据库扩展性架构设计(水平切分,秒级扩容,平滑迁移,在线表结构变更,一个大数据量多属性高并发的数据库设计等)
  12. Lattice FPGA 开发工具Diamond使用流程总结——仿真+debug
  13. MariaDB数据库导出导入
  14. linux系统相关文件夹讲解,Linux中重要文件夹介绍PPT课件
  15. 可能有用的技术社区(转载)
  16. 钟薛高为啥突然卖3-4元的雪糕?
  17. 7-2 地下迷宫探索
  18. Homekit智能家居DIY一智能插座
  19. 【SRE笔记 2022.8.16 Linux命令基础01】
  20. PAT | 1025 反转链表 (25分)【超时问题 + 柳神代码】

热门文章

  1. python编辑图像_在python中创建图像编辑应用程序
  2. linux如何制作服务,linux把jar做成服务
  3. jdbc连接orcal数据库
  4. Android之哭笑不得的BUG--xml设置的padding不起作用,幕后黑手竟然是?
  5. Hibernate - HHH000352: Unable to release batch statement
  6. Unity工程3D和2D开发模式切换
  7. 【转载】web.xml中的classpath和classpath*
  8. 分区表PARTITION table
  9. 诗歌,一路走来...
  10. spring_boot 与MyBatis 整合