python进阶—OpenCV之图像处理(二)


本篇续接OpenCV图像处理部分。到现在为止已经2个多月了,才写到opencv教程的第3部分,任重道远。

图像形态变换

图像的形态转换主要是基于二值化图像,进行图像的形状操作。形态变换操作需要2个输入;一个是二值化图像,另外一个被称为“结构化元素”;主要的形态转换操作是复试与膨胀,其它形态转换操作都基于此两者操作。

图像的腐蚀

对输入图像用特定结构元素进行腐蚀操作,表现出的现象是图像被缩小;此处的缩小不是指图像的尺寸变小,而是图像中的信息部分(主要是亮度较高的像素)缩小。
腐蚀算法:用结构元素对应的矩阵,例如3X3,扫描图像的每一个像素,用结构元素与其覆盖的图像做“与”运算,如果都为1,结构图像的该像素为1,否则为0;结果:使图像减小一圈。

  • 函数原型:dst = cv.erode( src, kernel[, dst[, anchor[, iterations[, borderType[, borderValue]]]]] )
  • src:输入始图像,通道数不限,depth必须是CV_8U,CV_16U,CV_16S,CV_32F或CV_64F
  • dst:输出图像:size与type与原始图像相同
  • kernel:用于腐蚀操作的结构元素,如果取值为Mat(),那么默认使用一个3 x 3 的方形结构元素,可以使用getStructuringElement来创建结构元素
  • anchor:卷积核的基准点(anchor),其默认值为(-1,-1)表示位于kernel中心位置。基准点即选取的kernel中心位置与进行处理像素重合的点。
  • iterations:腐蚀操作被递归执行的次数
  • borderType:图像边界模式
  • borderValue:边界值,默认值是BORDER_DEFAULT
import cv2 as cv
import numpy as np
img = cv.imread('j.png',0)
kernel = np.ones((5,5),np.uint8)
erosion = cv.erode(img,kernel,iterations = 1)

在这里插入图片描述在这里插入图片描述

图像的膨胀

对输入图像用特定结构元素进行膨胀操作,表现出的现象是图像被放大;此处的放大不是指图像的尺寸变大,而是图像中的信息部分(主要是亮度较高的像素)放大。
膨胀算法:用结构元素对应的矩阵,例如3X3,扫描图像的每一个像素,用结构元素与其覆盖的图像做“或”运算,如果都为0,结构图像的该像素为0,否则为1;结果:使图像放大一圈。

  • 函数原型:dst = cv.dilate( src, kernel[, dst[, anchor[, iterations[, borderType[, borderValue]]]]] )
  • src:输入始图像,通道数不限,depth必须是CV_8U,CV_16U,CV_16S,CV_32F或CV_64F
  • dst:输出图像:size与type与原始图像相同
  • kernel:用于腐蚀操作的结构元素,如果取值为Mat(),那么默认使用一个3 x 3 的方形结构元素,可以使用getStructuringElement来创建结构元素
  • anchor:卷积核的基准点(anchor),其默认值为(-1,-1)表示位于kernel中心位置。基准点即选取的kernel中心位置与进行处理像素重合的点。
  • iterations:腐蚀操作被递归执行的次数
  • borderType:图像边界模式
  • borderValue:边界值,默认值是BORDER_DEFAULT
import cv2 as cv
import numpy as np
img = cv.imread('j.png',0)
kernel = np.ones((5,5),np.uint8)
dilation = cv.dilate(img,kernel,iterations = 1)

在这里插入图片描述

图像的开操作

图像的开操作是指,先腐蚀后膨胀,适合去除图像有效信息外的白噪点

  • 函数原型:dst = cv.morphologyEx(src, op, kernel[, dst[, anchor[, iterations[, borderType[, borderValue]]]]])
  • src:输入始图像,通道数不限,depth必须是CV_8U,CV_16U,CV_16S,CV_32F或CV_64F
  • dst:输出图像:size与type与原始图像相同
  • op:形态学运算的类型,主要有
    cv.MORPH_OPEN: 开运算
    cv.MORPH_CLOSE :闭运算
    cv.MORPH_GRADIENT: 形态学梯度
    cv.MORPH_TOPHAT:顶帽运算
    cv.MORPH_BLACKHAT: 黑帽运算
    cv.MORPH_ERODE :腐蚀运算
    cv.MORPH_DILATE :膨胀运算
    cv.MORPH_HITMISS: 击中击不中运算(只支持CV_8UC1类型的二值图像)
  • kernel:用于腐蚀操作的结构元素,如果取值为Mat(),那么默认使用一个3 x 3 的方形结构元素,可以使用getStructuringElement来创建结构元素
  • anchor:卷积核的基准点(anchor),其默认值为(-1,-1)表示位于kernel中心位置。基准点即选取的kernel中心位置与进行处理像素重合的点。
  • iterations:腐蚀操作被递归执行的次数
  • borderType:图像边界模式
  • borderValue:边界值,默认值是BORDER_DEFAULT
