美翻你的朋友圈,Python生成蒙太奇马赛克图片

举报
北山啦 发表于 2021/05/26 13:46:54 2021/05/26
【摘要】 一、前言 我们有时候会听到这么一个词–“蒙太奇”,但却不知道这个词是什么意思。蒙太奇原为建筑学术语,意为构成、装配。而后又延伸为一种剪辑理论:当不同镜头拼接在一起时,往往又会产生各个镜头单独存在时所不具有的特定含义。这就是我们经常听到了蒙太奇手法,在电影《飞屋环游记》中皮克斯运用蒙太奇手法,用一个不到5分钟的短片展现了主角的大半人生,感动无数观众。下面我们就看看今天的...

一、前言

我们有时候会听到这么一个词–“蒙太奇”,但却不知道这个词是什么意思。蒙太奇原为建筑学术语,意为构成、装配。而后又延伸为一种剪辑理论:当不同镜头拼接在一起时,往往又会产生各个镜头单独存在时所不具有的特定含义。这就是我们经常听到了蒙太奇手法,在电影《飞屋环游记》中皮克斯运用蒙太奇手法,用一个不到5分钟的短片展现了主角的大半人生,感动无数观众。下面我们就看看今天的内容同蒙太奇有何关系。

二、效果展示

说这么多都是虚的,下面我们看看效果实现的效果,到底什么是蒙太奇马赛克图片,这里用小松菜奈的照片作为测试:
在这里插入图片描述
最左边的是蒙太奇图缩小的效果,第二个则是正常大小显示的效果,第三张是原图,第四张是截取的某个区域的细节。从图四可以很容易看出,我们的蒙太奇图片是使用许多不同的图片拼接而成的。

三、代码实现

程序的实现分为几个步骤,首先我们需要准备工作,一个是我们的底图,也就是上面的图三。另外就是需要一个图片集,这个图片集的选取有几个规范,首先不能有gif图和png图片,其次就是图片的颜色尽量丰富,图片数量也多一些,这样效果会更好。另外就是选取长宽比接近1的图片效果会更好。然后就是我们代码部分的工作了:

  1. 图片预处理
  2. 获取颜色的主色调列表
  3. 遍历底图的每个像素块
  4. 在色调列表中寻找与当前色调块最相近的图片
  5. 将图片修改大小后粘贴到当前遍历的色调块
  6. 保存图片

大家对于上面的步骤或许还有些疑问,这些疑问在具体实现中细说。先看看我们要用到的一些模块:

import os
import cv2
import math
import numpy as np

  
 
  • 1
  • 2
  • 3
  • 4

其中opencv的安装如下:

pip install opencv-python

  
 
  • 1

3.1、图片预处理

人工挑图片还是比较麻烦的,所以我们只要求人先挑好一些图片,然后我们将不符合规范的图片删除即可:

def renameImages(path):
	//获取图片路径列表 filelist = [path + i for i in os.listdir(path)]
	//用数字给图片命名 img_num = str(len(filelist)) name = int(math.pow(10, len(img_num))) //遍历列表 for file in filelist: //删除gif和png图片 if file.endswith('.gif') or file.endswith('.GIF') or file.endswith('.png') or file.endswith('.PNG'): os.remove(file) continue
		# 对图片以数字编号重命名 os.rename(file, path + str(name) + '.jpg') name += 1

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

执行上面的方法后我们就把合适的图片筛选出来了。

3.2、获取颜色的主色调列表

获取主色调列表前我们需要先获取主色调,这里直接使用bgr值的平均值作为主色调:

def getDominant(im): """获取主色调""" b = int(round(np.mean(im[:, :, 0]))) g = int(round(np.mean(im[:, :, 1]))) r = int(round(np.mean(im[:, :, 2]))) return (b, g, r)

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

通常RGB模式的图片我们接触的比较多,但是在OpenCV中图片是以BGR模式读取,每个字母的含义是一样的,只是顺序不同,这里需要注意一下。接下来我们获取主色调列表:

