最近遇到了一个需求,就是对图片进行色彩风格转换,让一个物体可以以各种不同的色彩来呈现。

比如一个红色的苹果,我想把它转化成绿色,这可怎么办呢?本来想的解决方案是先识别边界,然后对边界内区域进行色彩替换或者填充,这样整个流程就分成了两步,首先需要进行边界判断,有了边界之后才能对某些区域颜色进行替换填充,填充的区域还是不规则的,填充的颜色也需要根据实际的情况来变化,同时还要兼顾阴影、高光等等的处理,想想真是头大。

SRE实战 互联网时代守护先锋,助力企业售后服务体系运筹帷幄!一键直达领取阿里云限量特价优惠。

如何用 Python 给照片换色 Python 第1张如何用 Python 给照片换色 Python 第2张

然后我就突然想到了之前学 PS 的时候,曾经做过对某一张图片进行色调替换,我还记得当时调的内容是「色相」,通过调节这个值可以实现各种色调的转换。这之后我就心想,能不能用程序来做这件事呢?

于是我就开始了对图像色彩的一些研究,研究完了之后就实现了用 Python 来更换图片色调的功能,将这篇文章顺便记录下来。

先给大家看看效果吧,就比如我从百度上随便搜一张图,比如热气球吧,是这样子:

如何用 Python 给照片换色 Python 第3张如何用 Python 给照片换色 Python 第4张

原图

我把它转成红色、黄色、蓝色、紫色,基本就是这个样子:

如何用 Python 给照片换色 Python 第5张如何用 Python 给照片换色 Python 第6张

红色风格

如何用 Python 给照片换色 Python 第7张如何用 Python 给照片换色 Python 第8张

黄色风格

如何用 Python 给照片换色 Python 第9张如何用 Python 给照片换色 Python 第10张

绿色图片

如何用 Python 给照片换色 Python 第11张如何用 Python 给照片换色 Python 第12张

蓝色图片

这里的图片都是经过 Python 自动转换算法实现的,主要调节的就是色相。其实代码实现是比较简单的,但在实现之前需要了解一些图像色彩知识。了解了这些之后我们再实现才会更加游刃有余。

下面我们就首先来了解一下图像色彩的基本知识,然后用 Python 实现色调转换算法吧。

RGB

首先让我们来了解下颜色的三原色,RGB。

RGB,其实就是三种颜色,分别代表红色(Red)、绿色(Green)、蓝色(Blue),用这三种原色颜色混合可以表示任意的颜色。RGB 是根据颜色发光的原理来设计的,比如这里有红绿蓝三道光,当三束光混合在一起的时候,其呈现的最终的光效颜色就取决于这三种原色光的强弱了。

•比如说红光最强,绿光和蓝光几乎没有,那么最后结果肯定呈现为红光了。•如果蓝光很弱,红光和绿光非常强,那么结果就是红光和绿光的混合光,也就黄光。•如果红绿蓝三种光都非常强,那么就会呈现三种光的混合光,也就是白光。•如果三种光都非常弱,那就几乎没有光,自然就是黑色了。

如果大家了解过 RGB 的一些知识的话,想必也见过这张混合光图:

如何用 Python 给照片换色 Python 第13张如何用 Python 给照片换色 Python 第14张

但是这仅仅是一张平面图,并不能很好地体现三原色的混合关系,大家可以看这张图:

如何用 Python 给照片换色 Python 第15张如何用 Python 给照片换色 Python 第16张

 

实际上可以对 RGB 色值建立一个三维坐标系,坐标原点就在上图中所看不见的那个正方体顶点,像左延伸为 R 红色轴,向右延伸为 B 蓝色轴,向上延伸为 G 绿色轴。

RGB 在进行色彩表示时使用了 256 阶,也就是从 0-255,可以由一个字节来表示。数值越大,RGB 三个轴,每个轴对应的数值越大,就代表其亮度越高,最亮就对应着 255,最暗就对应着 0。三个轴上的三个数字联合起来可以用来表示一个颜色。

例如:

•(255, 255, 255) 就代表 RGB 都是满的,组成白色。•(255, 255, 0) 就代表 R 红色是满的,G 绿色是满的,B 蓝色是没有的,红色和绿色混合为黄色,所以它就表示黄色。•(0, 0, 0) 就代表 RGB 都是没有的,呈现黑色。

因此,通过这三个数值,我们就可以实现任何颜色的表示了。

HSV

但了解了 RGB 显然不能应对我们的需求,它和色相也没有什么关系,所以这里我们还需要研究另外一个颜色模型,叫做 HSV。

