opencv中轮廓检测以及轮廓近似的分析——轮廓近似原理,所有代码开源,所有函数的参数。

一、轮廓检测(实现步骤)

        对于轮廓检测的步骤可简述为:读取图像 -> 图像二值化 -> 找出轮廓 -> 在原图像上画出轮廓这么四个步骤。下面先是讲每个步骤的代码,步骤后会写关键步骤的原理。

        首先是读取图像,在本次实验中是将获取到的轮廓画在原图像上所以需要获取原图像和灰度图像两个图像,操作为(我是读取了两个图像其中一个是一个女生后面会看到,另一个是规则的几何图形的图像):

# 返回值有两个:contours(轮廓), hierarchy(层级)
img = cv2.imread('6.png') # 读取彩色图像(用来当作后面画轮廓的原图像)    
img2 = cv2.imread('10.png') # 读取彩色图像(用来当作后面画轮廓的原图像)
img_gray = cv2.imread('6.png',cv2.IMREAD_GRAYSCALE) # 读取灰度图像(这个是lina(女性角色)的图片)
img2_gray = cv2.imread('10.png' , cv2.IMREAD_GRAYSCALE) # 读取灰度图像(这个是那个很多规则图案的图片)

        随后进行对灰度图像的二值化操作:

# 将两张图片进行二值化处理(将值全部转化为255和0只有两种颜色)
ret , thresh = cv2.threshold(img_gray , 127 , 255 , cv2.THRESH_BINARY)
img_show(thresh , 'thresh')
ret2 , thresh2 = cv2.threshold(img2_gray , 127 , 255 , cv2.THRESH_BINARY)
img_show(thresh2 , 'thresh2')

        第三步为找出轮廓,下面为操作步骤:

# 传进来的是一个经过二值计算的灰度图,contours就是我们所需要的轮廓
contours , hierarchy = cv2.findContours(thresh , cv2.RETR_TREE , cv2.CHAIN_APPROX_SIMPLE)
contours2 , hierarchy2 = cv2.findContours(thresh2 , cv2.RETR_TREE , cv2.CHAIN_APPROX_SIMPLE)

        第四步为在原图像上画出轮廓,操作为:

draw_img = img.copy()# 这里是复制一份原图像,是因为画轮廓的时候会改变原图像的值,所以复制一份可以保证原图像不被破坏。
ret = cv2.drawContours(draw_img , contours , -1 , (0 , 0 , 255) , 2)
img_show(ret , 'ret')

draw_img2 = img2.copy()# 这里是复制一份原图像,是因为画轮廓的时候会改变原图像的值,所以复制一份可以保证原图像不被破坏。
ret2 = cv2.drawContours(draw_img2 , contours2 , -1 , (0 , 0 , 255) , 2)
img_show(ret2 , 'ret2')

        实验代码:

import numpy as np
import cv2
import matplotlib.pyplot as plt

def img_show(img , title):
    cv2.namedWindow(title, cv2.WINDOW_NORMAL) # 设置窗口标题
    cv2.resizeWindow(title, 300, 250)  # 你可以根据需要设置不同的宽度和高度
    cv2.imshow(title , img)
    cv2.waitKey(0)
    cv2.destroyAllWindows()

# 主要是利用一个图像轮廓的检测函数
# cv.findContours函数(查找轮廓):cv2.findContours(image, mode, method)
# ——image:输入图像,只能输入单通道图像,通常来说为灰度图。(最好是经过二值法处理的灰度图像)
# ——mode:轮廓检索模式
# cv2.RETR_EXTERNAL:只检测外轮廓
# cv2.RETR_LIST:检索所有的轮廓,并保存到一个链表之中。
# cv2.RETR_CCOMP:检索所有的轮廓,并将他们分为两个等级的轮廓。上一层是外部边界,里一层是空洞的边界。
# cv2.RETR_TREE:检索所有轮廓,并建立一个等级树结构的轮廓。(一般只用这一个检索模式)

