美翻你的朋友圈,Python生成蒙太奇馬賽克圖片

一、前言

我們有時候會聽到這麼一個詞–“蒙太奇”,但卻不知道這個詞是什麼意思。蒙太奇原爲建築學術語,意爲構成、裝配。而後又延伸爲一種剪輯理論:當不同鏡頭拼接在一起時,往往又會產生各個鏡頭單獨存在時所不具有的特定含義。這就是我們經常聽到了蒙太奇手法,在電影《飛屋環遊記》中皮克斯運用蒙太奇手法,用一個不到5分鐘的短片展現了主角的大半人生,感動無數觀衆。下面我們就看看今天的內容同蒙太奇有何關係。

二、效果展示

說這麼多都是虛的,下面我們看看效果實現的效果,到底什麼是蒙太奇馬賽克圖片,這裏用小松菜奈的照片作爲測試:
在這裏插入圖片描述
最左邊的是蒙太奇圖縮小的效果,第二個則是正常大小顯示的效果,第三張是原圖,第四張是截取的某個區域的細節。從圖四可以很容易看出,我們的蒙太奇圖片是使用許多不同的圖片拼接而成的。

三、代碼實現

程序的實現分爲幾個步驟,首先我們需要準備工作,一個是我們的底圖,也就是上面的圖三。另外就是需要一個圖片集,這個圖片集的選取有幾個規範,首先不能有gif圖和png圖片,其次就是圖片的顏色儘量豐富,圖片數量也多一些,這樣效果會更好。另外就是選取長寬比接近1的圖片效果會更好。然後就是我們代碼部分的工作了:

  1. 圖片預處理
  2. 獲取顏色的主色調列表
  3. 遍歷底圖的每個像素塊
  4. 在色調列表中尋找與當前色調塊最相近的圖片
  5. 將圖片修改大小後粘貼到當前遍歷的色調塊
  6. 保存圖片

大家對於上面的步驟或許還有些疑問,這些疑問在具體實現中細說。先看看我們要用到的一些模塊:

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。如果覺得文章有幫助可以動動小手點個贊哈~

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章