import cv2 as cv
import numpy as np
img = cv.imread('j.png',0)
kernel = np.ones((5,5),np.uint8)
opening = cv.morphologyEx(img, cv.MORPH_OPEN, kernel)

在这里插入图片描述

图像的闭操作

图像的闭操作是指,先膨胀后腐蚀,适合去除图像有效信息内的黑噪点
函数与开操作函数相同,只是op参数不同,取值为:cv.MORPH_CLOSE

import cv2 as cv
import numpy as np
img = cv.imread('j.png',0)
kernel = np.ones((5,5),np.uint8)
closing = cv.morphologyEx(img, cv.MORPH_CLOSE, kernel)

在这里插入图片描述

图像的形态学梯度

形态学梯度:膨胀图减去腐蚀图,dilation - erosion,这样会得到物体的轮廓
函数与开操作函数相同,只是op参数不同,取值为:cv.MORPH_GRADIENT

import cv2 as cv
import numpy as np
img = cv.imread('j.png',0)
kernel = np.ones((5,5),np.uint8)
gradient = cv.morphologyEx(img, cv.MORPH_GRADIENT, kernel)

在这里插入图片描述

图像的顶帽操作

顶帽(tophat):原图减去开操作后的图:src - open_dst
因为开操作带来的结果是放大了裂缝或者局部低亮度的区域,因此,从原图中减去开运算后的图,得到的效果图突出了比原图轮廓周围的区域更明亮的区域,且这一操作和选择的核的大小相关。
顶帽运算往往用来分离比邻近点亮一些的斑块。当一幅图像具有大幅的背景的时候,而微小物品比较有规律的情况下,可以使用顶帽运算进行背景提取。
函数与开操作函数相同,只是op参数不同,取值为:cv.MORPH_TOPHAT

import cv2 as cv
import numpy as np
img = cv.imread('j.png',0)
kernel = np.ones((5,5),np.uint8)
tophat = cv.morphologyEx(img, cv.MORPH_TOPHAT, kernel)

在这里插入图片描述

图像的黑帽操作

黑帽(blackhat):闭运算图像与原图像差值:close_dst - src
黑帽运算后的效果图突出了比原图轮廓周围的区域更暗的区域,且这一操作和选择的核的大小相关。所以,黑帽运算用来分离比邻近点暗一些的斑块,显示出图像的轮廓。
函数与开操作函数相同,只是op参数不同,取值为:cv.MORPH_BLACKHAT

import cv2 as cv
import numpy as np
img = cv.imread('j.png',0)
kernel = np.ones((5,5),np.uint8)
blackhat = cv.morphologyEx(img, cv.MORPH_BLACKHAT, kernel)

在这里插入图片描述

图像的梯度(Image Gradients)

opencv提供了三种梯度过滤器或高通过滤器Sobel、Scharr、Laplacian.(高通滤波器high-pass fliter(HPF) 寻找图片的边界)。
图像上的高通滤波器怎么理解,暂时不得而知,

Sobel and Scharr (索贝尔)算子

