写在前面
原理其实很简单,把目标图像划分成许多区域,然后从给定的图库里寻找一张和本区域最相似的图片,将找到的图片贴到对应区域上,重复这一过程,然后就得到了目标图片。
本文所用素材来自lol官方网站,详情参阅爬取LOL英雄皮肤,批量爬取的代码会抽时间写进去。
用到的包
#OpenCV
import cv2
#操作文件夹
import os
#构造像素点矩阵
import numpy as np
#用于获取点图中出现频率最高的像素点
import collections
一个小问题
"""
OpenCV无法直接加载含有中文路径的图片,所以需要自定义一个方法
同样的,如果要保存到中文路径,也需要定义一个方法,请自行搜索
"""
def cv_imread(file_path):
cv_img = cv2.imdecode(np.fromfile(file_path,dtype = np.uint8),-1)
return cv_img
缩小素材
#将素材修改为100x100像素的文件
def ready_img():
#主要路径
readpath = './英雄联盟'
#将路径下所有文件及文件夹加载为列表
directorys = os.listdir(readpath)
#创建点图的保存路径
if not os.path.exists('save'):
os.mkdir('save')
savepath = './save'
#n = 0
#遍历所有文件夹
for directory in directorys:
#获取文件夹下所有图片,加载为列表
photos = os.listdir(readpath + '/' + directory)
#逐个处理
for photo in photos:
#捕捉一下异常
try:
#获取图片相对路径
file = readpath + '/' + directory + '/' + photo
#使用自定义方法读取图片
img = cv_imread(file)
#将图片修改为100x100像素
img = cv2.resize(img,(100,100))
#将修改后的图片写入文件
cv2.imwrite(savepath + '/' + str(n) + '.jpg',img)
#n += 1
#遇到异常时输出图片名称并继续
except:
print(photo)
pass
获取索引
def get_index():
#设置点图路径
readpath = './save'
#将点图加载为列表
files = os.listdir(readpath)
#n = 0
#创建一个空字符串
s = ''
#遍历所有点图
for file in files:
#创建空列表
li = []
#n += 1
#OpenCV读取图片,注:如果包含中文路径还请使用cv_imread方法
imgpath = readpath + '/' + file
img = cv2.imread(imgpath)
#循环读取每个像素点
for i in range(100):
for j in range(100):
b = img[i,j,0]
g = img[i,j,1]
r = img[i,j,2]
#将每个像素点的bgr信息保存到列表中
li.append((b,g,r))
#获取出现最多的一个像素点
most = collections.Counter(li).most_common(1)
#将所有信息处理成规范格式,并加到字符串
s += file
s += ':'
s += str(most[0][0]).replace('(',"").replace(")","")
s += '\n'
#将信息保存为文本文件
f = open('filename.txt','w')
f.write(s)
f.close()
创建蒙太奇图片
#预处理,从文本中读取信息
def create_ready():
#文件路径
readpath = './save'
#读取信息
fs = open('filename.txt','r')
n = 0
#创建空列表
dic = []
#读取每一行
for line in fs.readlines():
n += 1
#拆分信息
temp = line.split(":")
#文件名
file = temp[0]
#bgr信息以逗号分隔,也需要拆分
bgr = temp[1].split(",")
b = int(bgr[0])
g = int(bgr[1])
r = int(bgr[2])
#将信息以元组方式添加到列表
dic.append((file,(b,g,r)))
return dic
#开始创建
def create_now():
#文件路径
readpath = './save'
#读取目标图片
img = cv2.imread('new_york.jpg')
#获取图片的尺寸
s = np.shape(img)
#创建一个一百倍大小的点阵,目标图片不要过大
big = np.zeros((100 * s[0],100 * s[1],3),dtype = np.uint8)
#调用前述方法获取信息
list1 = create_ready()
#遍历每一个像素点
for i in range(s[0]):
print(i)
for j in range(s[1]):
#获取该像素点的bgr信息
b = img[i,j,0]
g = img[i,j,1]
r = img[i,j,2]
#将信息打乱,防止相似像素点都取到相同点图
np.random.shuffle(list1)
#遍历每一条信息
for item in list1:
#获取信息中的bgr信息
imgb = item[1][0]
imgg = item[1][1]
imgr = item[1][2]
#计算欧氏距离
distance = (imgb - b) ** 2 + (imgg - g) ** 2 + (imgr - r) ** 2
#如果距离小于给定值则跳出循环,并获取对应的文件名
if distance < 75:
filepath = readpath + '/' + str(item[0])
break
#读取该点图
little = cv2.imread(filepath)
#填充大图中对应的像素点
big[i * 100:(i + 1) * 100,j * 100:(j + 1) * 100] = little
#保存填充完毕的蒙太奇图片
cv2.imwrite("bigyork.jpg",big)
#入口函数
if __name__ == '__main__':
ready_img()
get_index()
get_photo()
create_now()
写在后面
因为素材太少,所以最后得到的图片并不理想,就不再贴结果了。