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

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

安裝 Python與 Pillow

可參考以前的博文,Python 及其庫的安裝緩存

流程及思路

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

預計流程ui

  • 建立臨時文件夾緩存可能生成或後續須要使用的圖片
  • 讀取圖片,進行預處理後將處理後圖片按照必定命名規範保存至緩存文件夾
  • 切換路徑至臨時文件夾
  • 依次打開圖片,進行圖像合併
  • 合併完成後保存圖片
  • 刪除臨時文件夾
  • 展現合併圖片

其中圖片預處理能夠爲拉伸、旋轉、裁剪等變換,由於我後續須要拼圖時全部照片都應該爲正方形,所以我須要對圖片進行一個裁剪操做使圖片比例爲 1:1,爲了保證裁剪區域在圖像正中,須要進行判斷長短邊操做。this

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

DEMO

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

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

row 爲合併圖片分佈行參數,bol_auto_place == False 時有效。ip

col 爲合併圖片分佈列參數,bol_auto_place == False 時有效。ci

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 張照片拼圖

相關文章
相關標籤/搜索