这两天将我所有微信好友的头像弄出来了,一共5000多张。然后想着可以用它们来做些啥,最后用它们拼图玩。
Mac微信的头像保存在:
~/Library/Containers/com.tencent.xinWeChat/Data/Library/Application\ Support/com.tencent.xinWeChat/2.0b4.0.9/c1a30fcf75eedba12764b4d4170b977e/Avatar
其中倒数第二个那一长串字符串每个人会不同,根据自己情况进行修改。
用到的代码在文章最后,github上也开源了:
https://github.com/godweiyang/FunnyMedia
最终效果
直接上最终效果图,首先是5000多张头像拼接成一张图片:
如果看不清的话可以放大看看细节:
然后是5000多张头像拼成的杨超越:
如果看不清的话可以放大看看细节:
杨超越的原图是这样的:
实现方法
实现方法很简单,拼接的话就是把头像依次粘贴在一块大画布上的不同区域,就跟贴瓷砖一样。
为了实现用不同头像拼接出杨超越,就需要先将杨超越图片进行分割,每一块小区域寻找一个颜色最相近的头像粘贴上去。我直接将图像区域的RBG值求了加权平均,然后在头像中寻找RGB均值相等的粘贴上去。
代码放到文章最后了,运行命令如下:
python3 image_stitch.py -d [图像合集目录] -i [待拼凑的图片] -s [小图最终的边长] -r
其中-d
表示图像目录,你也可以放头像或者其他的图像;-i
是你想拼成的大图路径,如果不设置的话就是直接把图像拼在一起;-s
表示小图像最终的边长,实测设置为30效果最佳,图片大小和质量都比较好;-r
表示是否随机排列图片。
此外之前还写过字符画视频的生成方法,代码也开源在同一个github上了:
https://zhuanlan.zhihu.com/p/466993864
https://github.com/godweiyang/FunnyMedia
代码
import argparse
import os
import random
import math
from collections import defaultdict
import numpy as np
import PIL.Image as Image
from tqdm import tqdm, trange
def generate1(dir, size, rand):
print(f"正在拼接尺寸:{size}...")
nums = len(os.listdir(dir))
nums_width = int(math.sqrt(nums))
nums_height = int((nums + nums_width - 1) / nums_width)
img_width = nums_width * size
img_height = nums_height * size
image = Image.new("RGB", (img_width, img_height), "white")
x = 0
y = 0
files = os.listdir(dir)
if rand:
random.shuffle(files)
for i in tqdm(files):
try:
img = Image.open(os.path.join(dir, i))
except IOError:
print(i)
print("图像打开失败")
else:
img = img.resize((size, size), Image.ANTIALIAS)
image.paste(img, (x * size, y * size))
x += 1
if x == nums_width:
x = 0
y += 1
img.close()
image.save(f"avatar_{size}.jpg")
image.close()
def mean_pixel(colors):
colors = [0.3 * r + 0.59 * g + 0.11 * b for r, g, b in colors]
return int(np.mean(colors))
def generate2(dir, source, size, rand):
print(f"正在拼接尺寸:{size}...")
files = os.listdir(dir)
if rand:
random.shuffle(files)
image = Image.open(source)
image = image.convert("RGB")
img_width, img_height = image.size
img_width = ((img_width + size - 1) // size) * size * ((size + 9) // 10)
img_height = ((img_height + size - 1) // size) * size * ((size + 9) // 10)
image = image.resize((img_width, img_height), Image.ANTIALIAS)
colors = defaultdict(list)
for i in tqdm(files):
try:
img = Image.open(os.path.join(dir, i))
except IOError:
print(i)
print("图像打开失败")
else:
img = img.convert("RGB")
img = img.resize((size, size), Image.ANTIALIAS)
colors[mean_pixel(img.getdata())].append(i)
img.close()
for i in range(256):
if len(colors[i]) == 0:
for n in range(1, 256):
if len(colors[i - n]) != 0:
colors[i] = colors[i - n]
break
if len(colors[i + n]) != 0:
colors[i] = colors[i + n]
break
index = defaultdict(int)
for i in trange(0, img_width, size):
for j in range(0, img_height, size):
now_colors = []
for ii in range(i, i + size):
for jj in range(j, j + size):
now_colors.append(image.getpixel((ii, jj)))
mean_color = mean_pixel(now_colors)
img = Image.open(
os.path.join(
dir, colors[mean_color][index[mean_color] % len(colors[mean_color])]
)
)
img = img.convert("RGB")
img = img.resize((size, size), Image.ANTIALIAS)
image.paste(img, (i, j))
img.close()
index[mean_color] += 1
source_name = ".".join(source.split(".")[:-1])
image.save(f"{source_name}_{size}.jpg")
image.close()
if __name__ == "__main__":
parser = argparse.ArgumentParser()
parser.add_argument(
"--dir", "-d", type=str, default="avatar", help="directory of the avatars"
)
parser.add_argument(
"--img", "-i", type=str, default="", help="source image to be coverd"
)
parser.add_argument(
"--size",
"-s",
type=str,
default="30",
help="size of each avatar (size1,size2,...)",
)
parser.add_argument(
"--rand",
"-r",
action="store_true",
help="whether to shuffle the avatars",
)
args = parser.parse_args()
sizes = [int(s) for s in args.size.split(",")]
for size in sizes:
if len(args.img) == 0:
generate1(args.dir, size, args.rand)
else:
generate2(args.dir, args.img, size, args.rand)