学习Qt_OpenCV形态学腐蚀、膨胀及OpenCV实现

参考教程:https://blog.csdn.net/qingyang8513/article/details/80413402

数学形态学是一门建立在格论和拓扑学基础之上的图像分析学科,是数学形态学图像处理的基本理论,其基本运算包括:二值腐蚀和膨胀、二值开闭运算、骨架抽取、极限腐蚀、击中击不中变换、形态学梯度、Top-hat变换、颗粒分析、流域变换、灰度腐蚀和膨胀、灰度形态学梯度等。

    腐蚀与膨胀主要功能:

    1)消除噪声;

    2)分割;

    3)寻找图像中的明显极大值区域或极小值区域;

    4)求图像梯度;

    膨胀就是求局部最大值的操作,针对的是图像中的高亮部分。从数学角度,膨胀或者腐蚀均是将图像A与核B进行卷积运算。核可以是任意形状和大小,其拥有一个单独定义出来的参考点,称其为锚点。膨胀运算是将核B与图像A进行卷积运算,求出局部最大值,并幅值给锚点。直观地,膨胀处理的结果是使图像的高亮部分区域增加。

    腐蚀与膨胀的原理相同,均是采用图像A与核B进行卷积运算,不同的是腐蚀是求局部最小值的过程,运算结果是使图像高亮部分区域减少,或者可以说是图像较暗的区域增加。

膨胀原理:求局部最大值

实现方法:

①定义一个卷积核B,核可以是任何的形状和大小,且拥有一个单独定义出来的参考点 - 锚点(anchorpoint);

通常和为带参考点的正方形或者圆盘,可将核称为模板或掩膜;

②将核B与图像A进行卷积,计算核B覆盖区域的像素点最大值;

③将这个最大值赋值给参考点指定的像素;

因此,图像中的高亮区域逐渐增长。

腐蚀原理:求局部最小值(与膨胀相反);

实现方法:

①定义一个卷积核B,核可以是任何的形状和大小,且拥有一个单独定义出来的参考点 - 锚点(anchorpoint);

通常和为带参考点的正方形或者圆盘,可将核称为模板或掩膜;

②将核B与图像A进行卷积,计算核B覆盖区域的像素点最小值;

③将这个最小值赋值给参考点指定的像素;

因此,图像中的高亮区域逐渐减小。


原文:https://blog.csdn.net/qq_39861376/article/details/82111292 
 

OpenCV实现

1、膨胀函数原型:

dilate()

void cv::dilate ( InputArray  src,
    OutputArray  dst,
    InputArray  kernel,
    Point  anchor = Point(-1,-1),
    int  iterations = 1,
    int  borderType = BORDER_CONSTANT,
    const Scalar &  borderValue = morphologyDefaultBorderValue() 
  )    

#include <opencv2/imgproc.hpp>

通过使用特定的结构元素来扩展图像。

该函数使用指定的结构元素扩展源图像,该结构元素确定采用最大值的像素邻域的形状:

该功能支持就地模式。扩张可以应用几次(迭代)次。在多通道图像的情况下,每个通道被独立处理。

参数

SRC 输入图像; 通道数可以是任意的,但深度应该是CV_8U,CV_16U,CV_16S,CV_32F或CV_64F之一。
DST 输出与src相同大小和类型的图像。
核心 结构元素用于扩张; 如果elemenat = Mat(),则使用3 x 3矩形结构元素。可以使用getStructuringElement创建内核
锚点在元素内的位置; 默认值(-1,-1)表示锚点位于元素中心。
迭代 应用扩张的次数。
borderType 像素外推法,参见BorderTypes
borderValue 边界不变的边界值

 

2、腐蚀函数原型:

erode()

void cv::erode ( InputArray  src,
    OutputArray  dst,
    InputArray  kernel,
    Point  anchor = Point(-1,-1),
    int  iterations = 1,
    int  borderType = BORDER_CONSTANT,
    const Scalar &  borderValue = morphologyDefaultBorderValue() 
  )    

#include <opencv2/imgproc.hpp>

使用特定的结构元素侵蚀图像。

该函数使用指定的结构元素侵蚀源图像,该结构元素确定采用最小值的像素邻域的形状:

该功能支持就地模式。侵蚀可以应用几次(迭代)次。在多通道图像的情况下,每个通道被独立处理。

参数

SRC 输入图像; 通道数可以是任意的,但深度应该是CV_8U,CV_16U,CV_16S,CV_32F或CV_64F之一。
DST 输出与src相同大小和类型的图像。
核心 用于侵蚀的结构元素; if element=Mat(),使用3 x 3矩形结构元素。可以使用getStructuringElement创建内核。
锚点在元素内的位置; 默认值(-1,-1)表示锚点位于元素中心。
迭代 腐蚀的次数。
borderType 像素外推法,参见BorderTypes
borderValue 边界不变的边界值

