前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >[Python还能干嘛]微信好友头像完成马赛克拼图~

[Python还能干嘛]微信好友头像完成马赛克拼图~

作者头像
Awesome_Tang
发布2019-07-01 14:38:07
1.4K0
发布2019-07-01 14:38:07
举报
文章被收录于专栏:FSocietyFSociety
马赛克拼图

何谓马赛克拼图,简单来说就是将若干小图片平凑成为一张大图,如下图路飞一样,如果放大看你会发现里面都是一些海贼王里面的图片。

LUFFY

Our Traget
  • 爬取所有微信好友的头像??????
  • 将所有微信好友头像拼凑成一张图片???
  • 然后就可以去朋友圈愉快的装逼了???
Requirements

其实整个项目很小,项目总共代码量不过100行左右。

  • 爬取微信头像依赖第三方库itchat
  • 马赛克拼图依赖依赖numpyPIL库。
Content
爬取微信好友头像

我这边是用的所有微信好友头像作为的数据源,你们如果有其他的数据源也可以的,可以直接跳过这步。 爬取微信好友头像我使用的是itchat,里面已经有封装好了的API,直接调用就可以,将所有的好友头像保存到一个文件夹供后期调用。

  • 代码部分
代码语言:javascript
复制
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Author  : AwesomeTang
# @File    : Wechat_Icon.py
# @Version : Python 3.7
# @Time    : 2019-06-29 23:35

import os
import itchat

itchat.login()

friends = itchat.get_friends(update=True)

base_folder = 'wechat'
if os.path.isdir(base_folder):
    pass
else:
    os.mkdir(base_folder)

for item in friends:
    img = itchat.get_head_img(item['UserName'])

    # 使用用户昵称作为文件名
    path = os.path.join(base_folder, '{}.jpg'.format(item['NickName'].replace('/', '')))
    with open(path, 'wb') as f:
        f.write(img)
    print('{} 写入完成...'.format(item['NickName']))
  • 实现效果 WechatIMG97
马赛克拼图
思路梳理
  1. 选好你需要拼凑的图片,将其切割为若干小块,切割的越细生成的图片效果会更好。 gay里gay气的示意图:
  1. 分别去我们之前保存的图片中找与与之最相似的,最后将其拼接完成。

说起来好像很简单,但实际操作起来只能......

最相似的图片

聪明的你应该知道了,最麻烦的地方其实是怎么找到最相似的图片,什么切割图片,拼接图片都是没啥难度,寻找最相似的图片算法是参考了 阮一峰的博客。

  • 轮廓
  1. 将每张头像resize为(8,8),然后所有像素值的平均值。
  2. 我们总共有64(即
8*8
8*8

)个像素点,分别去与平均值比较大小,高于平均值的记为1,小于平均值的记为0,这样我们每张图片相当于得到了一个类似[0,1,1,0,1,0....0,1,1]的‘编码’。

  1. 对于切割的小图片我们也进行上述操作,注意要保证是同一顺序(譬如从左上角到右下角),然后分别去与每个头像的‘编码’进行比较,这边在阮一峰的博客中是采用的计算汉明距离,我这边使用的就直接通过np.equal()计算相同的点了,取相同位数最多的那张头像即为最相似的图片。

不过我一步一步的操作完成之后得到的是如下效果:

你们能看出来是什么吗,然后我又......

  • 颜色筛选 然后静下来想想,以上方法能找到的只有轮廓最相似的头像,但要让拼凑起来像回事的话,其实颜色相似也很重要。 那么颜色怎么筛选呢,其实很简单,我们之前不是求了平均值吗,所以我在对比编码之前先筛选了平均值差值绝对值最接近的50张图片,这样便能保证筛选的图片在主色调上会比较类似。

然后好像就成功了......

放大之后是这个效果:

然后便能去朋友圈愉快的装逼了???

  • Talk is cheap, show me the code.
代码语言:javascript
复制
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Author  : AwesomeTang
# @File    : Resemble.py
# @Version : Python 3.7
# @Time    : 2019-06-23 13:52