Sobel operators是高斯模糊操作 加上differentian(变异/分化);所以它对噪音更有抵抗;可以指明衍生物(derivatives)的方向,垂直或者是水平的(分别对应是yorder、xorder)。
在参数ksize中指明kernel 的大小。其中ksize=-1时,会使用3x3的Charr filter(在这个情况下,结果会更好)
Sobel算子是一阶导数的边缘检测算子,在算法实现过程中,通过3×3模板作为核与图像中的每个像素点做卷积和运算,然后选取合适的阈值以提取边缘。
采用3×3邻域可以避免在像素之间内插点上计算梯度。Sobel算子也是一种梯度幅值。
Sobel算子算法的优点是计算简单,速度快。但是由于只采用了2个方向的模板,只能检测水平和垂直方向的边缘,因此这种算法对于纹理较为复杂的图像,其边缘检测效果就不是很理想。
该算法认为:凡灰度新值大于或等于阈值的像素点时都是边缘点。这种判断欠合理,会造成边缘点的误判,因为许多噪声点的灰度值也很大。
scharr算子与Sobel的不同点是在平滑部分,这里所用的平滑算子是1/16∗[3,10,3],相比于1/4∗[1,2,1],中心元素占的权重更重,这可能是相对于图像这种随机性较强的信号,邻域相关性不大,所以邻域平滑应该使用相对较小的标准差的高斯函数,也就是更瘦高的模板

  • 函数原型:dst = cv.Sobel(src, ddepth, dx, dy[, dst[, ksize[, scale[, delta[, borderType]]]]])
  • src:输入图像
  • dst:输出图像size大小与channel数量与原始图像相同
  • ddepth:输出图像的像素深度,组合如下
输入图像 输出图像
CV_8U -1/CV_16S/CV_32F/CV_64F
CV_16U、CV_16S -1/CV_32F/CV_64F
CV_32F -1/CV_32F/CV_64F
CV_64F -1/CV_64F
  • dx: x 方向上的差分阶数
  • dy: y 方向上的差分阶数
  • ksize:Sobel kernel大小,必须是:1, 3, 5, or 7。
  • scale:可选的导数比例因子,默认未使用
  • delta:可选的delta值,输出结果前叠加到结果上
  • borderType:图像边界模式

  • 函数原型:dst = cv.Scharr( src, ddepth, dx, dy[, dst[, scale[, delta[, borderType]]]] )
  • src:输入图像
  • dst:输出图像size大小与channel数量与原始图像相同
  • ddepth:输出图像的像素深度。
  • dx: x 方向上的差分阶数
  • dy: y 方向上的差分阶数
  • ksize:Sobel kernel大小,必须是:1, 3, 5, or 7。
  • scale:可选的导数比例因子,默认未使用
  • delta:可选的delta值,输出结果前叠加到结果上
  • borderType:图像边界模式
import numpy as np
import cv2 as cv
from matplotlib import pyplot as plt
img = cv.imread('dave.jpg',0)
sobelx = cv.Sobel(img,cv.CV_64F,1,0,ksize=5)
sobely = cv.Sobel(img,cv.CV_64F,0,1,ksize=5)
plt.subplot(2,2,1),plt.imshow(img,cmap = 'gray')
plt.title('Original'), plt.xticks([]), plt.yticks([])
plt.subplot(2,2,3),plt.imshow(sobelx,cmap = 'gray')
plt.title('Sobel X'), plt.xticks([]), plt.yticks([])
plt.subplot(2,2,4),plt.imshow(sobely,cmap = 'gray')
plt.title('Sobel Y'), plt.xticks([]), plt.yticks([])
plt.show()

原图见拉普拉斯算子原图
在这里插入图片描述
注意:

  • 当输出的值为cv2.CV_8U或者是np.uint8时;Black- to -White transition 是positive slope(正数);反过来White- to - Black transition取negative slope(负数)。所以当输出时转化数据为unit8时,所有的negative slope 都会被置为0,这种情况都会丢失了一部分的边缘。
  • 更好的处理方式是,让输出的数据类型保存到更高的形式,例如cv2.CV_16S,cv2.CV_64F等。取它的绝对值,然后再把值转化回cv2.CV_8U。
import numpy as np
import cv2 as cv
from matplotlib import pyplot as plt
img = cv.imread('box.png',0)
# Output dtype = cv.CV_8U
sobelx8u = cv.Sobel(img,cv.CV_8U,1,0,ksize=5)
# Output dtype = cv.CV_64F. Then take its absolute and convert to cv.CV_8U
sobelx64f = cv.Sobel(img,cv.CV_64F,1,0,ksize=5)
abs_sobel64f = np.absolute(sobelx64f)
sobel_8u = np.uint8(abs_sobel64f)
plt.subplot(1,3,1),plt.imshow(img,cmap = 'gray')
plt.title('Original'), plt.xticks([]), plt.yticks([])
plt.subplot(1,3,2),plt.imshow(sobelx8u,cmap = 'gray')
plt.title('Sobel CV_8U'), plt.xticks([]), plt.yticks([])
plt.subplot(1,3,3),plt.imshow(sobel_8u,cmap = 'gray')
plt.title('Sobel abs(CV_64F)'), plt.xticks([]), plt.yticks([])
plt.show()

