说明:最近在看梵高的画册,我手上的这本画册(《文森特·梵高》杨建飞 主编)书中说,梵高用的颜料里有不耐久的合成颜料,原本的紫色褪成了我们现在所看到的灰蓝色。于是我想,能不能用程序将画中的颜色还原成原本的紫色。
盛开的桃花,1888年
生成图片
首先,写一段代码,用来读取图片,生成一个副本,代码如下:
from PIL import Image, ImageDraw
import numpy as np
# 生成转换后的画
def generate_img(in_path, out_path):
# 打开图片
im = Image.open(in_path)
# 生成后的图片大小,使用im.size[0],im.size[1],即原图大小
new_im_size = np.array([im.size[0], im.size[1]]).astype(int)
# 生成图片的背景颜色
bg_color = "black"
# 生成图片
im_out = Image.new("RGB", tuple(new_im_size), bg_color)
# 获取图片的颜色
im_color = np.array(im)
# 生成图片
draw = ImageDraw.Draw(im_out)
# 用于统计进度
count = 0
total_pixels = im.size[0] * im.size[1]
# 遍历图片的每个像素点,将颜色填充到新图片中
for i in range(im.size[0]):
for j in range(im.size[1]):
color = tuple(im_color[j, i])
draw.point((i, j), fill=color)
count += 1
print('生成进度:%d%%' % ((count / total_pixels) * 100))
# 保存图片
im_out.save(out_path)
if __name__ == "__main__":
in_path = r'C:\Users\10765\Desktop\1.jpg'
out_path = r'C:\Users\10765\Desktop\2.jpg'
# 读取图片,并将图片中的颜色转换
generate_img(in_path, out_path)
运行
可在桌面上生成一张几乎一模一样大小的图片;
颜色转换
接着,我们就是对颜色进行处理,也就是上面for循环里的这行代码;
color = tuple(im_color[j, i])
我们要写一个方法,对这里面的色值进行转换,我的思路是这样的,首先找到紫色和灰蓝色的RGB色值范围,然后对图片中的色值进行判断,如果是在灰蓝色的色值范围内,则对该RGB色值进行映射,映射到紫色的RGB色值范围内。通过问GPT,可知两种颜色的色值范围如下:
-
紫色:128-255,0-20,128-255;
-
灰蓝色:100-180,120-200,150-230;
代码如下:
# 色值转换,灰蓝色 => 紫色
def convert_color(gray_blue_rgb):
# 灰蓝色范围
gray_blue_range = [(100, 180), (120, 200), (150, 230)]
# 紫色范围
violet_range = [(128, 255), (0, 20), (128, 255)]
# 检查输入的 RGB 值是否在灰蓝色范围内
for i in range(3):
if not (gray_blue_range[i][0] <= gray_blue_rgb[i] <= gray_blue_range[i][1]):
return tuple(gray_blue_rgb)
# 根据灰蓝色范围的值转换到紫色范围
violet_color = []
for i in range(3):
gray_blue_min, gray_blue_max = gray_blue_range[i]
violet_min, violet_max = violet_range[i]
# 灰蓝色范围内的值映射到紫色范围
violet_val = int(
(gray_blue_rgb[i] - gray_blue_min)
/ (gray_blue_max - gray_blue_min)
* (violet_max - violet_min)
+ violet_min
)
violet_color.append(violet_val)
return tuple(violet_color)
为了方便理解,举个例子。如果一个A色值区间是 [20,100],另一个B色值区间是[100,180],现在一个A色值是80,需要转为B色值,过程如下:
-
(80 - 20)/ (100 - 20) * (180 - 100) + 100 = 160
-
(当前色值 - A色值范围的最低值) / (A色值的区间长度,即 100 - 20) * (B色值的区间长度,即 180 - 100) + B色值范围的最低值
160,就是A色值在B色值中的值;
代码写好了,在将颜色填充到图片前做一层转换即可,如下:
# 遍历图片的每个像素点,将颜色填充到新图片中
for i in range(im.size[0]):
for j in range(im.size[1]):
# 将颜色转换,然后填充到新图片中
color = tuple(convert_color(im_color[j, i]))
draw.point((i, j), fill=color)
count += 1
print('生成进度:%d%%' % ((count / total_pixels) * 100))
启动,看下效果,有点妖娆,色值范围没控制好;
我之前选的色值如下:
-
紫色:80-150,100-180,120-200;
-
灰蓝色:150-220,0-80,150-220;
转换后的效果如下:
完整代码
from PIL import Image, ImageDraw
import numpy as np
# 生成转换后的画
def generate_img(in_path, out_path):
# 打开图片
im = Image.open(in_path)
# 生成后的图片大小,使用im.size[0],im.size[1],即原图大小
new_im_size = np.array([im.size[0], im.size[1]]).astype(int)
# 生成图片的背景颜色
bg_color = "black"
# 生成图片
im_out = Image.new("RGB", tuple(new_im_size), bg_color)
# 获取图片的颜色
im_color = np.array(im)
# 生成图片
draw = ImageDraw.Draw(im_out)
# 用于统计进度
count = 0
total_pixels = im.size[0] * im.size[1]
# 遍历图片的每个像素点,将颜色填充到新图片中
for i in range(im.size[0]):
for j in range(im.size[1]):
# 将颜色转换,然后填充到新图片中
color = tuple(convert_color(im_color[j, i]))
draw.point((i, j), fill=color)
count += 1
print('生成进度:%d%%' % ((count / total_pixels) * 100))
# 保存图片
im_out.save(out_path)
# 色值转换,灰蓝色 => 紫色
def convert_color(gray_blue_rgb):
# 灰蓝色范围
gray_blue_range = [(100, 180), (120, 200), (150, 230)]
# 紫色范围
violet_range = [(128, 255), (0, 20), (128, 255)]
# 检查输入的 RGB 值是否在灰蓝色范围内
for i in range(3):
if not (gray_blue_range[i][0] <= gray_blue_rgb[i] <= gray_blue_range[i][1]):
return tuple(gray_blue_rgb)
# 根据灰蓝色范围的值转换到紫色范围
violet_color = []
for i in range(3):
gray_blue_min, gray_blue_max = gray_blue_range[i]
violet_min, violet_max = violet_range[i]
# 灰蓝色范围内的值映射到紫色范围
violet_val = int(
(gray_blue_rgb[i] - gray_blue_min)
/ (gray_blue_max - gray_blue_min)
* (violet_max - violet_min)
+ violet_min
)
violet_color.append(violet_val)
return tuple(violet_color)
if __name__ == "__main__":
# 输入图片路径
in_path = r'C:\Users\10765\Desktop\1.png'
# 输出图片路径
out_path = r'C:\Users\10765\Desktop\2.png'
# 读取图片,并将图片中的颜色转换
generate_img(in_path, out_path)
总结
本文介绍了如何使用Python程序对图片中的颜色进行转换