opencv學習(二十五)之開運算、閉運算、形態梯度、頂帽、黑帽

上一篇介紹了形態學的基本操作膨脹和腐蝕,我們這一篇將利用膨脹和腐蝕操作實現對圖像更高級的形態學操作,而這些都是建立在膨脹和腐蝕操作基礎之上。
首先形態學的主要用途是獲取物體拓撲和結果信息,它通過物體和結構元素的某些運算,得到物體更本質的形態,在圖像處理中的主要應用有:
(1). 利用形態學的基本運算對圖像進行觀察和處理,從而達到改善圖像質量的目的
(2). 描述和定義圖像的各種幾何參數和特徵如面積、周長、連通、顆粒度、骨架和方向性
我們通過腐蝕和膨脹兩種基本的形態學操作實現開運算、閉運算、形態梯度、頂帽、黑帽五種形態學操作。
1.開運算(Opening)
開運算是通過先對圖像腐蝕再膨脹實現,其原理表達式如下:

dst=open(src,element)=dilate(erode(src,element))

能夠排除小團塊物體(假設物體較背景明亮),開運算的結果刪除了不能包含結構元素的對象區域,平滑了對象的輪廓,斷開了狹窄的連接,去掉了細小的突出部分,如下圖所示:左圖是原圖像,右圖是採用開運算轉換之後的結果圖,可以發現字母拐彎處的白色空間消失。
這裏寫圖片描述

2.閉運算(Closing)
閉運算在數學上是先膨脹再腐蝕的結果,其原理表達式如下:

dst=close(src,element)=erode(dilate(src,element))

能夠排除小型黑洞(黑色區域),能夠平滑對象的輪廓,但是與開運算不同的是閉運算一般會將狹窄的缺口連接起來形成細長的彎口,並填充比結構元素小的洞。
如下圖所示:
這裏寫圖片描述

3.形態梯度(Morphological Gradient)
形態梯度是膨脹圖與腐蝕圖之差,其操作原理表達式如下

dst=morph(src,element)=dilate(src,element)-erode(src,element)

形態梯度操作能夠保留物體的邊緣輪廓,如下圖所示:
這裏寫圖片描述

4.頂帽(Top Hat)
頂帽操作是原圖像與開運算結果圖之差,其原理表達式如下:

dst=tophat(src,element)=src-open(src,element)

開運算的結果是放大了裂縫或局部降低亮度的區域,因此從原圖中減去開運算後的圖得到的效果圖能夠突出比原圖輪廓周圍的區域更明亮的區域,且這一操作與選擇的核的大小有關。
頂帽操作往往用來分離比鄰近點亮一些的板塊,在一幅圖像具有大幅背景而微小物品比較有規律的情況下,可以使用頂帽運算進行背景提取。如下圖所示:
這裏寫圖片描述

5.黑帽(Black Hat)
黑帽運算是閉運算結果圖與原圖像之差,其原理表達式如下:

dst=blackhat(src,element)=close(src,element)-src

黑帽運算後的效果圖突出了比原圖輪廓周圍的區域更暗的區域,這一操作也與選擇的核尺寸有關。所以黑帽運算用來分離比鄰近點暗一些的斑塊,效果圖有着非常完美的輪廓。如下圖所示:
這裏寫圖片描述

opencv中提供了形態學操作函數morphologyEx()來實現開運算、閉運算、形態學梯度、頂帽、黑帽等五種相對高級的操作,也可以實現膨脹核腐蝕兩種基本的形態學操作。其原型如下:

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() 
    )   

參數解釋:
. InputArray src: 輸入圖像,可以是Mat類型,對於圖像通道數無要求,但圖像深度必須是CV_8U、CV_16U、CV_16S、CV_32F或CV_64F
. OutPutArray dst: 目標圖像,與原圖像尺寸核類型相同
. int op: 形態學運算的類型,可以通過MorphTypes查看,如下所示:
標識符 | 運算類型
MORPH_OPEN: 開運算
MORPH_CLOSE :閉運算
MORPH_GRADIENT: 形態學梯度
MORPH_TOPHAT:頂帽運算
MORPH_BLACKHAT: 黑帽運算
MORPH_ERODE :腐蝕運算
MORPH_DILATE :膨脹運算
MORPH_HITMISS: 擊中擊不中運算(只支持CV_8UC1類型的二值圖像)

