【Python】爬取微信好友头像完成马赛克拼图~

news/2024/11/8 14:50:02/

代码已重新整理上传到了我的KLab,为了更好的阅读体验可点击🔗【千图成像】爬取微信好友头像完成马赛克拼图🧩查看~

马赛克拼图

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

Our Tragets

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

Requirements

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

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

Content

爬取微信好友头像

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

  • 代码部分
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Author  : AwesomeTang
# @File    : Wechat_Icon.py
# @Version : Python 3.7
# @Time    : 2019-06-29 23:35import os
import itchatitchat.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']))
  • 实现效果

马赛克拼图

思路梳理
  1. 选好你需要拼凑的图片,将其切割为若干小块,切割的越细生成的图片效果会更好。
    gay里gay气的示意图:
  2. 分别去我们之前保存的图片中找与与之最相似的,最后将其拼接完成。

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

最相似的图片

其实困难的地方很明显,那就是我们如何从一堆图片中找出最相似的那张。
我们可以分为两个步骤:

颜色相似

这个应该不难理解,我在代码中实现了灰度图像和RGB通道图像的筛选方法:

  • 灰度图像:
    直接计算所有像素灰度值的平均值,取最接近n个图像供后期再次筛选;
  • RGB通道:
    分别计算R,G,B的平均值,对于一个图像我们得到的是一个类似与[20, 30,40]的数组,然后我们计算欧式距离,取最接近n个图像供后期再次筛选。
结构相似

为什么还需要结构相似,举个例子:

如果单纯按照上述方法去判定相似,那上图中的图A和图B肯定是最相似的,所以我们不能单纯的因为两张图片中包含的颜色差不多就去判断为最相似,还需要去判断颜色的“位置”也要相似。
这部分实现方法参考了阮一峰的博客,具体逻辑如下:

  1. 只需要提取图片结构,颜色意义不大,为计算简便,我们直接将所有图片转为灰度通道;
  2. 将每张头像resize为(8,8),然后计算所有像素值的平均值。
  3. 我们总共有64(即 8 ∗ 8 8*8 88)个像素点,分别去与平均值比较大小,高于平均值的记为1,小于平均值的记为0,这样我们每张图片都会得到一个长度为64类似[0,1,1,0,1,0…0,1,1]的‘编码’。
  4. 对于切割的小图片我们也进行上述操作,注意要保证是同一顺序(譬如从左上角到右下角),然后分别去与每个头像的‘编码’进行比较,这边在阮一峰的博客中是采用的计算汉明距离,我这边使用的就直接通过np.equal()计算相同的点了,取相同位数最多的那张头像即为最相似的图片。