在这里插入图片描述

Laplacian (拉普拉斯)算子

Laplacian 算子是n维欧几里德空间中的一个二阶微分算子,定义为梯度grad的散度div。
这里不甚明白,还需要多加学习与练习,先看效果吧

  • 函数原型:dst=cv.Laplacian( src, ddepth[, dst[, ksize[, scale[, delta[, borderType]]]]] )
  • src:输入图像
  • dst:输出图像size大小与channel数量与原始图像相同
  • ddepth:期望输出图像的像素深度
  • dx: x 方向上的差分阶数
  • dy: y 方向上的差分阶数
  • ksize:Sobel kernel大小,必须是:1, 3, 5, or 7。
  • scale:可选的导数比例因子,默认未使用
  • delta:可选的delta值,输出结果前叠加到结果上
  • borderType:图像边界模式
import numpy as np
import cv2 as cv
from matplotlib import pyplot as plt
img = cv.imread('dave.jpg',0)
laplacian = cv.Laplacian(img,cv.CV_64F)
plt.subplot(2,2,1),plt.imshow(img,cmap = 'gray')
plt.title('Original'), plt.xticks([]), plt.yticks([])
plt.subplot(2,2,2),plt.imshow(laplacian,cmap = 'gray')
plt.title('Laplacian'), plt.xticks([]), plt.yticks([])
plt.show()

在这里插入图片描述

canny边缘检测

  1. 应用高斯滤波来平滑图像,目的是去除噪声
  2. 用一阶偏导有限差分计算梯度幅值和方向,找寻图像的强度梯度(intensity gradients)
  3. 应用非最大抑制(non-maximum suppression)技术来消除边误检(本来不是但检测出来是)
  4. 应用双阈值的方法来决定可能的(潜在的)边界
  5. 利用滞后技术来跟踪边界
    复杂的算法原理我描述不出来,只能上代码看效果
  • 函数原型:edges=cv.Canny( image, threshold1, threshold2[, edges[, apertureSize[, L2gradient]]] )
  • image:8-bit input image.
  • edges:单通道存储边缘的输出图像,size大小与输入图像相同
  • threshold1:滞后处理的第一个阈值,(低阈值)
  • threshold2:滞后处理的第一个阈值,(高阈值)
  • apertureSize:算子内核大小(滤波计算矩阵的大小默认为3)可以是1、3、5、7
  • L2gradient :标志位,用于指示是否使用更准确的梯度计算方法,参数为true时会增加计算量
import numpy as np
import cv2 as cv
from matplotlib import pyplot as plt
img = cv.imread('messi5.jpg',0)
edges = cv.Canny(img,100,200)
plt.subplot(121),plt.imshow(img,cmap = 'gray')
plt.title('Original Image'), plt.xticks([]), plt.yticks([])
plt.subplot(122),plt.imshow(edges,cmap = 'gray')
plt.title('Edge Image'), plt.xticks([]), plt.yticks([])
plt.show()

在这里插入图片描述

图像金字塔(Image Pyramids)

图像金字塔有两种:高斯金字塔、拉普拉斯金字塔

  • 高斯金字塔:向下降采样,图像每级缩小一半。金字塔从i层生成第i+1层(i从0开始),我们要先用高斯核对Gi进行卷积,然后,删除所有偶数行和偶数列。这样,新得到的图像面积会变为源图像的四分之一。循环上述过程,即可产生整个金字塔。
  • 拉普拉斯金字塔:从顶层图像中向上采样重建图像,每一级图像放大一倍。图像首先在每个维度上扩大为原来的两倍,新增的行以0填充,然后给指定的滤波器进行卷积(实际上是一个在每一维上都扩大为2倍的过滤器)去估计“丢失”像素的近似值。得到后的图像与原来的图像相比较会发觉比较模糊,丢失了一些信息。为了恢复出原来的图像,我们需要获得这些丢失的信息,这些信息就构成了拉普拉斯金字塔。