. InputArray kernel: 形態學運算的內核,如果是Mat()則表示的是參考點位於內核中心3x3的核,前面也提到一般使用前需要定義一個Mat變量結合getStructuringElement()函數使用,getStructuringElement會返回指定形狀和尺寸的結構元素,這裏再重申一下getStructuringElement的參數,其函數原型如下:

Mat cv::getStructuringElement   (   int     shape,
        Size    ksize,
        Point   anchor = Point(-1,-1) 
    ) 

參數解釋:
. int shape: kernel的形狀,由cv::MorphShapes指定,如下:
這裏寫圖片描述
分別是矩形(MORPH_RECT)、交叉形(MORPH_CROSS)、橢圓形(MORPH_ELLIPSE)
. Size ksize: kernel的尺寸
. Point anchor = Point(-1, -1): 錨點位置

. Point anchor=Point(-1, -1): 錨點位置
. int iterations=1: 迭代使用函數的次數,默認值爲1
. int borderType=BORDER_CONSTANT: 用於推斷圖像外部像素的某種邊界模式,有默認值BORDER_CONSTANT
. const Scalar & borderValue=morphologyDefaultBorderValue(): 當邊界爲常數時的邊界值,可以通過createMorphologyFilter() 查看更多細節。

這些形態學操作都是可執行就地操作(in-place),對於多通道圖像,每個圖像通道進行單獨操作。

示例代碼如下:

/*
 *本程序將會產生5副目標圖片與原圖像進行對比
 *每幅圖像採用的模板Type和Size均可通過軌跡條進行調節
 *如有興趣也可嘗試使用軌跡條改變形態學的運算類型可大幅減少代碼量
*/

#include <iostream>
#include <opencv2/core.hpp>
#include <opencv2/highgui.hpp>
#include <opencv2/imgproc.hpp>

using namespace std;
using namespace cv;

//定義全局變量
Mat g_srcImage;
Mat g_dstImageOpen, g_dstImageClose, g_dstImageGradient;
Mat g_dstImageTopHat, g_dstImageBlackHat;

//定義兩個軌跡條參數
const int kernelTypeMaxValue = 2;
const int kernelSizeMaxValue = 20;
int kernelTypeValue = 1;
int kernelSizeValue = 10;

//定義模板類型聲明函數
int kernelTypeFun(int kernelTypeValue);

//定義兩個回調函數
void openOperation(int, void*);
void closeOperation(int, void*);
void gradientOperation(int, void*);
void topHatOperation(int, void*);
void blackHatOperation(int, void*);

int main()
{
    g_srcImage = imread("baboon.jpg");

    //判斷圖像是否加載成功
    if(!g_srcImage.data)
    {
        cout << "圖像加載失敗!" << endl;
        return -1;
    }
    else
        cout << "圖像加載成功!" << endl << endl;

    namedWindow("原圖像", WINDOW_AUTOSIZE);
    imshow("原圖像", g_srcImage);

    //定義軌跡條參數
    char kernelTypeName[20];
    char kernelSizeName[20];

    sprintf(kernelTypeName, "模板類型 %d\n 0-Rect 1-Cross 2-Ellipse", kernelTypeMaxValue);
    sprintf(kernelSizeName, "模板尺寸 %d", kernelSizeMaxValue);

    namedWindow("開運算", WINDOW_AUTOSIZE);
    namedWindow("閉運算", WINDOW_AUTOSIZE);
    namedWindow("形態學梯度", WINDOW_AUTOSIZE);
    namedWindow("頂帽", WINDOW_AUTOSIZE);
    namedWindow("黑帽", WINDOW_AUTOSIZE);

    //創建兩個軌跡條
    createTrackbar(kernelTypeName, "開運算", &kernelTypeValue, kernelTypeMaxValue, openOperation);
    createTrackbar(kernelSizeName, "開運算", &kernelSizeValue, kernelSizeMaxValue, openOperation);
    openOperation(kernelTypeValue, 0);
    openOperation(kernelSizeValue, 0);

    createTrackbar(kernelTypeName, "閉運算", &kernelTypeValue, kernelTypeMaxValue, closeOperation);
    createTrackbar(kernelSizeName, "閉運算", &kernelSizeValue, kernelSizeMaxValue, closeOperation);
    openOperation(kernelTypeValue, 0);
    openOperation(kernelSizeValue, 0);

    createTrackbar(kernelTypeName, "形態學梯度", &kernelTypeValue, kernelTypeMaxValue, gradientOperation);
    createTrackbar(kernelSizeName, "形態學梯度", &kernelSizeValue, kernelSizeMaxValue, gradientOperation);
    openOperation(kernelTypeValue, 0);
    openOperation(kernelSizeValue, 0);

    createTrackbar(kernelTypeName, "頂帽", &kernelTypeValue, kernelTypeMaxValue, topHatOperation);
    createTrackbar(kernelSizeName, "頂帽", &kernelSizeValue, kernelSizeMaxValue, topHatOperation);
    openOperation(kernelTypeValue, 0);
    openOperation(kernelSizeValue, 0);

    createTrackbar(kernelTypeName, "黑帽", &kernelTypeValue, kernelTypeMaxValue, blackHatOperation);
    createTrackbar(kernelSizeName, "黑帽", &kernelSizeValue, kernelSizeMaxValue, blackHatOperation);
    openOperation(kernelTypeValue, 0);
    openOperation(kernelSizeValue, 0);



    waitKey(0);

    return 0;
}

