利用MATLAB来进行批量图片颜色匹配

Edit: FML,回到家才发现用MATLAB处理出来的颜色和死了妈似的,学校的垃圾TN显示器太坑了……下文已经尽量修正。

在图像处理尤其是对冻鳗扫图处理时,“颜色匹配”是一个经常需要进行的操作。因为印刷和扫描的技术和载体所限,扫描出来的结果经常和艺术家想呈现的有很大偏差。当然,最理想的纠正方式应该是从源头上去校准扫描仪,但是很多时候这是不现实的(例如,图片来自网络)。

在有官方提供的Sample的情况下(需要注意:在官网、亚马逊等地方获取样图时,诸如CMYK硬拉成RGB之类的错误屡见不鲜,一定要避免从源头上就选了个错误图片做reference),利用数字原档来校准、还原颜色就成为了可能。

Photoshop有个内建的匹配颜色(英文:Match color)功能:

2017-04-21

可以看到,功能还算丰富。除了上面的滑块选项以外,你可以选择用于计算的选取范围(包括源和目标的都能选),最后计算出来的映射/变换则可以应用到全图(最上面那个现在灰色的选项),而不是仅限于选区。

PS的颜色匹配效果还凑合,但是为了追求最佳效果,在匹配之前一定要尽量把图像对齐。如果两者的范围不一致,也要把多出的部分裁掉。还好,这个过程在PS里也很简单:

  1. 导入两张图片为两个不同的图层。你的扫图置于底层(底层是不会变形的),样图置于上层。注意,不能是“背景”,所以如果是的话,双击一下转换为图层先。
  2. 选中两个图层,然后点Edit->Auto align layers。
    • Projection选Auto就好,下面两个透镜扭曲修正的选项不用勾。确定之后过几秒,应该就对齐了。PS这个算法不是那么完美,尤其是在图像有缩放的时候做不到pixel级对齐,不过对于我们这里的应用足够了。
    • 如果样图太小,可能会无法自动对齐。这种情况下,预先用自由变换(Ctrl+T)调整一下样图的大小吧。记住拖拽时按住Shift可以保持宽高比。你不用刻意调整和你的扫图完全一样大,差不多随便拖一下就行,因为自动对齐时PS会自动帮你做。
  3. 把两者不一致的地方裁剪掉。也很简单:ctrl+点击第一个图层,会建立一个和该图层一样大的选区;然后Crop就好。如法炮制第二个图层。这样,剩下的部分就是两个图层都有的部分。如果只有一个图像上有水印、文字之类的,最好把那部分也裁掉,否则影响颜色匹配。

接下来,只要进行颜色匹配就行了,注意源和目标别选反就行。

但是这里一个问题:如果你有一组扫图,其色彩基本类似,理论上而言,你应该只需要找其中一张的sample匹配,然后把那个transofrmation存下来,适用到其他图即可。可惜,PS并不提供这样的功能。当然,有一个笨方法:先把所有图片拼到一起,然后只选中其中一张图的区域进行颜色匹配,但是应用时忽略选区。但是这个方法实在听上去太蠢了还不能自动化。

于是,我转而追求其他的办法。MATLAB的Image processing toolbox集成了许多现成的图像处理函数,当年学DIP的时候也用得不少,于是我第一个就想到它。随便找一下,这个Histogram matching(imhistmatch)似乎就符合我的需要。

虽然不知道PS的图像匹配是什么原理(毕竟是商业机密),但是我约莫着其实也八九不离十也就是histogram matching。说白了,就是找到一个映射,使得变换后的直方图累计分布函数(CDF)和reference相似。我先试了下MATLAB这函数的效果,嘿还不错,甚至可以说比PS还强一点:

compare

(点击大图,注意观察云彩的颜色)

修正!PS的效果其实秒杀MATLAB的…只是由于学校的垃圾屏幕太烂我才没看出来orz 不过后面的内容姑且保留了,从RGB改用YCbCr效果会好很多。

