[原創]使用 Python + Pillow 完成圖片牆拼圖


因為腦子里的一些想法,需要將一些照片拼接在一起,首先想到了使用APP直接操作,結果下載了許多應用后發現最多只能支持九張照片的拼接。然后又找了些美圖秀秀之類,都無法滿足我的需求,甚至我都想到使用PS去進行操作,但是如果使用PS那可就變成了一項耗時間的活了呢。於是繼續的查找解決方案,在一個小角落里找到了使用Pillow搭建照片牆的例子,心想這就是我想要的,細細查找發現果不其然,一下子明朗了許多。在此對使用 Python + Pillow 完成的拼圖實現進行記錄。

安裝 Python與 Pillow

可參考之前的博文,Python 及其庫的安裝

流程及思路

需求:將一個文件夾中按名稱排序的方式進行拼圖操作,逐行或逐列操作。

預計流程

  • 創建臨時文件夾緩存可能生成或后續需要使用的圖片
  • 讀取圖片,進行預處理后將處理后圖片按照一定命名規范保存至緩存文件夾
  • 切換路徑至臨時文件夾
  • 依次打開圖片,進行圖像合並
  • 合並完成后保存圖片
  • 刪除臨時文件夾
  • 展示合並圖片

其中圖片預處理可以為拉伸、旋轉、裁剪等變換,因為我后續需要拼圖時所有照片都應該為正方形,因此我需要對圖片進行一個裁剪操作使圖片比例為 1:1,為了保證裁剪區域在圖像正中,需要進行判斷長短邊操作。

進行合並圖片時需要空余區域盡可能少,且合並圖片比例不能太畸形。例如 30 張圖片可以分為 5 × 6 排布,31 張照片可以分布為 4 × 8 排布。最理想狀態是腳本自動識別圖片個數並合理分配,這塊功能暫時沒有寫入 DEMO 中,行與列目前需要手動分配。

DEMO

在 Python 腳本中引用 Pillow 的方法也可以參見 DEMO 程序。其中,

bol_auto_place 暫時為可選項,置為 True 表示將自動分配合並后畫布大小,目前只有根據圖片多少開平方,然后合並為一個大正方形圖片,手動設置合並排布時需要將其置為 False

row 為合並圖片分布行參數,bol_auto_place == False 時有效。

col 為合並圖片分布列參數,bol_auto_place == False 時有效。

nw 為緩存圖片寬度設定,nh 為緩存圖片高度設定。合並文件的大小由排布及緩存圖片大小自動設定。

DEMO 腳本中所使用到的一些 function 有不懂的可百度或谷歌,查看各自的詳細描述。腳本在使用時與圖片放在一起,然后點擊運行,運行期間將會顯示當前處理圖片,處理完成后將會展示合並圖片。合並完成后圖片以 PNG 格式存儲於同路徑下splicing_picture.png文件。DEMO 程序的源代碼及幾個參考文件可點此進行下載

#####################################################
# Notice !                                          #
# This script file should be placed in the same     #
# folder as the image.                              #
#####################################################

import sys, os, shutil, math
from PIL import Image

#####################################################
# parameter setting                                 #
#####################################################
bol_auto_place = False                     # auto place the image as a squared image, if 'True', ignore var 'row' and 'col' below
row            = 4                         # row number which means col number images per row
col            = 8                         # col number which means row number images per col
nw             = 400                       # sub image size, nw x nh
nh             = 400

path = os.getcwd();          # acquire current folder path

if os.path.exists('tmp'):    # ensure the 'tmp' folder is empty
   shutil.rmtree('tmp')
os.makedirs('tmp')

file_ls = os.listdir()       # list all files in this folder

i = 0                        # a counter for images
for file in file_ls:
	name, extension = os.path.splitext(file);    # get file info[name, extension]
	if (extension == '.png' or extension == '.jpg' or extension == '.jpeg') and name != 'splicing_picture':    # select the image
		i += 1                               # image counter++
		print('%s...%s%s' % (i, name, extension))
		os.chdir(path)                       # ensure the image folder in every loop
		im = Image.open(file)                # open the image
		w, h = im.size                       # get image info
		#print('Original image size: %sx%s' % (w, h))
		if nw == nh:                         # if image should be 1:1 size
			if w >= h:
				box = ((w - h) // 2, 0, (w + h) // 2, h)
			else:
				box = (0, (h - w) // 2, w, (h + w) // 2)
			region = im.crop(box)            # crop the image to 1:1 and keep center region
		else:
			region = im                      # do nothing
		sname = '%s%s' % (str(i), '.png')    # rename 'x.png', x is a number from 1 to N
		os.chdir('tmp')                      # get into the folder 'tmp'
		region.save(sname, 'png')            # save the square image

os.chdir(path)        # ensure the path
os.chdir('tmp')

if bol_auto_place:    # auto place a big 1:1 square image 
	row = math.ceil(i ** 0.5)
	col = math.ceil(i ** 0.5)

dest_im = Image.new('RGBA', (col * nw, row * nh), (255, 255, 255))    # the image size of splicing image, background color is white

for x in range(1, col + 1):          # loop place the sub image
	for y in range(1,row + 1):
		try:
			src_im = Image.open("%s.png" % str( x + ( y - 1 ) * col))  # open files in order
			resize_im = src_im.resize((nw, nh), Image.ANTIALIAS)       # resize again
			dest_im.paste(resize_im, ((x-1) * nw, (y-1) * nh))         # paste to dest_im
		except IOError:
			pass

os.chdir(path)        # ensure the path
shutil.rmtree('tmp')  # delete the 'tmp'

dest_im.save('splicing_picture.png', 'png')
dest_im.show()        # finish

運行效果圖

30 張照片按照 4 × 8 的排布方式,圖片拼合后效果圖如下所示。個人對這樣的結果還是相當滿意的,也可以調整成 5 × 6 的排布方式,只需更改 rowcol 的參數設定后重新運行即可。

30 張照片拼圖


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM