Opencv图像处理---提取水平线与垂直线

理论

形态学是一组图像处理操作,其基于预定义的结构元素(也称为内核)来处理图像。 输出图像中的每个像素的值基于输入图像中的对应像素与其邻居的比较。 通过选择内核的大小和形状,您可以构建一个对输入图像的特定形状敏感的形态学操作。

两种最基本的形态学操作是膨胀和侵蚀。 膨胀将像素添加到图像中对象的边界,而侵蚀恰恰相反。 添加或移除的像素量分别取决于用于处理图像的结构化元素的大小和形状。 一般而言,这两项操作所遵循的规则如下:

  • 膨胀:输出像素的值是落在结构元素大小和形状内的所有像素的最大值。 例如,在二进制图像中,如果落入内核范围内的输入图像的任何像素被设置为值1,则输出图像的对应像素也将被设置为1。 后者适用于任何类型的图像。
  • 侵蚀:反之亦然,适用于侵蚀操作。 输出像素的值是落在结构元素的大小和形状内的所有像素的最小值。 请看下面的示例数字:

构造内核

通常在任何形态学操作中,用于探测输入图像的结构化元素是最重要的部分。结构元素是仅由0和1组成的矩阵,可以具有任意形状和大小。 通常比正在处理的图像小得多,而值为1的像素定义邻域。 结构元素的中心像素(称为原点)标识感兴趣的像素 - 正在处理的像素。例如,以下示出了7×7尺寸的菱形结构元件。

结构元素可以具有许多常见形状,例如线条,菱形,圆盘,周期线以及圆形和大小。 通常选择与要在输入图像中处理/提取的对象具有相同大小和形状的结构元素。 例如,要在图像中查找线条,请创建线性结构元素。

代码

#include <iostream>
#include <opencv2/opencv.hpp>
using namespace std;
using namespace cv;
int main(int, char** argv)
{
    // Load the image
    Mat src = imread(argv[1]);
    // Check if image is loaded fine
    if(!src.data)
        cerr << "Problem loading image!!!" << endl;
    // Show source image
    imshow("src", src);
    // Transform source image to gray if it is not
    Mat gray;
    if (src.channels() == 3)
    {
        cvtColor(src, gray, CV_BGR2GRAY);
    }
    else
    {
        gray = src;
    }
    // Show gray image
    imshow("gray", gray);
    // Apply adaptiveThreshold at the bitwise_not of gray, notice the ~ symbol
    Mat bw;
    adaptiveThreshold(~gray, bw, 255, CV_ADAPTIVE_THRESH_MEAN_C, THRESH_BINARY, 15, -2);
    // Show binary image
    imshow("binary", bw);
    // Create the images that will use to extract the horizontal and vertical lines
    Mat horizontal = bw.clone();
    Mat vertical = bw.clone();
    // Specify size on horizontal axis
    int horizontalsize = horizontal.cols / 30;
    // Create structure element for extracting horizontal lines through morphology operations
    Mat horizontalStructure = getStructuringElement(MORPH_RECT, Size(horizontalsize,1));
    // Apply morphology operations
    erode(horizontal, horizontal, horizontalStructure, Point(-1, -1));
    dilate(horizontal, horizontal, horizontalStructure, Point(-1, -1));
    // Show extracted horizontal lines
    imshow("horizontal", horizontal);
    // Specify size on vertical axis
    int verticalsize = vertical.rows / 30;
    // Create structure element for extracting vertical lines through morphology operations
    Mat verticalStructure = getStructuringElement(MORPH_RECT, Size( 1,verticalsize));
    // Apply morphology operations
    erode(vertical, vertical, verticalStructure, Point(-1, -1));
    dilate(vertical, vertical, verticalStructure, Point(-1, -1));
    // Show extracted vertical lines
    imshow("vertical", vertical);
    // Inverse vertical image
    bitwise_not(vertical, vertical);
    imshow("vertical_bit", vertical);
    // Extract edges and smooth image according to the logic
    // 1. extract edges
    // 2. dilate(edges)
    // 3. src.copyTo(smooth)
    // 4. blur smooth img
    // 5. smooth.copyTo(src, edges)
    // Step 1
    Mat edges;
    adaptiveThreshold(vertical, edges, 255, CV_ADAPTIVE_THRESH_MEAN_C, THRESH_BINARY, 3, -2);
    imshow("edges", edges);
    // Step 2
    Mat kernel = Mat::ones(2, 2, CV_8UC1);
    dilate(edges, edges, kernel);
    imshow("dilate", edges);
    // Step 3
    Mat smooth;
    vertical.copyTo(smooth);
    // Step 4
    blur(smooth, smooth, Size(2, 2));
    // Step 5
    smooth.copyTo(vertical, edges);
    // Show final result
    imshow("smooth", vertical);
    waitKey(0);
    return 0;
}

解释

  • 加载源图像并检查它是否加载没有任何问题,然后显示它:
  • 然后将图像转换为灰度:
  • 然后将灰度图像转换为二进制。 注意〜符号表示我们使用它的逆版本:
  • 现在我们准备应用形态学操作来提取水平和垂直线条,从而将音乐笔记与乐谱分开,但首先让我们初始化我们将使用的输出图像:
  • 正如我们在理论中指出的那样,为了提取我们想要的对象,我们需要创建相应的结构元素。 由于这里我们想要提取水平线,为此目的的相应结构元素将具有以下形状:
  • 在源代码中,这由以下代码片段表示:
  • 这同样适用于垂直线,具有相应的结构元素:
  • 再次表示如下:
  • 你会注意到音符的边缘有点粗糙。 出于这个原因,我们需要细化边缘以获得更平滑的结果:

 

猜你喜欢

转载自blog.csdn.net/LYKymy/article/details/83153621