32、形态学处理
1、图像腐蚀
图像的腐蚀过程与图像的卷积操作类似,都需要模板矩阵来控制运算的结果,在图像的腐蚀和膨胀中这个模板矩阵被称为结构元素。与图像卷积相同,结构元素可以任意指定图像的中心点,并且结构元素的尺寸和具体内容都可以根据需求自己定义。定义结构元素之后,将结构元素的中心点依次放到图像中每一个非0元素处,如果此时结构元素内所有的元素所覆盖的图像像素值均不为0,则保留结构元素中心点对应的图像像素,否则将删除结构元素中心点对应的像素。图像的腐蚀过程示意图如图6-12所示,图6-12中左侧为待腐蚀的原图像,中间为结构元素,首先将结构元素的中心与原图像中的A像素重合,此时结构元素中心点的左侧和上方元素所覆盖的图像像素值均为0,因此需要将原图像中的A像素删除;当把结构元素的中心点与B像素重合时,此时结构元素中所有的元素所覆盖的图像像素值均为1,因此保留原图像中的B像素。将结构元素中心点依次与原图像中的每个像素重合,判断每一个像素点是否保留或者删除,最终原图像腐蚀的结果如图6-12中右侧图像所示。
图像腐蚀过程中使用的结构元素可以根据需求自己生成,但是为了研究人员的使用方便,OpenCV 4提供了getStructuringElement()函数用于生成常用的矩形结构元素、十字结构元素和椭圆结构元素。该函数的函数原型在代码清单6-10中给出。
代码清单6-10 getStructuringElement()函数原型 1.Mat cv::getStructuringElement(int shape, 2. Size ksize, 3. Point anchor = Point(-1,-1) 4. )
- shape:结构元素的种类,可以选择的参数及含义在表6-5中给出。
- ksize:结构元素的尺寸大小
- anchor:中心点的位置,默认参数为结构元素的几何中心点。
该函数用于生成图像形态学操作中常用的矩形结构元素、十字结构元素和椭圆结构元素。函数第一个参数为生成结构元素的种类,可以选择的参数及含义在表6-5给出,函数第二个参数是结构元素的尺寸大小,能够影响到图像腐蚀的效果,一般情况下,结构元素的种类相同时,结构元素的尺寸越大腐蚀效果越明显。函数的最后一个参数是结构元素的中心点,只有十字结构元素的中心点位置会影响图像腐蚀后的轮廓形状,其他种类的结构元素的中心点位置只影响形态学操作结果的平移量。
标志参数 | 简记 | 作用 |
---|---|---|
MORPH_RECT | 0 | 矩形结构元素,所有元素都为1 |
MORPH_CROSS | 1 | 十字结构元素,中间的列和行元素为1 |
MORPH_ELLIPSE | 2 | 椭圆结构元素,矩形的椭圆内接元素为1 |
OpenCV 4提供了用于图像腐蚀的erode()函数,该函数的函数原型在代码清单6-11中给出。
代码清单6-11 erode()图像腐蚀 1.void cv::erode(InputArray src, 2. OutputArray dst, 3. InputArray kernel, 4. Point anchor = Point(-1,-1), 5. int iterations = 1, 6. int borderType = BORDER_CONSTANT, 7. const Scalar & borderValue = morphologyDefaultBorderValue() 8. )
- 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像素为背景,那么腐蚀操作后会看到图像中的内容变得更粗更大。
代码清单6-12 myErode.cpp图像腐蚀 1.#include <opencv2\opencv.hpp> 2.#include <iostream> 3.#include <vector> 4. 5.using namespace cv; 6.using namespace std; 7.//绘制包含区域函数 8.void drawState(Mat &img, int number, Mat centroids, Mat stats, String str) { 9. RNG rng(10086); 10 vector<Vec3b> colors; 11. for (int i = 0; i < number; i++) 12. { 13. //使用均匀分布的随机数确定颜色 14. Vec3b vec3 = Vec3b(rng.uniform(0,256),rng.uniform(0,256),rng.uniform(0,256)); 15. colors.push_back(vec3); 16. } 17. 18. for (int i = 1; i < number; i++) 19. { 20. // 中心位置 21. int center_x = centroids.at<double>(i, 0); 22. int center_y = centroids.at<double>(i, 1); 23. //矩形边框 24. int x = stats.at<int>(i, CC_STAT_LEFT); 25. int y = stats.at<int>(i, CC_STAT_TOP); 26. int w = stats.at<int>(i, CC_STAT_WIDTH); 27. int h = stats.at<int>(i, CC_STAT_HEIGHT); 28. 29. // 中心位置绘制 30. circle(img, Point(center_x, center_y), 2, Scalar(0, 255, 0), 2, 8, 0); 31. // 外接矩形 32. Rect rect(x, y, w, h); 33. rectangle(img, rect, colors[i], 1, 8, 0); 34. putText(img, format("%d", i), Point(center_x, center_y), 35. FONT_HERSHEY_SIMPLEX, 0.5, Scalar(0, 0, 255), 1); 36. } 37. imshow(str, img); 38.} 39. 40.int main() 41.{ 42. //生成用于腐蚀的原图像 43. Mat src = (Mat_<uchar>(6, 6) << 0, 0, 0, 0, 255, 0, 44. 0, 255, 255, 255, 255, 255, 45. 0, 255, 255, 255, 255, 0, 46. 0, 255, 255, 255, 255, 0, 47. 0, 255, 255, 255, 255, 0, 48. 0, 0, 0, 0, 0, 0); 49. Mat struct1, struct2; 50. struct1 = getStructuringElement(0, Size(3, 3)); //矩形结构元素 51. struct2 = getStructuringElement(1, Size(3, 3)); //十字结构元素 52. 53. Mat erodeSrc; //存放腐蚀后的图像 54. erode(src, erodeSrc, struct2); 55. namedWindow("src", WINDOW_GUI_NORMAL); 56. namedWindow("erodeSrc", WINDOW_GUI_NORMAL); 57. imshow("src", src); 58. imshow("erodeSrc", erodeSrc); 59. 60. Mat LearnCV_black = imread("LearnCV_black.png", IMREAD_ANYCOLOR); 61. Mat LearnCV_write = imread("LearnCV_write.png", IMREAD_ANYCOLOR); 62. Mat erode_black1, erode_black2, erode_write1, erode_write2; 63. //黑背景图像腐蚀 64. erode(LearnCV_black, erode_black1, struct1); 65. erode(LearnCV_black, erode_black2, struct2); 66. imshow("LearnCV_black", LearnCV_black); 67. imshow("erode_black1", erode_black1); 68. imshow("erode_black2", erode_black2); 69. 70. //白背景腐蚀 71. erode(LearnCV_write, erode_write1, struct1); 72. erode(LearnCV_write, erode_write2, struct2); 73. imshow("LearnCV_write", LearnCV_write); 74. imshow("erode_write1", erode_write1); 75. imshow("erode_write2", erode_write2); 76. 77. //验证腐蚀对小连通域的去除 78. Mat img = imread("rice.png"); 79. Mat img2; 80. copyTo(img, img2, img); //克隆一个单独的图像,用于后期图像绘制 81. Mat rice, riceBW; 82. 83. //将图像转成二值图像,用于统计连通域 84. cvtColor(img, rice, COLOR_BGR2GRAY); 85. threshold(rice, riceBW, 50, 255, THRESH_BINARY); 86. 87. Mat out, stats, centroids; 88. //统计图像中连通域的个数 89. int number = connectedComponentsWithStats(riceBW, out, stats,centroids,8,CV_16U); 90. drawState(img, number, centroids, stats, "未腐蚀时统计连通域"); //绘制图像 91. 92. erode(riceBW, riceBW, struct1); //对图像进行腐蚀 93. number = connectedComponentsWithStats(riceBW, out, stats, centroids, 8, CV_16U); 94. drawState(img2, number, centroids, stats, "腐蚀后统计连通域"); //绘制图像 95. 96. waitKey(0); 97. return 0; 98.}
2、图像膨胀
代码清单6-13 dilate()图像膨胀 1.void cv::dilate(InputArray src, 2. OutputArray dst, 3. InputArray kernel, 4. Point anchor = Point(-1,-1), 5.int iterations = 1, 6.int borderType = BORDER_CONSTANT, 7.const Scalar & borderValue = morphologyDefaultBorderValue() 8. )
- 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像素为背景,那么膨胀操作后会看到图像中的内容变得更细更小。
3、开运算
开运算是对图像腐蚀和膨胀的组合,OpenCV 4没有提供只用于图像开运算的函数,而是提供了图像腐蚀和膨胀运算不同组合形式的morphologyEx()函数,以实现图像的开运算、闭运算、形态学提取、顶帽运算、黑帽运算以及击中击不中变换,该函数的函数原型在代码清单6-15中给出。
- 开运算:先腐蚀后膨胀,作用是去噪;
- 闭运算:先膨胀后腐蚀,作用是连通离散点,平滑轮廓;
- 形态学梯度:原图、腐蚀图、膨胀图三者中其中两者的差值,作用是凸显轮廓;
- 顶帽运算:原图与开运算的差值,作用是分离比临近点亮一些的斑块;
- 黑帽运算:原图与闭运算的差值;
- 击中击不中变换是比图像腐蚀要求更加苛刻的一种形态学操作,图像腐蚀只需要图像能够将结构元素中所有非0元素包含即可,但是击中击不中变换要求原图像中需要存在与结构元素一模一样的结构,即结构元素中非0元素也需要同时被考虑。
代码清单6-15 morphologyEx()函数原型 1.void cv::morphologyEx(InputArray src, 2. OutputArray dst, 3. int op, 4. InputArray kernel, 5. Point anchor = Point(-1,-1), 6. int iterations = 1, 7. int borderType = BORDER_CONSTANT, 8. const Scalar & borderValue = morphologyDefaultBorderValue() 9. )
- src:输入图像,图像的通道数可以是任意的,但是图像的数据类型必须是CV_8U,CV_16U,CV_16S,CV_32F或CV_64F之一。
- dst:形态学操作后的输出图像,与输入图像具有相同的尺寸和数据类型。
- op:形态学操作类型的标志,可以选择的标志及含义在表6-6中给出。
- kernel:结构元素,可以自己生成,也可以用getStructuringElement()函数生成。
- anchor:中心点在结构元素中的位置,默认参数为结构元素的几何中心点
- iterations:处理的次数
- borderType:像素外推法选择标志,取值范围在表3-5中给出。默认参数为BORDER_DEFAULT,表示不包含边界值倒序填充。
- borderValue:使用边界不变外推法时的边界值。
该函数根据结构元素对输入图像进行多种形态学操作,在处理多通道图像时每个通道独立进行处理。函数的第一个参数为待形态学处理的图像,图像通道数可以是任意的,但是图像的数据类型必须是CV_8U,CV_16U,CV_16S,CV_32F或CV_64F之一。函数第二个参数为形态学处理后的输出图像,与输入图像具有相同的尺寸和数据类型。函数第三个参数是形态学操作类型的选择标志,可以选择的形态学操作类型有开运算、闭运算、形态学梯度、顶帽运算、黑帽运算以及击中击不中变换,详细的参数在表6-6给出。函数第四个和第五个参数都是与结构元素相关的参数,第四个参数为结构元素,使用的结构元素尺寸越大效果越明显,第四个参数为结构元素的中心位置,第五个参数的默认值为Point(-1,-1),表示结构元素的几何中心处为结构元素的中心点。函数第六个参数是使用结构元素处理的次数,处理次数越多效果越明显。函数第七个参数是图像像素外推法的选择标志,第八个参数为使用边界不变外推法时的边界值,这两个参数对图像中主要部分的形态学操作没有影响,因此在多数情况下使用默认值即可。
表6-6 morphologyEX()函数中形态学操作类型标志可选参数及含义
标志参数 | 简记 | 作用 |
---|---|---|
MORPH_ERODE | 0 | 图像腐蚀 |
MORPH_DILATE | 1 | 图像膨胀 |
MORPH_OPEN | 2 | 开运算 |
MORPH_CLOSE | 3 | 闭运算 |
MORPH_GRADIENT | 4 | 形态学梯度 |
MORPH_TOPHAT | 5 | 顶帽运算 |
MORPH_BLACKHAT | 6 | 黑帽运算 |
MORPH_HITMISS | 7 | 击中击不中运算 |