HSV,又叫做 HSB,其实也是三个参数,分别是色调(Hue)、饱和度(Saturation)、明度(Brightness),在 HSV 中 V 代表 Value,也是明度的意思。通过这三个值,我们同样可以表示任意的颜色。

首先我们看看 HSV (HSB) 颜色模型的坐标轴图吧,它可以用这么一个锥形的坐标来表示:

如何用 Python 给照片换色 Python 第17张如何用 Python 给照片换色 Python 第18张

色调,Hue,它是一种角度度量,就是图中 Hue 所示的角度,绕圆锥体一周。它的值是从红色开始逆时针方向计算的,红色是 0,绿色是 120,蓝色为 240,形成一个红绿蓝三原色组成的色带。

饱和度,Saturation,它是图中 Saturation 所示的方向,由最上面圆的圆心向外扩散。它表示颜色接近光谱色的程度。一种颜色,可以看成是某种光谱色与白色混合的结果。其中光谱色所占的比例愈大,颜色接近光谱色的程度就愈高,颜色的饱和度也就愈高。饱和度高,颜色则深而艳。光谱色的白光成分为 0,饱和度达到最高。通常取值范围为 0-100,值越大,颜色越饱和。也就是说图中,圆心处饱和度为 0,越靠近边,饱和度越高,最高达到 100。

明度,Brightness,它表示颜色明亮的程度,对于光源色,明度值与发光体的光亮度有关,对于物体色,此值和物体的透射比或反射比有关。通常取值范围为 0-100,0 对应黑色,100 对应白色。

另外大家如果用到取色板的话,它是这样子的:

如何用 Python 给照片换色 Python 第19张如何用 Python 给照片换色 Python 第20张

你可以看到左边有个大的矩形,右边有个竖直的彩虹带。由于刚才所说的圆锥坐标不好表示,在调色板里面就把圆锥坐标拆成了两部分。

•右边的彩虹带就可以选择色调 Hue,拖动彩虹带的箭头位置就可以调整 Hue 的值。•左边的正方形就和饱和度 Saturation、明度 Brightness 有关,横坐标表示饱和度 Saturation,纵坐标表示 Brightness,从左到右,饱和度 Saturation 变高,从下到上,明度 Brightness 变高。•左下角,由于饱和度 Saturation 和明度 Brightness 都为 0,就呈现黑色。•左上角,由于饱和度 Saturation 为 0,所以相当于没有颜色,颜色最浅,但是它的明度 Brightness 达到了最高,所以它就显示没有颜色的明度最高的色,即白色。•右上角,由于饱和度 Saturation 和明度 Brightness 都为最高,那就显示最纯的 Hue 值。

这也就是 HSB 颜色模型的原理。

同样地,我们可以用 HSB 的三个值来表示任意的颜色,因此 HSB 也成为表示颜色的基本标准之一。

HSB 和 RGB 的转换

HSB 和 RGB 都能表示一个颜色,它们之间也是可以相互转换的,可以一一对应。

他们之间的转换逻辑这里就不再专门对其公式展开详解了,公式总结如下:

如何用 Python 给照片换色 Python 第21张如何用 Python 给照片换色 Python 第22张

RGB to HSB

如何用 Python 给照片换色 Python 第23张如何用 Python 给照片换色 Python 第24张

HSV to RGB

大家如果感兴趣想要研究的话也可以去:https://www.rapidtables.com/convert/color/hsv-to-rgb.html 来了解一下详情,这里还有详细的转换表格以及实时转换实现。

另外对于它们之间的转换算法,很多库都已经封装好了。我们可以直接调用,比如 Python 中的 colorsys 模块,就实现了 rgb_to_hsv 和 hsv_to_rgb 算法,我们也可以直接使用。

色调转换

了解了以上内容之后,我们就可以使用程序来实现色调转换了。相比我们已经知道应该改什么内容了,那就是修改 HSV 中的 H 值,通过不同的 H 值我们就可以将图片转换为不同的色调了。

具体思路是怎样的呢:

•首先获取图像每个像素的的 RGB 色值。•将 RGB 色值转化为 HSV 色值。•调整 HSV 色值中的 H。•将 HSV 色值转回 RGB 色值。•输出图像。

一共就是这么五步,这里的两个转换色值的操作我们就可以借助于 colorsys 模块。另外需要安装一个 pillow 模块:

pip3 install pillow
如何用 Python 给照片换色 Python 第25张

安装完毕之后,我们准备任意一张图片,然后实现上面的五个步骤。

最终的代码实现如下:

