大家好,我是小小明。
使用微信也有那么多年了,你有想过要给你的微信好友的头像生成图片墙吗?
例如:
注意:从效果看有不少重复头像,这只是因为我本地并没有那么多微信头像,只能重复选取。对于微信好友多的朋友,只需要把文章中
np.random.choice(imgs, size=data.sum())
这行代码加个参数replace=False
就能实现非重复选取。
如果有跟我一样想法的朋友,咱们Let’s go⏩👊。
首先我们需要先获取微信头像的缓存列表:
🌀获取微信头像的缓存列表🌀
在我们自己点开过目标的头像查看大图时,微信电脑版会将其保存到HDHeadImage
(高清大图)目录中。
这个目标可以直接通过python读取到:
wechat_id = "你的微信号"
path = os.path.expanduser(f"~/Documents/WeChat Files/{wechat_id}/FileStorage/General/HDHeadImage")
path
'C:\\Users\\ASUS/Documents/WeChat Files/你的微信号/FileStorage/General/HDHeadImage'
wechat_id
是你的微信号。
~/Documents
取决于你没有修改微信文件默认保存位置。如果已经修改的情况下需要改成你修改过的文件夹。
然后就可以通过该文件夹看到你在电脑上点开过头像的高清缓存:
读取高清大图图像缓存,就可以再微信电脑版的这个位置读取到。
但如果我们想获取所有微信头像的小图缓存呢?这个时候无法通过微信电脑版直接获取需要的数据,但我们可以借助有root权限的手机拿到小图缓存(几乎能包含所有的小图缓存)。
我使用了夜神模拟器,通过夜神模拟器登录个人微信后,多游览一段时间后,本地就会产生头像缓存。
然后进入/data/data/com.tencent.mm/MicroMsg
文件夹:
再进入最近产生修改而且名称比较长的文件夹,其中的avatar
文件夹就存放了所有的头像缓存:
此时将其复制到夜神模拟器的安卓共享路径,就可以在PC端上读取到这些图片文件了:
不过图片文件都在一个个的子文件夹中,这里我使用everything
搜索目标文件夹,然后将里面的图片一次性全部剪切出来:
提取出来后就是这样的效果:
准备好了头像路径之后,咱们开始绘制文字:
📣绘制文字图案⏩
为了制作文字照片墙,需要在绘制出文字后,分析像素点决定哪些位置摆放照片。
这里依然是PIL库进行文字绘制,经过多次调试,设计出如下绘制方法:
from PIL import Image, ImageFont, ImageDraw, ImageChops
def create_text_img(text, size=30, fontname="msyhbd.ttc"):
"作者:小小明"
# 获取字体对象
font = ImageFont.truetype(fontname, size)
width = len(text) * size
# 左上角对齐绘制文字
im = Image.new(mode='RGBA', size=(width, size))
draw = ImageDraw.Draw(im=im)
w, h = draw.textsize(text, font)
o1, o2 = font.getoffset(text)
draw.text(xy=(-o1, -o2), text=text,
fill="black", font=font)
# 裁切文字多余空白
bg = Image.new(mode='RGBA', size=im.size)
bbox = ImageChops.difference(im, bg).getbbox()
im = im.crop(bbox)
text_img = Image.new(mode='L', size=im.size, color=255)
text_img.paste(im, mask=im)
return text_img
display(create_text_img("好友头像", fontname="STHUPO.TTF"))
display(create_text_img("好友头像"))
分别用华文琥珀和默认的微软雅黑粗体测试一下:
⭐️绘制文字照片墙🔚
获取到文字图案的灰度图像对象,就可以很轻松的绘制文字照片墙了。
import numpy as np
im = create_text_img("照片墙", fontname="msyh.ttc")
data = np.array(im) != 255
h, w = data.shape
print(f"共需{data.sum()}张图片,宽{w}张,高{h}张")
共需1115张图片,宽90张,高29张
我使用了照片墙
作为文字图片,显示共需1890张头像图片,但是我的缓存文件夹并没有这么多图片,只能允许头像能够被重复选取。
获取随机头像列表:
import os
img_path = r"C:\Users\ASUS\Nox_share\ImageShare\avatar"
imgs = os.listdir(img_path)
img_lists = np.random.choice(imgs, size=data.sum())
然后就可以生成头像照片墙了:
# 设置每个头像的大小
size = 50
new_img = Image.new('RGB', (size * w, size * h), "white")
random_imgs = iter(img_lists)
for y, x in zip(*np.where(data)):
img_name = next(random_imgs)
src_img = Image.open(f'{img_path}/{img_name}')
src_img = src_img.resize((size, size), Image.ANTIALIAS)
# 将图片复制到 new_image
new_img.paste(src_img, (x * size, y * size))
new_img
生成结果:
可以看到我们已经顺利的给图片列表生成了照片墙,以后的中秋节,国庆节,情人节,都可以直接用。我们不仅仅可能使用文字生成照片墙,也可能根据特殊的形状图片,为了方便以后使用,将上述逻辑封装一下:
def create_picture_wall(data, imgs, size=50):
h, w = data.shape
random_imgs = iter(np.random.choice(imgs, size=data.sum()))
new_img = Image.new('RGB', (size * w, size * h), "white")
for y, x in zip(*np.where(data)):
img_name = next(random_imgs)
src_img = Image.open(f'{img_path}/{img_name}')
src_img = src_img.resize((size, size), Image.ANTIALIAS)
# 将图片复制到 new_image
new_img.paste(src_img, (x * size, y * size))
return new_img
调用示例:
im = create_text_img("小小明")
img_path = r"C:\Users\ASUS\Nox_share\ImageShare\avatar"
create_picture_wall(np.array(im) != 255, os.listdir(img_path))
im = create_text_img("小小明", fontname="msyh.ttc")
img_path = r"C:\Users\ASUS\Nox_share\ImageShare\avatar"
create_picture_wall(np.array(im) != 255, os.listdir(img_path))
❤️Python绘制爱心图案💖
想画出爱心图案照片墙,首先得使用PIL画出爱心图案。绘制爱心的函数有很多种,下面我分别演示一下,并先用matplotlib实现爱心图像的绘制。
最流行的参数方程是:
x ( t ) = 16 sin t 3 x(t)=16 {\sin t}^3 x(t)=16sint3
y ( t ) = 13 cos t − 5 cos 2 t − 2 cos 3 t − cos 4 t y(t)=13\cos t- 5\cos 2t - 2\cos 3t -\cos4t y(t)=13cost−5cos2t−2cos3t−cos4t
这个参数方程用python表达就是:
import math
import numpy as np
t = np.arange(0, 2*math.pi, 0.1)
x = 16*np.sin(t)**3
y = 13*np.cos(t)-5*np.cos(2*t)-2*np.cos(3*t)-np.cos(4*t)
用matplotlib可以直接绘制:
from matplotlib import pyplot as plt
plt.plot(x, y, color="r");
对x轴和y轴均加个偏移即使使其落在大于0的区间内。
绘制一个实心爱心:
plt.fill(x+15, y+15, color="r");
不过这种参数方程的形式只是得到边界的点坐标,要转换到绘制到PIL图像中还比较困难。下面我使用另一个不流行的心形函数方程进行绘制,函数方程分别由上下两个方程组成。
上半部分方程为: f ( x ) = 2 ⋅ x 2 − x 2 f(x)=\sqrt{2 \cdot \sqrt{x^{2}}-x^{2}} f(x)=2⋅x2−x2
下半部分方程为: g ( x ) = − 2.14 ⋅ 2 − ∣ x ∣ g(x)=-2.14 \cdot \sqrt{\sqrt{2}-\sqrt{|x|}} g(x)=−2.14⋅2−∣x∣
import math
from matplotlib import pyplot as plt
import numpy as np
x = np.linspace(-2, 2, 1000)
fx = np.sqrt(2*np.abs(x)-x**2)
gx = -2.14*np.sqrt(np.sqrt(2)-np.sqrt(np.abs(x)))
plt.plot(x, fx, color="r", label="upper")
plt.plot(x, gx, color="b", label="down")
plt.legend();
如何将其偏移到正数范围呢❓我采用下面的方式:
import math
from matplotlib import pyplot as plt
import numpy as np
x = np.linspace(-2, 2, 2000)
fx = np.sqrt(2*np.abs(x)-x**2)
gx = -2.14*np.sqrt(np.sqrt(2)-np.sqrt(np.abs(x)))
x = (x+2)*7.5
fx = (fx+2.5)*7.5
gx = (gx+2.5)*7.5
plt.plot(x, fx, color="r", label="upper")
plt.plot(x, gx, color="b", label="down")
plt.legend();
同时有上下两个函数方程时,画实心爱心也很简单:
plt.fill_between(x, gx, fx, color="r");
接下来我们去掉轴,并保存图片,就可以直接用PIL读取了。
产生爱心图像并保存到文件中:
import math
from PIL import Image
from matplotlib import pyplot as plt
import numpy as np
x = np.linspace(-2, 2, 100)
fx = np.sqrt(2*np.abs(x)-x**2)
gx = -2.14*np.sqrt(np.sqrt(2)-np.sqrt(np.abs(x)))
fx = (fx+2.5)*7
gx = (gx+2.5)*7
fig = plt.figure()
plt.axis("off")
plt.fill_between(x, gx, fx, color="black")
fig.savefig("t.jpg");
读取图片并转换为黑白图像:
im = Image.open("t.jpg").convert("1")
im
现在需要去除多余的空白,对于这种黑白图像,我直接使用numpy去除边界上的空白。
首先转换为numpy数组:
data = ~np.array(im)
print("去除前:")
display(Image.fromarray(data))
ys, xs = np.where(data)
data = data[min(ys):max(ys)+1, min(xs):max(xs)+1]
print("去除后:")
display(Image.fromarray(data))
💕绘制爱心照片墙💘
有了上的爱心蒙版就可以很简单的绘制出照片墙了。
为了减少最后的渲染量,将爱心图片缩放一下:
im = Image.fromarray(data).resize((60, 40), Image.ANTIALIAS)
data = np.array(im)
h, w = data.shape
print(f"共需{data.sum()}张图片,宽{w}张,高{h}张")
共需1504张图片,宽60张,高40张
然后开始绘制:
import os
def create_picture_wall(data, imgs, size=50):
h, w = data.shape
random_imgs = iter(np.random.choice(imgs, size=data.sum()))
new_img = Image.new('RGB', (size * w, size * h), "white")
for y, x in zip(*np.where(data)):
img_name = next(random_imgs)
src_img = Image.open(f'{img_path}/{img_name}')
src_img = src_img.resize((size, size), Image.ANTIALIAS)
# 将图片复制到 new_image
new_img.paste(src_img, (x * size, y * size))
return new_img
img_path = r"C:\Users\ASUS\Nox_share\ImageShare\avatar"
create_picture_wall(data, os.listdir(img_path))
当然一些系统相关的缓存也被加入到了图片列表,可以再人工删除这些系统图标后再进行生成。
🚩绘制任意图形照片墙🚀
其实我并不是一定要自己画一个爱心图形之后,才能画爱心照片墙。只要我们事先准备好图形的蒙版图片,用PIL读取后转换一下即可马上画成相应的照片墙。
这里有个微词云的网站:https://www.weiciyun.com/edit
可以下载各种各样的蒙版形状:
我们以大拇指为例进行演示,首先下载目标图片:
import requests
from io import BytesIO
url = "https://staticc.ywordle.com/static/2020-11-01/6d2e4f9d31d1b7201e23198869de2f9a_preview.png"
r = requests.get(url)
im = Image.open(BytesIO(r.content))
im.size
(190, 200)
图片过大,转换为bool数组,并缩放一下:
data = np.array(im) > 0
data = np.array(Image.fromarray(data).resize((48, 50), Image.ANTIALIAS))
display(Image.fromarray(data))
然后就可以生成大拇指的照片墙了:
将以上代码封装一下:
from PIL import Image
import requests
from io import BytesIO
import os
def get_mask_data(im, size=50):
width, height = im.size
if width > height:
height = height*size//width
width = size
else:
width = width*size//height
height = size
im = im.resize((width, height), Image.ANTIALIAS)
return np.array(im) > 0
def create_picture_wall(data, img_path, size=50):
h, w = data.shape
imgs = os.listdir(img_path)
random_imgs = iter(np.random.choice(imgs, size=data.sum()))
new_img = Image.new('RGB', (size * w, size * h), "white")
for y, x in zip(*np.where(data)):
img_name = next(random_imgs)
src_img = Image.open(f'{img_path}/{img_name}')
src_img = src_img.resize((size, size), Image.ANTIALIAS)
new_img.paste(src_img, (x * size, y * size))
return new_img
def download_img(url):
r = requests.get(url)
return Image.open(BytesIO(r.content))
测试一下:
url = "https://staticc.ywordle.com/static/2020-11-03/f18f814d52768eb29111c0be52b14ca2_preview.png"
im = download_img(url)
data = get_mask_data(im)
create_picture_wall(data, r"C:\Users\ASUS\Nox_share\ImageShare\avatar")
💎看完本文,相信你已经任何形式的照片墙都会画了吧?☀️