形態學圖像處理(二):開運算、閉運算、形態學梯度、頂帽、黑帽合輯

【OpenCV入門教程之十一】 形態學圖像處理(二):開運算、閉運算、形態學梯度、頂帽、黑帽合輯

一、概念

1.1 開操作(Opening Operation)

開操作(Opening Operation)先腐蝕后膨脹,其數學表達式如下:

開操作可以 消除小物體、在纖細點處分離物體、平滑較大物體的邊界 的同時 并不明顯改變其面積。

開操作

1.2 閉操作(Closing Operation)

閉操作(Closing Operation)先膨脹后腐蝕,其數學表達式如下:

閉操作可以 彌合較窄的間斷和細長的溝壑、消除小的孔洞、填補輪廓線中的斷裂、也會平滑輪廓的一部分

閉操作

1.3 形態學梯度(Morphological Gradient)

形態學梯度(Morphological Gradient)為膨脹圖與腐蝕圖之差,數學表達式如下:


對二值圖像進行這一操作可以將團塊(blob)的邊緣突出出來。我們可以用形態學梯度來 保留物體的邊緣輪廓

形態學梯度
形態學梯度 = 膨脹 - 腐蝕

1.4 頂帽(Top Hat)

頂帽運算(Top Hat)為原圖像與“開運算“的結果圖之差,數學表達式如下:

開運算放大了局部低亮度區域,因此,從原圖中減去開運算后的圖,得到的效果圖 突出了比原圖輪廓周圍的區域更明亮的區域,且這一操作和選擇的核的大小相關。
頂帽運算往往用來分離比鄰近點亮一些的斑塊。當一幅圖像具有大幅的背景的時候,而微小物品比較有規律的情況下,可以使用頂帽運算進行背景提取。

1.5 黑帽(Black Hat)

黑帽(Black Hat)運算為”閉運算“的結果圖與原圖像之差。數學表達式為:

閉運算放大了局部高亮度區域。黑帽運算后的效果圖 突出了比原圖輪廓周圍的區域更暗的區域,且這一操作和選擇的核的大小相關。
所以,黑帽運算用來分離比鄰近點暗一些的斑塊。

頂帽 = src - 開操作 黑帽 = 閉操作 - src

頂帽和黑帽都是做差求得的,最后顯示的都是放大的區域。

  • 頂帽:放大的暗區域,即原圖中的亮區域。
  • 黑帽:放大的亮區域,即原圖中的暗區域。

二、OpenCV 源碼

morph.cpp 2038
void cv::morphologyEx( InputArray _src, OutputArray _dst, int op,
                       InputArray _kernel, Point anchor, int iterations,
                       int borderType, const Scalar& borderValue )
{
    CV_INSTRUMENT_REGION()

    Mat kernel = _kernel.getMat();
    if (kernel.empty())
    {
        kernel = getStructuringElement(MORPH_RECT, Size(3,3), Point(1,1));
    }
#ifdef HAVE_OPENCL
    Size ksize = kernel.size();
    anchor = normalizeAnchor(anchor, ksize);

    CV_OCL_RUN(_dst.isUMat() && _src.dims() <= 2 && _src.channels() <= 4 &&
        anchor.x == ksize.width >> 1 && anchor.y == ksize.height >> 1 &&
        borderType == cv::BORDER_CONSTANT && borderValue == morphologyDefaultBorderValue(),
        ocl_morphologyEx(_src, _dst, op, kernel, anchor, iterations, borderType, borderValue))
#endif

    Mat src = _src.getMat(), temp;
    _dst.create(src.size(), src.type());
    Mat dst = _dst.getMat();

    Mat k1, k2, e1, e2;     //only for hit and miss op

    switch( op )
    {
    case MORPH_ERODE: // 腐蝕
        erode( src, dst, kernel, anchor, iterations, borderType, borderValue );
        break;
    case MORPH_DILATE: // 膨脹
        dilate( src, dst, kernel, anchor, iterations, borderType, borderValue );
        break;
    case MORPH_OPEN: // 開操作 = 先腐蝕再膨脹
        erode( src, dst, kernel, anchor, iterations, borderType, borderValue );
        dilate( dst, dst, kernel, anchor, iterations, borderType, borderValue );
        break;
    case CV_MOP_CLOSE: // 閉操作 = 先膨脹再腐蝕
        dilate( src, dst, kernel, anchor, iterations, borderType, borderValue );
        erode( dst, dst, kernel, anchor, iterations, borderType, borderValue );
        break;
    case CV_MOP_GRADIENT: // 形態學梯度 = 膨脹 - 腐蝕
        erode( src, temp, kernel, anchor, iterations, borderType, borderValue );
        dilate( src, dst, kernel, anchor, iterations, borderType, borderValue );
        dst -= temp;
        break;
    case CV_MOP_TOPHAT: // 頂帽 = src - 開操作
        if( src.data != dst.data )
            temp = dst;
        erode( src, temp, kernel, anchor, iterations, borderType, borderValue );
        dilate( temp, temp, kernel, anchor, iterations, borderType, borderValue );
        dst = src - temp;
        break;
    case CV_MOP_BLACKHAT: // 黑帽 = 閉操作 - src
        if( src.data != dst.data )
            temp = dst;
        dilate( src, temp, kernel, anchor, iterations, borderType, borderValue );
        erode( temp, temp, kernel, anchor, iterations, borderType, borderValue );
        dst = temp - src;
        break;
    case MORPH_HITMISS: // 擊不中
        CV_Assert(src.type() == CV_8UC1);
        k1 = (kernel == 1);
        k2 = (kernel == -1);
        if (countNonZero(k1) <= 0)
            e1 = src;
        else
            erode(src, e1, k1, anchor, iterations, borderType, borderValue);
        if (countNonZero(k2) <= 0)
            e2 = src;
        else
        {
            Mat src_complement;
            bitwise_not(src, src_complement);
            erode(src_complement, e2, k2, anchor, iterations, borderType, borderValue);
        }
        dst = e1 & e2;
        break;
    default:
        CV_Error( CV_StsBadArg, "unknown morphological operation" );
    }
}

三、實例

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

using namespace cv;
using namespace std;

// 窗口名稱
const string names[] = {"腐蝕", "膨脹", "開操作", "閉操作", "形態學梯度", "頂帽", "黑帽"};

// 7種運算
const int op[] = {MORPH_ERODE, MORPH_DILATE, MORPH_OPEN, MORPH_CLOSE, MORPH_GRADIENT, MORPH_TOPHAT, MORPH_BLACKHAT};
const int opNum = 7;

int main() {

    Mat src = imread("../pics/mo.png", 0);

    namedWindow("src");
    imshow("src", src);

    // 獲得結構元
    Mat se = getStructuringElement(MORPH_RECT, Size(15, 15));

    // 執行7種運算
    Mat dst;
    for (int i = 0; i < opNum; ++i) {
        morphologyEx(src, dst, op[i], se);
        namedWindow(names[i]);
        imshow(names[i], dst);
        dst = Mat::zeros(dst.size(), CV_8UC3); // 清空dst
    }

    waitKey(0);
}

原圖


mo.png
Size(15, 15)
Size(5, 5)

由于膨脹腐蝕操作的都是高亮部分,所以將原圖顏色反轉,可以通過觀察字的變化。

PS中圖像反相


反相之后

原圖


mob.png
Size(5, 5)

執行 7 個操作。

其他實驗結果


頂帽和黑帽的圖看著還行,開操作的眼睛也還行,其他的都看不成了??


最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容