目录

图像平滑处理,6种滤波总结的综合示例

【盒式滤波、均值滤波、高斯滤波、中值滤波、双边滤波导向滤波】

1-图像滤波

2-代码演示

3-显示结果

4-程序说明

5 角点检测(Harris,Fast,surf)


图像平滑处理,6种滤波总结的综合示例

【盒式滤波、均值滤波、高斯滤波、中值滤波、双边滤波导向滤波】

本文力求用最简洁的语言,详细的代码将此部分内容讲解清楚,但由于博主同样是刚刚接触OpenCV,或许表达上有些瑕疵,还望读者能够指教探讨,大家共同进步。

机器配置为:VS2017+opencv3.2.0+Win-64bit。

若本文能给读者带来一点点启示与帮助,我就很开心了。

====================分割线====================

1-图像滤波

  • 1.图像滤波,即在尽量保留图像细节特征的条件下对目标图像的噪声进行抑制,是图像预处理中不可缺少的操作,其处理效果的好坏将直接影响到后续图像处理和分析的有效性和可靠性。
  • 2.消除图像中的噪声成分叫作图像的平滑化或滤波操作。信号或图像的能量大部分集中在幅度谱的低频和中频段是很常见的,而在较高频段,感兴趣的信息经常被噪声淹没。因此一个能降低高频成分幅度的滤波器就能够减弱噪声的影响。
  • 3.平滑滤波是低频增强的空间域滤波技术。它的目的有两类:一类是模糊;另一类是消除噪音。空间域的平滑滤波一般采用简单平均法进行,就是求邻近像元点的平均亮度值。邻域的大小与平滑的效果直接相关,邻域越大平滑的效果越好,但邻域过大,平滑会使边缘信息损失的越大,从而使输出的图像变得模糊,因此需合理选择邻域的大小。
  • 4.关于滤波器,一种形象的比喻法是:我们可以把滤波器想象成一个包含加权系数的窗口,当使用这个滤波器平滑处理图像时,就把这个窗口放到图像之上,透过这个窗口来看我们得到的图像。
  • 滤波目的:
  • 1、消除图像中混入的噪声。2、为图像识别抽取出图像特征。

滤波要求

  • 1、不能损坏图像轮廓及边缘 。2、图像视觉效果应当更好。

滤波器的种类有很多, 本文结合前几节的内容,写了个综合示例,包含6种滤波方法:

盒式滤波、平滑处理1线性滤波之——盒式滤波(方框滤波)

均值滤波、平滑处理2线性滤波之——均值滤波

高斯滤波、平滑处理3线性滤波之——高斯滤波

中值滤波、平滑处理4非线性滤波之——中值滤波

双边滤波、平滑处理5非线性滤波之——双边滤波

导向滤波、平滑处理6——引导滤波/导向滤波(Guided Filter)

其中阈值量可通过滑动条来调节,下面来看看程序是如何实现的。

2-代码演示

