从去年年末开始,我陆陆续续收集了很多卡牌,也在网站上展示了部分原创卡。在拍摄卡牌的时候,当然是要尽量呈现出卡牌实物的色彩效果。但将高清的图片放在网上进行展示,存在盗用的风险。这样可能会给卡牌的出品方带来负面影响。所以我就考虑在图片上添加浅色的水印。几百张图,如果一个个分别用 PhotoShop 先压缩图片再添加水印,太麻烦了,直接写个 Python 脚本吧。
引入库文件
由于是图片处理类的脚本,引入的库如下所示。
1
2
| from PIL import Image, ImageDraw, ImageFont
import os
|
读取文件列表
首先设置需要读取的图片文件夹地址,以及输出图片的地址。目前只需要用到读取地址,输出地址还用不到。
1
2
| imgOpen = 'test/old' # 读取地址
imgSave = 'test/new' # 输出地址
|
然后用以下代码实现将文件夹下的所有图片名存放在一个列表中。文件名的后缀被删除。这样方便后续图片输出的时候统一命名。
1
2
3
4
| imgList = os.listdir(imgOpen)
for index in range(len(imgList)):
imgList[index] = imgList[index][:-4]
|
按照这样的思路,我们写好处理图片的函数,在主程序中,只需要使用循环 for index in range(len(imgList))
依次处理每张图片并输出就可以了。
添加水印
这里我要添加的水印样式是成 45 度周期分布在整张图片上的水印。需要先设置添加的字符的字体、字号、透明度,再将字符覆盖到图片上。这里我使用了 Google Fonts 开源的 Berkshire Swash 英文字体。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
| def add_text_to_image(image, text):
font = ImageFont.truetype('BerkshireSwash-Regular.ttf', 54)
# 添加背景
new_img = Image.new('RGBA', (image.size[0] * 3, image.size[1] * 3), (0, 0, 0, 0))
new_img.paste(image, image.size)
# 添加水印
font_len = len(text)
rgba_image = new_img.convert('RGBA')
text_overlay = Image.new('RGBA', rgba_image.size, (255, 255, 255, 0))
image_draw = ImageDraw.Draw(text_overlay)
for i in range(0, rgba_image.size[0], font_len*30+100):
for j in range(0, rgba_image.size[1], 200):
image_draw.text((i, j), text, font=font, fill=(255, 255, 255, 50))
text_overlay = text_overlay.rotate(45)
image_with_text = Image.alpha_composite(rgba_image, text_overlay)
# 裁切图片
image_with_text = image_with_text.crop((image.size[0], image.size[1], image.size[0] * 2, image.size[1] * 2))
return image_with_text
|
这一步由于涉及到了透明度的计算,所以在读取 JPG 格式的图片后,输出的图片变成了 PNG 格式。通过对比两个格式的图片可知,添加水印后的 PNG 图片要远大于原始的 JPG 图片。这显然是不理想的。虽然说 PNG 在图片的质量上要比 JPG 格式好很多,但我拍摄的原本就是 JPG 图片,转成 PNG 没什么意义,并且我要将最终的图片放在网站上,如果图片文件太大,加载网页的时候就会很慢。因此,我还需要对输出的 PNG 图片做压缩处理,再次转换成 JPG 图片。
图片格式转换
从 PNG 转换到 JPG 当然不仅仅是改个文件名的后缀那么简单。 PNG 图像涉及到了透明度的信息,但 JPG 图像没有。所以我们需要将透明度信息删除。此外,顺便将画质适当降低一些以便压缩图片文件的体积。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
| def png2jpg(pngPath):
img = Image.open(pngPath)
(w, h) = img.size
infile = pngPath
outfile = os.path.splitext(infile)[0] + ".jpg"
img = Image.open(infile)
img = img.resize((int(w), int(h)), Image.ANTIALIAS)
try:
if len(img.split()) == 4:
r, g, b, a = img.split()
img = Image.merge("RGB", (r, g, b))
img.convert('RGB').save(outfile, quality=70)
os.remove(pngPath)
else:
img.convert('RGB').save(outfile, quality=70)
os.remove(pngPath)
return outfile
except Exception as e:
print("PNG 转换 JPG 错误", e)
|
缩放图片到指定尺寸
原始图片的尺寸比较大,高度超过了 2000 像素,由于屏幕分辨率的限制,通常在网页上呈现的时候不需要太大的尺寸。所以我计划将图片高度压缩至 1600 像素,宽度按照相同比例缩放。
1
2
3
4
5
6
7
8
| def resizeByHeight(jpgPath, newHeight):
img = Image.open(jpgPath)
(x, y) = img.size
ratio = y / newHeight
x_s = int(x / ratio)
y_s = newHeight
out = img.resize((x_s, y_s), Image.ANTIALIAS)
out.save(jpgPath)
|
主程序
最后,就是主程序的编写。
1
2
3
4
5
6
7
8
9
10
11
| if __name__ == '__main__':
for index in range(len(imgList)):
fileName = imgList[index]
openAddress = imgOpen + '/' + fileName + '.jpg'
saveAddress = imgSave + '/' + fileName + '.png'
resizeByHeight(openAddress, 1600)
img = Image.open(openAddress)
im_after = add_text_to_image(img, u'Guan Qirui Collection')
im_after.save(saveAddress)
png2jpg(saveAddress)
print(fileName)
|
最终效果如下图所示。图片使用了燎原三国的关羽人物卡。