【 imgproc 模块. 图像处理】图像平滑处理
一、图像平滑
在本教程中,图像平滑处理主要有归一化块滤波器(Normalized Box Filter)、高斯滤波器(Gaussian Filter)、中值滤波器(Median Filter)、双边滤波器(Bilatera Filter)。
二、归一化快滤波器(Normalized Box Filter)
-
最简单的滤波器, 输出像素值是核窗口内像素值的 均值 ( 所有像素加权系数相等)
核如下:
void blur(InputArray src, OutputArray dst, Size ksize, Point anchor=Point(-1,-1), int borderType=BORDER_DEFAULT )
. InputArray src: 输入图像,可以是Mat类型,图像深度是CV_8U、CV_16U、CV_16S、CV_32F以及CV_64F其中的某一个。
. OutputArray dst: 输出图像,深度和类型与输入图像一致
. Size ksize: 滤波模板kernel的尺寸,一般使用Size(w, h)来指定,如Size(3,3)
. Point anchor=Point(-1, -1): 字面意思是锚点,也就是处理的像素位于kernel的什么位置,默认值为(-1, -1)即位于kernel中心点,如果没有特殊需要则不需要更改
. int borderType=BORDER_DEFAULT: 用于推断图像外部像素的某种边界模式,有默认值BORDER_DEFAULT
三、高斯滤波器(Gaussian Filter)
-
最有用的滤波器 (尽管不是最快的)。 高斯滤波是将输入数组的每一个像素点与 高斯内核 卷积将卷积和当作输出像素值。
1维高斯函数:
假设图像是1维的,那么观察上图,不难发现中间像素的加权系数是最大的, 周边像素的加权系数随着它们远离中间像素的距离增大而逐渐减小。
2维高斯函数可以表达为 :
其中 为均值 (峰值对应位置), 代表标准差 (变量 和 变量 各有一个均值,也各有一个标准差)
API:
void GaussianBlur( InputArray src, OutputArray dst, Size ksize, double sigmaX, double sigmaY = 0, int borderType = BORDER_DEFAULT );
四、中值滤波器(Median Filter)
实现:中值滤波将图像的每个像素用邻域 (以当前像素为中心的正方形区域)像素的 中值 代替 。
API:
void medianBlur( InputArray src, OutputArray dst, int ksize );
五、双边滤波器
- 目前我们了解的滤波器都是为了平滑 图像,有些时候这些滤波器不仅仅削弱了噪声,连带着把边缘也给磨掉了。 为避免这样的情形 (至少在一定程度上 ), 可以使用双边滤波。
- 类似于高斯滤波器,双边滤波器也给每一个邻域像素分配一个加权系数。 这些加权系数包含两个部分, 第一部分加权方式与高斯滤波一样,第二部分的权重则取决于该邻域像素与当前像素的灰度差值。
详细的解释可以查看 链接
API:
void bilateralFilter( InputArray src, OutputArray dst, int d, double sigmaColor, double sigmaSpace, int borderType = BORDER_DEFAULT );
六、opencv例程
#include "stdafx.h" /** * file Smoothing.cpp * brief Sample code for simple filters * author OpenCV team */ #include <iostream> #include "opencv2/imgproc.hpp" #include "opencv2/imgcodecs.hpp" #include "opencv2/highgui.hpp" using namespace std; using namespace cv; /// Global Variables 全局变量 int DELAY_CAPTION = 1500;//窗口延时,各个平滑模式切换的时间间隔 int DELAY_BLUR = 100;//同一个滤波模式下 改变核大小时的时间间隔 int MAX_KERNEL_LENGTH = 31;//核大小不超过31 正奇数 Mat src; Mat dst; char window_name[] = "Smoothing Demo"; /// Function headers int display_caption(const char* caption); int display_dst(int delay); /** * function main */ int main(int argc, char ** argv) { namedWindow(window_name, WINDOW_AUTOSIZE); /// Load the source image const char* filename = argc >= 2 ? argv[1] : "lena512color.tiff"; /*argc,argv 用命令行编译程序时有用。 主函数main中变量(int argc,char *argv[ ])的含义 有些编译器允许将main()的返回类型声明为void,这已不再是合法的C++ main(int argc, char *argv[ ], char **env)才是UNIX和Linux中的标准写法。 argc: 整数,用来统计你运行程序时送给main函数的命令行参数的个数 * argv[ ]: 指针数组,用来存放指向你的字符串参数的指针,每一个元素指向一个参数 argv[0] 指向程序运行的全路径名 argv[1] 指向在DOS命令行中执行程序名后的第一个字符串 argv[2] 指向执行程序名后的第二个字符串 ... argv[argc]为NULL。*/ src = imread(filename, IMREAD_COLOR); if (src.empty()) { /* if(img.empty())return -1; //是否加载成功 if(!img.data)return -1; //判断是否有数据*/ printf(" Error opening image\n"); return -1; } if (display_caption("Original Image") != 0) { return 0; } dst = src.clone(); /*clone 跟copyto的区别 roi=src.clone(); //1 src.copyTo(roi); //2 语句1:不管roi在之前有没有分配内存,clone都会为其分配新内存。如果roi指向某图像img的某个rect,此语句并不能实现对img(rect)的操作,clone分配新内存后,roi不再指向img(rect). 语句2:如果roi在之前未分配内存,copyTo会为其分配新内存,若roi已分配内存,copyTo不再为其分配。 结束:copyTo才是实现图像roi操作的途径啊。。。 */ if (display_dst(DELAY_CAPTION) != 0) { return 0; } /// Applying Homogeneous blur if (display_caption("Homogeneous Blur") != 0) { return 0; } //![blur] for (int i = 1; i < MAX_KERNEL_LENGTH; i = i + 2) { blur(src, dst, Size(i, i), Point(-1, -1)); /*参数: src:原图像。dst:目标图像。ksize:定义滤波器的大小。如Size(3,3)。 anchor:指定锚点位置(被平滑点), 如果是负值,取核的中心为锚点。可省略 borderType:推断边缘像素,一般取默认值BORDER_DEFAULT。可省略 例: blur(src,dst,Size(3,3));*/ if (display_dst(DELAY_BLUR) != 0) { return 0; } } //![blur] /// Applying Gaussian blur if (display_caption("Gaussian Blur") != 0) { return 0; } //![gaussianblur] /*参数: OpenCV2函数 GaussianBlur 执行高斯平滑,高斯滤波是将输入数组的每一个像素点与 高斯内核 卷积,将卷积和当作输出像素值。 void GaussianBlur( const Mat& src, Mat& dst, Size ksize,double sigmaX, double sigmaY=0,int borderType=BORDER_DEFAULT ); 参数: sigmaX:x方向的标准方差。可设置为0让系统自动计算。 sigmaY:y方向的标准方差。可设置为0让系统自动计算。 例: GaussianBlur(src,dst,Size(9,9),0,0);*/ for (int i = 1; i < MAX_KERNEL_LENGTH; i = i + 2) { GaussianBlur(src, dst, Size(i, i), 0, 0); if (display_dst(DELAY_BLUR) != 0) { return 0; } } //![gaussianblur] /// Applying Median blur if (display_caption("Median Blur") != 0) { return 0; } //![medianblur] /*OpenCV2函数 medianBlur 执行中值滤波操作,中值滤波将图像的每个像素用邻域 (以当前像素为中心的正方形区域)像素的 中值 代替 。*/ for (int i = 1; i < MAX_KERNEL_LENGTH; i = i + 2) { medianBlur(src, dst, i); if (display_dst(DELAY_BLUR) != 0) { return 0; } } //![medianblur] /// Applying Bilateral Filter if (display_caption("Bilateral Blur") != 0) { return 0; } //![bilateralfilter] /*OpenCV2函数 bilateralFilter 执行双边滤波操作,类似于高斯滤波器,双边滤波器也给每一个邻域像素分配一个加权系数。 这些加权系数包含两个部分, 第一部分加权方式与高斯滤波一样,第二部分的权重则取决于该邻域像素与当前像素的灰度差值。 void bilateralFilter( const Mat& src, Mat& dst, int d,double sigmaColor, double sigmaSpace,int borderType=BORDER_DEFAULT ); 参数: d:像素的邻域直径。 sigmaColor:颜色空间的标准方差. sigmaSpace:坐标空间的标准方差(像素单位). 例: bilateralFilter ( src, dst, i, i*2, i/2 );*/ for (int i = 1; i < MAX_KERNEL_LENGTH; i = i + 2) { bilateralFilter(src, dst, i, i * 2, i / 2); if (display_dst(DELAY_BLUR) != 0) { return 0; } } //![bilateralfilter] /// Done display_caption("Done!"); return 0; } /** * @function display_caption */ int display_caption(const char* caption) { dst = Mat::zeros(src.size(), src.type()); putText(dst, caption, Point(src.cols / 4, src.rows / 2), FONT_HERSHEY_COMPLEX, 1, Scalar(255, 255, 255)); /*第一个参数是:需要写字的原图像, 第二个:需要写的内容,string类型的; 第三个:需要写的内容的左下角的坐标 第五个:字体大小 第六个:颜色 第七个:字体的厚度 第八个:默认8*/ return display_dst(DELAY_CAPTION); } /** * @function display_dst */ int display_dst(int delay) { imshow(window_name, dst); int c = waitKey(delay); if (c >= 0) { return -1; } return 0; }