/* 功能:用滚动条来控制6种滤波方式的参数值。 盒式滤波、均值滤波、高斯滤波、中值滤波、双边滤波、导向滤波。
*/
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <iostream>
using namespace std;
using namespace cv;  #define WINDOWNAME "【滤波处理结果窗口】"  //---------------【全局变量声明部分】-------------------------
Mat g_srcIamge, g_dstImage1, g_dstImage2, g_dstImage3, g_dstImage4, g_dstImage5, g_dstImage6;
int g_nBoxFilterValue = 1;//盒式滤波内核值
int g_nMeanBlurValue = 1;//均值滤波内核值
int g_nGaussianBlurValue = 1;//高斯滤波内核值
int g_nMedianBlurValue = 1;//中值滤波内核值
int g_nBilateralFilterValue = 1;//双边滤波内核值
int g_nGuidedFilterValue = 1;//导向滤波内核值
const int g_nMaxVal = 20; //预设滑动条最大值
//--------------【全局函数声明部分】-------------------------
//轨迹条回调函数
static void on_BoxFilter(int, void*);//盒式滤波器
static void on_MeanBlur(int, void*);//均值滤波器
static void on_GaussianBlur(int, void*);//高斯滤波器
static void on_MedianBlur(int, void*);//中值滤波器
static void on_BilateralFilter(int, void*);//双边滤波器
static void on_GuidedFilter(int, void*);//导向滤波器
void guidedFilter(Mat &srcMat, Mat &guidedMat, Mat &dstImage, int radius, double eps);//导向滤波器  //----------------------------【主函数】---------------------------
int main()
{  //------------【1】读取源图像并检查图像是否读取成功------------    g_srcIamge = imread("D:\\OutPutResult\\ImageTest\\boatLong.jpg");  if (!g_srcIamge.data)  {  cout << "读取图片错误,请重新输入正确路径!\n";  system("pause");  return -1;  }  namedWindow("【源图像】", 1);//创建窗口  imshow("【源图像】", g_srcIamge);//显示窗口  //------------【2】在WINDOWNAME窗口上分别创建滤波6个滑动条------------         namedWindow(WINDOWNAME);//创建窗口    createTrackbar("方框滤波", WINDOWNAME, &g_nBoxFilterValue, g_nMaxVal, on_BoxFilter);//创建方框滤波轨迹条  on_BoxFilter(g_nBoxFilterValue, 0); //轨迹条的回调函数  createTrackbar("均值滤波", WINDOWNAME, &g_nMeanBlurValue, g_nMaxVal, on_MeanBlur);//创建均值滤波轨迹条  on_MeanBlur(g_nMeanBlurValue, 0);  createTrackbar("高斯滤波", WINDOWNAME, &g_nGaussianBlurValue, g_nMaxVal, on_GaussianBlur);//创建高斯滤波轨迹条  on_GaussianBlur(g_nGaussianBlurValue, 0);  createTrackbar("中值滤波", WINDOWNAME, &g_nMedianBlurValue, g_nMaxVal, on_MedianBlur);//创建中值滤波轨迹条  on_MedianBlur(g_nMedianBlurValue, 0);  createTrackbar("双边滤波", WINDOWNAME, &g_nBilateralFilterValue, g_nMaxVal, on_BilateralFilter);//创建双边滤波轨迹条  on_BilateralFilter(g_nBilateralFilterValue, 0);  createTrackbar("导向滤波", WINDOWNAME, &g_nGuidedFilterValue, g_nMaxVal, on_GuidedFilter);//创建导向滤波轨迹条  on_GuidedFilter(g_nGuidedFilterValue, 0);  //------------【3】退出程序------------    cout << "\t按下'q'键,退出程序~!\n" << endl;  while (char(waitKey(1)) != 'q'){}  return 0;
}  //----------------------【on_BoxFilter()函数】------------------------
static void on_BoxFilter(int, void*)
{  boxFilter(g_srcIamge, g_dstImage1, -1, Size(g_nBoxFilterValue * 2 + 1, g_nBoxFilterValue * 2 + 1));  cout << "\n当前为【盒式滤波】处理效果,其内核大小为:" << g_nBoxFilterValue * 2 + 1 << endl;  imshow(WINDOWNAME, g_dstImage1);
}
//----------------------【on_MeanBlur()函数】------------------------
static void on_MeanBlur(int, void*)
{  blur(g_srcIamge, g_dstImage2, Size(g_nMeanBlurValue * 2 + 1, g_nMeanBlurValue * 2 + 1), Point(-1, -1));  cout << "\n当前为【均值滤波】处理效果,其内核大小为:" << g_nMeanBlurValue * 2 + 1 << endl;  imshow(WINDOWNAME, g_dstImage2);
}
//----------------------【on_GaussianBlur()函数】------------------------
static void on_GaussianBlur(int, void*)
{  GaussianBlur(g_srcIamge, g_dstImage3, Size(g_nGaussianBlurValue * 2 + 1, g_nGaussianBlurValue * 2 + 1), 0, 0);  cout << "\n当前为【高斯滤波】处理效果,其内核大小为:" << g_nGaussianBlurValue * 2 + 1 << endl;  imshow(WINDOWNAME, g_dstImage3);
}
//----------------------【on_MedianBlur()函数】------------------------
static void on_MedianBlur(int, void*)
{  medianBlur(g_srcIamge, g_dstImage4, g_nMedianBlurValue * 2 + 1);  cout << "\n当前为【中值滤波】处理效果,其内核大小为:" << g_nMedianBlurValue * 2 + 1 << endl;  imshow(WINDOWNAME, g_dstImage4);
}
//----------------------【on_BilateralFilter()函数】------------------------
static void on_BilateralFilter(int, void*)
{  bilateralFilter(g_srcIamge, g_dstImage5, g_nBilateralFilterValue, g_nBilateralFilterValue * 2, g_nBilateralFilterValue / 2);  cout << "\n当前为【双边滤波】处理效果,其内核大小为:" << g_nBilateralFilterValue << endl;  imshow(WINDOWNAME, g_dstImage5);
}
//----------------------【on_GuidedFilter()函数】------------------------
static void on_GuidedFilter(int, void*)
{  vector<Mat> vSrcImage, vResultImage;  //【1】对源图像进行通道分离,并对每个分通道进行导向滤波操作  split(g_srcIamge, vSrcImage);  for (int i = 0; i < 3; i++)  {  Mat tempImage;  vSrcImage[i].convertTo(tempImage, CV_64FC1, 1.0 / 255.0);//将分通道转换成浮点型数据  Mat cloneImage = tempImage.clone(); //将tempImage复制一份到cloneImage  Mat resultImage;  guidedFilter(tempImage, cloneImage, resultImage, g_nGuidedFilterValue * 2 + 1, 0.01);//对分通道分别进行导向滤波  vResultImage.push_back(resultImage);//将分通道导向滤波后的结果存放到vResultImage中  }  //【2】将分通道导向滤波后结果合并  merge(vResultImage, g_dstImage6);  cout << "\n当前处理为【导向滤波】,其内核大小为:" << g_nGuidedFilterValue * 2 + 1 << endl;  imshow(WINDOWNAME, g_dstImage6);
}  //-------------------【实现导向滤波器函数部分】-------------------------
void guidedFilter(Mat &srcMat, Mat &guidedMat, Mat &dstImage, int radius, double eps)
{  //------------【0】转换源图像信息,将输入扩展为64位浮点型,以便以后做乘法------------  srcMat.convertTo(srcMat, CV_64FC1);  guidedMat.convertTo(guidedMat, CV_64FC1);  //--------------【1】各种均值计算----------------------------------  Mat mean_p, mean_I, mean_Ip, mean_II;  boxFilter(srcMat, mean_p, CV_64FC1, Size(radius, radius));//生成待滤波图像均值mean_p   boxFilter(guidedMat, mean_I, CV_64FC1, Size(radius, radius));//生成导向图像均值mean_I     boxFilter(srcMat.mul(guidedMat), mean_Ip, CV_64FC1, Size(radius, radius));//生成互相关均值mean_Ip  boxFilter(guidedMat.mul(guidedMat), mean_II, CV_64FC1, Size(radius, radius));//生成导向图像自相关均值mean_II  //--------------【2】计算相关系数,计算Ip的协方差cov和I的方差var------------------  Mat cov_Ip = mean_Ip - mean_I.mul(mean_p);  Mat var_I = mean_II - mean_I.mul(mean_I);  //---------------【3】计算参数系数a、b-------------------  Mat a = cov_Ip / (var_I + eps);  Mat b = mean_p - a.mul(mean_I);  //--------------【4】计算系数a、b的均值-----------------  Mat mean_a, mean_b;  boxFilter(a, mean_a, CV_64FC1, Size(radius, radius));  boxFilter(b, mean_b, CV_64FC1, Size(radius, radius));  //---------------【5】生成输出矩阵------------------  dstImage = mean_a.mul(srcMat) + mean_b;
}  

