1. 程式人生 > >006】形態學開運算、閉運算、形態學梯度、頂帽、黑帽及OpenCV實現

006】形態學開運算、閉運算、形態學梯度、頂帽、黑帽及OpenCV實現

一、開發環境

1、Windows 7 64位 SP1 旗艦版;

2、Qt 5.10.1;

3、OpenCV 3.4.1

二、形態學運算

2.1 開運算

    腐蝕與膨脹作為形態學的基本操作,經過組合後可以很容易的實現更高一級的形態學運算。開運算即是先腐蝕後膨脹的得到的結果,即dst = open(src, element) = dilate(erode(src, element))。

    開運算可以消除區域性小的白色雜點,在纖細點處分離物體,並且在平滑較大物體的邊界的同時不明顯改變其面積。

2.2 閉運算

    閉運算過程與開運算過程相反,其是先膨脹後腐蝕的過程,可以消除影象中的小黑點,dst = close(src, element) = erode(dilate(src, element))。。

2.3 形態學梯度
    形態學梯度是膨脹圖與腐蝕圖之差,其數學表示式為:dst = morph_grad(src, element) = dilate(src, element) - erode(src, element)。對二值影象進行形態學梯度運算,可以突出物體邊緣。通常,我們採用形態學梯度來保留物體的邊緣輪廓。
2.4 頂帽

    頂帽運算又常稱為禮帽運算,是原始影象與影象開運算結果之差,其數學表示式為:dst = tophat(src, element) = src - open(src, element)。頂帽運算通常用來分離比鄰近點亮一些的斑塊,在一幅影象具有大幅背景且微小細節比較有規律的情況下,頂帽運算可以用來進行背景提取。

2.5 黑帽

    黑帽運算是影象閉運算結果與原始影象之差,其數學表示式為:dst = tophat(src, element) = close(src, element) - src。黑帽運算通常用來分離比鄰近點暗一些的斑塊,可以突出比原始圖輪廓周圍區域更暗的區域。

三、OpenCV實現

    cv::morphologyEx()函式綜合形態學大部分運算,可以實現形態學腐蝕、膨脹、開運算、閉運算、形態學梯度、頂帽、黑帽等。cv::morphologyEx()宣告如下:

void 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() );
@param src Source image. The number of channels can be arbitrary. The depth should be one of
       CV_8U, CV_16U, CV_16S, CV_32F or CV_64F.
@param dst Destination image of the same size and type as source image.
@param op Type of a morphological operation, see #MorphTypes
@param kernel Structuring element. It can be created using #getStructuringElement.
@param anchor Anchor position with the kernel. Negative values mean that the anchor is at the
       kernel center.
@param iterations Number of times erosion and dilation are applied.
@param borderType Pixel extrapolation method, see #BorderTypes
@param borderValue Border value in case of a constant border. The default value has a special
       meaning.	

    cv::morphologyEx()函式在OpenCV_3.4.1_Source\modules\imgproc\src\morph.cpp檔案中定義,其中OpenCV_3.4.1_Source為原始碼下載解壓後的資料夾。morphologyEx()函式定義如下:

