如何用多张图片组成一张你偶像的图片
这里写目录标题
前言
最近沉迷python的图片处理不可自拔,然后发现了一个好玩的,可以由多张图片组成一张图片,放大才能看出每个小图片。
挺有意思的所以我查找资料学习了一下,因为有很多不懂的地方,所以我加了很多注释,这里分享个大家
原博客:2万8千张图片如何用python组成一张你女朋友的自画像!
代码
我就直接上代码了。
思路其实就是把一张大图分割成很多块,然后找你的素材图中与这一块颜色相近的图片替代他
import cv2
import glob
import argparse #解析命令行参数和选项
import numpy as np
from itertools import product #迭代器
from tqdm import tqdm #进度条
#图片文件
def parseArgs():
#创建ArgumentParser对象
parser=argparse.ArgumentParser('拼接马赛克图片')
#给parser 实例添加属性
parser.add_argument('--targetpath',type=str,default='examples/11.jpg',help='目标图像路径')
parser.add_argument('--outputpath',type=str,default='output.jpg',help='输出图像路径')
parser.add_argument('--sourcepath',type=str,default='sourceimages',help='用于拼接图像的所有源图像文件夹路径')
parser.add_argument('--blocksize',type=int,default=100,help='马赛克快大小')
#把parser 的所有属性 返回到args 子类实例中
#通过args 可以使用parser的所有属性
args=parser.parse_args()
return args
#读取所有源图片并计算对应颜色平均值
def readSourceImages(sourcepath,blocksize):
print('开始读取图像')
#合法图像列表
sourceimages=[]
#平均颜色列表
avgcolors=[]
#tqdm() :进度条,中间放任意迭代器
#glob.glob() :查找符合特定规则的文件路径名
for path in tqdm(glob.glob("{}/*.jpg".format(sourcepath))):
#读入一副彩色图片
#cv2.IMREAD_COLOR:代表彩色图片,但是忽略alpha通道
image=cv2.imread(path,cv2.IMREAD_COLOR)
#修改图片的大小为马赛克块的大小
image=cv2.resize(image,(blocksize,blocksize),interpolation=cv2.INTER_CUBIC)
#计算图片缩小以后的颜色值 缩小以后图片上的颜色的平均值
avgcolor=np.sum(np.sum(image,axis=0),axis=0)/(blocksize*blocksize)
sourceimages.append(image)
avgcolors.append(avgcolor)
print('读取结束')
return sourceimages,np.array(avgcolors)
#调用所有函数
def main(args):
#读入原图
targetimage=cv2.imread(args.targetpath)
#改变原图的尺寸,在这里我把原图的放大了20倍,这样最后的图片放大时,组成大图的小图片不会模糊,同时马赛克块的大小也要调大一些
#大家可以把原图放大更多,但是会耗费更多的内存
#(w,h)
targetimage = cv2.resize(targetimage, (int(targetimage.shape[1]*20),int(targetimage.shape[0]*20)), interpolation=cv2.INTER_CUBIC)
#返回一个和原图维度一致,数组元素个数一致的,用0填充的数组
outputimage=np.zeros(targetimage.shape,np.uint8)
sourceimages,avgcolors=readSourceImages(args.sourcepath,args.blocksize)
print('开始制作')
#原图的宽度/马赛克的大小=横向填充的图片的个数 i
#j:原图的高度/马赛克的大小=纵向填充的图片的个数
#一个rgb图像是由一个三维数组组成 ,第一维度数组的长度就是图片的高度,或者说是图片纵向的像素的个数
#第二维度数组的长度是图片的宽度,或者说是横向的像素的个数
#第三维度数组的长度为3,数组的元素为当前这个像素的三原色的颜色值,既r,g,b
#tqdm:进度条
#product:迭代器
#range:整数列表 从0到int(targetimage.shape[1]/args.blocksize)
for i,j in tqdm(product(range(int(targetimage.shape[1]/args.blocksize)), range(int(targetimage.shape[0]/args.blocksize)))):
#在原图上截取一个像素块的大小
block=targetimage[j*args.blocksize:(j+1)*args.blocksize,i*args.blocksize:(i+1)*args.blocksize,:]
#计算这一块的颜色平均值
avgcolor=np.sum(np.sum(block,axis=0),axis=0)/(args.blocksize*args.blocksize)
#求范数(线性代数需要学起来了) 求整体的矩阵平方和再开平方
distance=np.linalg.norm(avgcolor-avgcolors,axis=1)
#返回axis轴向上最小值的索引构成的矩阵。 (还是要学数学),
#按我的理解是返回所有缩小的素材图片中颜色与原图截取的图片颜色最相近的颜色值
idx=np.argmin(distance)
#把找出的缩小的素材图替换原图上的部分
outputimage[j*args.blocksize:(j+1)*args.blocksize,i*args.blocksize:(i+1)*args.blocksize,:]= sourceimages[idx]
#输出
cv2.imwrite(args.outputpath,outputimage)
#保存
cv2.imshow('result',outputimage)
print('制作完成')
if __name__=='__main__':
main(parseArgs())
效果
这是最后生成的效果,放大图片可以看出是有一个个的小图片组成的