第三节,使用OpenCV 3处理图像

一 不同色彩空间的转换

OpenCV中有数百种关于在不同色彩空间之间转换的方法。当前,在计算机中有三种常用的色彩空间:灰度,BGR以及HSV(Hue,Saturation,Value)。

  • 灰度色彩空间是通过去除色彩信息来将其转换成灰阶,灰度色彩空间对中间处理特别有效,比如人脸检测。
  • BGR,即蓝-绿-红色彩空间,每一个像素点都由一个三元数组来表示,分别代表蓝、绿、红三种颜色。网页开发者可能熟悉另一个与之相似的色彩空间:RGB,他们只是在颜色顺序上不同。
  • HSV,H(Hue)是色调,S(Saturation)是饱和度,V(Value)表示黑暗的程度(或光谱另一端的命令程度)。

在第一次处理BGR色彩空间的时候,可以不要其中的一个色彩分量,比如像素值[0 255 255](没有蓝色,绿色分量取最大值,红色分量取最大值)表示黄色。如果读者有艺术背景,会发现绿色和红色混合产生浑浊的褐色,这是因为计算所使用的颜色模型具有可加性并且处理的是光照,而绘画不是这样的(它遵从减色模型 subtractive color model)。计算机使用显示器发光来做颜色的媒介,因此运行在计算机上的软件所使用的色彩模型是加色模型。

二 傅里叶变换

在OpenCV中,对图像和视频的大多数处理或多或少都会涉及到傅里叶变换的概念。Joseph Fourier(约瑟夫.傅里叶)是以为18世纪的法国数学家,他发现并推广了很多数学概念,主要研究热学规律,在数学上,他认为一切都可以用波形来描述。具体而言,他观察到所有的波形都是由一系列简单且频率不同的正弦曲线叠加得到。

也就是说人们看到的波形都是由其他波形叠加得到的。这个概念对操作图像非常有帮助,因为这样我们可以区分图像哪些区域的信号变化特别强,哪些区域的信号变化不那么强,从而可以任意地标记噪声区域,感兴趣区域,前景和背景等。原始图像由许多频率组成,人们能够分离这些频率来处理图像和提取感兴趣的数据。

下面通过傅里叶变换来介绍图像的幅度谱(magnitude specturm)。图像的幅度谱是另一中图像,幅度谱图像呈现了原始图像在变化方面的一种表示:把一张图像中最明亮的像素放到图像中央,然后逐渐变暗,在边缘上像素最暗。这样可以发现图像中有多少亮的像素和安得像素,以及它们分布的比例。

傅里叶变换的概念是许多常见的图像处理操作的基础,比如边缘检测或线段和形状检测。

下面介绍两个概念:高通滤波器和低通滤波器。

1.高通滤波器

高通滤波器(HPF)是检测图像的某个区域,然后根据像素与周围像素的亮度差值来提升该像素的亮度的滤波器。

 以如下的核(kernel),即滤波器矩阵为例:

注:核是指一组权重的集合,它会应用在源图像的一个区域,并由此生成目标图像的一个像素。比如,大小为7的核意味着每49(7x7)个源图像的像素会产生目标图像的一个像素。
可把核看做一块覆盖在源图像上可移动的毛玻璃片,玻璃片覆盖区域的光线会按某种方式进行扩散混合后透过去。

 在计算完中央像素与周围邻近像素的亮度差值之和以后,如果亮度变化很大,中央像素的亮度会增加,反之则不会。换句话说,如果一个像素比它周围的像素更突出,就会提升它的亮度。

这在边缘检测上尤为有效,它采用一种称为高频提升滤波器(high boost filter)的高通滤波器。

高通和低通滤波器都有一个半径(radius)的属性,它决定了多达面积的临近像素参与滤波运算。

下面是一个高通滤波器的例子,代码如下:

# -*- coding: utf-8 -*-
"""
Created on Fri Apr 20 21:10:35 2018

@author: Administrator
"""

'''
OPenCV3 计算机视觉 笔记
第三章 :使用Open CV3处理图像
'''
import cv2
import numpy as np
from scipy import ndimage

'''
1.傅里叶变换
'''

'''
(1)高通滤波器
'''
kernel_3x3 = np.array([[-1,-1,-1],[-1,8,-1],[-1,-1,-1]]) 

