一、前言
我們有時候會聽到這麼一個詞–“蒙太奇”,但卻不知道這個詞是什麼意思。蒙太奇原爲建築學術語,意爲構成、裝配。而後又延伸爲一種剪輯理論:當不同鏡頭拼接在一起時,往往又會產生各個鏡頭單獨存在時所不具有的特定含義。這就是我們經常聽到了蒙太奇手法,在電影《飛屋環遊記》中皮克斯運用蒙太奇手法,用一個不到5分鐘的短片展現了主角的大半人生,感動無數觀衆。下面我們就看看今天的內容同蒙太奇有何關係。
二、效果展示
說這麼多都是虛的,下面我們看看效果實現的效果,到底什麼是蒙太奇馬賽克圖片,這裏用小松菜奈的照片作爲測試:
最左邊的是蒙太奇圖縮小的效果,第二個則是正常大小顯示的效果,第三張是原圖,第四張是截取的某個區域的細節。從圖四可以很容易看出,我們的蒙太奇圖片是使用許多不同的圖片拼接而成的。
三、代碼實現
程序的實現分爲幾個步驟,首先我們需要準備工作,一個是我們的底圖,也就是上面的圖三。另外就是需要一個圖片集,這個圖片集的選取有幾個規範,首先不能有gif圖和png圖片,其次就是圖片的顏色儘量豐富,圖片數量也多一些,這樣效果會更好。另外就是選取長寬比接近1的圖片效果會更好。然後就是我們代碼部分的工作了:
- 圖片預處理
- 獲取顏色的主色調列表
- 遍歷底圖的每個像素塊
- 在色調列表中尋找與當前色調塊最相近的圖片
- 將圖片修改大小後粘貼到當前遍歷的色調塊
- 保存圖片
大家對於上面的步驟或許還有些疑問,這些疑問在具體實現中細說。先看看我們要用到的一些模塊:
import os
import cv2
import math
import numpy as np
其中opencv的安裝如下:
pip install opencv-python
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
執行上面的方法後我們就把合適的圖片篩選出來了。
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)
通常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
有了色調列表,我們對比顏色的操作就可以直接同色調列表進行了。
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)
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
首先我們看看傳入的參數都是什麼含義:
im_path : 底圖的路徑
imgs_path : 圖片列表的根目錄
box_size : 像素塊的大小
multiple=1 : 圖片的縮放大小,默認爲1
前面兩個參數非常好理解。對於box_size
參數的解釋就是效果圖四種,每張照片的尺寸,因爲我全部以正方形處理,所以只有一個大小。而multiple
參數則是縮放大小,當我們底圖爲50*50
沒有設置縮放時,結果圖也是50*50
,當我們將縮放設置爲2,結果圖則爲100*100
。因爲圖片太小的話看不到像素塊中的圖片,所以利用縮放讓效果更好,但是縮放值設置過大的話圖片內存會大許多。其它部分的解釋都在代碼中了。最後再給大家看一張效果圖:
因爲事先效果不是非常樂觀,所以給大家看一張朦朧的效果圖。感興趣的讀者可以關注我的個人公衆號:ZackSock。如果覺得文章有幫助可以動動小手點個贊哈~