平滑 也称 模糊, 是一项简单且使用频率很高的图像处理方法。平滑处理的用途有很多,现在我们关注它减少噪声的功用 。平滑处理时需要用到一个 滤波器 。
前言
越来越多的开发人员选择基于开源的Qt框架与OpenCV来实现界面和算法,其原因不单单是无版权问题,更多是两个社区的发展蓬勃,可用来学习的资料与例程特别丰富。以下是关于利用Qt构建GUI并使用OpenCV中的Blur/Gaussian/Median/Bilateral函数进行图像平滑处理。
软件版本:Qt-5.12.0/OpenCV-4.5.3
平台:Windows10/11–64
一、函数介绍
1、均值滤波–Blur
函数原型
cv::blur(InputArray src, OutputArray dst, Size ksize, Point anchor=Point(-1,-1), int borderType=BORDER_DEFAULT )
参数解释:
src:输入图像,即源图像,填Mat类的对象即可。该函数对通道是独立处理的,且可以处理任意通道数的图片,但需要注意,待处理的图片深度应该为CV_8U, CV_16U, CV_16S, CV_32F 以及 CV_64F之一;
dst:即目标图像,需要和源图片有一样的尺寸和类型。比如可以用Mat::Clone,以源图片为模板,来初始化得到如假包换的目标图;
ksize:内核的大小。一般这样写Size( w,h )来表示内核的大小( 其中,w 为像素宽度, h为像素高度)。Size(3,3)就表示3x3的核大小,Size(5,5)就表示5x5的核大小
anchor:表示锚点(即被平滑的那个点),注意他有默认值Point(-1,-1)。如果这个点坐标是负值的话,就表示取核的中心为锚点,所以默认值Point(-1,-1)表示这个锚点在核的中心;
borderType:用于推断图像外部像素的某种边界模式。有默认值BORDER_DEFAULT;
2、高斯滤波–Gaussian
函数原型:
cv::GaussianBlur(InputArray src, OutputArray dst, Size ksize, double sigmaX, double sigmaY=0, int borderType=BORDER_DEFAULT);
参数解释:
src:输入图像,即源图像,填Mat类的对象即可。它可以是单独的任意通道数的图片,但需要注意,图片深度应该为CV_8U,CV_16U, CV_16S, CV_32F 以及 CV_64F之一;
dst:即目标图像,需要和源图片有一样的尺寸和类型。比如可以用Mat::Clone,以源图片为模板,来初始化得到如假包换的目标图;
ksize:高斯内核的大小。其中ksize.width和ksize.height可以不同,但他们都必须为正数和奇数(并不能理解)。或者,它们可以是零的,它们都是由sigma计算而来;
sigmaX:表示高斯核函数在X方向的的标准偏差;
sigmaY:表示高斯核函数在Y方向的的标准偏差。若sigmaY为零,就将它设为sigmaX,如果sigmaX和sigmaY都是0,那么就由ksize.width和ksize.height计算出来;
3、中值滤波–Median
函数原型:
cv::medianBlur( InputArray src, OutputArray dst, int ksize )
参数解释:
src:InputArray类型的src,输入图像,填Mat类的对象即可。待处理的图片深度应该为CV_8U, CV_16U, CV_16S, CV_32F 以及 CV_64F之一;
dst:OutputArray类型的dst,即输出图像,与源图片有一样的尺寸和类型;
ksize:Size类型的ksize,内核的大小,只能为奇数;
4、双边滤波–Bilateral
函数原型:
cv::bilateralFilter(InputArray src, OutpuArray dst, Int d, double sigmaColor, double sigmaSpace, Int borderType)
参数解释:
src: 是需要处理的图像,即原始图像。它能够有任意数量的通道,并能对各通道独立处理。图像深度应该是CV_8U、CV_16U、CV_16S、CV_32F或者CV_64F中的一 种。;
** dst**:是返回值,表示进行双边滤波后得到的处理结果;
d:是在滤波时选取的空间距离参数,这里表示以当前像素点为中心点的直径。如果该值为非正数,则会自动从参数 sigmaSpace计算得到。如果滤波空间较大(d>5),则速度较慢。因此,在实时应用中,推荐d=5。对于较大噪声的离线滤波,可以选择d=9;
sigmaColor:是滤波处理时选取的颜色差值范围,该值决定了周围哪些像素点能够参与到滤波中来。与当前像素点的像素值差值小于 sigmaColor 的像素点,能够参与到当前的滤波中。该值越大,就说明周围有越多的像素点可以参与到运算中。该值为0时,滤波失去意义;该值为255时,指定直径内的所有点都能够参与运算;
sigmaSpace:是坐标空间中的sigma值。它的值越大,说明有越多的点能够参与到滤波计算中来。当d>0时,无论sigmaSpace的值如何,d都指定邻域大小;否则,d与 sigmaSpace的值成比例;
borderType:是边界样式,该值决定了以何种方式处理边界。一般情况下,不需要考虑该值,直接采用默认值即可;
二、演示
1、GUI
如上图创建Blur/Gaussian/Median/Bilateral四个功能按钮QPushButton,对当前窗口的图像进行滤波操作,并输出状态信息。
2、实现代码
1、 blurBtn的clicked()槽函数实现
void MainWindow::on_blurBtn_clicked() { std::size_t numView = ui->tabWidget->currentIndex() % 3; if (dispMat[numView]->empty()) { outputInfo(2, tr("Please make sure the Mat exist!")); return; } int kernelVal = ui->kernelSpin->value(); if (kernelVal % 2 == 0) { kernelVal += 1; } cv::blur(*dispMat[numView], *dispMat[numView], cv::Size(kernelVal, kernelVal),\ // 均值滤波Blur cv::Point(-1, -1)); cvtMatPixmap(dispMat, dispPixmap, numView); outputInfo(1, tr("Blur done.")); }
2、 gaussianBtn的clicked()槽函数实现
void MainWindow::on_gaussianBtn_clicked() { std::size_t numView = ui->tabWidget->currentIndex() % 3; if (dispMat[numView]->empty()) { outputInfo(2, tr("Please make sure the Mat exist!")); return; } int kernelVal = ui->kernelSpin->value(); if (kernelVal % 2 == 0) { kernelVal += 1; } cv::GaussianBlur(*dispMat[numView], *dispMat[numView], cv::Size(kernelVal, kernelVal),\ // 高斯滤波Gaussian 0, 0); cvtMatPixmap(dispMat, dispPixmap, numView); outputInfo(1, tr("Gaussian done.")); }
3、 medianBtn的clicked()槽函数实现
void MainWindow::on_medianBtn_clicked() { std::size_t numView = ui->tabWidget->currentIndex() % 3; if (dispMat[numView]->empty()) { outputInfo(2, tr("Please make sure the Mat exist!")); return; } int kernelVal = ui->kernelSpin->value(); if (kernelVal % 2 == 0) { kernelVal += 1; } cv::medianBlur(*dispMat[numView], *dispMat[numView], kernelVal); // 中值滤波Median cvtMatPixmap(dispMat, dispPixmap, numView); outputInfo(1, tr("Meidan done.")); }
4、 bilateralBtn的clicked()槽函数实现
void MainWindow::on_bilateralBtn_clicked() { std::size_t numView = ui->tabWidget->currentIndex() % 3; if (dispMat[numView]->empty()) { outputInfo(2, tr("Please make sure the Mat exist!")); return; } int kernelVal = ui->kernelSpin->value(); if (kernelVal % 2 == 0) { kernelVal += 1; } cv::Mat tmpMat = cv::Mat::zeros(dispMat[numView]->rows, \ dispMat[numView]->cols, \ dispMat[numView]->type()); double time = static_cast<double>(cv::getTickCount()); cv::bilateralFilter(*dispMat[numView], tmpMat, kernelVal, \ // 双边滤波Bilateral static_cast<double>(kernelVal * 2), \ static_cast<double>(kernelVal / 2)); time = (static_cast<double>(cv::getTickCount()) - \ // 计算耗时 time) / cv::getTickFrequency(); QString costTime = "Cost time: " + QString::number(time); *dispMat[numView] = tmpMat.clone(); cvtMatPixmap(dispMat, dispPixmap, numView); outputInfo(1, costTime); outputInfo(1, tr("Bilateral done.")); }
总结
以上是关于利用Qt进行GUI构建并使用OpenCV中的Blur/Gaussian/Median/Bilateral函数进行图像平滑/滤波处理。