kernel_5x5 = np.array([[-1,-1,-1,-1,-1],
                       [-1,-1, 2, 1,-1],
                       [-1, 2, 4, 2,-2],
                       [-1, 1, 2, 2,-1],
                       [-1,-1,-1,-1,-1]])


#读取图像,指定格式为灰度图像 
img = cv2.imread('./image/img6.jpg',cv2.IMREAD_GRAYSCALE)

#进行卷积运算
k3 = ndimage.convolve(img,kernel_3x3)
k5 = ndimage.convolve(img,kernel_5x5)
#模糊滤波
blurred = cv2.GaussianBlur(img,(11,11),0)
#作
g_hpf = img - blurred


#显示图像
cv2.imshow('original',img)
cv2.imshow('3x3',k3)
cv2.imshow('5x5',k5)
cv2.imshow('g_hpf',g_hpf)
cv2.waitKey()
cv2.destroyAllWindows()

运行后显示如下:

导入 模块后,我们定义了一个3x3和一个5x5的核,然后将读入的图像转换成灰度格式。通常大多数图像处理都会用Numpy模块来完成,但是这里的情况比较特殊,因为需要用一个给定核与图像进行'卷积',但是Numpy碰巧只接受一维数组。

上面代码用了两个自定义卷积核来实现两个高通滤波器。最后又用一种不同的方法来实现高通滤波器:通过对图像应用低通滤波器之后,与原始图像计算差值。这样得到的效果会更好。

2 低通滤波器

高通滤波器是根据像素与邻近像素的亮度差值来提升该像素的亮度。低通滤波器(LPF)则是在像素与周围像素的亮度差值小于一定特征值,平滑该像素的亮度。它主要用于去噪和模糊化,比如说,高斯模糊是最常用的模糊滤波器(平滑滤波器)之一,它是削弱高频信号强度的低通滤波器。

三 创建模块

和CptureManager类和WindowManager类一样,滤波器需要在Cameo外也能被重用。所以需要把滤波器分割到各自的python模块或者python文件中。

在Cameo.py的同一目录下创建一个filters.py文件,在该文件下添加一些滤波函数和类,fliters.py文件中需要导入如下模块:

import cv2
import numpy as np
import utils

在同一目录下还要创建一个名为utils.py的文件,该文件存放一些通用的数学函数,同时需要导入以下模块:

import cv2
import numpy as np 
import scipy.interpolate

四 边缘检测

边缘在人类视觉和计算机视觉中均起着重要的作用。人类能够仅凭一张背景剪影或一个草图就能识别出物体的类型和姿态。

Open CV提供了许多边缘检测滤波函数,包括以下:

Laplacian()        #作为边缘检测函数,他会产生明显的边缘线条,灰度图像更是如此。
Sobel()
Scharr()

这些滤波函数都会将非边缘去赚转换为黑色,边缘区域转换成白色或其他饱和的颜色。但是这些函数都容易将噪声错误的识别为边缘。缓解这个问题的方法就是在找到边缘之前对图像进行模糊处理,去除噪声。

Open CV也提供了需要模糊滤波函数,包括以下:

blur()
medianBlur()         #它对去除数字化的视频噪声特别有效,特别是去除彩色图像的噪声
GaussianBlur()

边缘检测和模糊滤波的函数的参数有很多,但总会有一个ksize参数,它是一个奇数,表示滤波核的宽和高(以像素为单位)。

cv2.blur(src,ksize[,dst[,anchor[,borderType]]])函数,均值滤波。

均值滤波是一种典型的线性滤波算法,主要是利用像素点邻域的像素值来计算像素点的值。其具体方法是首先给出一个滤波kernel,该核将覆盖像素点周围的其他邻域像素点,去掉像素本身,将其邻域像素点相加然后取平均值即为该像素点的新的像素值,这就是均值滤波的本质。

  • src: 输入图像,图像深度是cv2.CV_8U、cv2.CV_16U、cv2.CV_16S、cv2.CV_32F以及cv2.CV_64F其中的某一个。
  • dst: 输出图像,深度和类型与输入图像一致。
  • ksize: 滤波kernel的尺寸,元组类型。
  • anchor: 字面意思是锚点,也就是处理的像素位于kernel的什么位置,默认值为(-1, -1)即位于kernel中心点,如果没有特殊需要则不需要更改。
  • borderType 用于推断图像外部像素的某种边界模式,有默认值cv2.BORDER_DEFAULT。

