Learning OpenCV with iOS: 图像模糊--非线性滤波

一、前言

上一篇我们讲解了OpenCV图像模糊中的线性滤波。本篇主要向大家介绍下非线性滤波。按惯例,先来看下效果图。

给铠祛痘
阿珂美颜

二、线性与非线性

上一篇中使用“卷积算子计算都是线性操作,所以又叫线性滤波”简单描述了线性滤波概念。下面我们详细了解下线性滤波非线性滤波

数学角度

数学里,一般说的线性,是说的线性映射:
线性 = 齐次性 + 可加性
齐次性: f(ax)=af(x)
可加性: f(x+y)=f(x)+f(y)
非线性就是这两条至少之一不成立.

图像角度

线性滤波:两个信号之和的响应和他们各自响应之和相等(可加性)。换句话说,每个像素的输出值是一些输入像素的加权和
非线性滤波:原始数据与滤波结果是一种逻辑关系,即通过比较一定邻域内的灰度值大小来实现的。

线性滤波器易于构造,并且易于从频率响应角度来进行分析。但是,线性滤波在处理散粒噪声(即图像偶尔会出现很大的值)的时候,无法将噪声像素去除,只能转换为更为柔和但仍然可见的散粒。

这时非线性滤波就该登场了。

三、非线性滤波

1、中值滤波(Median filter)

中值滤波是一种典型的非线性滤波技术,原理是用邻域像素灰度值的中值来代替该像素点的灰度值。

滤波过程
中值滤波

滤波操作:在9 x 9上面有3 x 3的窗口,从左到右,从上到下移动。将3 x 3窗口内的灰度值按顺序排列,然后取中值代替中心的灰度值。

中值滤波在一定的条件下可以克服常见线性滤波器,如均值滤波带来的图像细节模糊。而且对去除椒盐噪声非常有效,也常用于保护边缘信息, 保存边缘的特性使它在不希望出现边缘模糊的场合也很有用,是非常经典的平滑噪声处理方法。

中值滤波与均值滤波比较

中值滤波优势

在均值滤波中,将噪声像素与非噪声像素一并放入平均计算中,影响了输出。在中值滤波中,噪声像素很难被选成中值,所以几乎不会影响到输出。因此,中值滤波消除噪声和边缘保护方便都更胜一筹。

中值滤波劣势

因为中值滤波要进行排序操作,所以处理的时间长,是均值滤波的5倍以上。

给铠祛痘

给铠祛痘

OpenCV提供了中值滤波的API

/** 
@param ksize aperture linear size; it must be odd and greater than 1, for example: 3, 5, 7 ...
 */
void medianBlur( InputArray src, OutputArray dst, int ksize );

注意:ksize必须是奇数

+ (UIImage *)medianBlur:(UIImage *)image size:(int)size {
    Mat src;
    UIImageToMat(image, src);
    
    int finalSize = size;
    if (size%2 == 0) {
        finalSize = size + 1;
    }
    Mat dst;
    medianBlur(src, dst, finalSize);
    
    UIImage* result = MatToUIImage(dst);
    
    return result;
}

class NolinearBlurViewController: UIViewController {

    @IBOutlet weak var resultImageView: UIImageView!
    let image = #imageLiteral(resourceName: "ddkai")
    
    @IBAction func onSliderValueChanged(_ sender: UISlider) {
        resultImageView.image = OpenCV.medianBlur(self.image, size: Int32(Int(sender.value)))
    }
}

一些思考

为何ksize必须是奇数?

因为如果ksize是偶数,那么将像素灰度值从小到大排列后,必然就没有唯一的中值。即使得出中值,那么又将那个作为中心像素呢?因此,中值滤波要求ksize必须是奇数。

ksize

中值滤波有什么不适合的场景?

对一些细节多,特别是线、尖顶等细节多的图像不宜采用中值滤波。因为中值滤波会将这些细节也模糊掉。

2、双边滤波(Bilateral filter)

空间域&像素值域

对于图像滤波来说,图像在空间中变化缓慢,因此相邻的像素点会更相近。但是这个假设在图像的边缘处变得不成立。如果在边缘处也用这种思路来进行滤波的话,即认为相邻相近,则得到的结果必然会模糊掉边缘。因为边缘两侧的点的像素值差别很大,所以权重还需考虑像素值。

因此,滤波不但要考虑空间域(以下简称空域),还需要考虑像素值域(以下简称值域)。

滤波分析

  • 均值滤波无法克服边缘像素信息丢失。原因是均值滤波是基于平均权重,没有考虑空域值域
  • 高斯模糊部分克服了该缺陷(考虑了空域),但是无法完全避免,因为没有考虑像素值的不同,即没有考虑值域

双边滤波

双边滤波是一种非线性的滤波方法,具有保边去噪的效果。

双边滤波

双边滤波的基本思路是同时考虑像素点的空域值域
双边滤波在考虑值域时,利用像素点的值的大小进行补充,因为边缘两侧的点的像素值差别很大,因此会使得其加权的时候权重具有很大的差别,从而使得只考虑自己所属的一边的邻域。可以理解成先根据像素值对要用来进行滤波的邻域做一个分割或分类,再给该点所属的类别相对较高的权重,然后进行邻域加权求和,得到最终结果。

