使用Pillow对图片进行滤色

首页 / 技术 / 正文

在做网站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}}