cv2.medianBlur(src,ksize[,dst])中值滤波函数。

中值滤波是一种典型的非线性滤波,是基于排序统计理论的一种能够有效抑制噪声的非线性信号处理技术,基本思想是用像素点邻域灰度值的中值来代替该像素点的灰度值,让周围的像素值接近真实的值从而消除孤立的噪声点。该方法在取出脉冲噪声、椒盐噪声的同时能保留图像的边缘细节。这些优良特性是线性滤波所不具备的。
中值滤波首先也得生成一个滤波核,将该核内的各像素值进行排序,生成单调上升或单调下降的二维数据序列,二维中值滤波输出为g(x, y)=medf{f(x-k, y-1),(k, l∈w)},其中f(x,y)和g(x,y)分别是原图像和处理后图像, w为输入的二维模板,能够在整幅图像上滑动,通常尺寸为3*3或5*5区域,也可以是不同的形状如线状、圆形、十字形、圆环形等。通过从图像中的二维模板取出奇数个数据进行排序,用排序后的中值取代要处理的数据即可。
中值滤波对消除椒盐噪声非常有效,能够克服线性滤波器带来的图像细节模糊等弊端,能够有效保护图像边缘信息,是非常经典的平滑噪声处理方法。在光学测量条纹图像的相位分析处理方法中有特殊作用,但在条纹中心分析方法中作用不大。

  • src: 输入图像,图像为1、3、4通道的图像,当核尺寸为3或5时,图像深度只能为cv2.CV_8U、cv2.CV_16U、cv2.CV_32F中的一个,如而对于较大孔径尺寸的图片,图像深度只能是cv2.CV_8U。
  • dst: 输出图像,尺寸和类型与输入图像一致。
  •  ksize: 滤波核的尺寸大小,必须是大于1的奇数,如3、5、7……

cv2.GaussianBlur(src,ksize,sigmaX[,sigmaxY[,borderType]]])高斯滤波函数。

高斯滤波是一种线性平滑滤波,对于除去高斯噪声有很好的效果。在其官方文档中形容高斯滤波为”Probably the most useful filter”,同时也指出高斯滤波并不是效率最高的滤波算法。高斯算法在官方文档给出的解释是高斯滤波是通过对输入数组的每个点与输入的高斯滤波核执行卷积计算然后将这些结果一块组成了滤波后的输出数组,通俗的讲就是高斯滤波是对整幅图像进行加权平均的过程,每一个像素点的值都由其本身和邻域内的其他像素值经过加权平均后得到。高斯滤波的具体操作是:用一个核(或称卷积、掩模)扫描图像中的每一个像素,用模板确定的邻域内像素的加权平均灰度值去替代模板中心像素点的值。
在图像处理中高斯滤波一半有两种实现方式:一种是用离散化窗口滑窗卷积,另一种是通过傅里叶变换。最常见的就是第一种滑窗实现,只有当离散化的窗口非常大,用滑窗计算量非常大的情况下会考虑基于傅里叶变换的方法。
我们在参考其他文章的时候可能会出现高斯模糊和高斯滤波两种说法,其实这两种说法是有一定区别的。我们知道滤波器分为高通、低通、带通等类型,高斯滤波和高斯模糊就是一句滤波器是低通滤波器还是高通滤波器来区分的。比如低通滤波器,像素能量低的通过,而对于像素能量高的部分将会采取加权平均的方法重新计算像素的值,将能量像素的值编程能量较低的值,我们知道对于图像而言其高频部分展现图像细节,所以经过低通滤波器之后整幅图像变成低频造成图像模糊,这就被称为高斯模糊;相反高通滤波是允许高频通过而过滤掉低频,这样将低频像素进行锐化操作,图像变的更加清晰,被称为高斯滤波。说白了很简单就是:高斯滤波是指用高斯函数作为滤波函数的滤波操作而高斯模糊是用高斯低通滤波器。
高斯滤波在图像处理中常用来对图像进行预处理操作,虽然耗时但是数字图像用于后期应用但是其噪声是最大的问题,噪声会造成很大的误差而误差在不同的处理操作中会累积传递,为了能够得到较好的图像,对图像进行预处理去除噪声也是针对数字图像处理的无奈之举。
高斯滤波器是一类根据高斯函数的形状来选择权值的线性平滑滤波器,高斯滤波器对于服从正太分布的噪声非常有效,一维高斯函数如下:

