首页 > 代码库 > 基于形态学操作提取水平和垂直线条
基于形态学操作提取水平和垂直线条
目的:
结合自定义核,应用两个非常常见的形态学算子(例如,扩张和侵蚀),提取水平和垂直方向的线条。将会用到以下OpenCV函数:
- cv::erode
- cv::dilate
- cv::getStructuringElement
接下里的例子是从乐谱中提取音符(五线谱中音符和乐谱线的分离)
理论
Morphology Operations
形态学是一组图像处理操作, 基于预定义的structuring elements(也被称为核)。输出图像中的每个像素的值是基于中心像素与输入图像中相邻像素的值的比较确定。通过选择核的大小和形状,可以构造一个对输入图像的特定形状敏感的形态学运算。
两个最基本的形态操作是膨胀和腐蚀。膨胀会将像素添加到图像中的对象的边界,而腐蚀正好相反。添加或删除的像素的量,分别取决于用于处理图像的结构元素的大小和形状。一般来说,这两个操作的规则如下:
膨胀(Dilation):输出像素的值是在结构元素的大小和形状上的所有像素的最大值。例如,在一个二值图像中,如果在核的范围内的输入图像的任何一个像素都被设置为值1,则输出图像的相应的像素将被设置为1。后者适用于任何类型的图像(如灰度,BGR,等)。
- 二值图像的膨胀(Dilation on a Binary Image)灰度图像的膨胀(Dilation on a Grayscale Image)
Erosion: 反之腐蚀操作的应用亦然,输出像素的值是在结构元素的大小和形状上的所有像素的最小值。下图是例子:
Structuring Elements(结构元素)
正如前面所述,在任何形态操作中,用于探测输入图像的结构元素是最重要的一部分。一个结构元素是一个只包含0和1的矩阵,可以有任何任意形状和大小。通常情况下,远小于被处理的图像,值为1的元素定义的是相邻的邻域。结构元素的中心像素,称为原点,标识感兴趣的像素(正在处理的像素)。例如,下面说是一个菱形结构元素,有7x7个元素。
一个结构元素可以有许多常见的形状(如线,菱形,圆盘状,周期线,和圆圈)和大小。你通常会选择一个大小形状和待处理/提取的对象一致的结构元素。例如,在图像中查找线条,创建一个线性结构元素,稍后将看到。
代码
/** * @file Morphology_3(Extract_Lines).cpp * @brief Use morphology transformations for extracting horizontal and vertical lines sample code * @author OpenCV team */ #include <iostream> #include <opencv2/opencv.hpp> using namespace std; using namespace cv; int main(int, char** argv) { //! [load_image] // Load the image Mat src = http://www.mamicode.com/imread(argv[1]);>
解释和结果
1. 加载源图像,并检查它是否加载成功,然后显示它:// Load the image Mat src = http://www.mamicode.com/imread(argv[1]);>2. 如果图像不是灰度图像转换为灰度图像:// 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);3. 然后将灰度图像转换为二值化。注意~符号表明我们使用逆操作后版本(即bitwise_not):// 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);4. 在我们已经准备好应用形态学操作,以提取水平和垂直线,作为从乐谱中分离音符的结果,但首先让我们初始化的输出图像:
// Create the images that will use to extract the horizontal and vertical lines Mat horizontal = bw.clone(); Mat vertical = bw.clone();5. 正如我们在理论中所说的,为了提取我们所希望的对象,我们需要创建相应的结构元素。由于这里我们要提取水平线,一个相应的结构元素有以下形状:
在代码的实现方式如下:
// 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);
6. 垂直线条的的用法也是这样,相应的结构元素如下:
代码如下// 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);7. 正如你所看到的,你会注意到,音符的边缘是有点粗糙的。由于这个原因,我们需要光顺处理边缘,以获得更平滑的结果
// 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);基于形态学操作提取水平和垂直线条