作者 | godweiyang
出品 | 公众号:算法码上来(ID:GodNLP)
这两天将我所有微信好友的头像弄出来了,一共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://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)
- END -