//形態學操作模板類型判斷
int kernelTypeFun(int kernelTypeValue)
{
    int kernel_type;
    if(kernelTypeValue == 0)
        kernel_type = MORPH_RECT;
    else if(kernelTypeValue == 1)
        kernel_type = MORPH_CROSS;
    else
        kernel_type = MORPH_ELLIPSE;

    return kernel_type;
}

//開運算
void openOperation(int, void*)
{
    int kernel_type;
    kernel_type = kernelTypeFun(kernelTypeValue);

    Mat element = getStructuringElement(kernel_type, Size(kernelSizeValue * 2 + 1,
                    kernelSizeValue * 2 + 1));
    morphologyEx(g_srcImage, g_dstImageOpen, MORPH_OPEN, element);
    imshow("開運算", g_dstImageOpen);
}

//閉運算
void closeOperation(int, void*)
{
    int kernel_type;
    kernel_type = kernelTypeFun(kernelTypeValue);

    Mat element = getStructuringElement(kernel_type, Size(kernelSizeValue * 2 + 1,
                    kernelSizeValue * 2 + 1));
    morphologyEx(g_srcImage, g_dstImageClose, MORPH_CLOSE, element);
    imshow("閉運算", g_dstImageClose);
}

//形態學梯度
void gradientOperation(int, void*)
{
    int kernel_type;
    kernel_type = kernelTypeFun(kernelTypeValue);

    Mat element = getStructuringElement(kernel_type, Size(kernelSizeValue * 2 + 1,
                    kernelSizeValue * 2 + 1));
    morphologyEx(g_srcImage, g_dstImageGradient, MORPH_GRADIENT, element);
    imshow("形態學梯度", g_dstImageGradient);
}

//頂帽
void topHatOperation(int, void*)
{
    int kernel_type;
    kernel_type = kernelTypeFun(kernelTypeValue);

    Mat element = getStructuringElement(kernel_type, Size(kernelSizeValue * 2 + 1,
                    kernelSizeValue * 2 + 1));
    morphologyEx(g_srcImage, g_dstImageTopHat, MORPH_TOPHAT, element);
    imshow("頂帽", g_dstImageTopHat);
}

//黑帽
void blackHatOperation(int, void*)
{
    int kernel_type;
    kernel_type = kernelTypeFun(kernelTypeValue);

    Mat element = getStructuringElement(kernel_type, Size(kernelSizeValue * 2 + 1,
                    kernelSizeValue * 2 + 1));
    morphologyEx(g_srcImage, g_dstImageBlackHat, MORPH_BLACKHAT, element);
    imshow("黑帽", g_dstImageBlackHat);
}

程序運行結果如下:
這裏寫圖片描述
這裏寫圖片描述
這裏寫圖片描述
這裏寫圖片描述
這裏寫圖片描述
這裏寫圖片描述

發佈了98 篇原創文章 · 獲贊 630 · 訪問量 123萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章