那篇随笔开始开展平整管理,param3为高斯核的标准差(详见博文

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

首先看下OpenCV 官方文书档案对于cvSmooth各类参数的解释:

前言

(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卡塔尔(英语:State of Qatar)-
-对各种象素param1×param2邻域求和并做标准调换 1/(param1×param2卡塔尔(英语:State of Qatar)。
   CV_GAUSSIAN(gaussian blur卡塔尔(英语:State of Qatar) – -对图像实行核大小为 param1×param2
的高斯卷积。
   CV_MEDIAN(median blur卡塔尔 – -对图像进行核大小为param1×param1
的中值滤波 (邻域是方的卡塔尔国。
   CV_BILATERAL(双向滤波卡塔尔国 – -应用双向 3×3 滤波,彩色
sigma=param1,空间 sigma=param2.。

Smooths the image in one of several
ways.

上生机勃勃篇随笔,大家上课了图像的捏造边缘,那篇作品初始实行平整管理。

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)

基本原理

  未有缩放的图像平滑仅支持单通道图像,並且帮助8位到十四位的转移(与cvSobel和cvaplace形似卡塔尔(قطر‎和33位浮点数到31个人浮点数的调换格式。
  轻易模糊和高斯模糊扶持 1- 或 3-通道, 8-比特 和 32-比特
浮点图像。那三种艺术能够(in-place)方式管理图像。
  中值和双向滤波职业于 1- 或 3-通道, 8-位图像,可是无法以 in-place
格局管理图像。

其对于各样参数的分解如下:

此处直接引用OpenCV 2.4+ C++ 平滑管理和OpenCV 2.4+ C++
边缘梯度总结的相干内容:

从别处抄来的着力代码:

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

平整也称模糊, 是生龙活虎项轻松且使用成效超级高的图像管理格局。

平整管理时要求采取叁个滤波器

。 最常用的滤波器是线性

滤波器,线性滤波管理的输出像素值的加权平均:

称为核

, 它仅仅是多个加权周全。

//邻域平均滤波
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
 图片 1   (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;
}

 图片 2

核?!

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

Using standard sigma for small
kernels( 图片 3  to
图片 4 ) 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).

nbsp; dsds

#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
CvHaarClassifier卡斯卡特* 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 就一窍不通了。

在图像边缘的卷积如何做呢?

 

计量卷积前,必要通过复制源图像的疆界成立虚构像素,那样边缘的地方也是有丰盛像素总结卷积了。那正是干什么上黄金时代篇小说须求做设想边缘函数。

下边回到本题。

均值平滑

自身用来做转换的庐山真面目图像为:

均值平滑实际上正是内核成分全部都以1的卷积运算,然后再除以内核的大小,用数学表明式来代表正是:

 图片 5

下面大家来落到实处均值平滑函数blur:复制代码
代码如下:function blur(__src, __size1, __size2, __borderType,
__dst){ if(__src.type && __src.type == “CV_RGBA”){ var height =
__src.row, width = __src.col, dst = __dst || new Mat(height,
width, CV_RGBA), dstData = dst.data; var size1 = __size1 || 3, size2
= __size2 || size1, size = size1 * size2; if(size1 % 2 !== 1 || size2
% 2 !== 1){ console.error; return __src; } var startX = Math.floor,
startY = Math.floor; var withBorderMat = copyMakeBorder(__src, startY,
startX, 0, 0, __borderType), mData = withBorderMat.data, mWidth =
withBorderMat.col; var newValue, nowX, offsetY, offsetI; var i, j, c, y,
x; for{ offsetI = i * width; for{ for{ newValue = 0; for{ offsetY = *
mWidth * 4; for{ nowX = * 4 + c; newValue += mData[offsetY + nowX];
} } dstData[ * 4 + c] = newValue / size; } dstData[ * 4 + 3] =
mData[offsetY + startY * mWidth * 4 + * 4 + 3]; } } }else{
console.error; } return dst;
}此中size1和size2各自是核的横向和纵向大小,而且必需是正奇数。

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

高斯平滑

 图片 6

最管用的滤波器 。 高斯滤波是将输入数组的每三个像素点与高斯内核

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

卷积将卷积和当作输出像素值。

 图片 7

参照他事他说加以考察生机勃勃维高斯函数,大家得以瞥见,他是当中等大两侧小的函数。

地点这两幅图的视觉效果大约,事实是这两幅图像完全相像,其峰值信噪比为正无穷大。

故此高斯滤波器其加权数是中档大,四周小的。

为什么?

这几个维高斯函数为:

地点已经说了当核大小为零的时候,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

和 变量

其相应的高斯调换核为:

各有二个均值,也各有二个规范差卡塔尔(قطر‎。

图片 8

此间仿照效法OpenCV的落成,不过相应还会有优化空间,因为还没用到分离滤波器。

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

首先大家做二个getGaussianKernel来回到高斯滤波器的风姿洒脱维数组。复制代码 代码如下:function getGaussianKernel{
var SMALL_GAUSSIAN_SIZE = 7, smallGaussianTab = [[1], [0.25, 0.5,
0.25], [0.0625, 0.25, 0.375, 0.25, 0.0625], [0.03125, 0.109375,
0.21875, 0.28125, 0.21875, 0.109375, 0.03125] ]; var fixedKernel =
__n & 2 == 1 && __n <= SMALL_GAUSSIAN_SIZE && __sigma <= 0
? smallGaussianTab[__n >> 1] : 0; var sigmaX = __sigma >
0 ? __sigma : * 0.3 + 0.8, scale2X = -0.5 / , sum = 0; var i, x, t,
kernel = []; for{ x = i – * 0.5; t = fixedKernel ? fixedKernel[i] :
Math.exp; kernel[i] = t; sum += t; } sum = 1 / sum; for{ kernel[i]
*= sum; } return kernel;
};然后经过五个那些生龙活虎维数组,便得以总计出多少个完好无损的高斯内核,再选用blur里面用到的轮回方法,就足以算出高斯平滑后的矩阵了。复制代码 代码如下:function
GaussianBlur(__src, __size1, __size2, __sigma1, __sigma2,
__borderType, __dst){ if(__src.type && __src.type ==
“CV_RGBA”){ var height = __src.row, width = __src.col, dst =
__dst || new Mat(height, width, CV_RGBA), dstData = dst.data; var
sigma1 = __sigma1 || 0, sigma2 = __sigma2 || __sigma1; var size1 =
__size1 || Math.round | 1, size2 = __size2 || Math.round | 1, size =
size1 * size2; if(size1 % 2 !== 1 || size2 % 2 !== 1){ console.error;
return __src; } var startX = Math.floor, startY = Math.floor; var
withBorderMat = copyMakeBorder(__src, startY, startX, 0, 0,
__borderType), mData = withBorderMat.data, mWidth = withBorderMat.col;
var kernel1 = getGaussianKernel, kernel2, kernel = new Array; if(size1
=== size2 && sigma1 === sigma2) kernel2 = kernel1; else kernel2 =
getGaussianKernel; var i, j, c, y, x; for(i = kernel2.length; i–;){
for(j = kernel1.length; j–;){ kernel[i * size1 + j] = kernel2[i]
* kernel1[j]; } } var newValue, nowX, offsetY, offsetI; for{ offsetI
= i * width; for{ for{ newValue = 0; for{ offsetY = * mWidth * 4;
for{ nowX = * 4 + c; newValue += (mData[offsetY + nowX] * kernel[y
* size1 + x]); } } dstData[ * 4 + c] = newValue; } dstData[ * 4 +
3] = mData[offsetY + startY * mWidth * 4 + * 4 + 3]; } } }else{
console.error; } return dst; }

(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

中值平滑

其相应的高斯转换核为:

中值滤波将图像的每一种像素用邻域 像素的

图片 9

中值代替 。

能够见到,那五个矩阵除了第三个多了最外侧的方面两行和左右两列外,里面是千篇一律的,相同的时候,多出去的行依然列除了四个数值为0.0001外,别的基本是零。那也就分解了核大小为9和核大小为7的高斯滤波出来的结果少了一些是如出一辙的(作者那儿的例子是一丝一毫大同小异)。

依然选用blur里面用到的大循环,只要拿到核中的兼具值,再经过sort排序便足以赢得中值,然后锚点由该值替代。复制代码 代码如下:function medianBlur(__src,
__size1, __size2, __borderType, __dst){ if(__src.type &&
__src.type == “CV_RGBA”){ var height = __src.row, width =
__src.col, dst = __dst || new Mat(height, width, CV_RGBA), dstData
= dst.data; var size1 = __size1 || 3, size2 = __size2 || size1, size
= size1 * size2; if(size1 % 2 !== 1 || size2 % 2 !== 1){ console.error;
return __src; } var startX = Math.floor, startY = Math.floor; var
withBorderMat = copyMakeBorder(__src, startY, startX, 0, 0,
__borderType), mData = withBorderMat.data, mWidth = withBorderMat.col;
var newValue = [], nowX, offsetY, offsetI; var i, j, c, y, x; for{
offsetI = i * width; for{ for{ for{ offsetY = * mWidth * 4; for{ nowX
= * 4 + c; newValue[y * size1 + x] = mData[offsetY + nowX]; } }
newValue.sort(); dstData[ * 4 + c] = newValue[Math.round]; }
dstData[ * 4 + 3] = mData[offsetY + startY * mWidth * 4 + * 4 +
3]; } } }else{ console.error; } return dst; };

 

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

 图片 10

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

图片 11

能够看见那多少个结果的视觉效果就差别了,上边那么些比地点十二分药更模糊一点,事实也作证了那一点,这两张图像的PSNCRUISER为26.565127。

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

 

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

图片 12

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

图片 13

这两副图像的PSN奥德赛为22.688340,也归属差别比极大的两张图像。

可以得出,第二副图像转变核大小为37,与核大小为9的转移核滤波出来的结果自然有非常大不同。

 

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

        2.
在发生高斯转换核的规范差(sigma)指准期,借使四个核大小大致,很有十分的大可能出来的滤波结果图大概全盘一模二样;

        3.
在典型差固按时,转变核越大,滤波出来的图像越模糊,遗失的底细也就越来越多;

        4.
在转换核的尺寸稳依期,标准差(即sigma,param3)越大,滤波出来的图像越模糊,错过的内情也就越多。

相关文章