3-显示结果

原始图像窗口,如下图:

盒式滤波/方框滤波操作,如下图:

均值滤波操作,如下图:

高斯滤波操作,如下图:

中值滤波操作,如下图:

双边滤波操作,如下图:

导向滤波操作,如下图:

=====================分割线==================

4-程序说明

【滤波处理结果窗口】中显示的数值并非实际内核大小,真正内核大小还请看黑窗口的提示信息。

其中前五种滤波方式:盒式滤波、均值滤波、高斯滤波、中值滤波、双边滤波,OpenCV都已封装在函数里了,我们直接调用即可,而导向滤波,是需要自己编写,然后调用即可。

为了便于观察各种滤波方式的效果优缺点,我对原图进行了处理,左边的图像是添加了椒盐噪声,而右边的图像是添加了高斯噪声,同学们可以通过滑动条调节,试试看每种滤波方式对噪声的处理结果是如何情况。

====END===================

5 角点检测(Harris,Fast,surf)

前面分析了Sift算法的具体原理,就顺便想看看其他的角点检测算法,和对比。这些看完之后,相比sift的原理比较简单.

Harris:

这个算法通过两张图就看得很清楚了。将被检测点上的window移动一下,然后计算原来的window和移动之后的window对应位置像素差的平方和,如果变化不大说明这个点在物体内部,如果一个方向上变化大,另外一个方向上变化不大说明在边界上。如果任何方向上变化都很大说明这个点就是corner point。

