❝微信公众号:「Python干货铺子」关注即可开启快乐学习Python和Matlab的大门,您还在犹豫什么~
❞
前言当走上街头时能隐约察觉到多了些许“红白绿”的色彩,那是:圣诞老人?、雪花❄️、圣诞树?。霓虹灯闪烁,各类促销活动海报琳琅满目。没有错,圣诞节快来了~
从写文章开始,一路上得到了不少小伙伴的支持和鼓励,而这周CSDN上的总排名不偏不倚,恰好100名整。我想,这大概是某种巧合,更确切来说应该是一种激励。近些日,严酷寒冬似乎柔情了许多,有了那么几日的柔柔暖阳,而我希望可以把这份温暖也带给大家。
我将用粉丝的头像拼接出一幅圣诞树,正所谓,“「千图成像,集万千之美」”。
一、?圣诞树?该图的像素为6200x10250,将其局部放大后的效果为:今天所用到的代码是比较「通用」的,只需要替换背景图和图片库即可打造属于你自己的专属图片。详情分析,客观您往下瞅瞅~
帅小伙!圣诞来袭,还不赶快用来表白一波,还在犹豫啥呢?
二、代码分析1.头像爬取
通过爬虫将粉丝的头像下载到本地,具体分析大家可以参考我之前的博客【前方高能!看小伙是怎么表白粉丝的】,完整代码如下:
# -*- coding: utf-8 -*-
"""
Created on Sat Oct 17 12:27:33 2020
@author: kimol_love
"""
import requests
def get_fansInfo():
'''
获取粉丝相关信息
'''
url = 'https://me.csdn.net/api/relation/index?pageno=%d&pagesize=%d&relation_type=fans' # 接口地址
COOKIEs = {} # CSDN登陆后的COOKIEs
headers = { # 请求头
'User-Agent': 'Mozilla/5.0 (Windows NT 6.3; Win64; x64; rv:81.0) Gecko/20100101 Firefox/81.0',
'Accept': 'application/json, text/plain, */*',
'Accept-Language': 'zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2',
'Referer': 'https://i.csdn.net/',
'Origin': 'https://i.csdn.net',
'Connection': 'keep-alive',
'TE': 'Trailers',
}
# 获取粉丝总数
res = requests.get(url%(1,10),headers=headers,COOKIEs=COOKIEs)
res_json = res.json()
N_fans = res_json['data']['data_all']
print('一共有%d个粉丝'%N_fans)
# 获取全部粉丝数据
res = requests.get(url%(1,N_fans),headers=headers,COOKIEs=COOKIEs)
res_json = res.json()
return res_json
def download_avatar(username,url):
'''
下载用户头像
'''
savePath = './avatars' # 头像存储目录
res = requests.get(url)
with open('%s/%s.jpg'%(savePath,username),'wb') as f:
f.write(res.content)
if __name__ == '__main__':
fans = get_fansInfo()
for f in fans['data']['list']:
username = f['fans'] # 用户名
url = f['avatar'] # 头像地址
download_avatar(username,url)
print('用户"%s"头像下载完成!'%username)
将头像下载完毕并去重,结果如下:一共有2796张非重复图片,有了它们,我们便可以开始——搞事情!
2.千图成像
所谓「千图成像」就是用很多张图片拼接成一张完整的图片,它需要两个部分:「一张背景图」,「一个图片库」。根据背景图的结构用图片库中的图片来进行拼接,最终形成新的图片。
一种最简单直观的思路便是:遍历背景图中的每个像素点,并用图库中与之颜色最相近的图片粘贴在这个位置。
因此,我们首先需要计算每个图片的“平均颜色”,即图片像素点(R,G,B)的平均值,代码如下:
def compute_mean(imgPath):
'''
计算平均(R,G,B)
'''
img = Image.open(imgPath)
img = img.convert('RGB')
imgArray = np.array(img)
R = np.mean(imgArray[:,:,0])
G = np.mean(imgArray[:,:,1])
B = np.mean(imgArray[:,:,2])
return (R,G,B)
遍历图片库中的每张图片,并得到它们的平均距离,生成一个图片列表,以便后续的使用:
def get_imgList(imgDir):
'''
获取图片列表(图片目录及平均颜色)
'''
imgList = []
for imgName in os.listdir(imgDir):
path = imgDir+imgName
color = compute_mean(path)
imgList.append({'path':path,'RGB':color})
return imgList
该列表的每个元素是一个字典,包括了「path」和「RGB」,分别表示图片的路径以及图片的平均颜色,如下:在得到了图片的“平均颜色”后,便是比较背景图的像素点与它的相似性,这里采用了欧式距离:
def compute_distance(color1, color2):
'''
计算两张图的颜色差
'''
dis = 0
for i in range(len(color1)):
dis += (color1[i]-color2[i])**2
dis = dis**0.5
return dis
剩下的便是遍历背景图,并进行比较填充,代码如下:
def create_image(bgImg,imgDir,N=10,M=50):
'''
根据背景图,用头像填充出新图
bgImg:背景图地址
imgDir:头像目录
N:背景图缩放的倍率
M:头像的大小(MxM)
'''
# 获取图片列表
imgList = get_imgList(imgDir)
# 读取背景图
bg = Image.open(bgImg)
bg = bg.resize((bg.size[0]//N,bg.size[1]//N)) # 缩放
bgArray = np.array(bg)
width = bg.size[0]*M # 新生成图片的宽度
height = bg.size[1]*M # 新生成图片的高度
# 创建空白的新图
newImg = Image.new('RGB',(width,height))
# 循环填充图
for x in range(bgArray.shape[0]):
for y in range(bgArray.shape[1]):
## 找到距离最小的图片
minDis = 10000
index = 0
for img in imgList:
dis = compute_distance(img['RGB'],bgArray[x][y])
if dis index = img['path']
minDis = dis
## 填充
tempImg = Image.open(index)
tempImg = tempImg.resize((M,M))
newImg.paste(tempImg,(y*M,x*M))
print('(%d, %d)'%(x,y))
# 保存图片
newImg.save('千图成像.jpg')
调用它:
create_image('bg.jpg','./avatars(dr)/',5,50)
函数的输入有「bgImg」、「imgDir」、「N」、「M」,它们分别表示:
参数 | 含义 |
---|
bgImg | 背景图的地址 |
imgDir | 图片库的目录 |
N | 背景图的压缩比率 |
M | 填充的图片大小 |
注:imgDir最后需要有目录连接符/,否则会报错;N是为了将背景图的尺寸进行调整压缩,否则容易因为像素点太多而执行缓慢。
最后,代码一Run,小图在手,简单的快乐,美滋滋~
写在最后客观来说,该代码的执行效率并不高,特别是当N设置得较小(即背景图片像素点很多)或者图片库中的图片很多时。针对这个问题,也有很多优化方法,例如采用图片向量的方式对图片进行编码,进而比较等等。感兴趣的小伙伴可以参考这篇文章。如果需要完整代码,请在后台回复「千图成像」即可领取~
最后,祝愿最可爱的小伙伴们圣诞快乐?,愿我们聚于一图,焕尽万丈芒!另祝即将奔赴考研战场的小伙伴「考的都会,会的都考」!
我是kimol君,咋们下次再会~创作不易,大侠请留步… 动起可爱的双手,来波点赞关注再走呗 (๑◕ܫ←๑)