解决问题的艺术:半小时编程实现照片的反转负冲特效
如何直接有效的解决问题是一门艺术。我们是做产品、做系统、做服务的,不是玩技术的,需要做的是在最短的时间内以最有效的方式来解决工作中面对的难题。就在刚才,用了半小时不到时间,俺用一种极其简单、直接、有效、霸道的方法解决了照片的反转负冲特效问题。这种解决问题的思路值得总结推广。
反转负冲(反转片)是一种摄影拍摄技术,它通过拍摄后反转冲洗,得到色彩鲜艳的照片,非常养眼。现在都是数码相机了,可以通过图像处理算法来模拟反转片的效果,比如说,“光影魔术手”就提供了5种反转片特效:(1)素淡人像;(2)淡雅色彩;(3)真实色彩;(4)艳丽色彩;(5)浓郁色彩。且看(5)浓郁色彩的效果:
左边是处理前的图像,右边是处理后的图像,可以看到,处理后的图像颜色格外饱满。我目前开发的一款图像处理软件,一个难题就是处理后的照片色彩会变暗,这个反转负冲特效的效果正是我所需要的。
且看“光影魔术手”怎样描绘“反转片”功能——
“模拟反转片的效果,是《光影魔术手》最重要的功能之一。经处理后照片反差更鲜明,色彩更亮丽。算法经多次改良后,暗部细节得到最大程度的保留,高光部分无溢出,红色还原十分准确,色彩过渡自然艳丽,绝无色斑!提供多种模式供用户选择,其中人像模式对亚洲人的肤色进行了优化,不会出现肤色偏黄现象。独有的暗部增补算法不仅增强了暗部, 同时令高光部分的表现更出色,绝对没有雾感引入。精心设计,专业效果!强烈推荐所有用户使用! ”
下面且看俺怎么在半小时内将这个光影魔术手引以为豪的功能给集成到俺自己的软件中来。
第一步,先查资料。以关键字“反转片”+“算法”在google搜索,搜到的都是说“光影魔术手”这效果多NB多NB的文章,没参考意义。
第二步,在金山词霸网上输入“反转片”,查出来英文单词是 “reversal film”,然后在google scholar中搜索 “reversal film algorithm”,没介绍的。在 google 中搜索“reversal film algorithm”,没有一篇文章讲了实质性内容的。进 google code search 搜索 “reversal film”,没一个有实质性内容的。进 cnki 搜索 “reversal film”和“反转片”,没一个有实质性内容的。这下完蛋了,没参考的,眼看要个屁了。
第三步,反转片这个效果很重要,一定有很多人用 photoshop 处理过。搜“反转片”,专门找介绍怎么用 photoshop 进行反转负冲处理的文章。找到几篇讲如何手动进行反转负冲特效处理的文章,比如这一篇:http://www.windsn.com/article.asp?id=478。这篇文章中的效果是很NB的。通过阅读这篇文章发现,“反转片”仅仅是点操作即可,它与像素的邻域无关,与图像的统计信息无关。
第四步,天空一声巨响,头脑一片闪亮。通过第三步,可以推测,反转负冲实际上只是一个 RGB 空间到 RGB 空间的映射而已。只需要对于RGB空间的任意一个颜色,计算出它所对应的颜色,做一个替换即可。
第五步,如何寻找这个映射呢?利用归约解决问题。
Udi Manber 的《算法引论-一种创造性的方法》第10章《归约》的引言部分讲了这样一个故事——
有人问一个数学家和她的丈夫如下问题:假设你在地下室,想烧开水怎么办?数学家说,她会上到厨房并在那里烧水。她的丈夫回答类似。那人继续问:假设你在厨房,想烧开水怎么办?她丈夫回答说:“这很容易的,将水壶灌满然后点燃煤气。”而数学家回答:“我的答案更简单。到地下室去,在那里我已经知道如何解决这个问题了。”
就让我们回到“地下室”去解决这个问题。前面说过,光影魔术手可以实现反转片的效果。因此,建立一张图片,让这张图片上包含所有的RGB颜色值,然后用光影魔术手去处理这张图片,得到的效果图另存下来,这就得到了一个RGB空间到RGB空间的映射。通常,RGB的每一个Channel是1个Byte,那么将全部的RGB颜色值放到一张图片上去,这张图片就太大了,需要256×256×256个像素。为了减少处理量,对RGB空间进行采样,只选择Channel值为偶数的色彩,这样,就把RGB空间压缩到 128×128×128大小了,也就是一张1024×2048的图片即可。下面是生成算法:
ImageRgb24 img = new ImageRgb24(1024, 2048);
for (int x = 0; x < 128; x++)
{
for (int y = 0; y < 128; y++)
{
for (int z = 0; z < 128; z++)
{
int index = x * 128 * 128 + y * 128 + z;
img[index] = new Rgb24(x * 2, y * 2, z * 2);
}
}
}
将上面的图片存为 bmp 格式:
用 光影魔术手 的5种反转片特效分别进行处理,另存为5个 bmp 文件。每一个bmp文件,代表1种反转片特效,假设某个特效的 bmp 的图储加载在一个名为 img 的 ImageRgb24 中,则 img[color.Red / 2 * 128 * 128 + color.Green / 2 * 128 + color.Blue/2] 就是色彩 color 反转片处理后的色彩了。下面是使用“浓郁色彩”bmp图片映射后的效果图,是不是和光影魔术手处理结果很像呢?
通过对光影魔术手的进一步分析,发现它的反转片特效并不完全是点操作,比如说,截取一张图片的一部分,处理后相同位置的像素和全张图片处理后相同位置的像素并不一样,而是有比较小的差异。可能它根据色彩的概率分布进行了进一步的处理。不过无所谓了,前面这些已经够用了。
想起了读研究生时的一个故事。老师布置了阅读题目,让写一份读书报告。我将那个主题的文章查了几十篇,然后进行资料分类,写出了一篇很漂亮的读书报告。很幸运的被老师选为3份最优秀的读书报告,上讲台去讲。我这份报告写得很好,但是报告里写的内容我是完全不明白,结果讲的一塌糊涂。不一定非要懂才能写得好一份报告,你只需要善于归纳总结即可。
到目前为止,反转片的原理俺依然是毫无头绪,但问题已经解决了——我成功将这一特效用寥寥几行代码实现,集成在我的程序中!解决问题是一门艺术。
==== 完 ====