所谓的屡屡部分是指图像中像素值落差很大的局地,param3为高斯核的标准差(详见博文

   记录cvSmooth函数的用法和 OpenCV自带的人脸检测。

率先看下OpenCV 官方文档对于cvSmooth各类参数的诠释:

一、图像平滑与滤波概念

介绍图像滤波之前有须要精晓一下图像平滑的概念。

图像平滑(smoothing)也称之为图像模糊(blurring),是一种在图像处理中使用效能很高的操作,举行图像平滑的操作原因有那几个,在那里关键介绍使用平滑操作下降图片噪声。因为在图像中,噪声的能量几近集中在宽窄谱的低频和中频部分,而在较高的频段,一些首要的细节信息往往被噪声淹没。在一幅图像中,所谓的频仍部分是指图像中像素值落差很大的部分,而低频则是指像素值与一旁的像素值相差不大甚至同一,而图像的有的细节的局地往往由高频音信来呈现,图像中混杂的噪音往往也处在高频段,那就造成了有些细节信息被噪声淹没,可以按照不一样的噪音类型用分裂的滤波器进行处理。
滤波的目的有七个即:1.抽出对象的特色作为图像识其余特色形式;2.为适应图像处理需要,消除数字图像所混入的噪声
对图像滤波有三个须要:1.无法破坏图像的概况和边缘等要害新闻;2.使图像清晰视觉效果更好

为了拓展图像平滑操作,日常在图像上加一个滤波器(filter),最广大的门类是线性的,输出像素值g(x,
y)最后由原像素值和加权值决定。其经过如下:

图片 1

其中h(x,
y)被叫作核(kernel),是加到图像上滤波器(filter)的周密,它促进把滤波器举行可视化为一个窗口在图像上滑动,那么些关乎到邻域的卷积操作。

邻域算子值利用给定像素周围像素的值决定此像素的末梢输出。右边图像就是原像素的值,中间图像是滤波器(filter),filter的目标就是将滤波器的加权值进行如此的可视化窗口,最右侧的图像是原图像和滤波器一起卷积生成,图像中的青色部分是左图中藏黑色和中级滤波器卷积计算得到的结果。所以可以看出图像像素最后的值不仅与原像素有关也可滤波器选择的kernel窗口大小有关。

(1)cvSmooth函数
void cvSmooth( const CvArr* src, CvArr* dst,int
smoothtype=CV_GAUSSIAN,int param1, int param2, double param3, double
param4 );
src:输入图像.
dst:输出图像.
smoothtype平滑方法
  CV_BLUR_NO_SCALE(简单不带尺度变换的歪曲),对每个象素的
param1×param2 领域求和。即使邻域大小是浮动的,可以优先采纳函数 cvIntegral
计算积分图像。
   CV_BLUR (simple blur)-
-对各类象素param1×param2邻域求和并做规范变换 1/(param1×param2)。
   CV_GAUSSIAN(gaussian blur) – -对图像举行核大小为 param1×param2
的高斯卷积。
   CV_MEDIAN(median blur) – -对图像举行核大小为param1×param1
的中值滤波 (邻域是方的)。
   CV_BILATERAL(双向滤波) – -应用双向 3×3 滤波,彩色
sigma=param1,空间 sigma=param2.。

Smooths the image in one of several
ways.

二、方框滤波

在有了上述的辩护功底之后,首先介绍一下方框滤波。
那是拥有滤波器中最简便的一种滤波方式。每一个输出像素的是水源邻域像素值的平均值获得。
通用的滤波kernel如下:

图片 2

此间是一个长宽分别为Kwidth和Kheight的窗口函数,在此区域内邻域中像素值叠加求平均即可求出位于kernel大旨点像素的像素值。

在OpenCV中方框滤波的被封装在boxFilter函数中。
函数原型:

void boxFilter( InputArray src, OutputArray dst, int ddepth,
                             Size ksize, Point anchor = Point(-1,-1),
                             bool normalize = true,
                             int borderType = BORDER_DEFAULT );

逐条参数详解:

  • 率先个参数,InputArray类型的src,输入图像,即源图像,填Mat类的对象即可。该函数对通道是单身处理的,且可以拍卖任意通道数的图形,但须要留意,待处理的图样深度应该为CV_8U,
    CV_16U, CV_16S, CV_32F 以及 CV_64F之一。
  • 首个参数,OutputArray类型的dst,即目标图像,需求和源图片有同样的尺码和花色。
  • 其三个参数,int类型的ddepth,输出图像的吃水,-1意味行使原图深度,即src.depth()。
  • 第八个参数,Size类型(对Size类型稍后有教书)的ksize,内核的轻重。一般这样写Size(
    w,h )来表示内核的高低( 其中,w 为像素宽度,
    h为像素中度)。Size(3,3)就代表3×3的核大小,Size(5,5)就意味着5×5的核大小
  • 第多少个参数,Point类型的anchor,表示锚点(即被平整的极度点),注意她有默许值Point(-1,-1)。如若那个点坐标是负值的话,就意味着取核的着力为锚点,所以默许值Point(-1,-1)表示这几个锚点在核的中央。
  • 第多少个参数,bool类型的normalize,默许值为true,一个标识符,表示内核是不是被其区域归一化(normalized)了。
  • 第多少个参数,int类型的borderType,用于推测图像外部像素的某种边界情势。有默许值BORDER_DEFAULT,我们一般不去管它。

boxFilter()函数方框滤波所用的核为:

图片 3

其中:

图片 4

当normalize=true的时候,方框滤波就成为了下边要说的的均值滤波。

param1:平滑操作的第三个参数.
param2:平滑操作的第一个参数.
对于简易/非标准化变换的高斯模糊的情形,如若param2的值为零,则象征其被设定为param1。
param3
  对应高斯参数的 Gaussian sigma (标准差).
如若为零,则标准差由上面的核尺寸计算:
  sigma = (n/2 – 1)*0.3 + 0.8,
其中 n=param1 对应水平核,n=param2 对应垂直核.
  对小的卷积核 (3×3 to 7×7) 使用如上公式所示的正统 sigma
速度会快。如果 param3 不为零,而 param1 和 param2 为零,则核大小由sigma
统计 (以保证丰裕精确的操作)。

C: void cvSmooth(const CvArr* src,
CvArr* dst, int smoothtype=CV_GAUSSIAN, int param1=3, int param2=0,
double param3=0, double param4=0)

三、均值滤波

均值滤波的规律分外简单,就是出口图像的每一个像素是核窗口内输入图像对应像素的像素的平均值(
所有像素加权周全相等),其实简单它就是归一化后的正方滤波。

而是均值滤波本身存在着固有的症结,即它不可能很好地维护图像细节,在图像去噪的还要也破坏了图像的细节部分,从而使图像变得模糊,不可以很好地去除噪声点。

OpenCV中贯彻均值滤波的是blur函数

函数原型:

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,即目标图像,必要和源图片有一致的尺码和档次。比如可以用Mat::Clone,以源图片为模板,来早先化得到如假包换的靶子图。
  • 其七个参数,Size类型(对Size类型稍后有教学)的ksize,内核的轻重缓急。一般那样写Size(
    w,h )来代表内核的分寸( 其中,w 为像素宽度,
    h为像素低度)。Size(3,3)就象征3×3的核大小,Size(5,5)就代表5×5的核大小
  • 第七个参数,Point类型的anchor,表示锚点(即被平整的要命点),注意她有默许值Point(-1,-1)。若是那么些点坐标是负值的话,就代表取核的主旨为锚点,所以默许值Point(-1,-1)表示这几个锚点在核的主导。
  • 第一个参数,int类型的borderType,用于推测图像外部像素的某种边界模式。有默许值BORDER_DEFAULT,大家一般不去管它。

当然要贯彻均匀滤波也可以行使方面提到的boxFilter函数,与方框滤波差其余是第二个参数normalize设为true。从blur函数的概念中得以观察blur函数只是调用了boxFilter而已:

void cv::blur( InputArray src, OutputArray dst,
           Size ksize, Point anchor, int borderType )
{
    boxFilter( src, dst, -1, ksize, anchor, true, borderType );
}

  没有缩放的图像平滑仅援救单通道图像,并且接济8位到16位的转换(与cvSobel和cvaplace相似)和32位浮点数到32位浮点数的变换格式。
  简单模糊和高斯模糊帮助 1- 或 3-通道, 8-比特 和 32-比特
浮点图像。那两种艺术可以(in-place)情势处理图像。
  中值和双向滤波工作于 1- 或 3-通道, 8-位图像,可是不可以以 in-place
格局处理图像。

其对于每个参数的分解如下:

四、高斯滤波

图像的高斯模糊进度固然图像与坚守二维正态分布的卷积核做卷积。由于正态分布又叫作高斯分布,所以那项技能就叫作高斯模糊。
图像与圆圈卷积核做卷积将会变卦尤其纯粹的焦外成像效果。由于高斯函数的傅立叶变换是此外一个高斯函数,所以高斯模糊对于图像来说就是一个低通滤波操作。
高斯滤波器是一类依据高斯函数的形象来摘取权值的线性平滑滤波器。高斯平滑滤波器对于遏制遵循正态分布的噪声格外实惠。

一维零均值高斯函数为:

图片 5

内部,高斯分布参数σ决定了高斯函数的肥瘦。

对此二维图像来说,常用二维零均值离散高斯函数作平滑滤波器。
二维高斯函数为:

图片 6

OpenCV中函数GaussianBlur完毕了高斯滤波。
函数原型:

void GaussianBlur( InputArray src, OutputArray dst, Size ksize,
                                double sigmaX, double sigmaY = 0,
                                int borderType = BORDER_DEFAULT );

参数:

  • 率先个参数,InputArray类型的src,输入图像,即源图像,填Mat类的对象即可。它可以是单身的擅自通道数的图纸,但必要注意,图片深度应该为CV_8U,CV_16U,
    CV_16S, CV_32F 以及 CV_64F之一。
  • 第一个参数,OutputArray类型的dst,即目的图像,须求和源图片有雷同的尺寸和档次。比如可以用Mat::Clone,以源图片为模板,来先河化得到如假包换的对象图。
  • 其多个参数,Size类型的ksize高斯水源的大大小小。其中ksize.width和ksize.height可以分化,但他们都务必为正数和奇数。或者,它们能够是零的,它们都是由sigma计算而来。
  • 第多个参数,double类型的sigmaX,表示高斯核函数在X方向的的规范不是。
  • 第八个参数,double类型的sigmaY,表示高斯核函数在Y方向的的正规化不是。若sigmaY为零,就将它设为sigmaX,假若sigmaX和sigmaY都是0,那么就由ksize.width和ksize.height计算出来。
    为了结果的不错着想,最好是把第四个参数Size,首个参数sigmaX和第七个参数sigmaY全体指定到。
  • 第七个参数,int类型的borderType,用于算计图像外部像素的某种边界情势。有默许值BORDER_DEFAULT,大家一般不去管它。

从别处抄来的为主代码:

param1 – The first parameter of the
smoothing operation, the aperture width. Must be a positive odd number
(1, 3, 5, …)

五、综合示范代码

#include "opencv2/core/core.hpp"
#include "opencv2/highgui/highgui.hpp"  
#include<opencv2/imgproc/imgproc.hpp>  
#include <iostream>  

using namespace cv;

int main(){

    Mat img = imread("dog.jpg");

    Mat out1,out2,out3;

    boxFilter(img,out1,-1,Size(30,30));
    blur(img, out2, Size(30, 30));

    namedWindow("方框滤波", 2);
    imshow("方框滤波", out1);

    GaussianBlur(img, out3, Size(29, 29), 0, 0);

    namedWindow("均值滤波", 2);
    imshow("均值滤波", out2);

    namedWindow("高斯滤波", 2);
    imshow("高斯滤波", out3);

    waitKey(0);


    return 0;
}

参考:
http://blog.csdn.net/keith\_bb/article/details/53869626
http://blog.csdn.net/poem\_qianmo/article/details/22745559

//邻域平均滤波
cvSmooth(pImg,pImg,CV_BLUR,3,3,0,0);        //3x3
cvSmooth(pImg,pImg,CV_BLUR,5,5,0,0);        //5x5
//中值滤波 
cvSmooth(pImg,pImg,CV_MEDIAN,3,3,0,0);      //3x3
cvSmooth(pImg,pImg,CV_MEDIAN,5,5,0,0);      //5x5
//高斯滤波
cvSmooth(pImg,pImg,CV_GAUSSIAN,3,3,0,0);    //3x3
cvSmooth(pImg,pImg,CV_GAUSSIAN,5,5,0,0);    //5x5

param2 – The second parameter of the
smoothing operation, the aperture height. Ignored by CV_MEDIAN and
CV_BILATERAL methods. In the case of simple scaled/non-scaled and
Gaussian blur if param2 is zero, it is set to param1 . Otherwise it must
be a positive odd number.

自己要好抄来的一个简单例子:

param3 – In the case of a Gaussian
parameter this parameter may specify Gaussian
 图片 7   (standard deviation).
If it is zero, it is calculated from the kernel size:

#include "stdafx.h"
#include <iostream>

#pragma comment(lib,"../OpenCV-2.4.8/lib/opencv_core248d.lib")
#pragma comment(lib,"../OpenCV-2.4.8/lib/opencv_highgui248d.lib")
#pragma comment(lib,"../OpenCV-2.4.8/lib/opencv_imgproc248d.lib")

#include "../OpenCV-2.4.8/include/highgui.h"
#include "../OpenCV-2.4.8/include/cv.h"

using namespace std;
using namespace cv;


int _tmain(int argc, char *argv[]) 
{
   IplImage* img = cvLoadImage("3.jpg") ;
   cvNamedWindow("DEMO-in") ;
   cvNamedWindow("DEMO-out") ;
   cvShowImage("DEMO-in",img) ;

   IplImage* out = cvCreateImage(cvGetSize(img), IPL_DEPTH_8U, 3) ;

   cvSmooth(img, out, CV_MEDIAN, 3, 3) ;

   cvShowImage("DEMO-out", out);

   cvReleaseImage( &out ) ;

   cvWaitKey( 0 ) ;
   cvDestroyWindow("DEMO-in") ;
   cvDestroyWindow("DEMO-out") ;

   return 0;
}

 图片 8

(2)OpenCV自带的人脸检测
代码:

Using standard sigma for small
kernels( 图片 9  to
图片 10 ) gives better speed. If
param3 is not zero, while param1 and param2 are zeros, the kernel size
is calculated from the sigma (to provide accurate enough
operation).

#include "stdafx.h"
#include <stdio.h> 
#include <stdlib.h> 
#include <assert.h> 
#include <math.h> 
#include <float.h> 
#include <limits.h> 
#include <time.h> 
#include <ctype.h>

#include <opencv2\opencv.hpp>
#include <iostream>
#include <string>
using namespace cv;
using namespace std;

#ifdef _EiC 
#define WIN32 
#endif

static CvMemStorage* storage = 0; 
static CvHaarClassifierCascade* cascade = 0;

void detect_and_draw( IplImage* image );

const char* cascade_name = 
"haarcascade_frontalface_alt.xml"; 
/*    "haarcascade_profileface.xml";*/

int main( int argc, char** argv ) 
{ 
    cascade_name = "haarcascade_frontalface_alt.xml"; 
    cascade = (CvHaarClassifierCascade*)cvLoad( cascade_name, 0, 0, 0 ); 

    if( !cascade ) 
    { 
        fprintf( stderr, "ERROR: Could not load classifier cascade\n" ); 
        return -1; 
    } 
    storage = cvCreateMemStorage(0); 
    cvNamedWindow( "result", 0 ); 

    const char* filename = "2.jpg"; 
    IplImage* image = cvLoadImage( filename, 1 );

    if( image ) 
    { 
        detect_and_draw( image ); 
        cvWaitKey(0); 
        cvReleaseImage( &image );   
    }

    cvDestroyWindow("result"); 

    return 0; 
}


void detect_and_draw(IplImage* img ) 
{ 
    double scale=1.2; 
    static CvScalar colors[] = { 
        {{0,0,255}},{{0,128,255}},{{0,255,255}},{{0,255,0}}, 
        {{255,128,0}},{{255,255,0}},{{255,0,0}},{{255,0,255}} 
    };//Just some pretty colors to draw with

    //Image Preparation 
    // 
    IplImage* gray = cvCreateImage(cvSize(img->width,img->height),8,1); 
    IplImage* small_img=cvCreateImage(cvSize(cvRound(img->width/scale),cvRound(img->height/scale)),8,1); 
    cvCvtColor(img,gray, CV_BGR2GRAY); 
    cvResize(gray, small_img, CV_INTER_LINEAR);

    cvEqualizeHist(small_img,small_img); //直方图均衡

    //Detect objects if any 
    // 
    cvClearMemStorage(storage); 
    double t = (double)cvGetTickCount(); 
    CvSeq* objects = cvHaarDetectObjects(small_img, 
        cascade, 
        storage, 
        1.1, 
        2, 
        0/*CV_HAAR_DO_CANNY_PRUNING*/, 
        cvSize(30,30));

    t = (double)cvGetTickCount() - t; 
    printf( "detection time = %gms\n", t/((double)cvGetTickFrequency()*1000.) );

    //Loop through found objects and draw boxes around them 
    for(int i=0;i<(objects? objects->total:0);++i) 
    { 
        CvRect* r=(CvRect*)cvGetSeqElem(objects,i); 
        cvRectangle(img, cvPoint(r->x*scale,r->y*scale), cvPoint((r->x+r->width)*scale,(r->y+r->height)*scale), colors[i%8]); 
    } 
    for( int i = 0; i < (objects? objects->total : 0); i++ ) 
    { 
        CvRect* r = (CvRect*)cvGetSeqElem( objects, i ); 
        CvPoint center; 
        int radius; 
        center.x = cvRound((r->x + r->width*0.5)*scale); 
        center.y = cvRound((r->y + r->height*0.5)*scale); 
        radius = cvRound((r->width + r->height)*0.25*scale); 
        cvCircle( img, center, radius, colors[i%8], 3, 8, 0 ); 
    }

    cvShowImage( "result", img ); 
    cvReleaseImage(&gray); 
    cvReleaseImage(&small_img); 
}

对于参数smoothtype为CV_GAUSSIAN的高斯滤波来讲,param1和param2是高斯滤波核(Gaussian
kernel)的尺寸,param3为高斯核的标准差(详见博文:http://www.cnblogs.com/pegasus/archive/2011/05/20/2052031.html)。文档里早就说了当param3或者param4为零的时候,如何根据核的高低算出标准差,那么就像本题的境况,在param1和param2为零即变换核的高宽都为零的时候,如何根据param3和param4算出param1和param2呢?在OpenCV的smooth.cpp文件中有个createGaussianFilter函数,下边的代码即为当只提供标准差(平行向或者竖直向)的时候,如何算出核的尺寸,代码:

  
那实在是个Haar特征的级联分类器,haarcascade_frontalface_alt.xml那一个文件存的应该是正脸的Haar特征数据(猜想的,没有探索过,欢迎拍砖指正),这些文件是OpenCV自带的,使用的时候拷贝到自己的工程面即可。该数据被加载到static
CvHaarClassifierCascade* cascade变量,由detect_and_draw(IplImage*
img ) 函数里面的cvHaarDetectObjects函数来贯彻人脸的检测。

    // automatic detection of kernel size from sigma
    if( ksize.width <= 0 && sigma1 > 0 )
        ksize.width = cvRound(sigma1*(depth == CV_8U ? 3 : 4)*2 + 1)|1;
    if( ksize.height <= 0 && sigma2 > 0 )
        ksize.height = cvRound(sigma2*(depth == CV_8U ? 3 : 4)*2 + 1)|1;

那里的sigma1即为param3,sigma2即为param4,可以见到,当图像的数据类型为8为无符号型时,核的大小(直径)为
6 * sigma +
1,大概OpenCV认为半径为3*sigma的窗口就是高斯函数能量最集中的区域。至于在图像数据类型不为CV_8U的时候怎么核就改为了8*sigma

  • 1 就不得而知了。

 

下边回到本题。

自己用来做变换的原来图像为:

 图片 11

param1 = param2 = 9的结果图像(这时候的param3为1):

 图片 12

param1 = param2 = 0的结果图像(那时候的param3为1):

 图片 13

地方那两幅图的视觉效果几乎,事实是那两幅图像完全平等,其峰值信噪比为正无穷大。

为什么?

上边已经说了当核大小为零的时候,OpenCV会依据你提供的sigma大小来得出核的分寸,那里的sigma为1,所以获得的核大小应该是7(即param1=param2=7)

那么,核大小为9和核大小为7的高斯滤波出来的结果为何会一模一样?

在smooth.cpp文件中的createGaussianFilter函数中,有四个矩阵(数据类型为Mat)kx和ky,在size=param1=param2的时候,那四个矩阵一样,且矩阵的大小为size行1列,由此在param1=param2=9,param3=1的时候那多个矩阵为(前边的括号表示行列索引,前边为该职分的数值):

(0, 0) - 0.000134

(1, 0) - 0.004432

(2, 0) - 0.053991

(3, 0) - 0.241971

(4, 0) - 0.398943

(5, 0) - 0.241971

(6, 0) - 0.053991

(7, 0) - 0.004432

(8, 0) - 0.000134

其相应的高斯变换核为:

图片 14

在param1=param2=7,param3=1的时候那四个矩阵为:

(0, 0) - 0.004432

(1, 0) - 0.053991

(2, 0) - 0.241971

(3, 0) - 0.398943

(4, 0) - 0.241971

(5, 0) - 0.053991

(6, 0) - 0.004432

其相应的高斯变换核为:

图片 15

可以看出,那八个矩阵除了第三个多了最外面的方面两行和左右两列外,里面是一模一样的,同时,多出来的行如故列除了一个数值为0.0001外,其他基本是零。那也就分解了核大小为9和核大小为7的高斯滤波出来的结果差一些是一模一样的(我此刻的事例是完全一模一样)。

 

param1 = param2 = 9的结果图像(这时候的param3为4):

 图片 16

param1 = param2 = 0的结果图像(那时候的param3为4):

图片 17

可以看看那三个结实的视觉效果就不一样等了,上面这么些比上面分外药更模糊一点,事实也证实了那一点,那两张图像的PSNR为26.565127。

在param1=param2=0,param3=4的时候,其参加运算的核大小是25(4*6+1),因而与核大小为9的结果本来有出入。

 

param1 = param2 = 9的结果图像(那时候的param3为6):

图片 18

param1 = param2 = 0的结果图像(那时候的param3为6):

图片 19

那两副图像的PSNR为22.688340,也属于差异相比大的两张图像。

可以汲取,第二副图像变换核大小为37,与核大小为9的转移核滤波出来的结果当然有很大分别。

 

小结:1.
在OpenCV所完毕的高斯滤波中,param1或者param2为零(即变换核为0的时候),将基于param3或者param4的值算出变换核的尺寸;对于8位单通道图像,算法是size
= 6*sigma + 1;

        2.
在暴发高斯变换核的标准差(sigma)指定时,如果两个核大小大约,很有可能出来的滤波结果图大约全盘一模一样;

        3.
在标准差固定时,变换核越大,滤波出来的图像越模糊,丢失的底细也就越来越多;

        4.
在变换核的尺寸稳定时,标准差(即sigma,param3)越大,滤波出来的图像越模糊,丢失的底细也就更多。

相关文章