既然我们找到这个函数,那现在就只需要剖析一下他(选中函数ctrl+D可以看源代码),看看里面到底是怎么运行的,这样我们就能把那个transofrmation给导出来。

结果这个imhistmatch的代码相当简单,其核心部分居然是调用另外一个函数——histeq,即直方图均衡化(Histogram equalization)。其实也很好理解,直方图均匀化本来就是直方图匹配的一个特例——即匹配出来的目标CDF是一条直直的斜线。在MATLAB里,两者都集成到了histeq而不是imhistmatch里可能也是因为前者叫起来更顺口吧。histeq除了直接均衡化之外,还可以手动设一个作为目标/参照的直方图来match,所以这里就是把我们的目标/参照的直方图拿来作为input了。需要注意histeq只支持单通道(准确地说是imhist只支持单通道),所以需要手动写个矩阵运算或者循环来分别搞RGB。

那么,histeq里面的原理又是怎样呢?文档里可以看到,其实这个函数本身就可以返回一个变换T给你了。这个变换T的形式是1×256个double array,每个分别是一个0-1闭区间内的值。用法嘛其实不言而喻了,就是用每个位置对应的double去乘原图的[0, 255]的强度值,得出来的就是output,其实和PS里的Curve是一模一样的。不过,这里可以偷个懒,PS有个内部函数grayxformmex就是帮你算这个的,会自动处理所有支持的图像数据类型(从double,uint8到uint16),而且是用C/C++写的速度更快,推荐直接用它。不过这个内部函数不在path里所以不能直接用,你得先复制grayxformmex.mexw64到你得工作目录才行(是编译过的文件,源代码如果感兴趣的话,网上能找到一个老版本的)。

OK,有了这一切东西,我们就可以很简单地写一个script:

我尽量模仿了imhistmatch的格式,所以应该对多种数据类型都能处理。不过可能数据验证方面的鲁棒性会差一点。最后%%后的部分是批量处理其他文件的代码。

由于前面红字所说的原因,我紧急改用了YCbCr channel来进行处理,效果确实好很多,但是天空的颜色果然就不对了orz,而且还是不如PS的…HSV我也试了,结果会出现奇怪的色块,懒得深究了。上面的代码已经更新,可以选择用RGB(默认),YCbCr或是HSV(开关在最上面)。对比一下三种的效果:

compare2.png

不过需要注意,MATLAB的算出来的结果颜色有点离散,纯色区域多多少少有些banding。这点PS的祖传算法就略胜一筹了,出来的图像的直方图还是非常连续的,没有太多的banding的问题。不过后来发现其实只有RGB模式会有这个问题,换成YCbCr就基本无大碍了。无论如何,这里推荐俩解决办法:方法1,先跑色彩匹配,再进行其他去噪步骤,会很大地由于去噪中的模糊等因素缓解这个问题;方法2,加点白噪声先,至少观感会强很多。这两种方法可以结合使用。

另外,PS的明度控制明显要好很多,我用YCbCr跑出来的结果高光区域都变淡了(比如有几页的纯白背景)。

2017-05-21补充:

后来我又多次使用上文所述的方法进行校色。我发现这个方法其实还是相当可行的,在很多时候甚至比PS的结果要好。不过要注意的是,即使是用YCbCr,有时候(很少见)也会出现和HSV一样的色块问题,所以出了结果一定要检查一下。

Advertisements

One thought on “利用MATLAB来进行批量图片颜色匹配

  1. 一次性读了几个小时 这种小清新风格的个人blog 有翔实的技术流的味道同时又不枯燥
    请烈君千万要保持更新下去啊 🙂

发表评论

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / 更改 )

Twitter picture

You are commenting using your Twitter account. Log Out / 更改 )

Facebook photo

You are commenting using your Facebook account. Log Out / 更改 )

Google+ photo

You are commenting using your Google+ account. Log Out / 更改 )

Connecting to %s