图像色彩知识及python实现图片色调转换
图像色彩知识
- 首先我们讲解下图像的一种表示方法:RGB
- 其次我们再了解下图像的另一个表示:HSB,也叫作HSV
RGB
RGB,是指颜色的三原色,分别代表红色(red),绿色(green),蓝色(blue),用这是三种颜色混合可以表示任意的颜色。RGB是根据颜色发光的原理来设计的,比如这里有红绿蓝三道光,当三束光混合在一起的时候,其呈现的最终的光效颜色就取决于这三种原色光的强弱了。
- 比如说红光最强,绿光和蓝光几乎没有,那么最后结果肯定呈现为红光了。
- 如果蓝光很弱,红光和绿光非常强,那么结果就是红光和绿光的混合光,也就黄光。
- 如果红绿蓝三种光都非常强,那么就会呈现三种光的混合光,也就是白光。
- 如果三种光都非常弱,那就几乎没有光,自然就是黑色了。
混合光图
这是一张平面图,没有很好地体现三原色的混合关系,可以看下面的三维立体图。
实际上可以对 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 都是没有的,呈现黑色。
因此,通过这三个数值,我们就可以实现任何颜色的表示了。
HSB
如果我们要做一张图片的色彩的转换,了解RGB还不足以支撑我们去完成这个任务。因为要对一张图片实现色彩的转换,需要调节的是色相。那么色相是什么呢?它就是我们将要谈到的HSB中的H了。
HSB,又叫做 HSV,其实也是三个参数,分别是色调(Hue)、饱和度(Saturation)、明度(Brightness),在 HSV 中 V 代表 Value,也是明度(Brightness)的意思。通过这三个值,我们同样可以表示任意的颜色。
首先我们看看 HSV (HSB) 颜色模型的坐标轴图吧,它可以用这么一个锥形的坐标来表示:
色调,Hue,它是一种角度度量,就是图中 Hue 所示的角度,绕圆锥体一周。它的值是从红色开始逆时针方向计算的,红色是 0,绿色是 120,蓝色为 240,形成一个红绿蓝三原色组成的色带。
饱和度,Saturation,它是图中 Saturation 所示的方向,由最上面圆的圆心向外扩散。它表示颜色接近光谱色的程度。一种颜色,可以看成是某种光谱色与白色混合的结果。其中光谱色所占的比例愈大,颜色接近光谱色的程度就愈高,颜色的饱和度也就愈高。饱和度高,颜色则深而艳。光谱色的白光成分为 0,饱和度达到最高。通常取值范围为 0-100,值越大,颜色越饱和。也就是说图中,圆心处饱和度为 0,越靠近边,饱和度越高,最高达到 100。
明度,Brightness,它表示颜色明亮的程度,对于光源色,明度值与发光体的光亮度有关,对于物体色,此值和物体的透射比或反射比有关。通常取值范围为 0-100,0 对应黑色,100 对应白色。
另外大家如果用到取色板的话,它是这样子的:
你可以看到左边有个大的矩形,右边有个竖直的彩虹带。由于刚才所说的圆锥坐标不好表示,在调色板里面就把圆锥坐标拆成了两部分。
- 右边的彩虹带就可以选择色调 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 中的 colorsys 模块,就实现了 rgb_to_hsv 和 hsv_to_rgb 算法,我们可以直接使用。
python实现图片色调转换
了解了以上内容之后,我们就可以使用python来实现色调转换了。我们已经知道要实现图片的色调转换应该改什么内容了,那就是修改 HSV 中的 H 值,通过不同的 H 值我们就可以将图片转换为不同的色调了。
具体思路
- 首先获取图像每个像素的的 RGB 色值。
- 将 RGB 色值转化为 HSV 色值。
- 调整 HSV 色值中的 H。
- 将 HSV 色值转回 RGB 色值。
- 输出图像。
一共就是这么五步,这里的两个转换色值的操作我们就可以借助于 colorsys 模块。另外需要安装一个 pillow 模块:
!pip3 install pillow
安装完毕之后,我们准备任意一张图片,然后实现上面的五个步骤。
最终的代码实现如下:
import colorsys
from PIL import Image
# 输入文件
filename = '640_6.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 值进行更改,就会呈现不同的颜色风格了,就像文中开头所示的一样。
处理透明像素
上面的算法仅仅考虑了 RGB,如果有些图包含了透明像素,上面的程序对于透明像素是无法处理的,最后输出的结果会带有某种颜色的背景。
对于透明像素的处理,我们可以增加一个维度的值,就是 A,即 Alpha 透明度。
所以使用 RGBA 和 HSV 的转换我们就可以实现透明像素的处理了,代码实现如下:
import colorsys
from PIL import Image
# 输入文件
filename = '640_6.jpg'
# 目标色值
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)