static bool ocl_morphologyEx(InputArray _src, OutputArray _dst, int op,
                             InputArray kernel, Point anchor, int iterations,
                             int borderType, const Scalar& borderValue)
{
    _dst.createSameSize(_src, _src.type());
    bool submat = _dst.isSubmatrix();
    UMat temp;
    _OutputArray _temp = submat ? _dst : _OutputArray(temp);

    switch( op )
    {
    case MORPH_ERODE:
        if (!ocl_morphOp( _src, _dst, kernel, anchor, iterations, MORPH_ERODE, borderType, borderValue ))
            return false;
        break;
    case MORPH_DILATE:
        if (!ocl_morphOp( _src, _dst, kernel, anchor, iterations, MORPH_DILATE, borderType, borderValue ))
            return false;
        break;
    case MORPH_OPEN:
        if (!ocl_morphOp( _src, _temp, kernel, anchor, iterations, MORPH_ERODE, borderType, borderValue ))
            return false;
        if (!ocl_morphOp( _temp, _dst, kernel, anchor, iterations, MORPH_DILATE, borderType, borderValue ))
            return false;
        break;
    case MORPH_CLOSE:
        if (!ocl_morphOp( _src, _temp, kernel, anchor, iterations, MORPH_DILATE, borderType, borderValue ))
            return false;
        if (!ocl_morphOp( _temp, _dst, kernel, anchor, iterations, MORPH_ERODE, borderType, borderValue ))
            return false;
        break;
    case MORPH_GRADIENT:
        if (!ocl_morphOp( _src, temp, kernel, anchor, iterations, MORPH_ERODE, borderType, borderValue ))
            return false;
        if (!ocl_morphOp( _src, _dst, kernel, anchor, iterations, MORPH_DILATE, borderType, borderValue, MORPH_GRADIENT, temp ))
            return false;
        break;
    case MORPH_TOPHAT:
        if (!ocl_morphOp( _src, _temp, kernel, anchor, iterations, MORPH_ERODE, borderType, borderValue ))
            return false;
        if (!ocl_morphOp( _temp, _dst, kernel, anchor, iterations, MORPH_DILATE, borderType, borderValue, MORPH_TOPHAT, _src ))
            return false;
        break;
    case MORPH_BLACKHAT:
        if (!ocl_morphOp( _src, _temp, kernel, anchor, iterations, MORPH_DILATE, borderType, borderValue ))
            return false;
        if (!ocl_morphOp( _temp, _dst, kernel, anchor, iterations, MORPH_ERODE, borderType, borderValue, MORPH_BLACKHAT, _src ))
            return false;
        break;
    default:
        CV_Error( CV_StsBadArg, "unknown morphological operation" );
    }

    return true;
}

    其中,引數op用於選擇形態學運算型別,這裡用到的有下面幾個:

    1)MORPH_OPEN:開運算

    2)MORPH_CLOSE:閉運算

    3)MORPH_GRADIENT:形態學梯度

    4)MORPH_TOPHAT:頂帽

    5)MORPH_BLACKHAP:黑帽

    6)MORPH_ERODE:腐蝕

    7)MORPH_DILATE:膨脹

四、綜合示例及演示

4.1 介面設計

    介面設計如圖1所示。


圖1 介面設計

4.2 程式原始碼

1、mainwindow.h

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>

#include "opencv2/opencv.hpp"
using namespace cv;

namespace Ui {
class MainWindow;
}

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    explicit MainWindow(QWidget *parent = 0);
    ~MainWindow();

private:
    Ui::MainWindow *ui;

    int m_KernelValue;
    bool m_isOpenFile;
    int m_typeCurSel;

    Mat m_srcImage;
    Mat m_dstImage;

public:
    void on_MorphologyEx(int typeSel);
private slots:
    void on_pushButton_OpenImg_clicked();
    void on_comboBox_Type_currentIndexChanged(int index);
    void on_horizontalSlider_KernelValue_valueChanged(int value);
};

#endif // MAINWINDOW_H

2、mainwindow.cpp

#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QFileDialog>
#include <QMessageBox>

#define KERNEL_MIN_VALUE    0
#define KERNEL_MAX_VALUE    40

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    setWindowTitle(tr("Qt_OpenCV形態學運算"));

    //初始化變數
    m_KernelValue = 15;
    m_isOpenFile = false;
    m_typeCurSel = 0;

    //初始化控制元件
    ui->horizontalSlider_KernelValue->setMinimum(KERNEL_MIN_VALUE);
    ui->horizontalSlider_KernelValue->setMaximum(KERNEL_MAX_VALUE);
    ui->horizontalSlider_KernelValue->setValue(m_KernelValue);
    ui->label_KernelValue->setText(QString::number(m_KernelValue));

    ui->comboBox_Type->insertItem(0, "膨脹");
    ui->comboBox_Type->insertItem(1, "腐蝕");
    ui->comboBox_Type->insertItem(2, "開運算");
    ui->comboBox_Type->insertItem(3, "閉運算");
    ui->comboBox_Type->insertItem(4, "形態學梯度");
    ui->comboBox_Type->insertItem(5, "頂帽");
    ui->comboBox_Type->insertItem(6, "黑帽");
    ui->comboBox_Type->setCurrentIndex(m_typeCurSel);
}