高斯金字塔函数说明:

  • 函数原型:dst = cv.pyrUp( src[, dst[, dstsize[, borderType]]] )
  • src:输入图图像
  • dst:输出图像,图像的size为dstsize指定,type保持与src相同
  • dstsize:输出图像大小
  • borderType:边界模式,仅支持BORDER_DEFAULT
img = cv.imread('messi5.jpg')
lower_reso1 = cv.pyrDown(img)
lower_reso2 = cv.pyrDown(lower_reso1)
lower_reso3 = cv.pyrDown(lower_reso2)

在这里插入图片描述


拉普拉斯金字塔函数说明:

  • 函数原型:dst = cv.pyrDown( src[, dst[, dstsize[, borderType]]] )
  • src:输入图图像
  • dst:输出图像,图像的size为dstsize指定,type保持与src相同
  • dstsize:输出图像大小
  • borderType:边界模式,BORDER_CONSTANT不支持
img = cv.imread('messi5.jpg')
lower_reso = cv.pyrDown(higher_reso)
higher_reso2 = cv.pyrUp(lower_reso)

在这里插入图片描述

图像的轮廓(Contours in opencv)

如何找到图像轮廓

cv.findContours()函数可以查找图像的轮廓

  • 函数原型:image, contours, hierarchy = cv.findContours( image, mode, method[, contours[, hierarchy[, offset]]] )
  • image:单通道二值图像,一般是经过Canny、拉普拉斯等边缘检测算子处理过的二值图像
  • contours:检测到的轮廓集,每一个轮廓由一个vector保存的连续点组成。定义为“vector<vector> contours”,是一个向量,并且是一个双重向量,向量内每个元素保存了一组由连续的Point点构成的点的集合的向量,每一组Point点集就是一个轮廓;有多少轮廓,向量contours就有多少元素
  • hierarchy:是一个向量,向量内每个元素保存了一个包含4个int整型的数组。向量hiararchy内的元素和轮廓向量contours内的元素是一一对应的,向量的容量相同。hierarchy向量内每一个元素的4个int型变量——hierarchy[i][0] ~hierarchy[i][3],分别表示第 i个轮廓的后一个轮廓、前一个轮廓、父轮廓、内嵌轮廓的索引编号。如果当前轮廓没有对应的后一个
    轮廓、前一个轮廓、父轮廓或内嵌轮廓的话,则hierarchy[i][0] ~hierarchy[i][3]的相应位被设置为默认值-1
  • mode:轮廓的检索模式,
    CV_RETR_EXTERNAL:只检测最外围轮廓,包含在外围轮廓内的内围轮廓被忽略
    CV_RETR_LIST:检测所有的轮廓,包括内围、外围轮廓,但是检测到的轮廓不建立等级关系,彼此之间独立,没有等级关系,这就意味着这个检索模式下不存在父轮廓或内嵌轮廓,所以hierarchy向量内所有元素的第3、第4个分量都会被置为-1
    CV_RETR_CCOMP:检测所有的轮廓,但所有轮廓只建立两个等级关系,外围为顶层,若外围内的内围轮廓还包含了其他的轮廓信息,则内围内的所有轮廓均归属于顶层
    CV_RETR_TREE:检测所有轮廓,所有轮廓建立一个等级树结构。外层轮廓包含内层轮廓,内层轮廓还可以继续包含内嵌轮廓
  • method:轮廓的近似方法
    CV_CHAIN_APPROX_NONE:保存物体边界上所有连续的轮廓点到contours向量内
    CV_CHAIN_APPROX_SIMPLE:仅保存轮廓的拐点信息,把所有轮廓拐点处的点保存入contours向量内,拐点与拐点之间直线段上的信息点不予保留
    CV_CHAIN_APPROX_TC89_L1,CV_CHAIN_APPROX_TC89_KCOS:使用teh-Chinl chain 近 似算法
  • offset:偏移量,所有的轮廓信息相对于原始图像对应点的偏移量,相当于在每一个检测出的轮廓点上加上该偏移量,并且Point还可以是负值
import numpy as np
import cv2 as cv
im = cv.imread('test.jpg')
imgray = cv.cvtColor(im, cv.COLOR_BGR2GRAY)
ret, thresh = cv.threshold(imgray, 127, 255, 0)
im2, contours, hierarchy = cv.findContours(thresh, cv.RETR_TREE, cv.CHAIN_APPROX_SIMPLE)

