OpenCV 中的滤波函数

blur

也称为 box filter、均值滤波器,就是简单地将每个像素的值替换成邻域平均值。

cv::blur(image, result, cv::Size(3,3));

如果用 kernel (也称为 mask) 表示,就是

boxFilter1.png

如果采用积分图的方法,可以更快的计算这种 box filter 的结果。
在积分图中,只需要三次加法运算,一次乘法运算即可,即通过积分图,算出 kernel 内部区域的像素和,然后取平均。

积分图

积分图中每一点 (x,y)​ 的值是原图中对应位置左上角区域所有值的和:
I(x,y) = \sum_{x'\le x \\ y'\le y}i(x',y')
积分图的计算可以很高效:
I(x,y) = i(x,y) + I(x-1, y) + I(x,y-1) - I(x-1,y-1)

每次计算只需要新增一个像素值,其他值都是之前已经计算出来的。
积分图一旦计算出来,对任意矩形区域内像素和的计算都可以在常数时间(即计算时间固定,与区域的大小无关)内完成,例如:

integral.png

\sum_{A(x)<x'<C(x) \\ ~ A(y)<y'<C(y)} i(x', y') = I(C) -I(B) -I(D) + I(A)

GaussianBlur

在高斯滤波器中,当前像素值取邻域的加权平均,离当前像素越近,权重越大,权重服从高斯分布。
在实际应用中,几乎总是首选高斯滤波器,很少直接用 box filter.

cv::GaussianBlur(image, result, cv::Size(5,5), 1.5); //最后的参数表示高斯分布的方差 sigma

上述命令中,最后两个参数 kernel size 和 ​\sigma 如果只设置一个,则另一个可以通过以下公式推出:
\sigma = 0.3*((ksize-1)*0.5 - 1) + 0.8 \\ ksize = round(3 * \sigma * 2 + 1)

第二个式子很好理解,就是借助高斯函数的性质(距离均值 3 个标准差范围内的取值占总数的 99.7%),因此窗口大小就是 3 倍的 ​\sigma *2 (两边)然后再加上 1 (自身)。
第一个式子与第二个非常近似,但是又做了一些微调。

上述高斯滤波器内部实际上是先调用如下函数,产生服从高斯分布的一系列权重:

cv::getGaussianKernel(9, sigma, CV_32F); // 产生 9 个权重,与中心像素的距离依次为 -4,-3, ...3, 4

上述 9 个权重是经过归一化的,即和为 1,其公式为
k_i = \alpha \cdot e^{-\frac{(i-(ksize-1)/2)^2}{2\sigma ^2}}

其中 \alpha​ 满足归一化的要求,ksize 必须是奇数。如果要生成二维的高斯矩阵权重,则是先产生一个权重列向量,然后令该列向量与自身的转置相乘,得到高斯矩阵权重,最后再统一进行归一化,保证矩阵所有元素和为 1。

另外,还可以分别产生两个不同的高斯权重向量,对应行和列方向上的高斯模糊权重,然后把它们相乘得到高斯矩阵。由于满足这种分离的性质,高斯滤波器被称为可分离的滤波器。

非线性滤波器:中值滤波器

前边在进行滤波操作时,都只包含线性操作(算数平均、加权平均)。
另外还可以采用非线性操作,对应非线性滤波器。非线性滤波器不能表示成 kernel 矩阵卷积操作的形式。

中值滤波器是一种非线性滤波器。它把当前像素和邻域像素组成一个集合,排序之后,选择中间值(即排序中间位置的数值)替换当前像素值。

椒盐噪声:像素随机替换成白色或者黑点。在通讯时,如果部分像素值丢失,就会产生这种噪声。

中值滤波器可以有效的消除椒盐噪声,因为这些噪声点在排序时很难成为中间值,因此全都被剔除了。

Sobel 边缘检测滤波器

Sobel 也是线性滤波器,只不过采用了特殊的 kernel 矩阵:

sobel1.png

分别针对水平方向和垂直方向的操作。

用上述 kernel 进行操作,就是计算水平或者垂直方向像素值的差分,近似反映了像素值水平和垂直变化的速度,合在一起就是图像梯度的近似。

// 横向的
cv::Sobel(image,  // 输入
          sobelX, // 输出
          CV_8U,  // 输出数据类型
          1, 0,  // 采用第一个 kernel,即计算 x 轴方向(横向)像素变化率 
          3,  // kernel 的大小
          0.4, 128); // 缩放因子和偏移量

// 纵向的
cv::Sobel(image,  
          sobelY, 
          CV_8U,  
          0, 1,  // 采用第二个 kernel,即计算 y 轴方向(纵向)像素变化率 
          3, 
          0.4, 128); 

在默认情况下,差分运算的结果很可能超过 [0,255] 这个范围,而且有正有负,应该用 CV_16S 数据类型表示。经过上述缩放和偏移之后,才勉强适合用 CV_8U 表示,但还是需要饱和截断操作。

cv::Sobel(image, sobelX, CV_16S, 1, 0);

在分别得到横向、纵向变化率之后,可以整合起来计算梯度的大小

cv::Mat sobel;

sobel = abs(sobelX) + abs(sobelY); // 这里采用了 L1 范数

一般如果要显示最后的 sobel 边缘检测的结果,还需要把上述模值转化到 [0,255] 范围内。

