Python 合并两种变换,组合多个变换可以构建出复杂的映射关系,把原始数据转换为中间值,再生成最终结果。下面介绍如何将颜色截断和映射组合在一起。
在某些问题域中,难以执行截断操作,但在另一些场景中却很简单。例如从9位长的美国邮政编码中截取5位,或者进一步截取3位,来确定此邮编所在的大体地理方位。
对于颜色,可以使用前面讲的比特遮罩技术,将3个8比特值(共24比特,1600万种颜色)转换为3个3比特值(共9比特,512种颜色)。
创建一组颜色映射,其中包含到一组给定颜色的距离,以及对源颜色的截断,如下所示:
bit3 = range(0, 256, 0b100000)
best = (min((euclidean(rgb, c), rgb, c) for c in colors)
for rgb in product(bit3, bit3, bit3))
color_map = dict((b[1], b[2].rgb) for b in best)
通过range
创建对象bit3
,遍历3比特颜色的所有8个值。使用二进制值0b100000
有助于我们更好地理解使用比特的方法,忽略不重要的5比特,只使用最高的3比特。
range
对象与普通可迭代对象不同,可以重复使用,所以这里用product(bit3, bit3, bit3)
表达式生成所有512种颜色组合作为输出颜色。
我们为每个被截断的RGB颜色创建了一个三元组,包含元素:(0)到所有蜡笔颜色的距离,(1)这个RGB颜色本身,(2)代表蜡笔颜色的Color
对象。对该集合求最小值,就得到了与这个被截断RGB颜色最接近的蜡笔颜色对象。
这样就有了从任何被截断RGB颜色到与它最接近的蜡笔颜色的映射关系对象。在查找一个与源色彩最匹配的蜡笔颜色前,首先要对源色彩做截断处理。这里的截断计算与预先计算得到的颜色映射表是对映射技术的组合使用。
实现图像替换的代码如下所示:
mask = 0b11100000
clone = img.copy()
for xy, rgb in pixel_iter(img):
r, g, b = rgb
repl = color_map[(mask&r, mask&g, mask&b)]
clone.putpixel(xy, repl.rgb)
clone.show()
使用PIL库中的putpixel()
函数替换了图片中的像素。遮罩值保留了原始颜色值的最高3个比特,最终生成的颜色是原来颜色集合的一个子集。
可见使用函数式编程工具可以生成简洁明了但低效的算法,所以衡量算法复杂度的主要工具(也称大O分析)对于函数式编程和命令式编程同样重要。
这里的关键问题不是product()
函数低效,而是使用product()
函数创建的算法低效。