参考教程:https://blog.csdn.net/qingyang8513/article/details/80413320
线型滤波
图像平滑处理又称为图像模糊,主要是用来减少图像上的噪声点或者失真。
图像滤波是在尽量保留图像细节特征的条件下对目标图像的噪声进行抑制,是图像预处理中不可或缺的操作,其处理效果的好坏将直接影响后续图像处理和分析的效率。
图像滤波分为线型滤波和非线性滤波,其中线型滤波根据滤波器特性可以分为低通滤波、高通滤波、带通滤波、带阻滤波器、全通滤波和陷波滤波等。由于图像的细节信息包含在图像的高频部分,因此低通滤波具有图像模糊的效果,而高通滤波具有图像锐化的特点。
这里介绍的线型滤波包括方框滤波、均值滤波和高斯滤波,三者均为低通滤波,具有消除噪声,图像模糊的特点。其中,均值滤波为归一化后的方框滤波,而高斯滤波主要用来去除图像混入的高斯噪声。
这里仅介绍各滤波函数的实现方式,并通过OpenCV示例演示器效果,具体理论部分请参考相应教材或者论坛。
doc:https://docs.opencv.org/4.1.0/
3.1 方框滤波
OpenCV函数原型及参数说明:
void cv::boxFilter | ( | InputArray | src, |
OutputArray | dst, | ||
int | ddepth, | ||
Size | ksize, | ||
Point | anchor = Point(-1,-1) , | ||
bool | normalize = true , | ||
int | borderType = BORDER_DEFAULT | ||
) |
#include <opencv2/imgproc.hpp>
Blurs an image using the box filter.
The function smooths an image using the kernel:
where
参考:https://docs.opencv.org/4.1.0/d4/d86/group__imgproc__filter.html#gad533230ebf2d42509547d514f7d3fbc3
非标准化盒式滤波器可用于计算每个像素邻域上的各种积分特性,例如图像导数的协方差矩阵(用于密集光流算法等)。如果需要在可变大小的窗口上计算像素总和,请使用积分。
参数
SRC | 输入图像。 |
DST | 输出与src相同大小和类型的图像。 |
ddepth | 输出图像深度(-1使用src.depth())。 |
ksize | 模糊内核大小。 |
锚 | 锚点; 默认值Point(-1,-1)表示锚点位于内核中心。 |
正常化 | flag,指定内核是否按其区域进行规范化。 |
borderType | 用于外推图像外部像素的边框模式,请参阅BorderTypes |
3.2 均值滤波
OpenCV函数原型及参数说明:
void cv::blur | ( | InputArray | src, |
OutputArray | dst, | ||
Size | ksize, | ||
Point | anchor = Point(-1,-1) , | ||
int | borderType = BORDER_DEFAULT | ||
) |
#include <opencv2/imgproc.hpp>
使用标准化的盒式过滤器模糊图像。
该函数使用内核平滑图像:
参数
SRC | 输入图像; 它可以有任意数量的通道,这些通道是独立处理的,但深度应该是CV_8U,CV_16U,CV_16S,CV_32F或CV_64F。 |
DST | 输出与src相同大小和类型的图像。 |
ksize | 模糊内核大小。 |
锚 | 锚点; 默认值Point(-1,-1)表示锚点位于内核中心。 |
borderType | 用于外推图像外部像素的边框模式,请参阅BorderTypes |
3.3 高斯滤波
OpenCV函数原型及参数说明:
void cv::GaussianBlur | ( | InputArray | src, |
OutputArray | dst, | ||
Size | ksize, | ||
double | sigmaX, | ||
double | sigmaY = 0 , | ||
int | borderType = BORDER_DEFAULT | ||
) |
#include <opencv2/imgproc.hpp>
使用高斯滤波器模糊图像。
该函数将源图像与指定的高斯内核进行卷积。支持就地过滤。
参数
SRC | 输入图像; 图像可以有任意数量的通道,这些通道是独立处理的,但深度应该是CV_8U,CV_16U,CV_16S,CV_32F或CV_64F。 |
DST | 输出与src相同大小和类型的图像。 |
ksize | 高斯核大小。ksize.width和ksize.height可以不同,但它们都必须是正数和奇数。或者,它们可以是零,然后它们是从西格玛计算的。 |
sigmaX | X方向的高斯核标准偏差。 |
sigmaY | Y方向的高斯核标准偏差; 如果sigmaY为零,则将其设置为等于sigmaX,如果两个sigma均为零,则分别从ksize.width和ksize.height计算(有关详细信息,请参阅getGaussianKernel); 为了完全控制结果,无论将来可能修改所有这些语义,建议指定所有ksize,sigmaX和sigmaY。 |
borderType | 像素外推法,参见BorderTypes |
参考:https://docs.opencv.org/4.1.0/d4/d86/group__imgproc__filter.html#gaabe8c836e97159a9193fb0b11ac52cf1
设计图示
初始界面
原始图片效果:
分别调整至最大:
一起调整的效果:
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>791</width>
<height>542</height>
</rect>
</property>
<property name="windowTitle">
<string>MainWindow</string>
</property>
<widget class="QWidget" name="centralWidget">
<widget class="QLabel" name="label_Kernel_BoxFilter">
<property name="geometry">
<rect>
<x>380</x>
<y>350</y>
<width>141</width>
<height>21</height>
</rect>
</property>
<property name="text">
<string>label_Kernel_BoxFilter</string>
</property>
</widget>
<widget class="QLabel" name="label_Kernel_Blur">
<property name="geometry">
<rect>
<x>380</x>
<y>370</y>
<width>111</width>
<height>31</height>
</rect>
</property>
<property name="text">
<string>label_Kernel_Blur</string>
</property>
</widget>
<widget class="QLabel" name="label_Kernel_GuassianBlur">
<property name="geometry">
<rect>
<x>380</x>
<y>400</y>
<width>161</width>
<height>41</height>
</rect>
</property>
<property name="text">
<string>label_Kernel_GuassianBlur</string>
</property>
</widget>
<widget class="QPushButton" name="pushButton">
<property name="geometry">
<rect>
<x>690</x>
<y>450</y>
<width>75</width>
<height>23</height>
</rect>
</property>
<property name="text">
<string>打开图像</string>
</property>
</widget>
<widget class="QSlider" name="horizontalSlider_GaussianBlur">
<property name="geometry">
<rect>
<x>550</x>
<y>410</y>
<width>101</width>
<height>20</height>
</rect>
</property>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
<widget class="QSlider" name="horizontalSlider_BoxFilter">
<property name="geometry">
<rect>
<x>550</x>
<y>350</y>
<width>101</width>
<height>19</height>
</rect>
</property>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
<widget class="QSlider" name="horizontalSlider_Blur">
<property name="geometry">
<rect>
<x>550</x>
<y>380</y>
<width>101</width>
<height>19</height>
</rect>
</property>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
<widget class="QLabel" name="label_Kernel_All">
<property name="geometry">
<rect>
<x>380</x>
<y>450</y>
<width>101</width>
<height>16</height>
</rect>
</property>
<property name="text">
<string>label_Kernel_All</string>
</property>
</widget>
<widget class="QSlider" name="horizontalSlider_FilterAll">
<property name="geometry">
<rect>
<x>550</x>
<y>440</y>
<width>101</width>
<height>19</height>
</rect>
</property>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
<widget class="QLabel" name="label_Display1">
<property name="geometry">
<rect>
<x>50</x>
<y>60</y>
<width>271</width>
<height>181</height>
</rect>
</property>
<property name="text">
<string>label_Display1</string>
</property>
</widget>
<widget class="QLabel" name="label_Display2">
<property name="geometry">
<rect>
<x>380</x>
<y>50</y>
<width>271</width>
<height>181</height>
</rect>
</property>
<property name="text">
<string>label_Display2</string>
</property>
</widget>
<widget class="QLabel" name="label_Display3">
<property name="geometry">
<rect>
<x>40</x>
<y>270</y>
<width>271</width>
<height>181</height>
</rect>
</property>
<property name="text">
<string>label_Display3</string>
</property>
</widget>
<widget class="QLabel" name="label">
<property name="geometry">
<rect>
<x>50</x>
<y>30</y>
<width>141</width>
<height>21</height>
</rect>
</property>
<property name="text">
<string>方框滤波boxFilter</string>
</property>
</widget>
<widget class="QLabel" name="label_2">
<property name="geometry">
<rect>
<x>380</x>
<y>30</y>
<width>141</width>
<height>21</height>
</rect>
</property>
<property name="text">
<string>均值滤波Blur</string>
</property>
</widget>
<widget class="QLabel" name="label_3">
<property name="geometry">
<rect>
<x>50</x>
<y>240</y>
<width>141</width>
<height>21</height>
</rect>
</property>
<property name="text">
<string>高斯滤波GaussianBlur</string>
</property>
</widget>
</widget>
<widget class="QMenuBar" name="menuBar">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>791</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.cpp
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QFileDialog>
#include <QMessageBox>
#define KERNEL_MIN_VALUE 0
#define KERNEL_MAX_VALUE 40
Mat g_srcImage;
Mat g_dstImage_BoxFilter;
Mat g_dstImage_Blur;
Mat g_dstImage_GaussianBlur;
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
ui->menuBar->hide(); //隐藏菜单栏
ui->mainToolBar->hide();//隐藏工具栏
ui->statusBar->hide(); //隐藏状态栏
setWindowTitle(tr("Qt_OpenCV线型滤波"));
//初始化变量
m_kernelValueBoxFilter = 3;
m_kernelValueBlur = 3;
m_kernelValueGaussianBlur = 3;
m_kernelAll = 3;
m_isOpenFile = false;
//初始化控件
ui->horizontalSlider_BoxFilter->setMinimum(KERNEL_MIN_VALUE);
ui->horizontalSlider_BoxFilter->setMaximum(KERNEL_MAX_VALUE);
ui->horizontalSlider_BoxFilter->setValue(m_kernelValueBoxFilter);
ui->horizontalSlider_Blur->setMinimum(KERNEL_MIN_VALUE);
ui->horizontalSlider_Blur->setMaximum(KERNEL_MAX_VALUE);
ui->horizontalSlider_Blur->setValue(m_kernelValueBlur);
ui->horizontalSlider_GaussianBlur->setMinimum(KERNEL_MIN_VALUE);
ui->horizontalSlider_GaussianBlur->setMaximum(KERNEL_MAX_VALUE);
ui->horizontalSlider_GaussianBlur->setValue(m_kernelValueGaussianBlur);
ui->horizontalSlider_FilterAll->setMinimum(KERNEL_MIN_VALUE);
ui->horizontalSlider_FilterAll->setMaximum(KERNEL_MAX_VALUE);
ui->horizontalSlider_FilterAll->setValue(m_kernelAll);
ui->label_Kernel_BoxFilter->setText(QString::number(m_kernelValueBoxFilter));
ui->label_Kernel_Blur->setText(QString::number(m_kernelValueBlur));
ui->label_Kernel_GuassianBlur->setText(QString::number(m_kernelValueGaussianBlur));
ui->label_Kernel_All->setText(QString::number(m_kernelAll));
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::on_pushButton_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;//修改打开标志
on_BoxFilter();
on_Blur();
on_GaussianBlur();
}
void MainWindow::on_horizontalSlider_BoxFilter_valueChanged(int value)
{
if (m_isOpenFile)
{
m_kernelValueBoxFilter = value;
ui->label_Kernel_BoxFilter->setText(QString::number(m_kernelValueBoxFilter));
on_BoxFilter();
}
}
void MainWindow::on_horizontalSlider_Blur_valueChanged(int value)
{
if (m_isOpenFile)
{
m_kernelValueBlur = value;
ui->label_Kernel_Blur->setText(QString::number(m_kernelValueBlur));
on_Blur();
}
}
void MainWindow::on_horizontalSlider_GaussianBlur_valueChanged(int value)
{
if (m_isOpenFile)
{
m_kernelValueGaussianBlur = value;
ui->label_Kernel_GuassianBlur->setText(QString::number(m_kernelValueGaussianBlur));
on_GaussianBlur();
}
}
void MainWindow::on_horizontalSlider_FilterAll_valueChanged(int value)
{
if (m_isOpenFile)
{
m_kernelAll = value;
ui->label_Kernel_All->setText(QString::number(m_kernelAll));
ui->horizontalSlider_BoxFilter->setValue(m_kernelAll);
ui->horizontalSlider_Blur->setValue(m_kernelAll);
ui->horizontalSlider_GaussianBlur->setValue(m_kernelAll);
}
}
void MainWindow::on_BoxFilter()
{
boxFilter(m_srcImage, m_dstImage_BoxFilter, -1, Size(m_kernelValueBoxFilter + 1, m_kernelValueBoxFilter + 1));
cvtColor(m_dstImage_BoxFilter, m_dstImage_BoxFilter, COLOR_BGR2RGB);//图像格式转换
QImage disImage = QImage((const unsigned char*)(m_dstImage_BoxFilter.data),m_dstImage_BoxFilter.cols,m_dstImage_BoxFilter.rows,QImage::Format_RGB888);
ui->label_Display1->setPixmap(QPixmap::fromImage(disImage.scaled(ui->label_Display1->width(), ui->label_Display1->height(), Qt::KeepAspectRatio)));
}
void MainWindow::on_Blur()
{
blur(m_srcImage, m_dstImage_Blur, Size(m_kernelValueBlur + 1, m_kernelValueBlur + 1), Point(-1, -1));
cvtColor(m_dstImage_Blur, m_dstImage_Blur, COLOR_BGR2RGB);//图像格式转换
QImage disImage = QImage((const unsigned char*)(m_dstImage_Blur.data),m_dstImage_Blur.cols,m_dstImage_Blur.rows,QImage::Format_RGB888);
ui->label_Display2->setPixmap(QPixmap::fromImage(disImage.scaled(ui->label_Display2->width(), ui->label_Display2->height(), Qt::KeepAspectRatio)));
}
void MainWindow::on_GaussianBlur()
{
GaussianBlur(m_srcImage, m_dstImage_GaussianBlur, Size(m_kernelValueGaussianBlur * 2 + 1, m_kernelValueGaussianBlur * 2 + 1), 0, 0);
cvtColor(m_dstImage_GaussianBlur, m_dstImage_GaussianBlur, COLOR_BGR2RGB);//图像格式转换
QImage disImage = QImage((const unsigned char*)(m_dstImage_GaussianBlur.data),m_dstImage_GaussianBlur.cols,m_dstImage_GaussianBlur.rows,QImage::Format_RGB888);
ui->label_Display3->setPixmap(QPixmap::fromImage(disImage.scaled(ui->label_Display3->width(), ui->label_Display3->height(), Qt::KeepAspectRatio)));
}
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 slots:
void on_pushButton_clicked();
void on_horizontalSlider_BoxFilter_valueChanged(int value);
void on_horizontalSlider_Blur_valueChanged(int value);
void on_horizontalSlider_GaussianBlur_valueChanged(int value);
void on_horizontalSlider_FilterAll_valueChanged(int value);
private:
Ui::MainWindow *ui;
int m_kernelValueBoxFilter;
int m_kernelValueBlur;
int m_kernelValueGaussianBlur;
int m_kernelAll;
bool m_isOpenFile;
Mat m_srcImage;
Mat m_dstImage_BoxFilter;
Mat m_dstImage_Blur;
Mat m_dstImage_GaussianBlur;
public:
void on_BoxFilter(void);
void on_Blur(void);
void on_GaussianBlur(void);
};
#endif // MAINWINDOW_H
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
实验中,调整的值是核的大小,也就是函数中的ksize.width以及ksize.height。最小值为0,故预先加一,高斯滤波的核大小需要为正奇数。
boxFilter(m_srcImage, m_dstImage_BoxFilter, -1, Size(m_kernelValueBoxFilter + 1, m_kernelValueBoxFilter + 1));
blur(m_srcImage, m_dstImage_Blur, Size(m_kernelValueBlur + 1, m_kernelValueBlur + 1), Point(-1, -1));
GaussianBlur(m_srcImage, m_dstImage_GaussianBlur, Size(m_kernelValueGaussianBlur * 2 + 1, m_kernelValueGaussianBlur * 2 + 1), 0, 0);