高斯导数

sobel 实际上包含了平滑和求导两个操作,其中邻域像素累加相当于高斯平滑,距离越近的像素权重越高。
sobel 的 kernel size 可以选择 1, 3, 5 和 7,其中 1 代表 1×3 或者 3×1,此时是没有高斯平滑的。
对于大的 size,这种平滑更明显。此时,sobel 不是高通滤波器,而是带通滤波器,既消除了部分高频,又消除了部分低频。

其他梯度算子

与 Sobel 算子类似的还有其他几个计算梯度的算子,只是采用不同的 kernel.

  • Prewitt
    cv::Prewitt(image, prewittX, CV_16S, 1, 0);
    
prewitt1.png
  • Roberts
    cv::Roberts(image, robertsX, CV_16S, 1, 0);
    
roberts1.png
  • Scharr
    cv::Scharr(image, robertsX, CV_16S, 1, 0);
    
scharr1.png

上述所有的滤波器都是近似计算图像函数的一阶导数,像素变化大的区域计算得到的值较大,平坦的区域计算值较小。

Laplacian 算子

sobel 算子通过对图片函数求导,那些数值绝对值较高的点对应了边界区域:

laplacian1.jpg

如果继续求二阶导,则导数较大的点对应了过零点:

laplacian2.jpg

因此,也可以通过搜索二阶导的过零点来检测边界点。

Laplacian 算子的定义
L[I(x,y)] = \frac{\partial ^2 I}{\partial x^2} + \frac{\partial ^2 I}{\partial y^2}

对照 Hessian 矩阵:
H[I(x,y)] = \begin{bmatrix} \frac{\partial^2 I} {\partial x^2} & \frac{\partial^2 I}{\partial x \partial y} \\ \frac{\partial^2 I}{\partial x \partial y} & \frac{\partial^2 I}{\partial y^2} \end{bmatrix}

Laplacian 算子实际上就是 Hessian 矩阵的 Trace。
具体到图像操作中,二阶导有如下表达式:
\frac{\partial ^2 I} {\partial x^2} = [f(x+1,y)-f(x,y)]-[f(x,y)-f(x-1,y)] = f(x+1,y)+f(x-1,y)-2f(x,y) \\ \frac{\partial ^2 I}{\partial y^2} = [f(x,y+1)-f(x,y)]-[f(x,y)-f(x,y-1)] = f(x,y+1)+f(x,y-1)-2f(x,y)

所以最终 Laplacian 算子表达式为:
L[I(x,y)] = f(x+1,y)+f(x,y+1)+f(x-1,y)+f(x,y-1)-4f(x,y)

在具体实现中,可以用以下 kernel 进行卷积操作:

laplacian1.png

Laplacian 算子具有旋转 90 度不变性,即一幅图旋转 90 度及其倍数,对应的 Laplacian 算子操作结果相同。
为了得到更好的旋转不变性,可以将 Laplacian 算子 kernel 扩展为如下形式:

laplacian2.png

这样就具有了旋转 45 度及其倍数的不变性。

cv::Laplacian(input, 
              output, 
              int ddepth,  // 元素类型,由于输入的是CV_8U,为了避免数据溢出,一般设定为 CV_16S
              int ksize=1, // 求导数的 Sobel 算子的窗口大小,一般 ksize >1 奇数,如果取 ksize=1,则直接用上述 [1, -4, ...] kernel.
              double scale=1, // 是否对计算得到的值进行放缩
              double delta, // 在存储到 output 之前,是否添加 bias
              int borderType); // 边界像素插值方式,具体选项可以官网查看 https://docs.opencv.org/3.4/d2/de8/group__core__array.html#ga209f2f4869e304c82d07739337eae7c5
LoG (Laplacian of Gaussian)

Laplacian 算子对噪声比较敏感,因此一般在进行 Laplacian 之前先进行高斯平滑滤波。
两个步骤合并称为 LoG (Laplacian of Gaussian)。
在具体实现中,我们并不需要先高斯再拉普拉斯,而是两步并作一步:将拉普拉斯算子作用在高斯 kernel 上,得到新的 kernel,再与 image 做卷积:
\triangledown ^2(f*g) = f*\triangledown^2g

最后作用在 (x,y) ​ 位置上的卷积权重为
LoG(x,y) = -\frac{1}{\pi \sigma^4}\left[ 1-\frac{x^2+y^2}{2\sigma^2}e^{-\frac{x^2+y^2}{2\sigma^2} } \right]

同样也是通过 \sigma​ 设定滤波范围。

对高斯函数取拉普拉斯算子操作是什么样子的?

  • 一维形式
log1.png
  • 二维形式
log2.png

二维情况下得到的曲面很像“墨西哥草帽”。
\sigma​ 的大小决定了检测的粗粒度:

log3.png
DoG ( LoG 的近似)

Difference of Gaussians
为了减少 LoG 计算量,用两个不同 ​\sigma 的高斯做差,来近似 LoG

log4.png

上图中两个 \sigma​ 的取值好像反了。。。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,470评论 6 501
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,393评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,577评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,176评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,189评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,155评论 1 299
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,041评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,903评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,319评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,539评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,703评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,417评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,013评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,664评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,818评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,711评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,601评论 2 353

推荐阅读更多精彩内容