说明:最近在看梵高的画册,我手上的这本画册(《文森特·梵高》杨建飞 主编)书中说,梵高用的颜料里有不耐久的合成颜料,原本的紫色褪成了我们现在所看到的灰蓝色。于是我想,能不能用程序将画中的颜色还原成原本的紫色。
盛开的桃花,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 = 0total_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 += 1print('生成进度:%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 += 1print('生成进度:%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 = 0total_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 += 1print('生成进度:%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程序对图片中的颜色进行转换