OpenCV实战 | 基于形态学运算提取图像中的音符 灰太狼 2022-09-16 13:18 175阅读 0赞 图像形态学运算,顾名思义是应用**形态学操作**改变**图像**中物体的形状的过程。其中形态学操作比较基础的是膨胀、腐蚀、开运算和闭运算;形态学操作的对象通常是二值化图像。 本文会先介绍形态学中常见的操作的原理部分,然后介绍在OpenCV中这些操作的函数和参数含义,最后通过一个提取图像中音符的样例来贯穿全部的内容。 ## 1 形态学操作 ## ### 1.1 腐蚀 ### 腐蚀过程是将一个核与一个图像进行卷积,其中核可以是任何的形状,比如大小为 3 ∗ 3 3 \* 3 3∗3的正方形核,也可以是大小为 5 ∗ 1 5 \* 1 5∗1的长条形核。核有一个参考点,参考点位于核的正中心。 将核与图像进行卷积,就是从左到右、从上到下滑动窗口运算的过程。在这个过程中,对于腐蚀操作,每次计算核在图像中覆盖的区域的最小值,并将这个最小值赋值给核的参考点指定的像素。 以下图为例,当核的参考点位于原图的第2行第8列时(从0开始),此时整个核的覆盖区域就是以参考点为中心的正方形区域,在这个区域内的最小值为0,那么腐蚀后的图像中第2行第8列的值就是0;当核的参考点位于第4行第4列时,此时整个核的覆盖区域的最小值是255,那么腐蚀后的图像中第4行第4列的值就是255。 ![在这里插入图片描述][watermark_type_ZHJvaWRzYW5zZmFsbGJhY2s_shadow_50_text_Q1NETiBA6KGM6Lev5Y2X_size_20_color_FFFFFF_t_70_g_se_x_16_pic_center] 图1 腐蚀 可以看到经过腐蚀后的图像,消除了一些斑点噪音,同时确保图中较大的区域仍然存在,最终得到我们想要的区域。 在下文的示例中,我们还会演示一个提取音符的例子,在那个例子中,我们会设计一种核结构,使得图中所有的横线都作为噪音被清除掉,也就达到了提取音符的目的。 ### 1.2 膨胀 ### 膨胀和腐蚀相反,膨胀是将核和一个图像进行卷积,计算的是核区域内像素的最大值。 ![在这里插入图片描述][watermark_type_ZHJvaWRzYW5zZmFsbGJhY2s_shadow_50_text_Q1NETiBA6KGM6Lev5Y2X_size_20_color_FFFFFF_t_70_g_se_x_16_pic_center 1] 图2 膨胀 此外,膨胀也可以填补凹洞,如下图所示。 ![在这里插入图片描述][watermark_type_ZHJvaWRzYW5zZmFsbGJhY2s_shadow_50_text_Q1NETiBA6KGM6Lev5Y2X_size_20_color_FFFFFF_t_70_g_se_x_16_pic_center 2] 图3 膨胀 在试图找到连通分支时通常使用膨胀操作,因为大多数情况下一个大的区域可能被一些噪音等分割为多个区域,而通过膨胀操作能够使这些多个区域结合在一起。 ### 1.3 开运算 ### 开运算,是先进行腐蚀操作,再进行膨胀操作。开运算的效果是**消除了高于临近点的孤立点**。 ![在这里插入图片描述][watermark_type_ZHJvaWRzYW5zZmFsbGJhY2s_shadow_50_text_Q1NETiBA6KGM6Lev5Y2X_size_20_color_FFFFFF_t_70_g_se_x_16_pic_center 3] 图4 开运算的过程 ### 1.4 闭运算 ### 闭运算,是先膨胀后腐蚀。这里我们的输入图像和1.3节的输入图像一样,在下图中我们可以看到先膨胀后腐蚀的效果,即**消除了低于临近点的孤立点**。 ![在这里插入图片描述][watermark_type_ZHJvaWRzYW5zZmFsbGJhY2s_shadow_50_text_Q1NETiBA6KGM6Lev5Y2X_size_20_color_FFFFFF_t_70_g_se_x_16_pic_center 4] 图5 闭运算的过程 ## 2 核 ## 在上文中,我们都是使用大小为 3 ∗ 3 3\*3 3∗3的核来进行膨胀或者腐蚀的操作。(有些教材中,核也称为结构元素) 在实际使用中,核的选择是非常重要的一个部分,针对不同的情况,我们需要灵活需要不同的核,比如矩形的核、十字交叉的核,菱形的核等等。 同时我们也可以根据自己的需要自定义自己的核。 ### 2.1 核具体是什么 ### 所谓核,就是仅仅由0和1组成的一个矩阵,可以具有任意的形状和大小,通常比我们要处理的图像要小得多。 在核中通过值为1的像素定义了核的邻域,表明了核的形状。核的中心像素,称为核的参考点,或者叫原点,表示正在处理的像素。 ### 2.2 几种常见的核 ### ![在这里插入图片描述][watermark_type_ZHJvaWRzYW5zZmFsbGJhY2s_shadow_50_text_Q1NETiBA6KGM6Lev5Y2X_size_20_color_FFFFFF_t_70_g_se_x_16_pic_center 5] 图6 几种常见的核结构 上图中展示了常用的正方形核、长方形核、菱形核、十字交叉核,此外还有圆盘核、周期核等等。我们在选择核的时候,需要根据在输入图像中要提取的对象的大小和形状,来选择相对应的核结构。例如,我们想提取图像中的线条,那么选择一个线性结构的核比较合适。 ## 3 在OpenCV中形态学运算的函数 ## ### 3.1 形态学运算 ### void cv::morphologyEx ( InputArray src, OutputArray dst, int op, InputArray kernel, Point anchor = Point(-1,-1), int iterations = 1, int borderType = BORDER_CONSTANT, const Scalar & borderValue = morphologyDefaultBorderValue() ) 执行高级形态转换。功能 cv: : morphologyEx 可以使用腐蚀和膨胀作为基本操作进行高级形态转换。任何操作都可以就地完成。对于多通道图像,每个通道都是独立处理的。 **参数说明:** <table> <thead> <tr> <th>src</th> <th>源图像。通道的数量可以是任意的。深度应该是CV_8U, CV_16U, CV_16S, CV_32F or CV_64F 中的一个</th> </tr> </thead> <tbody> <tr> <td>dst</td> <td>与源图像大小和类型相同的目标图像。</td> </tr> <tr> <td>op</td> <td>形态学运算的类型; 包括MORPH_ERODE、MORPH_DILATE 、MORPH_OPEN 、MORPH_CLOSE 等</td> </tr> <tr> <td>kernel</td> <td>核(结构元素) 可以通过 <a href="https://docs.opencv.org/master/d4/d86/group__imgproc__filter.html#gac342a1bb6eabf6f55c803b09268e36dc" rel="nofollow">getStructuringElement</a>.函数来创建,在下文详细说明。</td> </tr> <tr> <td>anchor</td> <td>核的参考点位置,默认值是负值,表示位于内核中心。</td> </tr> <tr> <td>iterations</td> <td>应用腐蚀和膨胀的次数,默认为1</td> </tr> </tbody> </table> ### 3.2 核(结构元素) ### Mat cv::getStructuringElement ( int shape, Size ksize, Point anchor = Point(-1,-1) ) 返回用于形态学操作的指定大小和形状的结构元素。 该功能构造和返回的结构元素,可以进一步通过腐蚀,扩张或形态。但是您也可以自己构造一个任意的二进制掩码,并使用它作为结构元素。 **参数说明:** <table> <thead> <tr> <th>shape</th> <th>核的形状,支持三种。分别是MORPH_RECT、MORPH_CROSS、MORPH_ELLIPSE</th> </tr> </thead> <tbody> <tr> <td>ksize</td> <td>核的大小</td> </tr> <tr> <td>anchor</td> <td>元素内的锚定位置。默认值(−1.−1) 表示锚点位于中心。请注意,只有十字形图元的形状取决于锚定位置。在其他情况下,锚仅仅调节形态学操作的结果被移动的程度。</td> </tr> </tbody> </table> ## 4 C++样例 ## #include <opencv2/opencv.hpp> int main(int argc, char** argv) { // 第一步,读取原始图像 cv::Mat img = cv::imread("/home/lxn/图片/src.png"); cv::imshow("ori img", img); cv::waitKey(0); // 第二步,转灰度图 cv::Mat gray; cv::cvtColor(img, gray, cv::COLOR_BGR2GRAY); cv::imshow("gray", img); cv::waitKey(0); // 第三步,取反 // 需特别注意的是,原始图像中物体是黑色,背景是白色; // 在膨胀或者腐蚀操作,我们针对的都是高亮的物体,所以为了便于理解,我们对物体的像素值做一个取反操作(value = 255 - value) cv::Mat bw_gray; cv::bitwise_not(gray, bw_gray); cv::imshow("bw_gray", bw_gray); cv::waitKey(0); // 第四步,二值化 cv::Mat threshold_gray; cv::threshold(bw_gray, threshold_gray, 100, 255, cv::THRESH_BINARY); cv::imshow("threshold_gray", threshold_gray); cv::waitKey(0); // 第五步,提取垂直线(过滤掉水平的横线,得到音符).这里的核要既保证清除掉横线,同时小的音符又能够保留。所以核的高度选择大于横线像素高度的最小值 cv::Mat vertical; cv::Mat vertical_kernel = cv::getStructuringElement(cv::MORPH_RECT, cv::Size(1, 4)); cv::morphologyEx(threshold_gray, vertical, cv::MORPH_OPEN, vertical_kernel); cv::imshow("vertical", vertical); cv::waitKey(0); // 第六步, 取反,得到前景为黑,背景为白的图像 cv::bitwise_not(vertical, vertical); cv::imshow("bw_vertical", vertical); cv::waitKey(0); return 0; } ![在这里插入图片描述][9d590864c2b34d7cbc1f5f7c49b303a9.jpg_pic_center] 图7 原图 ![在这里插入图片描述][2a21255648b644259518787dc3ad9f3c.jpg_pic_center] 图8 灰度图 ![在这里插入图片描述][6547c32007954aea809227c3330f7d76.jpg_pic_center] 图9 取反的灰度图 ![在这里插入图片描述][d5ca6b7e0fcf441984bf8e245bdf51e6.jpg_pic_center] 图10 二值化图 ![在这里插入图片描述][494ede913aaf41629bbb8921f545744d.jpg_pic_center] 图11 横线过滤图 ![在这里插入图片描述][31b88f9d7d534b19bcd33536968d5ee5.jpg_pic_center] 图12 再次取反的最终结果 [watermark_type_ZHJvaWRzYW5zZmFsbGJhY2s_shadow_50_text_Q1NETiBA6KGM6Lev5Y2X_size_20_color_FFFFFF_t_70_g_se_x_16_pic_center]: /images/20220828/2a19b7ce606245d99b03975e695dc856.png [watermark_type_ZHJvaWRzYW5zZmFsbGJhY2s_shadow_50_text_Q1NETiBA6KGM6Lev5Y2X_size_20_color_FFFFFF_t_70_g_se_x_16_pic_center 1]: /images/20220828/33b76cd39e0444ed884203952618528c.png [watermark_type_ZHJvaWRzYW5zZmFsbGJhY2s_shadow_50_text_Q1NETiBA6KGM6Lev5Y2X_size_20_color_FFFFFF_t_70_g_se_x_16_pic_center 2]: /images/20220828/9d4ba6618473411194894bc94c612b96.png [watermark_type_ZHJvaWRzYW5zZmFsbGJhY2s_shadow_50_text_Q1NETiBA6KGM6Lev5Y2X_size_20_color_FFFFFF_t_70_g_se_x_16_pic_center 3]: /images/20220828/ed651fbf5ceb47f296b6a71250d5d81c.png [watermark_type_ZHJvaWRzYW5zZmFsbGJhY2s_shadow_50_text_Q1NETiBA6KGM6Lev5Y2X_size_20_color_FFFFFF_t_70_g_se_x_16_pic_center 4]: /images/20220828/2fa31cd03420487384b94f7a094b9ba7.png [watermark_type_ZHJvaWRzYW5zZmFsbGJhY2s_shadow_50_text_Q1NETiBA6KGM6Lev5Y2X_size_20_color_FFFFFF_t_70_g_se_x_16_pic_center 5]: /images/20220828/49f21617cba24f449cdca0375bf9f9b8.png [9d590864c2b34d7cbc1f5f7c49b303a9.jpg_pic_center]: /images/20220828/757fa45538b34b059e49b581398a3380.png [2a21255648b644259518787dc3ad9f3c.jpg_pic_center]: /images/20220828/e4179db887f5438eab4e9957c17a720a.png [6547c32007954aea809227c3330f7d76.jpg_pic_center]: /images/20220828/182629a2d1a1459bbc40406ca6af1f74.png [d5ca6b7e0fcf441984bf8e245bdf51e6.jpg_pic_center]: /images/20220828/bb8f0d97f60c43228de69ece4a8d613e.png [494ede913aaf41629bbb8921f545744d.jpg_pic_center]: /images/20220828/1968b0443e5846858dec5c6130561473.png [31b88f9d7d534b19bcd33536968d5ee5.jpg_pic_center]: /images/20220828/01dcf957f9a544b6bdf49eff937bcb6d.png
还没有评论,来说两句吧...