Python绘制蒙太奇图片(opencv)


写在前面

原理其实很简单,把目标图像划分成许多区域,然后从给定的图库里寻找一张和本区域最相似的图片,将找到的图片贴到对应区域上,重复这一过程,然后就得到了目标图片。

本文所用素材来自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()

写在后面

因为素材太少,所以最后得到的图片并不理想,就不再贴结果了。