一、开发环境
1、Windows 7 64位 SP1 旗舰版;
2、Qt 5.10.1;
3、OpenCV 3.4.1
二、非线型滤波
线型滤波易于构造,易于从频率响应角度分析,但是,很多情况下,如果脉冲噪声、椒盐噪声明显时,或者需要较好的保存边缘时,线型滤波往往无法达到需要的效果,此时就需要用到非线性滤波。
常见的非线性滤波有:中值滤波、双边滤波。
中值滤波基本思想是用像素点邻域灰度值的中值来代替该像素点的灰度值,该方法在去除脉冲噪声、椒盐噪声的同时又能保存图像的边缘细节。和均值滤波相比,中值滤波可以有效滤除高频噪声的同时保存边缘,但其由于运算过程中需要进行排序,因此运行时间约是均值滤波的5倍以上。中值滤波邻域大小一般采用奇数点来运算。
双边滤波是基于空间分布的高斯滤波函数,比高斯滤波多一个高斯方差sigma-d,在边缘附近,离得远的像素不会对边缘上的像素影响太多,可以很好的做边缘保存。
三、OpenCV实现
3.1 中值滤波
OpenCV函数原型及参数说明:
void medianBlur( InputArray src, OutputArray dst, int ksize );
@param src input 1-, 3-, or 4-channel image; when ksize is 3 or 5, the image depth should be
CV_8U, CV_16U, or CV_32F, for larger aperture sizes, it can only be CV_8U.
@param dst destination array of the same size and type as src.
@param ksize aperture linear size; it must be odd and greater than 1, for example: 3, 5, 7 ...
3.2 双边滤波
OpenCV函数原型及参数说明:
CV_EXPORTS_W void bilateralFilter( InputArray src, OutputArray dst, int d,
double sigmaColor, double sigmaSpace,
int borderType = BORDER_DEFAULT );
@param src Source 8-bit or floating-point, 1-channel or 3-channel image.
@param dst Destination image of the same size and type as src .
@param d Diameter of each pixel neighborhood that is used during filtering. If it is non-positive,
it is computed from sigmaSpace.
@param sigmaColor Filter sigma in the color space. A larger value of the parameter means that
farther colors within the pixel neighborhood (see sigmaSpace) will be mixed together, resulting
in larger areas of semi-equal color.
@param sigmaSpace Filter sigma in the coordinate space. A larger value of the parameter means that
farther pixels will influence each other as long as their colors are close enough (see sigmaColor
). When d\>0, it specifies the neighborhood size regardless of sigmaSpace. Otherwise, d is
proportional to sigmaSpace.
@param borderType border mode used to extrapolate pixels outside of the image, see cv::BorderTypes
四、示例设计与测试
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;
Mat m_srcImage;
Mat m_dstImage;
public:
void on_MedianBlur(void);
private slots:
void on_pushButton_OpenImg_clicked();
void on_horizontalSlider_DValue_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 = 3;
m_isOpenFile = false;
//初始化控件
ui->horizontalSlider_DValue->setMinimum(KERNEL_MIN_VALUE);
ui->horizontalSlider_DValue->setMaximum(KERNEL_MAX_VALUE);
ui->horizontalSlider_DValue->setValue(m_KernelValue);
ui->label_KernelValue->setText(QString::number(m_KernelValue));
}
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_MedianBlur();
}
void MainWindow::on_horizontalSlider_DValue_valueChanged(int value)
{
if (m_isOpenFile)
{
m_KernelValue = value;
ui->label_KernelValue->setText(QString::number(m_KernelValue));
on_MedianBlur();
}
}
void MainWindow::on_MedianBlur()
{
medianBlur(m_srcImage, m_dstImage, m_KernelValue * 2 + 1);//中值滤波
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)));
}
2、运行效果:
图2 中值滤波KSize = 1
图3 中值滤波KSize = 3
图4 中值滤波KSize = 7
4.3 双边滤波
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;
Mat m_srcImage;
Mat m_dstImage;
public:
void on_BilateralFilter(void);
private slots:
void on_pushButton_OpenImg_clicked();
void on_horizontalSlider_DValue_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 = 3;
m_isOpenFile = false;
//初始化控件
ui->horizontalSlider_DValue->setMinimum(KERNEL_MIN_VALUE);
ui->horizontalSlider_DValue->setMaximum(KERNEL_MAX_VALUE);
ui->horizontalSlider_DValue->setValue(m_KernelValue);
ui->label_KernelValue->setText(QString::number(m_KernelValue));
}
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_BilateralFilter();
}
void MainWindow::on_horizontalSlider_DValue_valueChanged(int value)
{
if (m_isOpenFile)
{
m_KernelValue = value;
ui->label_KernelValue->setText(QString::number(m_KernelValue));
on_BilateralFilter();
}
}
void MainWindow::on_BilateralFilter()
{
bilateralFilter(m_srcImage, m_dstImage, m_KernelValue, m_KernelValue * 2, m_KernelValue / 2);//双边滤波
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)));
}
2、运行效果:
备注:中值滤波和双边滤波源码,仅调用函数不同,其他软件框架均一样,为了便于快速复制与测试,上面均给处理全部源码程序。