二维高斯函数如下:

 

  • src: 输入图像,图像深度为cv2.CV_8U、cv2.CV_16U、cv2.CV_16S、cv2.CV_32F、cv2.CV_64F。
  • dst: 输出图像,与输入图像有相同的类型和尺寸。
  • ksize: 高斯内核大小,元组类型
  • sigmaX: 高斯核函数在X方向上的标准偏差
  • sigmaY: 高斯核函数在Y方向上的标准偏差,如果sigmaY是0,则函数会自动将sigmaY的值设置为与sigmaX相同的值,如果sigmaX和sigmaY都是0,这两个值将由ksize[0]和ksize[1]计算而来。具体可以参考getGaussianKernel()函数查看具体细节。建议将size、sigmaX和sigmaY都指定出来。
  • borderType: 推断图像外部像素的某种便捷模式,有默认值cv2.BORDER_DEFAULT,如果没有特殊需要不用更改,具体可以参考borderInterpolate()函数。

cv2. bilateralFilter()双边滤波函数。

这里使用medianBlur()作为模糊函数,使用Laplacian()作为边缘检测函数。在使用medianBlur()之后,需要将图像从BGR色彩空间转换为灰度色彩空间。在得到Laplacian()函数结果之后,需要将图像转换为黑色边缘和白色背景(之前是白色边缘黑色背景)。然后将其归一化,并乘以源图像以便能将边缘变黑。在filters.py文件中实现这个函数:

def  strokeEdges(src,blurKsize=7,edgeKsize=5):
    '''
    该函数实现性能更好的边缘检测
    
    这里使用medianBlur()作为模糊函数,使用Laplacian()作为边缘检测函数。在使用medianBlur()之后,
    需要将图像从BGR色彩空间转换为灰度色彩空间。在得到Laplacian()函数结果之后,需要将图像转换为
    黑色边缘和白色背景(之前是白色边缘黑色背景)。然后将其归一化,并乘以源图像以便能将边缘变黑。
    
    args:
        src:源图像数据 BGR色彩空间       
        blurKsize:模糊滤波卷积核的宽和高  小于3,不进行模糊处理
        edgeKsize:边缘检测卷积核的宽和高

  return:
     dst:目标图像数据 灰度色彩空间
''' if blurKsize >= 3: #先模糊处理 blurredSrc = cv2.medianBlur(src,blurKsize) cv2.imshow('blurredSrc',blurredSrc) #BGR格式转化为灰度格式 graySrc = cv2.cvtColor(blurredSrc,cv2.COLOR_BGR2GRAY) else: graySrc = cv2.cvtColor(src,cv2.COLOR_BGR2GRAY) cv2.imshow('graySrc',graySrc) #边缘检测 对灰度图像检测效果更好 cv2.Laplacian(graySrc,cv2.CV_8U,graySrc,ksize = edgeKsize) cv2.imshow('laplacian',graySrc) #颜色反向处理 并归一化 normalizedInverseAlpha = (1.0/255)*(255 - graySrc) cv2.imshow('normalizedInverseAlpha',normalizedInverseAlpha) #通道分离 B,G,R 单通道图像 channels = cv2.split(src) cv2.imshow('B',channels[0]) #计算后的结果分别与每个通道相乘 for channel in channels: #这里是点乘,即对应元素相乘 channel[:] = channel * normalizedInverseAlpha cv2.imshow('B1',channels[0]) #通道合并(只能合并多个单通道成为多通道) return cv2.merge(channels) img = cv2.imread('./image/img6.jpg',cv2.IMREAD_COLOR) dst = strokeEdges(img) cv2.imshow('dst',dst) cv2.waitKey() cv2.destroyAllWindows()

 运行结果如下:最后一张是我们输出的目标,我们从图片上可以看到图片上还是有很多噪声被识别成边缘了。

五 用定制内核做卷积

Open CV预定的许多滤波器(滤波函数)都会使用核。其实核是一组权重,它决定了如何通过临近像素点来计算新的像素点。核也称作卷积矩阵,它对一个区域的像素做调和或卷积运算。通常基于核的滤波器被称为卷积滤波器。