示例设计与测试

 界面设计

mainwindow.ui

<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
 <class>MainWindow</class>
 <widget class="QMainWindow" name="MainWindow">
  <property name="geometry">
   <rect>
    <x>0</x>
    <y>0</y>
    <width>655</width>
    <height>416</height>
   </rect>
  </property>
  <property name="windowTitle">
   <string>MainWindow</string>
  </property>
  <widget class="QWidget" name="centralWidget">
   <widget class="QLabel" name="label">
    <property name="geometry">
     <rect>
      <x>30</x>
      <y>20</y>
      <width>54</width>
      <height>12</height>
     </rect>
    </property>
    <property name="text">
     <string>原始图像:</string>
    </property>
   </widget>
   <widget class="QLabel" name="label_2">
    <property name="geometry">
     <rect>
      <x>360</x>
      <y>20</y>
      <width>54</width>
      <height>12</height>
     </rect>
    </property>
    <property name="text">
     <string>运行效果:</string>
    </property>
   </widget>
   <widget class="QLabel" name="label_OriginalImg">
    <property name="geometry">
     <rect>
      <x>30</x>
      <y>70</y>
      <width>261</width>
      <height>191</height>
     </rect>
    </property>
    <property name="styleSheet">
     <string notr="true">border:1px solid black</string>
    </property>
    <property name="text">
     <string>Original  Image</string>
    </property>
   </widget>
   <widget class="QLabel" name="label_ProcessedImg">
    <property name="geometry">
     <rect>
      <x>360</x>
      <y>70</y>
      <width>261</width>
      <height>191</height>
     </rect>
    </property>
    <property name="styleSheet">
     <string notr="true">border:1px solid black</string>
    </property>
    <property name="text">
     <string>Processed  Image</string>
    </property>
   </widget>
   <widget class="QLabel" name="label_5">
    <property name="geometry">
     <rect>
      <x>30</x>
      <y>320</y>
      <width>54</width>
      <height>12</height>
     </rect>
    </property>
    <property name="text">
     <string>核大小:</string>
    </property>
   </widget>
   <widget class="QLabel" name="label_KernelValue">
    <property name="geometry">
     <rect>
      <x>120</x>
      <y>320</y>
      <width>21</width>
      <height>20</height>
     </rect>
    </property>
    <property name="styleSheet">
     <string notr="true">background-color: rgb(255, 85, 127);</string>
    </property>
    <property name="text">
     <string/>
    </property>
    <property name="alignment">
     <set>Qt::AlignCenter</set>
    </property>
   </widget>
   <widget class="QSlider" name="horizontalSlider_KernelValue">
    <property name="geometry">
     <rect>
      <x>160</x>
      <y>320</y>
      <width>221</width>
      <height>20</height>
     </rect>
    </property>
    <property name="orientation">
     <enum>Qt::Horizontal</enum>
    </property>
   </widget>
   <widget class="QPushButton" name="pushButton_OpenImg">
    <property name="geometry">
     <rect>
      <x>550</x>
      <y>320</y>
      <width>75</width>
      <height>23</height>
     </rect>
    </property>
    <property name="text">
     <string>打开图像</string>
    </property>
   </widget>
   <widget class="QComboBox" name="comboBox_Type">
    <property name="geometry">
     <rect>
      <x>468</x>
      <y>321</y>
      <width>71</width>
      <height>21</height>
     </rect>
    </property>
   </widget>
   <widget class="QLabel" name="label_3">
    <property name="geometry">
     <rect>
      <x>400</x>
      <y>320</y>
      <width>61</width>
      <height>16</height>
     </rect>
    </property>
    <property name="text">
     <string>操作类型:</string>
    </property>
   </widget>
  </widget>
  <widget class="QMenuBar" name="menuBar">
   <property name="geometry">
    <rect>
     <x>0</x>
     <y>0</y>
     <width>655</width>
     <height>23</height>
    </rect>
   </property>
  </widget>
  <widget class="QToolBar" name="mainToolBar">
   <attribute name="toolBarArea">
    <enum>TopToolBarArea</enum>
   </attribute>
   <attribute name="toolBarBreak">
    <bool>false</bool>
   </attribute>
  </widget>
  <widget class="QStatusBar" name="statusBar"/>
 </widget>
 <layoutdefault spacing="6" margin="11"/>
 <resources/>
 <connections/>
