这个学期,由于脑袋抽风涉嫌装逼(不是),校选课选了四大名挂之数字图像处理- -。但在慢慢深入学习之后,却渐渐对其产生了兴趣(真香)
于是在朋友和百度的帮助下,总算是完成了最后的拙作。我选择的是数字图像处理中的高斯滤波和中值滤波的实现,话不多说,正文开始:
在实现滤波操作之前,需要先介绍一下数字图像处理中图像的噪声和卷积:
图像噪声是指存在于图像数据中的不必要的或多余的干扰信息。各类图像处理系统在图像的采集、获取、传送和转换(如成像、复制扫描、传输以及显示等)过程中,均处在复杂的环境中,光照、电磁多变,所有的图像均不同程度地被可见或不可见的噪声干扰,导致图像质量的下降,掩盖图片重要细节
而图像噪声的去除在数字图像处理技术中的重要性越来越明显,如高放大倍数航片的判读,X射线图像系统中的噪声去除等已经成为不可缺少的技术步骤。
卷积:其实就是一个带权值的n维矩阵(这里也叫滤波器)在图像的像素图上滚来滚去对每个像素点进行加权运算,滚完了滤波也就做完了~
总所周知,图片其实是有千万个像素组成的,而每个像素点,其实就是表示图像的二维数组中的每个房间地址,灰度值,就是每个房间中的值。在图像的生成过程中,由于不可控因素,会造成临近灰度值所形成的函数的导数过大——也就是噪声,让其与真实颜色产生误差,掩盖图片细节,之后的各种后期图像处理便会不断放大这种误差。
而滤波则是对图片预处理时消除噪音的一种重要方式。
这里再简单的介绍一下高斯滤波和中值滤波:
高斯滤波的滤波器中的权值的计算公式就是他啦~也就是高斯滤波名字的由来啦!
中值滤波正如其名,它是将像素(中值计算中包括的原像素值)邻域内灰度的中值代替该像素的值,也就是我们在图像中取3*3的矩阵,里面有9个像素点,我们将9个像素进行排序,最后将这个矩阵的中心点赋值为这九个像素的中值
介绍了这么多,其实滤波器复杂的运算并不需要我们去实现,opencv中提供了medianBlur()和GaussianBlur()函数来实现中值滤波和高斯滤波的操作。原型如下:
C++: void medianBlur(InputArray src, OutputArray dst, int ksize)
参数解释:
C++:void GaussianBlur( InputArray src, OutputArray dst, Size ksize,
double sigmaX, double sigmaY = 0,
int borderType = BORDER_DEFAULT );
参数解释:
#pragma once
#ifndef QSecond_H
#define QSecond_H
#include
#include "ui_QSecond.h"
#include
#include
#include
#include
namespace Ui {//namespace:命名空间Ui:区分不同代码作者的相同函数名,相当于不同文件夹下的相同文件名得以区分
class MainWindow;//空间成员
}
class QSecond : public QMainWindow
{
Q_OBJECT
public:
explicit QSecond(QWidget *parent = nullptr);//使用explcit防止Qsecond被自动地隐式类型转换
~QSecond();//析构函数
private slots://private:只能在定义它的类QSecond中使用
void on_inputPushButton_pressed();
void on_outputPushButton_pressed();
void on_savePushButton_pressed();
void on_showPushButton_pressed();
private://只能在定义它的类中使用
Ui::MainWindow *ui;
};
#endif // MAINWINDOW_H
#include "QSecond.h"
#include "ui_QSecond.h"
#include
#include
#include
QSecond::QSecond(QWidget *parent) ://QWidget:所有用户界面对象的基类。
QMainWindow(parent),//创建主界面
ui(new Ui::MainWindow)
{
ui->setupUi(this);
}
QSecond::~QSecond()//析构函数:在QSecond类使用完后自动删除(抛弃)
{
delete ui;
}
void QSecond::on_inputPushButton_pressed()//Input键功能实现
{
QString fileName = QFileDialog::getOpenFileName(this,//Q:FileDialogL-文件选择窗口。
"Open Input Image",
QDir::currentPath(),//获取当前工作目录
"Images (*.jpg *.png *.bmp)");
if (QFile::exists(fileName)) {
ui->inputLineEdit->setText(fileName);
}
}
void QSecond::on_outputPushButton_pressed()//Output键功能实现
{
QString fileName = QFileDialog::getSaveFileName(this,
"Select Output Image",
QDir::currentPath(),
"*.jpg;;*.png;;*.bmp");
if (!fileName.isEmpty())
ui->outputLineEdit->setText(fileName);
}
void QSecond::on_savePushButton_pressed()//Save键功能实现
{
if (!ui->outputLineEdit->text().isEmpty()) {
cv::Mat inImg, outImg;
inImg = cv::imread(ui->inputLineEdit->text().toStdString());
if (ui->medianBlurRadioButton->isChecked())
cv::medianBlur(inImg, outImg, 5);
else if (ui->gaussianBlurRadioButton->isChecked())
cv::GaussianBlur(inImg, outImg, cv::Size(5, 5), 1.25);
if (ui->displayImageCheckBox->isChecked())//dispaly键功能实现
cv::imshow("Output Img", outImg);
}
}
void QSecond::on_showPushButton_pressed()//Show键功能实现
{
if (ui->gaussianBlurRadioButton->isChecked() | ui->medianBlurRadioButton->isChecked())//ischecked:该键是否被选中
if (!ui->inputLineEdit->text().isEmpty()) {
cv::Mat inImg, outImg;//初始化定义两个图像:输入输出图
inImg = cv::imread(ui->inputLineEdit->text().toStdString());//imread读取图像
if (ui->medianBlurRadioButton->isChecked())
cv::medianBlur(inImg, outImg, 5);//5:滤波模板尺寸大小
else if (ui->gaussianBlurRadioButton->isChecked())
cv::GaussianBlur(inImg, outImg, cv::Size(5, 5), 1.25);//5.5为高斯滤波模板器尺寸,1.25为边缘点插值类型大小
cv::namedWindow("Input Img",cv::WINDOW_NORMAL);//窗口命名。NORMAL:窗口可以通过拖动改变大小
cv::namedWindow("Output Img",cv::WINDOW_NORMAL);//AUTOSIZE:窗口大小等于图片大小
cv::imshow("Input Img", inImg);//imshow:输出图像
cv::imshow("Output Img", outImg);
if (cv::waitKey(200) == 27)//waitKey:不断刷新图像,刷新率为200ms.当输入键值为27(ESC)时,停止刷新,关闭窗口
cv::destroyAllWindows();
}
}
#include "QSecond.h"
#include
int main(int argc, char *argv[])
{
QApplication a(argc, argv);//创建对象
QSecond w;
w.show();//显示无模式对话框。(无模式:即可以切换焦点程序)
return a.exec();//程序进程开始
}
因为我也是初学,所以大多还只是略懂皮毛,但这次的实验也让我打开了一扇新的大门,后面等待的是更多未知的有趣的代码与知识。希望在未来,还能在这里与大家分享自己摸爬滚打的学习经验~
#include
#include
#include
#include
int main(int argc, char* argv[])
{
using namespace cv;
using namespace std;
Mat img;
VideoCapture cap;
cap.open(0);
if (!cap.isOpened())
return 0;
namedWindow("JT", WINDOW_NORMAL);
while (1)
{
cap >> img;
if (img.empty())
break;
imshow("JT", img);
if (waitKey(30) == 27)
break;
}
destroyAllWindows();
return 0;
}
别问运行后是什么,问就是蔡徐坤/滑稽脸
下次再见~