最直观的感受就是,相比于其他格式,png格式多了一个alpha通道,用来存储透明度信息,像素位置 img(x,y) 取值255为不透明,取值为0则为透明。这就使得png具有背景透明的显示效果。如下图所示,这是本人项目中的一部分,提取人脸面部的某些特征,用绿色显示,而在将像素矩阵存储为jpg格式图片时,背景区域会显示为黑色(0,0,0)。
在很多场合,如AE、Edius等制作视频时,添加素材,我们需要的只是素材主体,而不需要背景,即这种效果
这就需要在存储图片时将背景去掉。这就是png格式的好处之一,可以设置透明度,将背景去掉。
python的pillow库中有对应api,直接添加A通道。这里有一篇参考文章。https://blog.csdn.net/A156348933/article/details/85503453,将其程序段贴出来
# pip install Pillow
import PIL.Image as Image
# 以第一个像素为准,相同色改为透明
def transparent_back(img):
img = img.convert('RGBA')
L, H = img.size
color_0 = (255,255,255,255)#要替换的颜色
for h in range(H):
for l in range(L):
dot = (l,h)
color_1 = img.getpixel(dot)
if color_1 == color_0:
color_1 = color_1[:-1] + (0,)
img.putpixel(dot,color_1)
return img
if __name__ == '__main__':
img=Image.open('img.png')
img=transparent_back(img)
img.save('img2.png')
当然,图片中那一部分像素设置成透明,这需要我们根据自己的项目需求自行设定。以上文章中,设定的条件是
color_0 = (255,255,255,255)#要替换的颜色
其中,前三个通道的自己设定,最后一个通道即新添加的alpha通道,默认值全是255。而这个通道,也就是我们要针对修改的值。上述代码中有两个for循环,逐个像素进行处理,效率比较低。笔者用来处理9张1632x1224尺寸的图片,在8核2.4赫兹主频的服务器上运行时间为
这很难满足生产需要。因此这一次的改进主要是去掉上述代码的for循环。
def convertPNG(img1):
img = img1.convert('RGBA')
r, g, b, a = img.split()
a0 = np.array(b) #转换为np矩阵
a1 = cv2.threshold(a0,10,255,cv2.THRESH_BINARY) #设定阈值
a2 = Image.fromarray(a1[1]) #转换为Image的tube格式,注意为a1[1]
a3 = np.array(a2)
a4 = Image.fromarray(a3.astype('uint8')) #由float16转换为uint8
img = Image.merge("RGBA", (b, g, r, a4))
return img
if __name__ == '__main__':
img=Image.open('white.png')
img1=convertPNG(img)
img1.save('img2.png')
上述代码,简易言之,就是将pillow图片格式转换到opencv能够处理的格式(uint8的ndarray),然后用矩阵计算的方式来代替两个for循环,之后再转换为pillow能够处理的格式。这样的好处就是节约了大量时间。同样环境下,处理同样图片,时间如下
效率提升2个数量级。
另外要讨论的是,这一次用到了cv2.threshhold设定阈值,将阈值以下的像素,alpha通多直接设置成透明。如果需要指定某一特定值(0-255)的像素为透明,则可以用两次cv2.threshhold,其中一次用INV取反,也可以进行矩阵计算。