Open CV提供了一个通用分人filter2D()函数,它运用由用户指定的任意核或卷积矩阵。

卷积矩阵是一个二维数组,有奇数行,奇数列,中心的元素对应于感兴趣的像素,其它的元素对应于这个像素周围的邻近像素,每个像素都有一个整数或浮点数的值,这些值就是应用在像素上的权重。如下:

kernel = np.array([[-1,-1,-1],
                   [-1, 9,-1],
                   [-1,-1,-1]])

上面感兴趣的像素权重为9,其余邻近像素上的权重为-1。对于感兴趣的像素来说,新像素值使用当前像素值乘以9,然后减去8个邻近像素值。如果感兴趣的像素和邻近像素有一点差别,那么这个差别会增加。这样会使图片锐化,因为该像素与邻近像素值之间的差距拉大了。

接下来的例子在源图像和目标图像上分别使用卷积核:

cv2.filter2D(src,ddepth,kernel[,dst[,anchor[,delta[,borderType]]]])函图卷积运算函数。

该函数使用于任意线性滤波器的图像,支持就地操作。当其中心移动到图像外,函数可以根据指定的边界模式进行插值运算。

  • src: 输入图像。
  • ddepth: 目标图像深度,如果没写将生成与原图像深度相同的图像。当ddepth输入值为-1时,目标图像和原图像深度保持一致。输入和输出对应关系:
  • kernel: 卷积核(或者是相关核),一个单通道浮点型矩阵。如果想在图像不同的通道使用不同的kernel,可以先使用split()函数将图像通道事先分开。
  • dst: 输出图像,和输入图像具有相同的尺寸和通道数量
  • anchor: 内核的基准点(anchor),其默认值为(-1,-1)说明位于kernel的中心位置。基准点即kernel中与进行处理的像素点重合的点。
  • delta: 在储存目标图像前可选的添加到像素的值,默认值为0
  • borderType: 像素向外逼近的方法,默认值是cv2.BORDER_DEFAULT,即对全部边界进行计算。

接着我们向filter.py文件中添加几个类,第一个类为VconvolutionFilter,它表示一般的卷积滤波器;第二个子类是SharpenFilter,它表示特定的锐化滤波器。第三个子类是FindEdgesFilter,是一个特定的边缘检测器。第四个子类是BlurFilter,是一个模糊滤波器。最后一个EmbossFilter子类。

  
class VConvolutionFilter(object):
    '''
    卷积滤波器
    '''
    def __init__(self,kernel):
        '''
        args:
            kernel:卷积核
        '''
        self.__kernel = kernel
        
    def apply(self,src,dst):
        '''
        对源BGR或者弧度色彩图像src应用滤波器
        '''
        cv2.filter2D(src,-1,self.__kernel,dst)
        
        
    
class SharpenFilter(VConvolutionFilter):
    '''
    锐化滤波器:锐化图像,使得感兴趣的像素与邻近像素的差距拉大了
    '''
    def __init__(self):
        #注意这里权重加起来为1,如果修改卷积核,使得权重加起来为0,这样得到就是一个边缘检测器,这会把
        #边缘转换为白色,非边缘转换为黑色。这样就改变了图像的亮度了。
        kernel = np.array([[-1,-1,-1],
                           [-1, 9,-1],
                           [-1,-1,-1]])
        VConvolutionFilter.__init__(self,kernel)
        
        
class FindEdgesFilter(VConvolutionFilter):
    '''
    边缘滤波器
    '''
    def __init__(self):   
        #注意这里权重加起来为0,这样得到就是一个边缘检测器,这会把
        #边缘转换为白色,非边缘转换为黑色。
        kernel = np.array([[-1,-1,-1],
                           [-1, 8,-1],
                           [-1,-1,-1]])
        VConvolutionFilter.__init__(self,kernel)
        
        

class BlurFilter(VConvolutionFilter):
    '''
    模糊滤波器:实现一个平均滤波器
    '''
    def  __init__(self):
        '''
        权重和为1,邻近像素的权重全为正。
        '''        
        kernel = np.array([[-1,-1,-1],
                           [-1, 8,-1],
                           [-1,-1,-1]])
        VConvolutionFilter.__init__(self,kernel)
                  
        