MainWindow::~MainWindow()
{
    delete ui;
}

void MainWindow::on_pushButton_OpenImg_clicked()
{
    QString fileName = QFileDialog::getOpenFileName(this,tr("Open Image"),".",tr("Image File(*.png *.jpg *.jpeg *.bmp)"));
    if (fileName.isEmpty())
    {
        return;
    }

    m_srcImage = imread(fileName.toLatin1().data());//讀取圖片資料
    if (!m_srcImage.data)
    {
        m_isOpenFile = false;
        QMessageBox box(QMessageBox::Critical, "開啟影象", "讀取影象檔案失敗!請重新開啟.");
        box.setStandardButtons(QMessageBox::Ok);
        box.setButtonText(QMessageBox::Ok, QString("確定"));
        box.exec();
        return;
    }
    m_isOpenFile = true;//修改開啟標誌

    Mat disImageTemp;
    cvtColor(m_srcImage, disImageTemp, COLOR_BGR2RGB);//影象格式轉換
    QImage disImage = QImage((const unsigned char*)(disImageTemp.data),disImageTemp.cols,disImageTemp.rows,QImage::Format_RGB888);
    ui->label_OriginalImg->setPixmap(QPixmap::fromImage(disImage.scaled(ui->label_OriginalImg->width(), ui->label_OriginalImg->height(), Qt::KeepAspectRatio)));

    on_MorphologyEx(m_typeCurSel);
}

void MainWindow::on_horizontalSlider_KernelValue_valueChanged(int value)
{
    if (m_isOpenFile)
    {
        m_KernelValue = value;
        ui->label_KernelValue->setText(QString::number(m_KernelValue));
        on_MorphologyEx(m_typeCurSel);
    }
}

void MainWindow::on_MorphologyEx(int typeSel)
{
    //獲取核心形狀和尺寸
    Mat element = getStructuringElement(MORPH_RECT, Size(m_KernelValue * 2 + 1, m_KernelValue * 2 + 1), Point(m_KernelValue, m_KernelValue));

    //腐蝕操作
    switch (typeSel) {
    case 0:
        morphologyEx(m_srcImage, m_dstImage, MORPH_DILATE, element);
        break;
    case 1:
        morphologyEx(m_srcImage, m_dstImage, MORPH_ERODE, element);
        break;
    case 2:
        morphologyEx(m_srcImage, m_dstImage, MORPH_OPEN, element);
        break;
    case 3:
        morphologyEx(m_srcImage, m_dstImage, MORPH_CLOSE, element);
        break;
    case 4:
        morphologyEx(m_srcImage, m_dstImage, MORPH_GRADIENT, element);
        break;
    case 5:
        morphologyEx(m_srcImage, m_dstImage, MORPH_TOPHAT, element);
        break;
    case 6:
        morphologyEx(m_srcImage, m_dstImage, MORPH_BLACKHAT, element);
        break;
    default:
        break;
    }

    //顯示
    cvtColor(m_dstImage, m_dstImage, COLOR_BGR2RGB);//影象格式轉換
    QImage disImage = QImage((const unsigned char*)(m_dstImage.data),m_dstImage.cols,m_dstImage.rows,QImage::Format_RGB888);
    ui->label_ProcessedImg->setPixmap(QPixmap::fromImage(disImage.scaled(ui->label_ProcessedImg->width(), ui->label_ProcessedImg->height(), Qt::KeepAspectRatio)));
}

void MainWindow::on_comboBox_Type_currentIndexChanged(int index)
{
    m_typeCurSel = index;
    if (m_isOpenFile)
    {
        on_MorphologyEx(m_typeCurSel);
    }
}

4.3 執行效果

    執行效果如下:


圖2 膨脹


圖3 腐蝕


圖4 開運算


圖5 閉運算


圖6 形態學梯度(1)


圖7 形態學梯度(2)


圖8 頂帽


圖9 黑帽