OpenCV通過使用形態(tài)學(xué)操作來提取水平和垂直線

2018-09-01 11:38 更新

目標(biāo)

在本教程中,您將學(xué)習(xí)如何:

  • 應(yīng)用兩個(gè)非常常見的形態(tài)運(yùn)算符(即擴(kuò)張和侵蝕),創(chuàng)建自定義內(nèi)核,以便在水平軸和垂直軸上提取直線。為此,您將使用以下OpenCV功能:CV ::侵蝕CV ::擴(kuò)張CV :: getStructuringElement在一個(gè)例子中,您的目標(biāo)是從音樂表中提取音樂筆記。

理論

形態(tài)操作

形態(tài)學(xué)是一組圖像處理操作,其基于預(yù)定義的也稱為內(nèi)核的結(jié)構(gòu)元素來處理圖像。輸出圖像中的每個(gè)像素的值基于輸入圖像中的對(duì)應(yīng)像素與其鄰居的比較。通過選擇內(nèi)核的大小和形狀,您可以構(gòu)建對(duì)輸入圖像的特定形狀敏感的形態(tài)操作。

兩個(gè)最基本的形態(tài)操作是擴(kuò)張和侵蝕。擴(kuò)散將像素添加到圖像中對(duì)象的邊界,而侵蝕恰恰相反。添加或刪除的像素?cái)?shù)量分別取決于用于處理圖像的結(jié)構(gòu)元素的大小和形狀。一般來說,這兩個(gè)操作遵循的規(guī)則如下:

  • 擴(kuò)展:輸出像素的值是屬于結(jié)構(gòu)元素大小和形狀的所有像素的最大值。例如,在二進(jìn)制圖像中,如果落入內(nèi)核范圍內(nèi)的輸入圖像的任何像素被設(shè)置為值1,則輸出圖像的相應(yīng)像素也將被設(shè)置為1。后者適用于任何類型的圖像(例如灰度,bgr等)。

二進(jìn)制圖像的擴(kuò)張

二進(jìn)制圖像的擴(kuò)張

灰度圖像的擴(kuò)張

灰度圖像的擴(kuò)張

  • 侵蝕:反之亦然適用于侵蝕作業(yè)。輸出像素的值是落在結(jié)構(gòu)元素的大小和形狀內(nèi)的所有像素的最小值??聪旅娴睦樱?/p>

    侵蝕二進(jìn)制圖像

侵蝕二進(jìn)制圖像

灰度圖像侵蝕

灰度圖像侵蝕

結(jié)構(gòu)元素

如上所述,通常在任何形態(tài)學(xué)操作中,用于探測(cè)輸入圖像的結(jié)構(gòu)元素是最重要的部分。

一個(gè)結(jié)構(gòu)化元素是一個(gè)由只有0和1組成的矩陣,可以有任意的任意形狀和大小。通常比正在處理的圖像小得多,而值為1的像素定義鄰域。稱為原點(diǎn)的結(jié)構(gòu)元素的中心像素標(biāo)識(shí)感興趣的像素 - 正在處理的像素。

例如,下面示出了7×7尺寸的菱形結(jié)構(gòu)元件。

OpenCV

鉆石形結(jié)構(gòu)元件及其起源

結(jié)構(gòu)元素可以具有許多常見的形狀,例如線,菱形,圓盤,周期線以及圓和尺寸。您通常選擇與要在輸入圖像中處理/提取的對(duì)象相同的大小和形狀的結(jié)構(gòu)元素。例如,要在圖像中查找行,請(qǐng)創(chuàng)建一個(gè)線性結(jié)構(gòu)元素,您將在后面看到。

Code

本教程代碼如下所示。您也可以從這里下載。


#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;
}

說明/結(jié)果

  • 加載源圖像并檢查它是否加載沒有任何問題,然后顯示:
    // 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);

OpenCV提取水平和垂直線

  • 然后將圖像轉(zhuǎn)換為灰度,如果還沒有:

    // 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);

OpenCV通過使用形態(tài)學(xué)操作來提取水平和垂直線

  • 然后將灰度圖像轉(zhuǎn)換為二進(jìn)制。注意?符號(hào),表示我們使用它的逆(即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);

OpenCV提取水平和垂直線

  • 現(xiàn)在,我們準(zhǔn)備應(yīng)用形態(tài)學(xué)操作來提取水平和垂直線,并將音樂筆記與樂譜分開,但首先讓我們初始化我們將使用的輸出圖像:

    // Create the images that will use to extract the horizontal and vertical lines
    Mat horizontal = bw.clone();
    Mat vertical = bw.clone();

正如我們?cè)诶碚撝兄付ǖ?,為了提取我們想要的?duì)象,我們需要?jiǎng)?chuàng)建相應(yīng)的結(jié)構(gòu)元素。因?yàn)檫@里我們要提取水平線,所以用于此目的的相應(yīng)結(jié)構(gòu)元素將具有以下形狀:

OpenCV

在源代碼中,它由以下代碼片段代表:

   // 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);

OpenCV通過使用形態(tài)學(xué)操作來提取水平和垂直線

  • 同樣適用于垂直線,具有相應(yīng)的結(jié)構(gòu)元素:

OpenCV通過使用形態(tài)學(xué)操作來提取水平和垂直線

這又表示如下:

    // 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);

OpenCV通過使用形態(tài)學(xué)操作來提取水平和垂直線

  • 你可以看到我們幾乎在那里 但是,在這一點(diǎn)上你會(huì)注意到這些筆記的邊緣有點(diǎn)粗糙。為此,我們需要細(xì)化邊緣以獲得更平滑的結(jié)果:

    // 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);

OpenCV通過使用形態(tài)學(xué)操作來提取水平和垂直線

以上內(nèi)容是否對(duì)您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號(hào)
微信公眾號(hào)

編程獅公眾號(hào)