图像的膨胀与图像腐蚀是一对相反的过程,与图像腐蚀相似,图像膨胀同样需要结构元素用于控制图像膨胀的效果。结构元素可以任意指定结构的中心点,并且结构元素的尺寸和具体内容都可以根据需求自己定义。定义结构元素之后,将结构元素的中心点依次放到图像中每一个非0元素处,如果原图像中某个元素被结构元素覆盖,但是该像素的像素值不与结构元素中心点对应的像素点的像素值相同,那么将原图像中的该像素的像素值修改为结构元素中心点对应点的像素值。图像的膨胀过程示意图如图6-12所示,图6-12中左侧为待膨胀的原图像,中间为结构元素,首先将结构元素的中心与原图像中的A像素重合,将结构元素覆盖的所有像素的像素值都修改为1,将结构元素中心点依次与原图像中的每个像素重合,判断是否有需要填充的像素。原图像膨胀的结果如图6-17中右侧图像所示。
图像膨胀数学表示形式如式(6.5)所示,通过公式可以发现,其实图像A的膨胀运算就是生成能够将结构元素B全部包含的图像。
OpenCV 4提供了用于图像膨胀的dilate()函数,该函数的函数原型在代码清单6-13中给出。
void cv::dilate(InputArray src,
OutputArray dst,
InputArray kernel,
Point anchor = Point(-1,-1),
int iterations = 1,
int borderType = BORDER_CONSTANT,
const Scalar & borderValue = morphologyDefaultBorderValue()
)
- src:输入的待膨胀图像,图像的通道数可以是任意的,但是图像的数据类型必须是CV_8U,CV_16U,CV_16S,CV_32F或CV_64F之一。
- dst:膨胀后的输出图像,与输入图像src具有相同的尺寸和数据类型。
- kernel:用于膨胀操作的结构元素,可以自己定义,也可以用getStructuringElement()函数生成。
- anchor:中心点在结构元素中的位置,默认参数为结构元素的几何中心点
- iterations:膨胀的次数,默认值为1。
- borderType:像素外推法选择标志,取值范围在表3-5中给出。默认参数为BORDER_DEFAULT,表示不包含边界值倒序填充。
- borderValue:使用边界不变外推法时的边界值。
该函数根据结构元素对输入图像进行膨胀,在膨胀多通道图像时每个通道独立进行膨胀运算。函数的第一个参数为待膨胀的图像,图像通道数可以是任意的,但是图像的数据类型必须是CV_8U,CV_16U,CV_16S,CV_32F或CV_64F之一。函数第二个参数为膨胀后的输出图像,与输入图像具有相同的尺寸和数据类型。函数第三个和第四个参数都是与结构元素相关的参数,第三个参数为结构元素,膨胀时使用的结构元素尺寸越大效果越明显,第四个参数为结构元素的中心位置,第四个参数的默认值为Point(-1,-1),表示结构元素的几何中心处为结构元素的中心点。函数第五个参数是使用结构元素膨胀的次数,膨胀次数越多效果越明显,默认参数为1,表示只膨胀1次。函数第六个参数是图像像素外推法的选择标志,第七个参数为使用边界不变外推法时的边界值,这两个参数对图像中主要部分的膨胀操作没有影响,因此在多数情况下使用默认值即可。
需要注意的是该函数的膨胀过程只针对图像中的非0像素,因此如果图像是以0像素为背景,那么膨胀操作后会看到图像中的内容变得更粗更大;如果图像是以255像素为背景,那么膨胀操作后会看到图像中的内容变得更细更小。
为了更加了解图像膨胀的效果以及dilate()函数的使用方法,在代码清单6-14中给出了对图6-17中的原图像进行膨胀的示例程序,程序运行结果如图6-18所示。另外,程序中分别利用矩形结构元素和十字结构元素对像素值为0做背景的图像和像素值为255做背景的图像进行膨胀,结果在图6-19、图6-20给出。最后为了验证膨胀与腐蚀效果之间的关系,求取黑背景图像的腐蚀结果与白背景图像的膨胀结果进行逻辑和、逻辑异或运算,证明两个过程的相反性,结果在图6-21给出。
#include <opencv2\opencv.hpp>
#include <iostream>
#include <vector>
using namespace cv;
using namespace std;
int main()
{
//生成用于腐蚀的原图像
Mat src = (Mat_<uchar>(6, 6) << 0, 0, 0, 0, 255, 0,
0, 255, 255, 255, 255, 255,
0, 255, 255, 255, 255, 0,
0, 255, 255, 255, 255, 0,
0, 255, 255, 255, 255, 0,
0, 0, 0, 0, 0, 0);
Mat struct1, struct2;
struct1 = getStructuringElement(0, Size(3, 3)); //矩形结构元素
struct2 = getStructuringElement(1, Size(3, 3)); //十字结构元素
Mat erodeSrc; //存放腐蚀后的图像
dilate(src, erodeSrc, struct2);
namedWindow("src", WINDOW_GUI_NORMAL);
namedWindow("dilateSrc", WINDOW_GUI_NORMAL);
imshow("src", src);
imshow("dilateSrc", erodeSrc);
Mat LearnCV_black = imread("LearnCV_black.png", IMREAD_ANYCOLOR);
Mat LearnCV_write = imread("LearnCV_write.png", IMREAD_ANYCOLOR);
if (LearnCV_black.empty()||LearnCV_write.empty())
{
cout << "请确认图像文件名称是否正确" << endl;
return-1;
}
Mat dilate_black1, dilate_black2, dilate_write1, dilate_write2;
//黑背景图像膨胀
dilate(LearnCV_black, dilate_black1, struct1);
dilate(LearnCV_black, dilate_black2, struct2);
imshow("LearnCV_black", LearnCV_black);
imshow("dilate_black1", dilate_black1);
imshow("dilate_black2", dilate_black2);
//白背景图像膨胀
dilate(LearnCV_write, dilate_write1, struct1);
dilate(LearnCV_write, dilate_write2, struct2);
imshow("LearnCV_write", LearnCV_write);
imshow("dilate_write1", dilate_write1);
imshow("dilate_write2", dilate_write2);
//比较膨胀和腐蚀的结果
Mat erode_black1, resultXor, resultAnd;
erode(LearnCV_black, erode_black1, struct1);
bitwise_xor(erode_black1, dilate_write1, resultXor);
bitwise_and(erode_black1, dilate_write1, resultAnd);
imshow("resultXor", resultXor);
imshow("resultAnd", resultAnd);
waitKey(0);
return0;
}