cv.drawContours函数用于画图像的轮廓

  • 函数原型:image = cv.drawContours( image, contours, contourIdx, color[, thickness[, lineType[, hierarchy[, maxLevel[, offset]]]]] )
  • image:用以绘制轮廓的图像。和其他绘图函数一样,边界图像被感兴趣区域(ROI)所剪切
  • contours:所有输入图像的轮廓集
  • contourIdx:指示轮廓集中的哪一个轮廓被画出,如果是负值,则所有轮廓被画出
  • color:轮廓颜色
  • thickness:绘制轮廓线的宽度。如果为负值(例如,等于CV_FILLED),则contour内部将被绘制
  • lineType:轮廓线段的类型,具体查看cvLine的描述
  • hierarchy:可选的轮廓层次信息,如果只想画某些层次的轮廓,才需要此参数。
  • maxLevel:绘制轮廓的最大等级。如果等级为0,绘制单独的轮廓。如果为1,绘制轮廓及在其后的相同的级别下轮廓。如果值为2,所有的轮廓。如果等级为2,绘制所有同级轮廓及所有低一级轮廓,诸此种种。如果值为负数,函数不绘制同级轮廓,但会升序绘制直到级别为abs(max_level)-1的子轮廓。
  • offset:按照给出的偏移量移动每一个轮廓点坐标.当轮廓是从某些感兴趣区域(ROI)中提取的然后需要在运算中考虑ROI偏移量时,将会用到这个参数。
    在这里插入图片描述

轮廓的特点

  1. 轮廓矩Moments
    opencv中的矩主要包括以下几种:空间矩,中心矩和中心归一化矩。从图像中计算出来的矩通常描述了图像不同种类的几何特征如:大小、灰度、方向、形状等,图像矩广泛应用于模式识别、目标分类、目标识别与防伪估计、图像编码与重构等领域。
import numpy as np
import cv2 as cv
img = cv.imread('star.jpg',0)
ret,thresh = cv.threshold(img,127,255,0)
im2,contours,hierarchy = cv.findContours(thresh, 1, 2)
cnt = contours[0]
M = cv.moments(cnt)
print( M )
  1. 轮廓面积Contour Area
area = cv.contourArea(cnt)
  1. 轮廓周长Contour Perimeter
perimeter = cv.arcLength(cnt,True)
  1. 轮廓近似图Contour Approximation
epsilon = 0.1*cv.arcLength(cnt,True)
approx = cv.approxPolyDP(cnt,epsilon,True)
  1. 轮廓凸包Convex Hull
hull = cv.convexHull(points[, hull[, clockwise[, returnPoints]]
  1. 轮廓凸包检查Checking Convexity
k = cv.isContourConvex(cnt)
  1. 轮廓矩形边框Bounding Rectangle
x,y,w,h = cv.boundingRect(cnt)
cv.rectangle(img,(x,y),(x+w,y+h),(0,255,0),2)
  1. 轮廓最小圆Minimum Enclosing Circle
rect = cv.minAreaRect(cnt)
box = cv.boxPoints(rect)
box = np.int0(box)
cv.drawContours(img,[box],0,(0,0,255),2)

12.轮廓最椭圆 Fitting an Ellipse

(x,y),radius = cv.minEnclosingCircle(cnt)
center = (int(x),int(y))
radius = int(radius)
cv.circle(img,center,radius,(0,255,0),2)
  1. 轮廓直线Fitting a Line
rows,cols = img.shape[:2]
[vx,vy,x,y] = cv.fitLine(cnt, cv.DIST_L2,0,0.01,0.01)
lefty = int((-x*vy/vx) + y)
righty = int(((cols-x)*vy/vx)+y)
cv.line(img,(cols-1,righty),(0,lefty),(0,255,0),2)

轮廓的属性

  1. 轮廓宽高比Aspect Ratio
    轮廓宽高比是轮廓的矩形边框的宽高比
x,y,w,h = cv.boundingRect(cnt)
aspect_ratio = float(w)/h
  1. 轮廓区域比Extent
    轮廓区域比是轮廓的面积与矩形边框的面积比
area = cv.contourArea(cnt)
x,y,w,h = cv.boundingRect(cnt)
rect_area = w*h
extent = float(area)/rect_area
  1. 轮廓固态Solidity
    轮廓固态是轮廓的面积与凸包面积的比
area = cv.contourArea(cnt)
hull = cv.convexHull(cnt)
hull_area = cv.contourArea(hull)
solidity = float(area)/hull_area
  1. 轮廓直径Equivalent Diameter
area = cv.contourArea(cnt)
equi_diameter = np.sqrt(4*area/np.pi)
  1. 轮廓方向Orientation
(x,y),(MA,ma),angle = cv.fitEllipse(cnt)
  1. 掩膜与像素位置Mask and Pixel Points
mask = np.zeros(imgray.shape,np.uint8)
cv.drawContours(mask,[cnt],0,255,-1)
pixelpoints = np.transpose(np.nonzero(mask))
pixelpoints = cv.findNonZero(mask)
  1. 最大与最小值位置Maximum Value, Minimum Value and their locations
min_val, max_val, min_loc, max_loc = cv.minMaxLoc(imgray,mask = mask)
  1. 平均颜色与强度Mean Color or Mean Intensity
mean_val = cv.mean(im,mask = mask)
  1. 轮廓极点Extreme Points
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])