# ——method:轮廓逼近方法
# cv2.CHAIN_APPROX_NONE:以Freeman链码的方式输出轮廓,所有其他方法输出多边形(顶点的序列)
# cv2.CHAIN_APPROX_SIMPLE:压缩水平的、垂直的和斜的部分,也就是,函数只保留他们的终点部分(就是保留一些关键点)
# 一般是使用上面两种轮廓逼近的方法。
# cv2.CHAIN_APPROX_TC89_L1:使用teh-Chinl chain 近似算法
# cv2.CHAIN_APPROX_TC89_KCOS:使用teh-Chinl chain 近似算法

# 返回值有两个:contours(轮廓), hierarchy(层级)
img = cv2.imread('6.png') # 读取彩色图像(用来当作后面画轮廓的原图像)    
img2 = cv2.imread('10.png') # 读取彩色图像(用来当作后面画轮廓的原图像)
img_gray = cv2.imread('6.png',cv2.IMREAD_GRAYSCALE) # 读取灰度图像(这个是lina(女性角色)的图片)
img2_gray = cv2.imread('10.png' , cv2.IMREAD_GRAYSCALE) # 读取灰度图像(这个是那个很多规则图案的图片)

# 将两张图片进行二值化处理(将值全部转化为255和0只有两种颜色)
# 函数原型:cv2.threshold(src, thresh, maxval, type, dst=None)
ret , thresh = cv2.threshold(img_gray , 127 , 255 , cv2.THRESH_BINARY)
img_show(thresh , 'thresh')
ret2 , thresh2 = cv2.threshold(img2_gray , 127 , 255 , cv2.THRESH_BINARY)
img_show(thresh2 , 'thresh2')

# 传进来的是一个经过二值计算的灰度图,contours就是我们所需要的轮廓
contours , hierarchy = cv2.findContours(thresh , cv2.RETR_TREE , cv2.CHAIN_APPROX_SIMPLE)
contours2 , hierarchy2 = cv2.findContours(thresh2 , cv2.RETR_TREE , cv2.CHAIN_APPROX_SIMPLE)


# 画出轮廓(相当于把轮廓画在原图像上,注意画的时候会改变原图像的值,所以最好是先复制一份原图像)
# 函数原型:cv2.drawContours(image, contours, contourIdx, color, thickness)
# ——image:原图像
# ——contours:轮廓本身,是一个list,list中每个元素都是一个轮廓点集(不是每个轮廓点集都是一个闭合的轮廓)
# ——contourIdx:轮廓的索引(等于是画第几个轮廓,-1则为画所有的轮廓)
# ——color:轮廓的颜色。
# ——thickness:轮廓的粗细。
draw_img = img.copy()# 这里是复制一份原图像,是因为画轮廓的时候会改变原图像的值,所以复制一份可以保证原图像不被破坏。
ret = cv2.drawContours(draw_img , contours , -1 , (0 , 0 , 255) , 2)
img_show(ret , 'ret')

draw_img2 = img2.copy()# 这里是复制一份原图像,是因为画轮廓的时候会改变原图像的值,所以复制一份可以保证原图像不被破坏。
ret2 = cv2.drawContours(draw_img2 , contours2 , -1 , (0 , 0 , 255) , 2)
img_show(ret2 , 'ret2')

        最终效果为:

二、轮廓检测(步骤原理)

1.灰度图像二值化操作的原理

函数原型:cv2.threshold(src, thresh, maxval, type, dst=None)

——src:输入图,只能输入单通道图像,通常来说为灰度图。

——thresh:阈值。

——maxval:当像素值超过了阈值(或者小于阈值,根据type来决定),所赋予的值。

——type:二值化操作的类型,包含以下5种类型:  

cv2.THRESH_BINARY:超过阈值部分取maxval(最大值),否则取0

cv2.THRESH_BINARY_INV:THRESH_BINARY的反转

cv2.THRESH_TRUNC:大于阈值部分设为阈值,否则不变

cv2.THRESH_TOZERO:大于阈值部分不改变,否则设为0

cv2.THRESH_TOZERO_INV:THRESH_TOZERO的反转

例如:cv2.THRESH_BINARY模式下,假设设置max=100,则像素点的值为120时则像素点的值设为100,若像素点的值为99小于100则