但是这个方法效果不是很好,主要存在两方面的问题:

  1. .0-1window对噪声比较敏感
  2. window移动的方向太少,导致该算法对边缘敏感(准确的说是和window移动方向不一致的边缘)

harris(1988)harris(1988)年提出了改进方案。

  1. 将01window改成了Gaussian window,中心权重比较高,离中心越远权重越低。
  2. 使用泰勒展开考虑所有方向

通过泰勒展开可以将原window和shift之后的window对应位置像素差的平方和表示为移动方向*该点Hessian矩阵*移动方向。Hessian矩阵的较大特征值和对应特征向量分别表示变化最大方向和对应变化幅度第二特征值和对应特征向量,表示和变化最大方向相垂直的方向以及该方向变化的幅度。如果最大特征值很小那么说明该点在物体内部;如果第一特征值很大,另外一个特征值接近零说明该点是一个edge point如果两个特征值都远大于零说明该点是一个corner point。
Harris 为了简化计算提出了一个cornerness的量,越大越说明这个点越corner

角点计算数学原理:

基本数学公式如下:

其中W(x, y)表示移动窗口,I(x, y)表示像素灰度值强度,范围为0~255。根据泰勒级数,计算一阶到N阶的偏导数,最终得到一个Harris矩阵公式:

根据Harris的矩阵计算矩阵特征值,然后计算Harris角度响应值:

其中K为系数值,通常取值范围为0.04 ~ 0.06之间。

算法详细步骤

  1. 计算图像X方向与Y方向的一阶高斯偏导数Ix与Iy
  2. 根据第一步结果得到Ix^2 , Iy^2与Ix*Iy值
  3. 高斯模糊第二步三个值得到Sxx, Syy, Sxy
  4. 定义每个像素的Harris矩阵,计算出矩阵的两个特质值
  5. 计算出每个像素的R值
  6. 使用3X3或者5X5的窗口,实现非最大值压制
  7. 根据角度检测结果计算,最提取到的关键点以绿色标记,显示在原图上。

Fast角点检测:

Edward Rosten and TomDrummond 在2006年提出了一种简单快速的角点探测算法,该算法检测的角点定义为在像素点的周围邻域内有足够多的像素点与该点处于不同的区域。应用到灰度图像中,即有足够多的像素点的灰度值大于该点的灰度值或者小于该点的灰度值。

考虑下图中p点附近半径为3的圆环上的16个点,一个思路是若其中有连续的12个点的灰度值与p点的灰度值差别超过某一阈值,则可以认为p点为角点。

这一思路可以使用机器学习的方法进行加速。对同一类图像,例如同一场景的图像,可以在16个方向上进行训练,得到一棵决策树,从而在判定某一像素点是否为角点时,不再需要对所有方向进行检测,而只需要按照决策树指定的方向进行2-3次判定即可确定该点是否为角点。

