概述

  一个好看的滤镜一般会包括多种效果:模糊,颗粒,过曝,暗角等。通常,设计师使用Photoshop类似的软件制作出一个滤镜效果,我们再用程序将这些图像处理步骤实现,实现自动生成。

  Photoshop(简写PS)实现一张图片的滤镜效果分很多步骤,可能包括曲线调整,模糊,锐化等工作。这里我只用PS调一个非常简单的滤镜,然后用Python,将PS过程用代码实现。代码很好理解,明白这个Introduction是处理更复杂的滤镜效果基本能力。

Python加载图像

加载,显示图像

  在很多程序语言中都会有处理数字图像的代码库,并且都按照了现有标准的数字模型(RGB, HSV等)进行了实现,以Python为例,有OpenCV-Python, Scikit-Image, Scipy 它们可以用来以某种颜色模型加载数字图像到一个对象里。

  这里使用Scikit-Image库加载图像,之后用Matplotlib显示图像,Numpy处理图片数据
原图

  im变量存储着我们从文件中解码出来的图像,im.shape输出了它的数据结构: (height, width, rgb_channel),一个三维的矩阵

PS

  我设计滤镜的方式,只是调下颜色曲线(Photoshop的‘曲线工具’),下面是效果,调成了一个颜色偏冷的色调。

drawing

  下图是PS调整后的的曲线面板,背景是当前通道颜色值的直方图,前景的曲线,就是每个颜色通道的调整曲线。调整曲线的原理是对颜色通道直方图的分布进行重新分配。在实现部分会介绍这两个基础。
drawing

  能看到,我分别对RGB三个颜色通道都做了曲线调整。如果在代码上,能否实现一样的效果?是的可以。

实现

为了做出和PS类似的效果,需要先了解下基础

基础

  图像的颜色直方图是指每个颜色通道色值频率分布直方图,即表示,图像中每个颜色值(0-255)出现的频率,我们可以用直方图来观察图像的色值情况,下图是原始图像红色通道的直方图。

红色通道直方图

          (Fig 红色通道直方图,红色通道,颜色值在180左右的值是最多的)

  PS中做的曲线操作,我们可以用插值方法替代

  使用线性插值来完成曲线调整的工作,线性插值比较简单,取出插值period的斜率,最终y = px + k * x;

Numpy中为我们提供了现成的插值函数 interp(x, xp, fp),例子如下:

xp = [1, 2, 3]
fp = [3, 2, 4]
np.interp(2.5, xp, fp)
# 3

线性插值

              (Fig 线性插值)

我们这个例子中插值函数的fp应该是什么值,根据PS给出的曲线参考图就能看出,以蓝色通道为例:

蓝通道插值

            (Fig 蓝通道插值)

目测,数据大概是这样(上图蓝色的标记):

Py插值

滤镜Python实现

有了前面的分析,在代码中,需要来实现相应的方法和数据,最后再测试效果。

所以,我们先需要一个实现插值的方法,和将要插值的数据,然后对图像进行插值,我发现光是曲线调整了,这张图像看起来有点不够干净,我再加上一个锐化。

1. 曲线工作

锐化对比

2. 模糊和锐化

  模糊操作,是对单位像素进行一个均值操作,来让当前的像素和周围的像素色值相近。最常用的是高斯模糊,详细的解释可以参考ruanyifeng的文章

  锐化s操作,目的是使图像变得更加清晰,可以理解为模糊的逆操作。

  完成了颜色调整之后,让我们将图像变得更锐利一些,需要使用一下锐化操作,使效果更加突出。

锐化对比

              (Fig 锐化对比,右边是色彩曲线,左边曲线之后加了锐化操作)

3. 最后一步,组合代码看效果
4. 显示直方图对比效果

  当滤镜完成之后,通过肉眼我们当然可以确认需求完成度怎样,而直方图是更具体的数值分析方式。如果想要模仿一下其他滤镜效果,在没有设计师帮助的时候,通过分析对方的直方图来进行拟合,也可以做出差不多的效果。

结尾

  Python实现起来速度快,简单,这样我们便可以更关注效果,了解实现算法。
  另外,有了这些了解,在OpenGL中处理图像纹理时候,可以用同样的方式实现滤镜了。


源码:
http://shengwn.com/page/2018-07-22/filter_source.ipynb
参考:
https://www.practicepython.org/blog/2016/12/20/instagram-filters-python.html