def getColors(path): """获取图片列表的色调表""" colors = [] # 获取图片列表 filelist = [path + i for i in os.listdir(path)] # 遍历列表 for file in filelist: # 读取图片 im = cv2.imdecode(np.fromfile(file, dtype=np.uint8), -1) try: # 获取图片主色调 dominant = getDominant(im) except: continue # 将主色调添加到色调列表中 colors.append(dominant) return colors

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

有了色调列表,我们对比颜色的操作就可以直接同色调列表进行了。

3.3、寻找主色调最接近的图片

我是通过比较两张图片主色调的BGR值,然后将差的绝对值相加的方式获得色调的差异:

def fitColor(color1, color2): """返回两个颜色之间的差异大小"""
	# 求出b通道之间的差异 b = color1[0] - color2[0] # 求出g通道之间的差异 g = color1[1] - color2[1] # 求出r通道之间的差异 r = color1[2] - color2[2] # 返回绝对值的和 return abs(b) + abs(g) + abs(r)

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

3.4、遍历,寻找并粘贴

这里就是我们的方法主体了,内容比较多,我们先看看代码:

def generate(im_path, imgs_path, box_size, multiple=1): """生成图片""" # 读取图片列表 img_list = [imgs_path + i for i in os.listdir(imgs_path)] # 读取图片 im = cv2.imread(im_path) im = cv2.resize(im, (im.shape[1]*multiple, im.shape[0]*multiple)) # 获取图片宽高 width, height = im.shape[1], im.shape[0] # 遍历图片像素 for i in range(height // box_size+1): for j in range(width // box_size+1): # 图块起点坐标 start_x, start_y = j * box_size, i * box_size # 初始化图片块的宽高 box_w, box_h = box_size, box_size # 截取当前遍历到的图块 box_im = im[start_y:, start_x:] if i == height // box_size: box_h = box_im.shape[0] if j == width // box_size: box_w = box_im.shape[1] if box_h == 0 or box_w == 0: continue # 获取主色调 dominant = getDominant(im[start_y:start_y+box_h, start_x:start_x+box_w]) img_loc = 0 # 差异,同主色调最大差异为255*3 dif = 255 * 3 # 遍历色调表,查找差异最小的图片 for index in range(colors.__len__()): if fitColor(dominant, colors[index]) < dif: dif = fitColor(dominant, colors[index]) # 色调列表同图片列表的位置是一致的,所以我们获取色调下标即可 img_loc = index # 读取差异最小的图片,img_list[img_loc]为差异最小的图片 box_im = cv2.imdecode(np.fromfile(img_list[img_loc], dtype=np.uint8), -1) # 转换成合适的大小 box_im = cv2.resize(box_im, (box_w, box_h)) # 铺垫色块 im[start_y:start_y+box_h, start_x:start_x+box_w] = box_im j += box_w i += box_h # 返回结果图 return im

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61

首先我们看看传入的参数都是什么含义:

im_path	: 底图的路径
imgs_path : 图片列表的根目录
box_size : 像素块的大小
multiple=1 : 图片的缩放大小,默认为1

  
 
  • 1
  • 2
  • 3
  • 4

前面两个参数非常好理解。对于box_size参数的解释就是效果图四种,每张照片的尺寸,因为我全部以正方形处理,所以只有一个大小。而multiple参数则是缩放大小,当我们底图为50*50没有设置缩放时,结果图也是50*50,当我们将缩放设置为2,结果图则为100*100。因为图片太小的话看不到像素块中的图片,所以利用缩放让效果更好,但是缩放值设置过大的话图片内存会大许多。其它部分的解释都在代码中了。最后再给大家看一张效果图:
在这里插入图片描述
因为事先效果不是非常乐观,所以给大家看一张朦胧的效果图。感兴趣的读者可以关注我的个人公众号:ZackSock。如果觉得文章有帮助可以动动小手点个赞哈~

文章来源: zacksock.blog.csdn.net,作者:ZackSock,版权归原作者所有,如需转载,请联系作者。

原文链接:zacksock.blog.csdn.net/article/details/106106539

【版权声明】本文为华为云社区用户转载文章,如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。