常见的角点检测算法比较:

  图像匹配能够应用的场合非常多,如目标跟踪,检测,识别,图像拼接等,而图像匹配最核心的技术就要属角点匹配了,所谓角点匹配是指寻找两幅图像之间的特征像素点的对应关系,从而确定两幅图像的位置关系。

  角点匹配可以分为以下四个步骤:

  1. 提取检测子:在两张待匹配的图像中寻找那些最容易识别的像素点(角点),比如纹理丰富的物体边缘点等。 
  2. 提取描述子:对于检测出的角点,用一些数学上的特征对其进行描述,如梯度直方图,局部随机二值特征等。
  3. 检测子和描述子的常用提取方法有:sift, harris, surf, fast, agast, brisk, freak, brisk,orb等。 
  4. .匹配:通过各个角点的描述子来判断它们在两张图像中的对应关系。常用方法如 flann  
  5. 去外点:去除错误匹配的外点,保留正确的内点。常用方法有Ransac, GTM。

  我对上述这些常用的检测子和描述子的性能和速度做了一个测试,以找出其中性价比最好的组合(不考虑后两步)

                        图一

                      图二

  上面两幅图是对上述部分检测子与描述子进行测试的结果。

  其中图一和图二的x坐标表示不同的检测子与描述子的组合,从1到30分别对应

(surf、agast、brisk、fast、sift、orb)检测子与 (brisk、agast、surf、sift、orb)描述子的线性组合,如第四组表示surf检测子+sift描述子的组合。

  图一和图二的y坐标表示一系列测试图片组,每组由两张图组成,这些图片都是质量较低的卫星地图,约2000*2000像素,每组的两张图片是由不同卫星对地面上同一地区进行拍摄的。它们之间的关系可能包含旋转,缩放,仿射变换,亮度变化,模糊,噪音等。从1到11的测试图片组大概对应着以下变换关系:

  1. 强烈亮度变化
  2. 旋转
  3. 仿射变换+尺度变化+旋转
  4. 仿射变换+亮度变化+旋转
  5. 仿射变换+噪音
  6. 模糊+亮度变化
  7. 旋转+噪音
  8. 旋转+尺度变化
  9. 亮度变化+旋转+模糊+噪音
  10. 亮度变化+旋转+尺度变化
  11. 亮度变化+旋转+尺度变化+强烈噪音。

  图一的z坐标表示成功匹配的像素对的个数。图二的z坐标表示所需计算时间。

  从测试结果来看,orb检测子与surf描述子配合的效果是最好的,不过速度也是最慢的。古老的sift和surf依然好用,速度也还是那么不给力。在不考虑旋转和仿射变换的情况下,fast是很不错的选择,在小幅旋转(20度内)的情况下,fast也还是有一定的容错能力的。在旋转变化和尺度变化方面,各家武功相差不多,虽然在理论上sift支持旋转变化,不过测试中它并没有表现出明显的优势。在噪音方面,sift和orb明显强于其它算法;在亮度变化和仿射变换上,orb的鲁棒性是最好的;综合比较,orb的性价比在此次华山论剑中略胜一筹。

  下面两张图是上面11组图片的均值,从平均值来看,orb也是最好的

  

  

  1. :http://blog.csdn.net/ben_ben_niao/article/details/47446627
  2. :http://blog.csdn.net/sinat_36264666/article/details/77823400
  3. :https://blog.csdn.net/xw20084898/article/details/21822565

