腐蚀:卷积核沿着图像滑动,如果与卷积核对应的原图像的所有像素值都是 1,那么中心元素就保持原来的像素值,否则就变为零。
膨胀:与卷积核对应的原图像的像素值中只要有一个是 1,中心元素的像素值就是 1。
这个描述看起来有点生硬,为了进一步理解opencv对腐蚀与膨胀都是针对灰度图的形态学操作,还是自己操作一遍。
先看下 Python wrapper for OpenCV里的定义
anchor – 卷积核锚点。其默认值为(-1,-1)说明位于kernel的中心位置
==>查看源码opencv-4.1.1\modules\imgproc\src\filterengine.hpp
static inline Point normalizeAnchor( Point anchor, Size ksize )
{
if( anchor.x == -1 )
anchor.x = ksize.width/2;
if( anchor.y == -1 )
anchor.y = ksize.height/2;
CV_Assert( anchor.inside(Rect(0, 0, ksize.width, ksize.height)) );
return anchor;
}
首先定义一个5*5的图片矩阵表示,修改其中几个像素为1
img_5X5= np.zeros((5,5), dtype=np.uint8)
img_5X5[2,2]=1
img_5X5[3,3]=1
img_5X5[3,4]=1
再义一个3 * 3的结构元素kernel,该结构元素用3*3的矩阵表示
kernel = np.zeros((3,3),np.uint8)
kernel[1,1]=1;
kernel[0,0]=1;
腐蚀操作
-举个有操作影响的例子当kernel移动到img_5X5(2,2)即图中心点位置,腐蚀操作:
腐蚀img_5X5(2,2)应该等是 min(0,1)最小值0
同理移动到img_5X5(3,4)腐蚀结果也是0
但是到img_5X5(3,3),min(1,1)=1
整体img_5X5的腐蚀结果为:
这里可以看出,其实3*3的kernel在默认锚点为(-1,-1)只有时候只有黄色填充部分有效:
膨胀操作
kernel在默认锚点为(-1,-1)膨胀操作结果:
锚点改变为坐标anchor=(0,0),即红色字体1为锚点,黄色填充部分为有效单元:
当kernel移动到img_5X5(2,2)即图中心点位置,腐蚀操作:
腐蚀img_5X5(3,4)腐蚀结果min(1)=1 理解为当腐蚀时在opencv中kernel为1的单元格和图像img_5X5没有交叉的有效单元,锚点参与运算的情况,其值不变。
cv2.erode(img_5X5,kernel,iterations = 1,anchor=(2,2))
cv2.dilate(img_5X5,kernel,iterations = 1,anchor=(2,2))
此为特殊情况:
当kernel移动到img_5X5(0,0)位置,opencv中,没有交叉的有效单情况锚点值:当腐蚀时,此时(0,0)位置像素值为255,当膨胀时,(0,0)位置像素值为0
腐蚀:img_5X5(0,0)的值为 min(255)=255
以此类推img_5X5第一行,第一列的腐蚀值都为255
膨胀:img_5X5(0,0)的值为 max(0)=0
以此类推img_5X5第一行,第一列的膨胀值都为0
img_5X5(4,4)位置的值为min(1,1)=1
腐蚀结果:
膨胀结果
总结:完全没有交叉的有效单情况的(且无borderType)锚点值分析 ,锚点分为有没有值的二种情况:
1 锚点有值:原有像素值不变
2 锚点无值:当腐蚀时像素值为255,当膨胀时置像素值为0
此处设定图像img_5X5外填充值,重新按照kernel锚点anchor=(2,2)的方式腐蚀及膨胀图像
/*
Various border types, image boundaries are denoted with '|'
* BORDER_REPLICATE: aaaaaa|abcdefgh|hhhhhhh
* BORDER_REFLECT: fedcba|abcdefgh|hgfedcb
* BORDER_REFLECT_101: gfedcb|abcdefgh|gfedcba
* BORDER_WRAP: cdefgh|abcdefgh|abcdefg
* BORDER_CONSTANT: iiiiii|abcdefgh|iiiiiii with some specified 'i'
*/
erosion = cv2.erode(img_5X5,kernel,iterations = 1,anchor=(2,2),borderType=cv2.BORDER_CONSTANT,borderValue=1)
dilate =cv2.dilate(img_5X5,kernel,iterations = 1,anchor=(2,2),borderType=cv2.BORDER_CONSTANT,borderValue=1)
图示绿色部分为img_5X5,蓝色为填充值1:
kernel移动到img_5X5(0,0)位置 锚点为anchor=(2,2)的腐蚀操作值:min(1,1)=1
kernel移动到img_5X5(0,0)位置 锚点为anchor=(2,2)的膨胀操作值:max(1,1)=1
kernel移动到img_5X5(1,1)位置 锚点为anchor=(2,2)的膨胀操作值:max(1,0)=1
kernel移动到img_5X5(1,1)位置 锚点为anchor=(2,2)的腐蚀操作值:min(1,0)=0
img_5X5,锚点为anchor=(2,2),borderValue=1时 其腐蚀结果为绿色部分
img_5X5,锚点为anchor=(2,2),borderValue=1时,第一二行及一二列膨胀值全部为1.其膨胀结果为绿色部分:
其余改变kernel的形态及锚点位置可自行操作研究,原理相同。
不同锚定点对应卷积的起始位置与锚定像素输出示意:
用到的代码:
import cv2
import numpy as np
#自定义5*5矩阵
img_5X5= np.zeros((5,5), dtype=np.uint8)
img_5X5[2,2]=1
img_5X5[3,3]=1
img_5X5[3,4]=1
print(img_5X5)
#卷积核设定生成
kernel = np.zeros((3,3),np.uint8)
kernel[1,1]=1;
kernel[0,0]=1;
print(kernel)
erosion = cv2.erode(img_5X5,kernel,iterations = 1,anchor=(-1,-1))
print(erosion)
dilate =cv2.dilate(img_5X5,kernel,iterations = 1 ,anchor=(-1,-1))
print(dilate)
erosion = cv2.erode(img_5X5,kernel,iterations = 1,anchor=(0,0),borderValue=255)
print(erosion)
dilate =cv2.dilate(img_5X5,kernel,iterations = 1,anchor=(0,0),borderValue=0)
print(dilate)
erosion = cv2.erode(img_5X5,kernel,iterations = 1,anchor=(2,2),borderType=cv2.BORDER_CONSTANT,borderValue=1)
print(erosion)
dilate =cv2.dilate(img_5X5,kernel,iterations = 1,anchor=(2,2),borderType=cv2.BORDER_CONSTANT,borderValue=1)
print(dilate)
cv2.waitKey(0)
cv2.destroyAllWindows()