class EmbossFilter(VConvolutionFilter):
    '''
    模糊(有正的权重)和锐化(有负的权重)
    产生一个脊状或者浮雕的效果。
    '''
    def __init__(self):
        kernel = np.array([[-2,-1, 0],
                           [-1, 1, 1],
                           [ 0, 1, 2]])
        VConvolutionFilter.__init__(self,kernel)
                

修改应用,在上一节Cameo项目捕获的数据帧上,对图像进行处理,修改如下两处,代码如下;

class Cameo(object):
    def __init__(self):
        #创建窗口管理器和视频捕获管理器
        self.__window_manager = WindowManager('Cameo',self.on_key_press)
        self.__capture_manager = CaptureManager(cv2.VideoCapture(0),self.__window_manager,True)
        self.__filter = filters.SharpenFilter()
        
    def run(self):
        '''
        运行主循环
        '''
        #创建窗口
        self.__window_manager.create_window()
               
        
        #循环捕获每一帧图像,并显示 直至窗口销毁
        while self.__window_manager.is_window_created:
            
            #获取一帧图像 并在指定的窗口显示
            self.__capture_manager.enter_frame()
            frame = self.__capture_manager.frame            
          
            '''
            这里可以对这一帧图像进行处理
            '''      
            filters.strokeEdges(frame,blurKsize=5)
            self.__filter.apply(frame,frame)
   
          
                                    
            self.__capture_manager.exit_frame()            
            #执行键盘回调函数
            self.__window_manager.process_event()
          
        #销毁对象
        self.__capture_manager.release()               

六 Canny边缘检测

Open CV还提供了一个非常方便的Canny()函数,该算法非常流行,不仅是因为它的效果,还因为在Open CV程序中实现时非常简单:

'''
Canny边缘检测
'''
can = cv2.Canny(img,200,300)
cv2.imshow('candy',can)


        
cv2.waitKey()
cv2.destroyAllWindows()

Candy边缘检测算法算法非常复杂,但是也很有趣,它有5个步骤,即使用高斯滤波器对图像进行去噪,计算梯度,在边缘上使用非最大抑制,在检测到的边缘上使用阈值去除假阳性,最后还会分析所有的边缘及其之间的连接,以保留真正的边缘并消除不明显的边缘。

七 轮廓检测

在计算机视觉中,轮廓检测是另一个比较重要的任务,不单是用来检测图像或者视频帧中物体的轮廓,而且还有其他操作与轮廓检测相关。这些操作中,计算多边形边界,形状逼近和计算机感 兴趣区域。这是与图像数据交互时的简单操作,因为numpy中的矩阵中的矩形区域可以使用数组切片(slice)定义。在介绍物体检测(包括人脸)和物体跟踪的概念时会大量使用这种技术。

cv2.threshold(src,thresh,maxval,type[,dst])函数用于图像阈值操作。

为了从一幅图像中提取我们需要的部分,应该用图像中的每一个像素点的灰度值与选取的阈值进行比较,并作出相应的判断(阈值的选取依赖于具体的问题,物体在不同的图像中可能会有不同的灰度值)。opencv提供了threshold()函数对图像的阈值进行处理,threshold()共支持五中类型的阈值化方式,分别是二进制阈值化、反二进制阈值化、截断阈值化、阈值化为0和反阈值化为0。返回阈值操作后的图像。

  • src: 输入图像,图像必须为单通道8位或32位浮点型图像
  • thresh: 设定的阈值
  • maxval: 使用cv2.THRESH_BINARY和cv2.THRESH_BINARY_INV类型的最大值
  • type: 阈值化类型,可以通过ThresholdTypes查看,下面给出opencv中五种阈值化类型及其对应公式:
  • dst: 输出图像,与输入图像尺寸和类型相同

 cv2.findContours(image,mode,method[,contours,hierarchy[,offset]])用于寻找寻找图像轮廓。