图像滤波总结(面试经验总结)相关推荐

  1. 华栖云科技图形图像视音频算法岗面试经验

    注:此经验写于工作半个月后,主要为想从事图形图像算法岗的应届毕业生提供一些面试经验参考. 面试公司:成都华栖云科技有限公司(chinamcloud) 面试岗位:图形图像视音频算法 面试时间:2018年 ...

  2. 图像滤波常用算法实现及原理解析

    点击上方"小白学视觉",选择加"星标"或"置顶" 重磅干货,第一时间送达 导读 图像滤波是一种非常重要的图像处理技术,本文详细介绍了四种常见 ...

  3. python人工智能方向面试准备_关于机器学习面试的经典题目(面试经验和建议)...

    今年年初以来,作者一直在印度找数据科学.机器学习以及深度学习领域的工作.在找工作的这三十四天里,他面试了8到10家公司,其中也包括初创公司.基于服务的公司以及基于产品的公司.作者希望他的面试经验能够为 ...

  4. 一番星大厂面试经验分享第一弹

    点击上方蓝字,订阅关注我们! 前言 感谢一番星汇总本次的面试经验,给出了部分面试问题的答案,希望更多人能从中收益,不断积累经验,在面试中优异表现,斩获更多大厂offer,成为offer收割机! 也欢迎 ...

  5. 计算机视觉算法岗 面试经验 (转载)

    相关链接:[计算机视觉算法岗面经]"吐血"整理:2019秋招面经 //2018/09/27 兵荒马乱.浩浩荡荡的秋招终于差不多要结束了. 秋招这段时间真是感慨很多,一时得意一时失意 ...

  6. C++面试题目及面试经验

    文章目录 秋招历程 C++ 基础篇: 数据结构与算法篇: 计网与操作系统篇 : 游戏技术篇: 逻辑思维篇: 1.老鼠喝药的问题: 2.100大小的数组放了1-99 100个数,只有一个数重复了,找出重 ...

  7. 三种经典图像滤波方法介绍——双边滤波(Bilateral filter)、导向滤波(Guided Fliter)、滚动导向滤波(RollingGuidedFilter)

    文章目录 一.前言 二.双边滤波(Bilateral filter) 2.1 双边滤波的理论介绍及公式推导 2.2 双边滤波的matlab程序实现 三.导向滤波(Guided Fliter) 3.1 ...

  8. 引导图像滤波(Guided Image Filtering)

    [Paper] Guided Image Filtering(2013) 引导图像滤波 摘要--在本文中,我们提出了一种新的显式图像滤波器,称为引导滤波器.从局部线性模型导出,引导滤波器通过考虑引导图 ...

  9. python图像锐化滤波_OpenCV-Python学习(九):图像滤波

    目录: 1.滤波的相关概念 2.卷积操作 3.平滑操作(低通滤波) 均值滤波 中值滤波 高斯滤波 双边滤波 4.锐化操作(高通滤波) 自定义锐化核 USM锐化(UnsharpMask) 5.梯度操作( ...

最新文章

  1. linux怎么配置命令模式,Linux几种命令模式
  2. ios相机黑边_黑边很碍眼很难看 苹果iPhone6黑边有什么用呢?
  3. echarts地图罗平县的json_Echarts全国省市区县地图数据文件(含js及json)
  4. Windows XP with SP3 All Edtion Download
  5. codematic2连接mysql失败_怎么连接oracle数据库
  6. JAVA面试题解惑系列(四)——final、finally和finalize的区别
  7. 从ReadImage到ML- 一个不错的博客
  8. TC SRM 593 DIV2 1000
  9. .NET Core / C# 开发 IOT 嵌入式设备的个人见解
  10. 抢了个票,还以为发现了12306的系统BUG
  11. qt mysql乱码_Qt连接mysql 并且 解决中文乱码问题
  12. 从0 开始 WPF MVVM 企业级框架实现与说明 ---- 第十讲 WPF 系统MainWindow
  13. 【FFMPEG系列】之ffmpeg怎么利用gdb调试以及gdb调试快捷键
  14. RHEL 6 配置yum源
  15. Linux操作系统内核源码目录结构详解
  16. R语言数据挖掘实战系列(4)
  17. JAVA程序设计的一次作业
  18. 1.2 神经网络的介绍
  19. LncRNA与代谢组联合分析
  20. 移动式无线蓝牙RFID+二维码扫描一体机|读卡器HX-R58C-B在安卓与苹果手机上如何操作?

热门文章

  1. 如何设置PHP常量,我应该如何保持我的常量在PHP
  2. android 定义集合长度,Android Dex文件结构解析
  3. php图片转字符程序,PHP文字转图片功能原理与实现方法分析
  4. centos php ioncube_Linux/Centos 安装PHP ioncube扩展
  5. c++小项目:通讯录管理系统
  6. php计算属相,一个判断干支、属相和星座的php函数
  7. html文件显示不了box,Workbox.js registerNavigationRoute找不到/加载html文件
  8. java三种循环的区别_JavaSE三种循环注意点
  9. 使用cv2.Sobel()、cv2.Scharr()、cv2.Laplacian()寻找图像的梯度、边缘
  10. 使用KMeanCluster对多个区域进行聚类,并结合Matplotlib绘制中心点、最大最小距离点