from PIL import Image
import os
import numpy as np


class Config:
    corp_size = 40
    filter_size = 50


def mapping_table(pic_folder='wechat'):
    """
    What this function do?
    1. transverse every image in PIC_FOLDER;
    2. resize every image in (8, 8) and covert into GREY;
    3. CODE for every image, CODE like [1, 0, 1, 1, 0....1]
    4. build a dict to gather all image and its CODE.
    :param pic_folder: path of pictures folder.
    :return: a dict
    """
    suffix = ['jpg', 'jpeg', 'JPG', 'JPEG', 'gif', 'GIF', 'png', 'PNG']
    if not os.path.isdir(pic_folder):
        raise OSError('Folder [{}] is not exist, please check.'.format(pic_folder))

    pic_list = os.listdir(pic_folder)
    results = {}
    pic_dic = {}
    for idx, pic in enumerate(pic_list):
        if pic.split('.')[-1] in suffix:
            path = os.path.join(pic_folder, pic)
            try:
                img = Image.open(path).resize((Config.corp_size, Config.corp_size), Image.ANTIALIAS).convert('L')
                results[idx] = pic_code(np.array(img.resize((8, 8), Image.ANTIALIAS)))
                pic_dic[idx] = img
            except OSError:
                pass
    return results, pic_dic


def pic_code(image: np.ndarray):
    """
    To make a one-hot code for IMAGE.
    AVG is mean of the array(IMAGE).
    Traverse every pixel of IMAGE, if the pixel value is more then AVG, make it 1, else 0.
    :param image: an array of picture
    :return: A sparse list with length [picture's width * picture's height].
    """
    width, height = image.shape
    avg = image.mean()
    one_hot = np.array([1 if image[i, j] > avg else 0 for i in range(width) for j in range(height)])
    return one_hot


class PicMerge:

    def __init__(self, pic_path, corp_size=20):
        self.mapping_table, self.pictures = mapping_table(pic_folder='wechat')
        self.picture = Image.open(pic_path).convert('L')
        self.corp_size = corp_size

    def corp(self):
        width, height = self.picture.size
        width = (width // Config.corp_size) * Config.corp_size
        height = (height // Config.corp_size) * Config.corp_size
        self.picture.resize((width, height), Image.ANTIALIAS)
        picture = np.array(self.picture)
        for i in range(width // Config.corp_size):
            for j in range(height // Config.corp_size):
                slice_mean = picture[i * Config.corp_size:(i + 1) * Config.corp_size,
                             j * Config.corp_size:(j + 1) * Config.corp_size].mean()
                candidate = sorted([(key_, abs(np.array(value_).mean() - slice_mean))
                                    for key_, value_ in self.pictures.items()],
                                   key=lambda item: item[1])[:Config.filter_size]
                slice_ = Image.fromarray(picture[i * Config.corp_size:(i + 1) * Config.corp_size,
                                         j * Config.corp_size:(j + 1) * Config.corp_size]).convert('L')
                one_hot = pic_code(np.array(slice_.resize((8, 8), Image.ANTIALIAS)))
                a = [(key_, np.equal(one_hot, self.mapping_table[key_]).mean()) for key_, _ in candidate]
                a = max(a, key=lambda item: item[1])
                picture[i * Config.corp_size:(i + 1) * Config.corp_size,
                j * Config.corp_size:(j + 1) * Config.corp_size] = self.pictures[a[0]]
        picture = Image.fromarray(picture)
        picture.show()
        picture.save('result.jpg')


if __name__ == "__main__":
    p = PicMerge(pic_path='WechatIMG97.jpeg')
    p.corp()

代码部分目前只完成灰度图片的,RGB模式下的暂未开整,搞定之后再继续更新。

skr~~ skr~~~

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2019.06.30 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

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

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

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 马赛克拼图
  • Our Traget
  • Requirements
  • Content
    • 爬取微信好友头像
      • 马赛克拼图
        • 思路梳理
        • 最相似的图片
    领券
    问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档