在做网站logo的时候,我遇到了一个棘手难以解释的问题:logo图片需要与背景融合在一起,虽然我使用了PNG格式对图片进行编辑,但仍有部分区域呈现白色。于是我又使用了Ps对图片进行二值化处理,但此番操作下来图片却有了许多锯齿,十分难看。
最终我结合一些图像处理的知识,使用Pillow实现了理想的效果,并做了一些扩展研究。
思路
PNG
PNG图像采用RGBA存储图像色彩信息,其中RGB分别指红绿蓝三色光的亮度,A指的是Alpha通道,存储透明度信息,因此PNG图像可以实现半透明效果。
PNG图片上的每一个点都有RGBA这四个数值。
灰度
对于上文的辣种图片是纯黑白的图,所以也可以用灰度来存储它的信息。
总结
由此可以先把上文的图片转成灰度图,获取到每个像素点的灰度值,将其作为Alpha通道的值,RGB色值为纯黑(0, 0, 0)即可实现。
实现
# 导入Pillow
from PIL import Image, ImageDraw
def imGreyAlpha(im):
grey = im.convert('L') # 转成灰度
px = grey.load() # 获取灰度数组
im2 = Image.new(mode="RGBA", size=(w, h), color=(255,255,255,0)) # 新建图片
draw = ImageDraw.Draw(im2) # 获取绘制句柄
w,h = im.size # 获取图片大小
# 遍历像素点
for i in range(w):
for j in range(h):
alpha = px[i, j] # 获取灰度作为alpha值
draw.point((i, j), fill=(0, 0, 0, 255 - alpha)) # 填充像素点
return im2 # 返回图像
# 函数使用示例
demoInput = Image.open('xxx.png') # 打开图像
demoOutput = imGreyAlpha(demoInput) # 丢进函数输出图像
demoOutput.save('ouput.png') # 保存图像
拓展
彩色图
如果仅仅是需要简单的彩色输出,那么只需修改以上代码即可:
# 导入Pillow
from PIL import Image, ImageDraw
def imGreyAlpha(im):
################
color = im.convert('RGB') # 获取原图RGB模式
pxColor = color.load() # 获取色彩数组
################
grey = im.convert('L') # 转成灰度
px = grey.load() # 获取灰度数组
im2 = Image.new(mode="RGBA", size=(w, h), color=(255,255,255,0)) # 新建图片
draw = ImageDraw.Draw(im2) # 获取绘制句柄
w, h = im.size # 获取图片大小
# 遍历像素点
for i in range(w):
for j in range(h):
alpha = px[i, j] # 获取灰度作为alpha值
########
r, g, b = pxColor[i, j] # 获取原图颜色
draw.point((i, j), fill=(r, g, b, 255 - alpha)) # 填充像素点
########
return im2 # 返回图像
# 函数使用示例
demoInput = Image.open('xxx.png') # 打开图像
demoOutput = imGreyAlpha(demoInput) # 丢进函数输出图像
demoOutput.save('ouput.png') # 保存图像
抠图滤色
如图所示,可以将背景色值周围的色值设置Alpha值,这个周围的范围和PS里的魔棒工具的容差概念相似
据此可写出如下代码:
# bgColor为背景颜色,是一个包含三个数的Tuple
# intv为容差
def imColorAlpha(im, bgColor, intv):
imConverted = im.convert('RGB')
w, h = imConverted.size
rb = bgColor[0]
gb = bgColor[1]
bb = bgColor[2]
im2 = Image.new(mode="RGBA", size=(w, h), color=(255,255,255,0))
draw = ImageDraw.Draw(im2)
tempPx = imConverted.load()
for i in range(w):
for j in range(h):
r, g, b = tempPx[i, j]
ar = (255/intv*(r-rb+intv)) if r<rb else (-255/intv*(r-rb-intv))
ag = (255/intv*(g-gb+intv)) if g<gb else (-255/intv*(g-gb-intv))
ab = (255/intv*(b-bb+intv)) if b<bb else (-255/intv*(b-bb-intv))
if ar<=0 or ag<=0 or ab<=0:
ar, ag, ab = (0, 0, 0)
a = 255 - int((ar + ag + ab)/3)
#r1 = int((r - rb)*a/255 + rb)
#g1 = int((g - gb)*a/255 + gb)
#b1 = int((b - bb)*a/255 + bb)
draw.point((i, j), fill=(r, g, b, a))
return im2
滤色
如上图所示,此处的“滤色”是指计算所有像素点颜色相对于背景色的差距,以其作为Alpha通道的值
实现代码如下:
def imColorAlpha(im, bgColor):
imConverted = im.convert('RGB')
w, h = imConverted.size
rb = bgColor[0]
gb = bgColor[1]
bb = bgColor[2]
im2 = Image.new(mode="RGBA", size=(w, h), color=(255,255,255,0))
draw = ImageDraw.Draw(im2)
tempPx = imConverted.load()
for i in range(w):
for j in range(h):
r, g, b = tempPx[i, j]
ar = (255/rb*r) if r<rb else (255/(rb-255)*(r-255))
ag = (255/gb*g) if g<gb else (255/(gb-255)*(g-255))
ab = (255/bb*b) if b<bb else (255/(bb-255)*(b-255))
a = 255 - int((ar + ag + ab)/3)
#r1 = int((r - rb)*a/255 + rb)
#g1 = int((g - gb)*a/255 + gb)
#b1 = int((b - bb)*a/255 + bb)
draw.point((i, j), fill=(r, g, b, a))
return im2
2020年07月12日 09:17
遇到这个问题的时候怎么不想到我 {{baiyan}}