我在代码中是先筛选颜色最接近的50张图片,然后再在这50中图片去寻找结构最相似的图片,最后实现效果如下:

  • RGB通道
    因为图片源只有500来张,效果只能说凑合,勉强能看出来是啥。

  • 灰度
    )

  • 放大之后是这个效果:

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

  • Talk is cheap, show me the code.
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Author  : AwesomeTang
# @Version : Python 3.7
# @Time    : 2019-06-23 13:52from PIL import Image
import os
import numpy as np
from tqdm import tqdmclass Config:corp_size = 40filter_size = 20num = 100class PicMerge:def __init__(self, pic_path, mode='RGB', pic_folder='wechat'):if mode.upper() not in ('RGB', 'L'):raise ValueError('Only accept "RGB" or "L" MODE, but we received "{}".'.format(self.mode))else:self.mode = mode.upper()print('Coding for every picture in folder "{}".'.format(pic_folder))self.mapping_table, self.pictures = self.mapping_table(pic_folder)self.picture = self.resize_pic(pic_path).convert(self.mode)@staticmethoddef resize_pic(pic_path):picture = Image.open(pic_path)width, height = picture.sizeto_width = Config.corp_size * Config.numto_height = ((to_width / width) * height // Config.corp_size) * Config.corp_sizepicture = picture.resize((int(to_width), int(to_height)), Image.ANTIALIAS)return picturedef merge(self):width, height = self.picture.sizew_times, h_times = int(width / Config.corp_size), int(height / Config.corp_size)picture = np.array(self.picture)print('Corp & Merge...')for i in tqdm(range(w_times), desc='CORP'):for j in range(h_times):if self.mode == 'L':section = picture[j * Config.corp_size:(j + 1) * Config.corp_size,i * Config.corp_size:(i + 1) * Config.corp_size]section_mean = section.mean()candidate = sorted([(key_, abs(np.array(value_).mean() - section_mean))for key_, value_ in self.pictures.items()],key=lambda item: item[1])[:Config.filter_size]most_similar = self.structure_similarity(section, candidate)picture[j * Config.corp_size:(j + 1) * Config.corp_size,i * Config.corp_size:(i + 1) * Config.corp_size] = most_similarelif self.mode == 'RGB':section = picture[j * Config.corp_size:(j + 1) * Config.corp_size,i * Config.corp_size:(i + 1) * Config.corp_size, :]candidate = self.color_similarity(section)most_similar = self.structure_similarity(section, candidate)picture[j * Config.corp_size:(j + 1) * Config.corp_size,i * Config.corp_size:(i + 1) * Config.corp_size, :] = most_similarpicture = Image.fromarray(picture)picture.show()picture.save('result.jpg')print('Work Done...')def structure_similarity(self, section, candidate):section = Image.fromarray(section).convert('L')one_hot = self.pic_code(np.array(section.resize((8, 8), Image.ANTIALIAS)))candidate = [(key_, np.equal(one_hot, self.mapping_table[key_]).mean()) for key_, _ in candidate]most_similar = max(candidate, key=lambda item: item[1])return self.pictures[most_similar[0]]def color_similarity(self, pic_slice, top_n=Config.filter_size):slice_mean = self.rgb_mean(pic_slice)diff_list = [(key_, np.linalg.norm(slice_mean - self.rgb_mean(value_)))for key_, value_ in self.pictures.items()]filter_ = sorted(diff_list, key=lambda item: item[1])[:top_n]return filter_@staticmethoddef rgb_mean(rgb_pic):"""if picture is RGB channel, calculate average [R, G, B]."""r_mean = np.mean(rgb_pic[:, :, 0])g_mean = np.mean(rgb_pic[:, :, 1])b_mean = np.mean(rgb_pic[:, :, 2])val = np.array([r_mean, g_mean, b_mean])return valdef mapping_table(self, pic_folder):"""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 tqdm(enumerate(pic_list), desc='CODE'):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)results[idx] = self.pic_code(np.array(img.convert('L').resize((8, 8), Image.ANTIALIAS)))if self.mode == 'RGB':pic_dic[idx] = np.array(img.convert(self.mode))else:pic_dic[idx] = np.array(img.convert(self.mode))except OSError:passreturn results, pic_dic@staticmethoddef 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.shapeavg = 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_hotif __name__ == "__main__":P = PicMerge(pic_path='海贼王.jpeg', mode='RGB')P.merge()

参考资料

  1. 相似图片搜索的原理 :点我跳转

skr~~ skr~~~


http://www.ppmy.cn/news/709575.html

相关文章

【PyTorch】12 生成对抗网络实战——用GAN生成动漫头像

GAN 生成动漫头像 1. 获取数据2. 用GAN生成2.1 Generator2.2 Discriminator2.3 其它细节2.4 训练思路 3. 全部代码4. 结果展示与分析小结 深度卷积生成对抗网络(DCGAN):Unsupervised Representation Learning with Deep Convolutional Generative Advers…

航海王燃烧意志服务器响应格式非法,航海王燃烧意志充值异常怎么处理 航海王燃烧意志充值异常申诉方法_斗蟹游戏网...

【斗蟹-航海王燃烧意志】航海王燃烧意志游戏中玩家在充值后发现没有到账,那要在怎么处理充值异常,下面小编带大家一起看看航海王燃烧意志充值异常申诉方法,希望能在游戏中帮到大家。 在航海王燃烧意志手游中,不少朋友在充值后彩钻…

这顶海贼王的帽子,我Python给你带上了 | 【人脸识别应用】

微信公众号:AI算法与图像处理如果你觉得对你有帮助,欢迎分享和转发哈 https://zhuanlan.zhihu.com/p/32299758?utm_sourcewechat_session&utm_mediumsocial&utm_oi704056637840695296 内容目录 故事起因思路与实现准备工作详细代码和效果总结1.…

程序员的发展之道---海贼王(山治)

对于日本动漫,我唯一喜欢,也是一直在追的就是海贼王,尤其喜欢里面的厨师山治,至于为什么喜欢他,也许是因为他绅士,儒雅,对梦想的执着 当然这只是我的个人看法,但是就是这样一个没有超…

【数据结构与算法】Huffman编码/译码(C/C++)

实践要求 1. 问题描述 利用哈夫曼编码进行信息通讯可以大大提高信道利用率,缩短信息传输时间,降低传输成本。但是,这要求在发送端通过一个编码系统对待传数据预先编码;在接收端将传来的数据进行译码(复原)。对于双工信道(即可以…

Vuejs 3.0 正式版发布!One Piece. 代号:海贼王

文末有送书福利 译者:夜尽天明 (译者授权转载) 原文地址:https://mp.weixin.qq.com/s/0oet-MTo__LWNZNYl5Fpqw Vue 团队于 2020 年 9 月 18 日晚 11 点半发布了 Vue 3.0 版本。 那个男人总喜欢在深夜给我们带来意外惊喜&#xff0…

如何查看 MySQL 建表时间

MySQL是一款性能良好,易于使用的关系型数据库管理系统。我们可以使用 SQL 语句查看 MySQL 建表时间,以便获取建立表时的更多信息。 1、 首先,在MySQL中执行以下命令,获取表的列表: SELECT create_time,table_name FR…

资料下载链接

大家好!这是我整理的免费视频教程以及电子书,每天都会有更新,希望对大家能有帮助。 与大家共勉!大家可以根据自己感兴趣的方向浏览下载哦!祝大家事业有成!学习进步! Linux: LAMP兄弟连Linux视频…