import colorsysfrom PIL import Image
# 输入文件filename = 'input.jpg'# 目标色值target_hue = 0
# 读入图片,转化为 RGB 色值image = Image.open(filename).convert('RGB')
# 将 RGB 色值分离image.load()r, g, b = image.split()result_r, result_g, result_b = [], [], []# 依次对每个像素点进行处理for pixel_r, pixel_g, pixel_b in zip(r.getdata(), g.getdata(), b.getdata()):    # 转为 HSV 色值    h, s, v = colorsys.rgb_to_hsv(pixel_r / 255., pixel_b / 255., pixel_g / 255.)    # 转回 RGB 色系    rgb = colorsys.hsv_to_rgb(target_hue, s, v)    pixel_r, pixel_g, pixel_b = [int(x * 255.) for x in rgb]    # 每个像素点结果保存    result_r.append(pixel_r)    result_g.append(pixel_g)    result_b.append(pixel_b)
r.putdata(result_r)g.putdata(result_g)b.putdata(result_b)
# 合并图片image = Image.merge('RGB', (r, g, b))# 输出图片image.save('output.png')

具体的实现在代码注释里面已经很清楚了。

在这里我们将 target_hue 定义为 0。通过前文我们知道,Hue 为 0 代表红色,120 代表绿色,240 代表蓝色。我们可以自定义 0-355 这 360 个数值,实现不同的色调转换。

如果想输出其他颜色的图片,就把 target_hue 这个值改一下就好了。

看看最后的运行效果,输入是一张原图:

如何用 Python 给照片换色 Python 第26张如何用 Python 给照片换色 Python 第27张

所以最后的输出效果就是如下的结果:

如何用 Python 给照片换色 Python 第28张如何用 Python 给照片换色 Python 第29张

 

如果将代码中的 target_hue 值进行更改,就会呈现不同的颜色风格了,就像文中开头所示的一样。

 

比如把飞猪的 Logo 由黄色变为红色,都是可以做到的:

 

如何用 Python 给照片换色 Python 第30张如何用 Python 给照片换色 Python 第31张

如何用 Python 给照片换色 Python 第32张如何用 Python 给照片换色 Python 第33张

处理透明像素

上面的算法仅仅考虑了 RGB,如果有些图包含了透明像素,上面的程序对于透明像素是无法处理的,最后输出的结果会带有某种颜色的背景。

对于透明像素的处理,我们可以增加一个维度的值,就是 A,即 Alpha 透明度。

所以使用 RGBA 和 HSV 的转换我们就可以实现透明像素的处理了,代码实现如下:

import colorsysfrom PIL import Image
# 输入文件filename = 'input.png'# 目标色值target_hue = 0
# 读入图片,转化为 RGB 色值image = Image.open(filename).convert('RGBA')
# 将 RGB 色值分离image.load()r, g, b, a = image.split()result_r, result_g, result_b, result_a = [], [], [], []# 依次对每个像素点进行处理for pixel_r, pixel_g, pixel_b, pixel_a in zip(r.getdata(), g.getdata(), b.getdata(), a.getdata()):    # 转为 HSV 色值    h, s, v = colorsys.rgb_to_hsv(pixel_r / 255., pixel_b / 255., pixel_g / 255.)    # 转回 RGB 色系    rgb = colorsys.hsv_to_rgb(target_hue, s, v)    pixel_r, pixel_g, pixel_b = [int(x * 255.) for x in rgb]    # 每个像素点结果保存    result_r.append(pixel_r)    result_g.append(pixel_g)    result_b.append(pixel_b)    result_a.append(pixel_a)
r.putdata(result_r)g.putdata(result_g)b.putdata(result_b)a.putdata(result_a)
# 合并图片image = Image.merge('RGBA', (r, g, b, a))# 输出图片image.save('output.png')

在这里就是增加了对透明像素的处理,在图像 convert 和 merge 的时候都使用 RGBA 模式,就可以保留原有图片中的透明像素了。

以上便是使用 Python 程序自动调整色调的实现。

其他应用

另外对于图像调色的应用还有很多,比如我们可以不直接指定 Hue 的值,而是将 Hue 的值在原来的基础上增加或减少,则可以实现彩虹变色,如:

原图是这样子:

如何用 Python 给照片换色 Python 第34张如何用 Python 给照片换色 Python 第35张

 

经过处理之后我们可以得到这样的效果:

如何用 Python 给照片换色 Python 第36张如何用 Python 给照片换色 Python 第37张

 

 
扫码关注我们
微信号:SRE实战
拒绝背锅 运筹帷幄