基于形态学操作提取水平和垂直线条(五线谱中音符和乐谱线的分离)
目的:
結合自定義核,應用兩個非常常見的形態學算子(例如,擴張和侵蝕),提取水平和垂直方向的線條。將會用到以下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: 反之腐蝕操作的應用亦然,輸出像素的值是在結構元素的大小和形狀上的所有像素的最小值。下圖是例子:
二值圖像的腐蝕(Erosion on a Binary Image)
灰度圖像的腐蝕(Erosion on a Grayscale Image)
Structuring Elements(結構元素)
正如前面所述,在任何形態操作中,用于探測輸入圖像的結構元素是最重要的一部分。一個結構元素是一個只包含0和1的矩陣,可以有任何任意形狀和大小。通常情況下,遠小于被處理的圖像,值為1的元素定義的是相鄰的鄰域。結構元素的中心像素,稱為原點,標識感興趣的像素(正在處理的像素)。例如,下面說是一個菱形結構元素,有7x7個元素。
一個菱形結構元素和結構的原點(A Diamond-Shaped Structuring Element and its Origin)
一個結構元素可以有許多常見的形狀(如線,菱形,圓盤狀,周期線,和圓圈)和大小。你通常會選擇一個大小形狀和待處理/提取的對象一致的結構元素。例如,在圖像中查找線條,創建一個線性結構元素,稍后將看到。
代碼
/*** @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 imageMat src = imread(argv[1]);// Check if image is loaded fineif(!src.data)cerr << "Problem loading image!!!" << endl;// Show source imageimshow("src", src); //! [load_image]//! [gray]// Transform source image to gray if it is notMat gray;if (src.channels() == 3){cvtColor(src, gray, CV_BGR2GRAY);}else{gray = src;}// Show gray imageimshow("gray", gray); //! [gray]//! [bin]// Apply adaptiveThreshold at the bitwise_not of gray, notice the ~ symbolMat bw;adaptiveThreshold(~gray, bw, 255, CV_ADAPTIVE_THRESH_MEAN_C, THRESH_BINARY, 15, -2);// Show binary imageimshow("binary", bw); //! [bin]//! [init]// Create the images that will use to extract the horizontal and vertical linesMat horizontal = bw.clone();Mat vertical = bw.clone(); //! [init]//! [horiz]// Specify size on horizontal axisint horizontalsize = horizontal.cols / 30;// Create structure element for extracting horizontal lines through morphology operationsMat horizontalStructure = getStructuringElement(MORPH_RECT, Size(horizontalsize,1));// Apply morphology operationserode(horizontal, horizontal, horizontalStructure, Point(-1, -1));dilate(horizontal, horizontal, horizontalStructure, Point(-1, -1));// Show extracted horizontal linesimshow("horizontal", horizontal); //! [horiz]//! [vert]// Specify size on vertical axisint verticalsize = vertical.rows / 30;// Create structure element for extracting vertical lines through morphology operationsMat verticalStructure = getStructuringElement(MORPH_RECT, Size( 1,verticalsize));// Apply morphology operationserode(vertical, vertical, verticalStructure, Point(-1, -1));dilate(vertical, vertical, verticalStructure, Point(-1, -1));// Show extracted vertical linesimshow("vertical", vertical); //! [vert]//! [smooth]// Inverse vertical imagebitwise_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 1Mat edges;adaptiveThreshold(vertical, edges, 255, CV_ADAPTIVE_THRESH_MEAN_C, THRESH_BINARY, 3, -2);imshow("edges", edges);// Step 2Mat kernel = Mat::ones(2, 2, CV_8UC1);dilate(edges, edges, kernel);imshow("dilate", edges);// Step 3Mat smooth;vertical.copyTo(smooth);// Step 4blur(smooth, smooth, Size(2, 2));// Step 5smooth.copyTo(vertical, edges);// Show final resultimshow("smooth", vertical); //! [smooth]waitKey(0);return 0; }
解釋和結果
1. 加載源圖像,并檢查它是否加載成功,然后顯示它: // 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);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 linesMat horizontal = bw.clone(); Mat vertical = bw.clone();
5. 正如我們在理論中所說的,為了提取我們所希望的對象,我們需要創建相應的結構元素。由于這里我們要提取水平線,一個相應的結構元素有以下形狀:
在代碼的實現方式如下:
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. 正如你所看到的,你會注意到,音符的邊緣是有點粗糙的。由于這個原因,我們需要光順處理邊緣,以獲得更平滑的結果
總結
以上是生活随笔為你收集整理的基于形态学操作提取水平和垂直线条(五线谱中音符和乐谱线的分离)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 膨胀和腐蚀之外的其他形态学变换
- 下一篇: OpenCV实现图像金字塔