</ui>

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_Dilate(void);
    void on_Erode(void);
private slots:
    void on_pushButton_OpenImg_clicked();
    void on_comboBox_Type_currentIndexChanged(int index);
    void on_horizontalSlider_KernelValue_valueChanged(int value);
};

#endif // MAINWINDOW_H

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 = 1;
    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->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)));

    if (m_typeCurSel == 0)
    {
        on_Dilate();
    }
    else
    {
        on_Erode();
    }
}

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

void MainWindow::on_Dilate()
{
    //获取内核形状和尺寸
    Mat element = getStructuringElement(MORPH_RECT, Size(m_KernelValue * 2 + 1, m_KernelValue * 2 + 1), Point(m_KernelValue, m_KernelValue));

    //膨胀操作
    dilate(m_srcImage, m_dstImage, element);

    //显示
    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_Erode()
{
    //获取内核形状和尺寸
    Mat element = getStructuringElement(MORPH_RECT, Size(m_KernelValue * 2 + 1, m_KernelValue * 2 + 1), Point(m_KernelValue, m_KernelValue));

    //腐蚀操作
    erode(m_srcImage, m_dstImage, element);

    //显示
    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)
    {
        if (m_typeCurSel == 0)
        {
            on_Dilate();
        }
        else
        {
            on_Erode();
        }
    }
}

pro文件:

#-------------------------------------------------
#
# Project created by QtCreator 2019-07-12T14:41:02
#
#-------------------------------------------------

QT       += core gui

greaterThan(QT_MAJOR_VERSION, 4): QT += widgets

TARGET = OpenCV_Filter
TEMPLATE = app

# The following define makes your compiler emit warnings if you use
# any feature of Qt which has been marked as deprecated (the exact warnings
# depend on your compiler). Please consult the documentation of the
# deprecated API in order to know how to port your code away from it.
DEFINES += QT_DEPRECATED_WARNINGS

# You can also make your code fail to compile if you use deprecated APIs.
# In order to do so, uncomment the following line.
# You can also select to disable deprecated APIs only up to a certain version of Qt.
#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000    # disables all the APIs deprecated before Qt 6.0.0

CONFIG += c++11

SOURCES += \
        main.cpp \
        mainwindow.cpp

HEADERS += \
        mainwindow.h

FORMS += \
        mainwindow.ui

# Default rules for deployment.
qnx: target.path = /tmp/$${TARGET}/bin
else: unix:!android: target.path = /opt/$${TARGET}/bin
!isEmpty(target.path): INSTALLS += target
INCLUDEPATH += $$PWD/../opencv_qt/include\
             $$PWD/../opencv_qt/include/opencv\
              $$PWD/../opencv_qt/include/opencv2

unix|win32: LIBS += -L$$PWD/../opencv_qt/bin/ -llibopencv_calib3d410
unix|win32: LIBS += -L$$PWD/../opencv_qt/bin/ -llibopencv_core410
unix|win32: LIBS += -L$$PWD/../opencv_qt/bin/ -llibopencv_dnn410
unix|win32: LIBS += -L$$PWD/../opencv_qt/bin/ -llibopencv_features2d410
unix|win32: LIBS += -L$$PWD/../opencv_qt/bin/ -llibopencv_flann410
unix|win32: LIBS += -L$$PWD/../opencv_qt/bin/ -llibopencv_highgui410
unix|win32: LIBS += -L$$PWD/../opencv_qt/bin/ -llibopencv_imgcodecs410
unix|win32: LIBS += -L$$PWD/../opencv_qt/bin/ -llibopencv_imgproc410
unix|win32: LIBS += -L$$PWD/../opencv_qt/bin/ -llibopencv_ml410
unix|win32: LIBS += -L$$PWD/../opencv_qt/bin/ -llibopencv_objdetect410
unix|win32: LIBS += -L$$PWD/../opencv_qt/bin/ -llibopencv_photo410
unix|win32: LIBS += -L$$PWD/../opencv_qt/bin/ -llibopencv_stitching410
unix|win32: LIBS += -L$$PWD/../opencv_qt/bin/ -llibopencv_video410
unix|win32: LIBS += -L$$PWD/../opencv_qt/bin/ -llibopencv_videoio410
unix|win32: LIBS += -L$$PWD/../opencv_qt/bin/ -lopencv_ffmpeg410

演示结果:

可以看到膨胀就是让企鹅图像中白色(高亮)部分增多,腐蚀让企鹅黑色(暗色)部分增多。

发布了46 篇原创文章 · 获赞 5 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/qq_36808245/article/details/95950385
今日推荐