opencv中提供findContours()函数来寻找图像中物体的轮廓,并结合drawContours()函数将找到的轮廓绘制出。这个函数会修改输入图像,因此建议使用原始图像的一份拷贝(比如说img.copy()作为输入图像)。函数返回三个值:返回修改后的图像,图像的轮廓以及它们的层次。

  • image:输入图像,图像必须为8-bit单通道图像,图像中的非零像素将被视为1,0像素保留其像素值,故加载图像后会自动转换为二值图像。我们同样可以使用cv2.compare,cv2.inRange,cv2.threshold,cv2.adaptiveThreshold,cv2.Canny等函数来创建二值图像,如果第二个参数为cv2.RETR_CCOMP或cv2.RETR_FLOODFILL,输入图像可以是32-bit整型图像(cv2.CV_32SC1)
  • mode轮廓检索模式,如下

其中

  1. RETR_EXTERNAL:表示只检测最外层轮廓,这对消除包含在其他轮廓中的轮廓很有用(比如在大多数情况下,不需要检测一个目标包含在另一个与之相同的目标里面).对所有轮廓设置hierarchy[i][2]=hierarchy[i][3]=-1
  2. RETR_LIST:提取所有轮廓,并放置在list中,检测的轮廓不建立等级关系
  3. RETR_CCOMP:提取所有轮廓,并将轮廓组织成双层结构(two-level hierarchy),顶层为连通域的外围边界,次层位内层边界
  4. RETR_TREE:提取所有轮廓并重新建立网状轮廓结构
  5. RETR_FLOODFILL:官网没有介绍,应该是洪水填充法
  • method:轮廓近似方法

  1. CHAIN_APPROX_NONE:获取每个轮廓的每个像素,相邻的两个点的像素位置差不超过1
  2. CHAIN_APPROX_SIMPLE:压缩水平方向,垂直方向,对角线方向的元素,值保留该方向的重点坐标,如果一个矩形轮廓只需4个点来保存轮廓信息
  3. CHAIN_APPROX_TC89_L1和CHAIN_APPROX_TC89_KCOS使用Teh-Chinl链逼近算法中的一种
  • contours:检测到的轮廓,每个轮廓都是以元组的形式进行存储。
  • hierarchy:可选的输出向量,包含了图像的拓扑信息,作为轮廓数量的表示hierarchy包含了很多元素,每个轮廓contours[i]对应hierarchy中hierarchy[i][0]~hierarchy[i][3],分别表示后一个轮廓,前一个轮廓,父轮廓,内嵌轮廓的索引,如果没有对应项,则相应的hierarchy[i]设置为负数。
  • offset:轮廓点可选偏移量,有默认值。

cv2.drawContours(image,contours,contourIdx,color[,thickness[,lineType[,hierarchy[,maxLevel[,offset]]]]])函数轮廓绘制。

该函数返回绘制有轮廓的图像。

  • image:输入/输出图像。
  • contours:使用findContours检测到的轮廓数据。
  • contourIdx:绘制轮廓的只是变量,如果为负值则绘制所有输入轮廓
  • color:轮廓颜色。
  • thickness:绘制轮廓所用线条粗细度,如果值为负值,则在轮廓内部绘制。
  • lineTpye:线条类型,有默认值LINE_8,有如下可选类型

这里写图片描述

  • hierarchy:可选层次结构信息
  • maxLevel:用于绘制轮廓的最大等级。
  • offset:可选轮廓便宜参数,用制定偏移量offset=(dx, dy)给出绘制轮廓的偏移量。
'''
轮廓检测
'''

#加载图像img
img = cv2.imread('./image/img6.jpg',cv2.IMREAD_COLOR)
cv2.imshow('img',img)


#转换为灰色gray_img
gray_img = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
cv2.imshow('gray_img',gray_img)


#对图像二值化处理 输入图像必须为单通道8位或32位浮点型
ret,thresh = cv2.threshold(gray_img,127,255,0)
cv2.imshow('thresh',thresh)



#寻找图像轮廓 返回修改后的图像 图像的轮廓  以及它们的层次
image,contours,hierarchy = cv2.findContours(thresh,cv2.RETR_TREE,cv2.CHAIN_APPROX_SIMPLE)
cv2.imshow('image',image)
print('contours[0]:',contours[0])


#在原图img上绘制轮廓contours
img = cv2.drawContours(img,contours,-1,(0,255,0),2)
cv2.imshow('contours',img)

        
cv2.waitKey()
cv2.destroyAllWindows()

 八 边界框、最小矩形区域和最小闭圆的轮廓

猜你喜欢

转载自www.cnblogs.com/zyly/p/8893579.html