轮廓的层次

cv2.findContours()函数来找轮廓的时候,我们传入了一个参数,Contour Retrieval Mode。我们一般传的是cv2.RETR_LIST或者cv2.RETR_TREE这样就可以了。但是这个参数实际是什么意思呢?
函数输出时我们得到了三个数组,第一个是图像,第二个是我们的轮廓,第三个输出名字是hierarchy层次。
什么是层次呢?
用cv2.findContours()函数来检测图像里的轮廓,有时候轮廓在不同的地方,但是在有些情况下,有些图形在别的图形里面,就像图形嵌套,在这种情况下,我们把外面那层图形叫做parent,里面的叫child。这样图形里的轮廓之间就有了关系。我们可以指定一个轮廓和其他之间的是如何连接的,这种关系就是层级。

每个轮廓有他自己的关于层级的信息,谁是他的孩子,谁是他的父亲等。OpenCV用一个包含四个值得数组来表示:[Next, Previous, First_Child, Parent]"Next表明同一层级的下一个轮廓"比如,在我们的图片里的contour-0,水上hi他相同层级的下一个轮廓?是contour-1,所以Next=1,对于Contour-1,下一个是contour-2,所以Next=2那对于contour-2呢?没有同层级的下一个轮廓,所以Next=-1。那么对于contour-4呢?同层级的下一个是contour-5,所以下一个轮廓是contour-5.Next=5"Previous指同层级的前一个轮廓"和上面一样,contour-1的前一个是contour-0.contour-2的前一个contour-1.对于contour-0没有前序,所以-1"First_Child指它的第一个孩子轮廓"不用解释,对于contour-2,孩子是contour-2a,所以这里是contour-2a的索引,contour-3a有两个孩子,但我们只取第一个,是contour-4,所以First_Child=4."Parent指它的父轮廓索引"和First_Child相反,contour-4和contour-5的parent都是contour-3a,对于contour-3a,是contour-3注意:如果没有孩子或者父亲,就为-1
在这里插入图片描述

  1. RETR_LIST
    获取所有轮廓,但是不建立父子关系,他们都是一个层级。所以,层级属性第三个和第四个字段(父子)都是-1,但是Next和Previous还是有对应值。
  2. RETR_EXTERNAL
    这个模式,它返回最外层的。所有孩子轮廓都不要,我们可以说在这种情况下,只有家族里最老的会被照顾,其他都不管。
  3. RETR_CCOMP
    这个模式获取所有轮廓并且把他们组织到一个2层结构里,对象的轮廓外边界在等级1里,轮廓内沿(如果有的话)放在层级2里。如果别的对象在它里面,里面的对象轮廓还是放在层级1里,它的内沿在层级2
  4. RETR_TREE
    这个取回所有的轮廓并且创建完整的家族层级列表,它甚至能告诉你谁是祖父,父亲,儿子,孙子。

写到这里发现,OPENCV的内容越来越多,需要消化的时间越来越长,往后几篇时间也许会在1个月以后了,就现在这些写的东西也需要多次的练习与理解,继续努力吧。

猜你喜欢

转载自blog.csdn.net/zhaoyun_zzz/article/details/85259975