2.查找轮廓函数原型

函数原型:cv2.findContours(image, mode, method)

——image:输入图像,只能输入单通道图像,通常来说为灰度图。(最好是经过二值法处理的灰度图像)

——mode:轮廓检索模式

cv2.RETR_EXTERNAL:只检测外轮廓

cv2.RETR_LIST:检索所有的轮廓,并保存到一个链表之中。

cv2.RETR_CCOMP:检索所有的轮廓,并将他们分为两个等级的轮廓。上一层是外部边界,里一层是空洞的边界。

cv2.RETR_TREE:检索所有轮廓,并建立一个等级树结构的轮廓。(一般只用这一个检索模式)

——method:轮廓逼近方法

cv2.CHAIN_APPROX_NONE:以Freeman链码的方式输出轮廓,所有其他方法输出多边形(顶点的序列一般就是所有点

cv2.CHAIN_APPROX_SIMPLE:压缩水平的、垂直的和斜的部分,也就是,函数只保留他们的终点部分(就是保留一些几何图形的关键点(例如矩形的四个顶点)

一般是使用上面两种轮廓逼近的方法。

cv2.CHAIN_APPROX_TC89_L1:使用teh-Chinl chain 近似算法

cv2.CHAIN_APPROX_TC89_KCOS:使用teh-Chinl chain 近似算法

返回值有两个:contours(轮廓), hierarchy(层级),其中轮廓是一个list形式的参数,list中每个元素都是一个轮廓点集。

3.画出轮廓函数原型

函数原型:cv2.drawContours(image, contours, contourIdx, color, thickness)

实际操作:
img = cv2.imread('6.png') # 读取彩色图像(用来当作后面画轮廓的原图像)    
img_gray = cv2.imread('6.png',cv2.IMREAD_GRAYSCALE) # 读取灰度图像

ret , thresh = cv2.threshold(img_gray , 127 , 255 , cv2.THRESH_BINARY)# 图像二值化处理

contours , hierarchy = cv2.findContours(thresh , cv2.RETR_TREE , cv2.CHAIN_APPROX_SIMPLE)# 获取到轮廓

draw_img = img.copy()# 这里是复制一份原图像,是因为画轮廓的时候会改变原图像的值,所以复制一份可以保证原图像不被破坏。

ret = cv2.drawContours(draw_img , contours , -1 , (0 , 0 , 255) , 2)
img_show(ret , 'ret')

——image:原图像

——contours:轮廓本身,是一个list,list中每个元素都是一个轮廓点集(不是每个轮廓点集都是一个闭合的轮廓)

——contourIdx:轮廓的索引(等于是画第几个轮廓,-1则为画所有的轮廓)

——color:轮廓的颜色。

——thickness:轮廓的粗细。

需要注意的是!!在画图的时候最好把需要被画的原图像先复制一份然后再将复制的图像进行画轮廓操作,这样可以防止直接对原图像进行画导致再次操作时原图像上已经被画上了轮廓。

三、轮廓的近似

轮廓的近似的实现:实际上轮廓的近似的实现就是将复杂的曲线用一些直线来代替,使图像更加的光滑和规则化。

1.轮廓近似函数原理

# 函数原型:cv2.approxPolyDP(curve, epsilon, closed)

实际操作:
img3 = cv2.imread('11.png') # 读取彩色图像(用来当作后面画轮廓的原图像)
img3_gray = cv2.imread('11.png',cv2.IMREAD_GRAYSCALE) # 读取灰度图像

ret3 , thresh3 = cv2.threshold(img3_gray , 100 , 255 , cv2.THRESH_BINARY)# 二值化处理
contours3 , hierarchy3 = cv2.findContours(thresh3 , cv2.RETR_TREE , cv2.CHAIN_APPROX_SIMPLE)# 查找轮廓

cnt3 = contours3[0] # 提取出第一个轮廓

eplision = 0.01 * cv2.arcLength(cnt3 , True) # 这里是指定的精度,也就是原始曲线与近似曲线之间的最大距离.
approx = cv2.approxPolyDP(cnt3 , eplision , True) # 这里是对轮廓进行近似处理.

img_draw3 = img3.copy()# 这里是复制一份原图像,是因为画轮廓的时候会改变原图像的值,所以复制一份可以保证原图像不被破坏。
res3 = cv2.drawContours(img_draw3, [approx], -1, (0, 0, 255), 3)
img_show(res3 , 'res3')

——curve:输入的点集

——epsilon:指定的精度,也就是原始曲线与近似曲线之间的最大距离。

——closed:曲线是否是闭合的

        其中可以看到函数中有一个参数是需要我们自己进行计算的:指定的精度(epsilon),这个参数也是由前面的系数决定的,在示例中用的系数是0.01,实际上可以根据自己对精度的需求自行调整。epsilon = 系数 ✖ 轮廓的周长。

        那么近似的具体原理是什么呢,如以下图所示:

        如图所示,当AB曲线中到AB虚线距离最大时,距离还是小于我们设置的epsilon参数则就用直线AB代替掉曲线AB。若最大距离小于epsilon时则有:

        若AD,DB两条直线还无法取代AB这条曲线则就再将AE,ED,DF,FB,连接起来看看最大距离是否小于epsilon,这样便可以用多条直线来取代整条曲线,无论曲线是什么样子都能实现。具体效果只决定于参数epsilon。

        2.最终实现的效果

         

计算精度系数:         0.02                                                               0.01

          

                                0.005                                                                  0.001

        至此便是轮廓近似的全部原理。以下提供整个轮廓实验的源代码供大家学习参考。有写错的地方也欢迎大家指出!

import numpy as np
import cv2
import matplotlib.pyplot as plt

def img_show(img , title):
    cv2.namedWindow(title, cv2.WINDOW_NORMAL) # 设置窗口标题
    cv2.resizeWindow(title, 300, 250)  # 你可以根据需要设置不同的宽度和高度
    cv2.imshow(title , img)
    cv2.waitKey(0)
    cv2.destroyAllWindows()

# 主要是利用一个图像轮廓的检测函数
# cv.findContours函数(查找轮廓):cv2.findContours(image, mode, method)
# ——image:输入图像,只能输入单通道图像,通常来说为灰度图。(最好是经过二值法处理的灰度图像)
# ——mode:轮廓检索模式
# cv2.RETR_EXTERNAL:只检测外轮廓
# cv2.RETR_LIST:检索所有的轮廓,并保存到一个链表之中。
# cv2.RETR_CCOMP:检索所有的轮廓,并将他们分为两个等级的轮廓。上一层是外部边界,里一层是空洞的边界。
# cv2.RETR_TREE:检索所有轮廓,并建立一个等级树结构的轮廓。(一般只用这一个检索模式)

# ——method:轮廓逼近方法
# cv2.CHAIN_APPROX_NONE:以Freeman链码的方式输出轮廓,所有其他方法输出多边形(顶点的序列)
# cv2.CHAIN_APPROX_SIMPLE:压缩水平的、垂直的和斜的部分,也就是,函数只保留他们的终点部分(就是保留一些关键点)
# 一般是使用上面两种轮廓逼近的方法。
# cv2.CHAIN_APPROX_TC89_L1:使用teh-Chinl chain 近似算法
# cv2.CHAIN_APPROX_TC89_KCOS:使用teh-Chinl chain 近似算法

# 返回值有两个:contours(轮廓), hierarchy(层级)
img = cv2.imread('6.png') # 读取彩色图像(用来当作后面画轮廓的原图像)    
img2 = cv2.imread('10.png') # 读取彩色图像(用来当作后面画轮廓的原图像)
img_gray = cv2.imread('6.png',cv2.IMREAD_GRAYSCALE) # 读取灰度图像(这个是lina(女性角色)的图片)
img2_gray = cv2.imread('10.png' , cv2.IMREAD_GRAYSCALE) # 读取灰度图像(这个是那个很多规则图案的图片)

# 将两张图片进行二值化处理(将值全部转化为255和0只有两种颜色)
# 函数原型:cv2.threshold(src, thresh, maxval, type, dst=None)
ret , thresh = cv2.threshold(img_gray , 127 , 255 , cv2.THRESH_BINARY)
img_show(thresh , 'thresh')
ret2 , thresh2 = cv2.threshold(img2_gray , 127 , 255 , cv2.THRESH_BINARY)
img_show(thresh2 , 'thresh2')

# 传进来的是一个经过二值计算的灰度图,contours就是我们所需要的轮廓
contours , hierarchy = cv2.findContours(thresh , cv2.RETR_TREE , cv2.CHAIN_APPROX_SIMPLE)
contours2 , hierarchy2 = cv2.findContours(thresh2 , cv2.RETR_TREE , cv2.CHAIN_APPROX_SIMPLE)


# 画出轮廓(相当于把轮廓画在原图像上,注意画的时候会改变原图像的值,所以最好是先复制一份原图像)
# 函数原型:cv2.drawContours(image, contours, contourIdx, color, thickness)
# ——image:原图像
# ——contours:轮廓本身,是一个list,list中每个元素都是一个轮廓点集(不是每个轮廓点集都是一个闭合的轮廓)
# ——contourIdx:轮廓的索引(等于是画第几个轮廓,-1则为画所有的轮廓)
# ——color:轮廓的颜色。
# ——thickness:轮廓的粗细。
draw_img = img.copy()# 这里是复制一份原图像,是因为画轮廓的时候会改变原图像的值,所以复制一份可以保证原图像不被破坏。
ret = cv2.drawContours(draw_img , contours , -1 , (0 , 0 , 255) , 2)
img_show(ret , 'ret')

draw_img2 = img2.copy()# 这里是复制一份原图像,是因为画轮廓的时候会改变原图像的值,所以复制一份可以保证原图像不被破坏。
ret2 = cv2.drawContours(draw_img2 , contours2 , -1 , (0 , 0 , 255) , 2)
img_show(ret2 , 'ret2')

# 轮廓特征
# 首先要计算轮廓的特征需要把第一个轮廓提取出来随后再利用函数进行计算。
cnt = contours[0] # 提取出第一个轮廓(可以理解为把轮廓第一个节点的地址给出来(因为contours得到的是返回的一个list类型的变量))
cnt2 = contours2[0] # 提取出第一个轮廓(可以理解为把轮廓第一个节点的地址给出来(因为contours得到的是返回的一个list类型的变量))
# 计算轮廓面积函数原型:cv2.contourArea(contours)
print(cv2.contourArea(cnt2))
# 计算轮廓周长函数原型:cv2.arcLength(contours , True) 
print(cv2.arcLength(cnt , True)) # True表示轮廓是闭合的,False表示轮廓是不闭合的


# 轮廓的近似
# 函数原型:cv2.approxPolyDP(curve, epsilon, closed)
# ——curve:输入的点集
# ——epsilon:指定的精度,也就是原始曲线与近似曲线之间的最大距离。
# ——closed:曲线是否是闭合的
img3 = cv2.imread('11.png') # 读取彩色图像(用来当作后面画轮廓的原图像)
img3_gray = cv2.imread('11.png',cv2.IMREAD_GRAYSCALE) # 读取灰度图像

ret3 , thresh3 = cv2.threshold(img3_gray , 100 , 255 , cv2.THRESH_BINARY)# 二值化处理
contours3 , hierarchy3 = cv2.findContours(thresh3 , cv2.RETR_TREE , cv2.CHAIN_APPROX_SIMPLE)# 查找轮廓

cnt3 = contours3[0] # 提取出第一个轮廓

eplision = 0.01 * cv2.arcLength(cnt3 , True) # 这里是指定的精度,也就是原始曲线与近似曲线之间的最大距离.
approx = cv2.approxPolyDP(cnt3 , eplision , True) # 这里是对轮廓进行近似处理.

img_draw3 = img3.copy()# 这里是复制一份原图像,是因为画轮廓的时候会改变原图像的值,所以复制一份可以保证原图像不被破坏。
res3 = cv2.drawContours(img_draw3, [approx], -1, (0, 0, 255), 3)
img_show(res3 , 'res3')

  • 45
    点赞
  • 40
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值