前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >拼图还能这么玩?

拼图还能这么玩?

作者头像
godweiyang
发布2022-06-09 10:24:53
4770
发布2022-06-09 10:24:53
举报
文章被收录于专栏:算法码上来算法码上来

作者 | godweiyang

出品 | 公众号:算法码上来(ID:GodNLP)

这两天将我所有微信好友的头像弄出来了,一共5000多张。然后想着可以用它们来做些啥,最后用它们拼图玩。

Mac微信的头像保存在:

代码语言:javascript
复制
~/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均值相等的粘贴上去。

代码放到文章最后了,运行命令如下:

代码语言:javascript
复制
python3 image_stitch.py -d [图像合集目录] -i [待拼凑的图片] -s [小图最终的边长] -r

其中-d表示图像目录,你也可以放头像或者其他的图像;-i是你想拼成的大图路径,如果不设置的话就是直接把图像拼在一起;-s表示小图像最终的边长,实测设置为30效果最佳,图片大小和质量都比较好;-r表示是否随机排列图片。

此外之前还写过字符画视频的生成方法,代码也开源在同一个github上了: 我用字符画出了一个谷爱凌!

https://github.com/godweiyang/FunnyMedia

代码

代码语言:javascript
复制
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 -

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2022-04-23,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 算法码上来 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 最终效果
  • 实现方法
  • 代码
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档