在双边滤波器中,输出像素的值依赖于邻域像素值的加权值组合:

双边滤波
  • w(i,j,k,l): 加权系数, 取决于空域核和值域核的乘积。
  • (i,j),(k,l): 指两个像素点的坐标。

空域核:

空域核

值域核:

值域核

双边滤波权重函数:

双边滤波权重函数

空域核(d)函数是根据像素距离选择权重,距离越近权重越大。
值域核(r)函数则是根据像素的差异来分配权值。如果两个像素值越接近,即使相距较远,也比差异大而距离近的像素点权重大。这点使得边缘(即相距近但差异大的像素点)的特性得以保留。

阿珂美颜

阿珂美颜
阿珂美颜

OpenCV提供了双边滤波的API

/** 
_Sigma values_: 为了简单起见,可以将2 Sigma值设置为相同。
如果它们很小(<10)滤波器不会有太大的效果。
如果它们很大(>150),它们将具有非常强烈的效果,使图像看起来“卡通化”。

_Filter size_: 大的滤波器(D> 5)非常慢,因此建议在进行实时处理应用程序时使用d=5。对于需要重噪声过滤的离线应用程序可以试下d=9。

@param src : 即源图像,需要为8位或者浮点型单通道、三通道的图像。
@param d:过滤过程中每个像素邻域的直径。如果这个值我们设其为非正数,那么OpenCV会从第五个参数sigmaSpace来计算出它来。
@param sigmaColor :颜色空间滤波器的sigma值。这个参数的值越大,就表明该像素邻域内有更宽广的颜色会被混合到一起,产生较大的半相等颜色区域。
@param sigmaSpace:坐标空间中滤波器的sigma值,坐标空间的标注方差。
他的数值越大,意味着越远的像素会相互影响,从而使更大的区域足够相似的颜色获取相同的颜色。
当d>0,d指定了邻域大小且与sigmaSpace无关。否则,d正比于sigmaSpace。
 */ 
void bilateralFilter( InputArray src, OutputArray dst, int d,
                                   double sigmaColor, double sigmaSpace,
                                   int borderType = BORDER_DEFAULT );
+ (UIImage *)bilateralFilter:(UIImage *)image
                           d:(int)d
                  sigmaColor:(double)sigmaColor
                  sigmaSpace:(double)sigmaSpace {
    Mat src;
    UIImageToMat(image, src);
    
    if (src.channels() == 4) {
        cvtColor(src, src, CV_BGRA2BGR);
    }
    
    Mat dst;
    bilateralFilter(src, dst, d, sigmaColor, sigmaSpace);
    
    UIImage* result = MatToUIImage(dst);
    
    return result;
}


class BilateralFilterViewController: UIViewController {

    @IBOutlet weak var imageView: UIImageView!
    @IBOutlet weak var blurImageView: UIImageView!
    @IBOutlet weak var gBlurImageView: UIImageView!
    @IBOutlet weak var resultImageView: UIImageView!
    
    private var d: Int32 = 1
    private var color: Double = 1.0
    private var space: Double = 1.0
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        blurImageView.image = OpenCV.blur(imageView.image, sizeX: 3, sizeY: 3)
        gBlurImageView.image = OpenCV.gaussianblur(imageView.image, sizeX: 3, sizeY: 3)
    }

    @IBAction func onSliderValueChanged(_ sender: UISlider) {
        d = Int32(sender.value)
        transform()
    }
    
    @IBAction func onSlider2ValueChanged(_ sender: UISlider) {
        color = Double(sender.value)
        transform()
    }
    
    @IBAction func onSlider3ValueChanged(_ sender: UISlider) {
        space = Double(sender.value)
        transform()
    }
    
    private func transform() {
        resultImageView.image = OpenCV.bilateralFilter(imageView.image, d: d, sigmaColor: color, sigmaSpace: space)
    }
}

小小经验

如何选取合适的参数?
  • 使用OpenCV API时可以先看下API的注释文档,比如在双边滤波的注释文档中对Sigma的取值做了说明。这些值一般都是经验值
_Sigma values_: 为了简单起见,可以将2 Sigma值设置为相同。
如果它们很小(<10)滤波器不会有太大的效果。
如果它们很大(>150),它们将具有非常强烈的效果,使图像看起来“卡通化”。
  • 使用滑竿帮助快速调节参数,观察效果。

  • 原理公式出发,假定一些参数,观察其趋势,掌握规律。

四、小结

本篇主要介绍了非线性滤波的概念,并通过例子讲解了中值滤波和双边滤波。 非线性滤波的应用广泛,不但要掌握API的调用,更要明白各种滤波的原理,这样才能创造个性化的滤波,也许有一天你就创造出自己的美颜滤镜了。 今天就到这了,有疑问的朋友可以给我留言,咱们下篇见!

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

推荐阅读更多精彩内容