OpenCV实战 | 基于形态学运算提取图像中的音符
Posted 行路南
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了OpenCV实战 | 基于形态学运算提取图像中的音符相关的知识,希望对你有一定的参考价值。
图像形态学运算,顾名思义是应用形态学操作改变图像中物体的形状的过程。其中形态学操作比较基础的是膨胀、腐蚀、开运算和闭运算;形态学操作的对象通常是二值化图像。
本文会先介绍形态学中常见的操作的原理部分,然后介绍在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。
可以看到经过腐蚀后的图像,消除了一些斑点噪音,同时确保图中较大的区域仍然存在,最终得到我们想要的区域。
在下文的示例中,我们还会演示一个提取音符的例子,在那个例子中,我们会设计一种核结构,使得图中所有的横线都作为噪音被清除掉,也就达到了提取音符的目的。
1.2 膨胀
膨胀和腐蚀相反,膨胀是将核和一个图像进行卷积,计算的是核区域内像素的最大值。
此外,膨胀也可以填补凹洞,如下图所示。
在试图找到连通分支时通常使用膨胀操作,因为大多数情况下一个大的区域可能被一些噪音等分割为多个区域,而通过膨胀操作能够使这些多个区域结合在一起。
1.3 开运算
开运算,是先进行腐蚀操作,再进行膨胀操作。开运算的效果是消除了高于临近点的孤立点。
1.4 闭运算
闭运算,是先膨胀后腐蚀。这里我们的输入图像和1.3节的输入图像一样,在下图中我们可以看到先膨胀后腐蚀的效果,即消除了低于临近点的孤立点。
2 核
在上文中,我们都是使用大小为 3 ∗ 3 3*3 3∗3的核来进行膨胀或者腐蚀的操作。(有些教材中,核也称为结构元素)
在实际使用中,核的选择是非常重要的一个部分,针对不同的情况,我们需要灵活需要不同的核,比如矩形的核、十字交叉的核,菱形的核等等。
同时我们也可以根据自己的需要自定义自己的核。
2.1 核具体是什么
所谓核,就是仅仅由0和1组成的一个矩阵,可以具有任意的形状和大小,通常比我们要处理的图像要小得多。
在核中通过值为1的像素定义了核的邻域,表明了核的形状。核的中心像素,称为核的参考点,或者叫原点,表示正在处理的像素。
2.2 几种常见的核
上图中展示了常用的正方形核、长方形核、菱形核、十字交叉核,此外还有圆盘核、周期核等等。我们在选择核的时候,需要根据在输入图像中要提取的对象的大小和形状,来选择相对应的核结构。例如,我们想提取图像中的线条,那么选择一个线性结构的核比较合适。
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 可以使用腐蚀和膨胀作为基本操作进行高级形态转换。任何操作都可以就地完成。对于多通道图像,每个通道都是独立处理的。
参数说明:
src | 源图像。通道的数量可以是任意的。深度应该是CV_8U, CV_16U, CV_16S, CV_32F or CV_64F 中的一个 |
---|---|
dst | 与源图像大小和类型相同的目标图像。 |
op | 形态学运算的类型; 包括MORPH_ERODE、MORPH_DILATE 、MORPH_OPEN 、MORPH_CLOSE 等 |
kernel | 核(结构元素) 可以通过 getStructuringElement.函数来创建,在下文详细说明。 |
anchor | 核的参考点位置,默认值是负值,表示位于内核中心。 |
iterations | 应用腐蚀和膨胀的次数,默认为1 |
3.2 核(结构元素)
Mat cv::getStructuringElement ( int shape,
Size ksize,
Point anchor = Point(-1,-1)
)
返回用于形态学操作的指定大小和形状的结构元素。
该功能构造和返回的结构元素,可以进一步通过腐蚀,扩张或形态。但是您也可以自己构造一个任意的二进制掩码,并使用它作为结构元素。
参数说明:
shape | 核的形状,支持三种。分别是MORPH_RECT、MORPH_CROSS、MORPH_ELLIPSE |
---|---|
ksize | 核的大小 |
anchor | 元素内的锚定位置。默认值(−1.−1) 表示锚点位于中心。请注意,只有十字形图元的形状取决于锚定位置。在其他情况下,锚仅仅调节形态学操作的结果被移动的程度。 |
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;
}
以上是关于OpenCV实战 | 基于形态学运算提取图像中的音符的主要内容,如果未能解决你的问题,请参考以下文章
youcans 的 OpenCV 例程200篇116. 形态学操作之闭运算
youcans 的 OpenCV 例程200篇117. 形态学操作之顶帽运算
youcans 的 OpenCV 例程 200 篇118. 形态学操作之底帽运算