提出需求
做图像处理时,有时会遇到换色问题,就例如下面图片(网上找的小姐姐图片,侵删)的背景图并不喜欢,我想换成蓝色的要怎么做呢这时候肯定会有小伙伴会想到PS,PS 工具当然是首选,但是如果有大量图片需要更换的时候 PS 显然并不是一个好方案,这时候可以借助 Python 来实现
原理讲解
在换色之前,需要了解一下图片成像编码知识,平常我们常用到的图片编码都为 RGB
格式,是由R、G、B
三通道所组的
每个通道的取值范围在 0-255
之间,而这三通道值的任意组合可以组成色板上的任意颜色,当R、G、B
为 [0,0,0] 时是黑色,[255,255,255]
是是白色,一幅图像的呈像原理就是由不同像素值按一定的顺序排列也就是说 更换图像颜色的本质就是改变指定色素值即可这里多说一嘴,平常我们会遇到一些透明图片,透明的图片相对正常 RGB
图片多加了一个通道,多出来的通道取值范围也是在0-255
之间用,于控制图像的透明程度 ;数值越大,越不透明;这种模式在 PIL 库中叫做 RGBA
格式
代码实操
下面进入代码部分,这里首先利用图像库要读取一幅图片(这里我用的是 PIL),把图像格式转化为数组类型,方便后面更改像素值
from PIL import Image
import numpy as np
img = Image.open('E:/23.png')
arr = np.asarray(img)
#打印数组的形状
print('数组的维度尺寸为:')
print(arr.shape)
print('\n')
print('打印像素值')
for i in arr:
for j in i:
print(j)
从结果中,我们可以了解到图片的背景像素为 [255 255 255]
,我们知道背景像素值之后接下来就是把我们想要替换的像素值把它替换掉即可
Snipaste_2020-03-21_23-20-30.jpg替换过程中,先用最传统有效的递归方法,用两个 for
循环 对像素值做个判断,像素值如果是[255 255 255]
就进行更换,否则不做修改,这里用 [0 0 255]
蓝色作为更换背景,
start_time = time.time()
#对原数组进行copy,源于原数组不可更改;
arr1 =arr.copy()
for i in arr1:
for j in i:
if (j[0] ==255and j[1]==255and j[2]==255):
#满足条件进行像素值更换
j[0] = 0
j[1] = 0
j[2] = 255
print('迭代法更换用时{}s'.format(time.time()-start_time))
plt.imsave('E:/1235.png',arr1)
利用 for 循环进行逐像素修改也可以,但是因为是一个一个像素地修改因此会出现一个问题就是耗时 ,图像分辨率越大,这个劣势越明显,因为是数组修改因此我们可以利用 Numpy
来进行操作;借助 Numpy
先把三通道分离,然后相加得到一个新的数组,把需要更改的 R、G、B三通道值也相加起来得到一个特征值,利用 Numpy 数组的布尔值与特征值之间的条件筛选 对数组中的值进行更新(通道相加和等于特征值就替换,否则Pass ),完整代码如下:
#需要更换的像素值
scr_img = [255,255,255]
#新的像素值
new_img = [0,0,255]
start_time = time.time()
#三通道分离
r_img,g_img,b_img = arr[:,:,0].copy(),arr[:,:,1].copy(),arr[:,:,2].copy()
#三通道值相加形式变成单通道,组合在一起,进行编码
img = r_img + g_img + b_img
value_const = scr_img[0] + scr_img[1] + scr_img[2]
#符合条件进行更改像素值
r_img[img == value_const] = new_img[0]
g_img[img == value_const] = new_img[1]
b_img[img == value_const] = new_img[2]
#numpy三通道合并
arr1 = np.dstack([r_img,g_img,b_img])
print('通道法用时时间:{}'.format(time.time()-start_time))
plt.imsave('E:/2315.png',arr1)
最后我们可以看一下结果,因为找的图片中,小姐姐头像周围像素并不全是 [255 255 255](纯白)
,所以替换出来的图片会出现瑕疵,但总体效果还是比较不错的
之所以引出第二种方法,是得益于 Numpy 数组操作的高性能运算,这里列出了两种方法在运行时所耗时间,Numpy 通道法不足 0.08s,而迭代法长达 1.3s ,相对来说还是通道法性能更好一点