python3一鍵排版證件照(一寸照、二寸照),附源代碼


 

又到了一年一度辦公園年卡的日子了,每年需要一張一寸照片,庫存今年告罄

如果拿着一寸照片去沖印,商家那個沖印的價格可比沖印普通照片不知道貴了多少唄(目測10倍以上)

其實是一樣的相紙啊

於是乎,為了省這點錢就自己排版好了

如果選用工具的話,不會ps的推薦“光影魔術手v3.1.2”(最新版感覺不太好用)

 

 

 

  

 

but,作為一個技術宅,我當然是選擇自己來實現啦!

話不多說,說干就干


 

思路:

1. 首先需要一張已經拍攝好的證件照,尺寸比例可以不完全按照標准

2. 按比例裁剪(1寸、2寸的比例不同)

3. 縮放到標准尺寸

4. 排版到5寸或者6寸照片上

 

照片尺寸:寬*高(單位:像素)
1寸照片:295*413
2寸照片:413*626
5寸照片(橫版):1500*1050
6寸照片(橫版):1800*1200

 

環境:

python3 + pillow庫

具體操作:

 

1. 裁剪

以1寸照片為例,其高:寬 = 1.4,為了使原始照片不失真,應該按照這個1.4的比例進行裁剪

若大於1.4說明高度多了,需要進行上下裁剪;若小於1.4說明寬度多了,需要進行左右裁剪。這里的裁剪均是對稱平均裁剪

Image.crop((left, up, right, below))

參數:

需要四個參數,分別是目標照片的四個邊線距離左邊和上邊的距離

 

 值得注意的是:參數是一個元組,因此看起來有兩對括號!

 

2. 縮放

將已經調整好比例的照片進行標准縮放,標准如下(單位:像素):

1寸照片:295*413
2寸照片:413*626

Image.resize(width, height)

參數:

width:寬

heght:高

 

3. 排版

先想好沖印5寸照片還是6寸照片,以及是需要1寸、2寸還是混合排版

這兩個因素都會影響到排版問題,主要是橫or豎的問題

以5寸照片上排版1寸照片為例(5寸橫版,1寸豎版,2*4排列),是這樣滴:

 

 

 

在排列之前,我細心的用畫筆畫出了裁剪線,這樣手殘的人再也不用哆哆嗦嗦剪歪了

bk = Image.new("RGB", [WIDTH_5IN,HEIGHT_5IN], (255,255,255))# 創建一個5寸大小的,白色背景的畫板  
draw = ImageDraw.Draw(bk)# 創建畫筆
draw.line([(0,HEIGHT_5IN/2),(WIDTH_5IN,HEIGHT_5IN/2)],fill=128) # 橫線,fill是填充顏色

新建畫板就不多講了,注意一下參數的位置,先寬后高,顏色參數其實有多種寫法

這里主要講一下怎么在畫板上畫線,其實是需要起點坐標和終點坐標,坐標原點是畫板左上角,橫軸向右x,縱軸向下y

還是畫個圖更直觀

 

 

 

注意坐標的寫法 [(起點x,起點y),(終點x,終點y)],坐標點必須為int類型

 

我這個畫完是這樣嘞:

 

 

然后就是把照片貼上去,注意要放在每個“格子”的中心呦

Image.paste(photo, (photo左上角坐標))

因此,我們需要計算單個1寸照片在整個畫板里左上角坐標,還是畫個圖說明下:

先求每個小格子里中心點focus_point的坐標,然后根據1寸照片的大小求左上角坐標start_point

 

 

 然后根據每個start_point循環把照片貼上去就好啦!

 

 我生成的是這樣嘞,看起來很完美啊!

 

 

 

 

 同理,5寸照片排版2寸的。這里注意新建畫板時候的尺寸,因為相當於是豎版照片

 

 

5寸照片混合排列1寸、2寸的:

 

 這里注意一下:

2寸照片旋轉了90度,用到的函數中,參數必須寫上expand=True

Image.rotate(90,expand=True))

如果沒寫這個參數默認是False,這樣旋轉后的照片是按照原來的大小,會有裁剪或者黑邊,例如:

 

 

6寸照片排版1寸:

 

 

6寸照片排版2寸按照5寸照片的排版模式會有高度上的溢出

同理,6寸照片混合排列1寸、2寸的也是有溢出

解決方法:新建畫布時候按照6寸比例3:2進行適當的放大,因為沖印的時候也沒要求就是標准大小嘛

比如,我將大小改為1950*1300

 

 

 同理,6寸照片混合排列1寸、2寸的也是有溢出。修改完尺寸,混合排版的樣子就多了去了,比如:

 

 

 

 

 

 


 

源代碼:

感覺寫的有點啰嗦了,應該使用對象的方法精簡一下,很多重復的代碼改起來也很麻煩

  1 #Author:ZM
  2 
  3 """
  4 照片尺寸,寬*高(單位:像素)
  5 1寸照片:295*413
  6 2寸照片:413*626
  7 5寸照片(橫版):1500*1050
  8 6寸照片(橫版):1800*1200
  9 """
 10 from PIL import Image,ImageDraw
 11 
 12 WIDTH_1IN = 295
 13 HEIGHT_1IN = 413
 14 
 15 WIDTH_2IN = 413
 16 HEIGHT_2IN = 626
 17 
 18 WIDTH_5IN = 1500
 19 HEIGHT_5IN = 1050
 20 
 21 # 非全景6寸照片
 22 WIDTH_6IN = 1950
 23 HEIGHT_6IN = 1300
 24 
 25 def cut_photo(photo,choice):
 26     """
 27     將照片按照比例進行裁剪成1寸、2寸
 28     :param photo: 待處理的照片
 29     :param choice: <int> 1代表1寸,2代表2寸
 30     :return: 處理后的照片
 31     """
 32     width = photo.size[0] #
 33     height = photo.size[1] #
 34     rate = height / width
 35     if choice == 1:
 36         if rate < (HEIGHT_1IN/WIDTH_1IN):
 37             x = (width - int(height / HEIGHT_1IN * WIDTH_1IN)) / 2
 38             y = 0
 39             cutted_photo = photo.crop((x, y, x + (int(height / HEIGHT_1IN * WIDTH_1IN)), y + height))
 40 
 41         else:
 42             x = 0
 43             y = (height - int(width / WIDTH_1IN * HEIGHT_1IN)) / 2
 44             cutted_photo = photo.crop((x, y, x + width, y + (int(width / WIDTH_1IN * HEIGHT_1IN))))
 45         return cutted_photo
 46 
 47     if choice == 2:
 48         if rate < (HEIGHT_2IN/WIDTH_2IN):
 49             x = (width - int(height / HEIGHT_2IN * WIDTH_2IN)) / 2
 50             y = 0
 51             cutted_photo = im.crop((x, y, x + (int(height / HEIGHT_2IN * WIDTH_2IN)), y + height))
 52 
 53         else:
 54             x = 0
 55             y = (height - int(width / WIDTH_2IN * HEIGHT_2IN)) / 2
 56             cutted_photo = im.crop((x, y, x + width, y + (int(width / WIDTH_2IN * HEIGHT_2IN))))
 57 
 58         return cutted_photo
 59 
 60 def resize_photo(photo,choice):
 61     '''
 62     縮放照片
 63     :param photo: 待處理的照片
 64     :param choice: <int> 1代表1寸,2代表2寸
 65     :return: 處理后的照片
 66     '''
 67     if choice == 1:
 68         resized_photo = photo.resize((WIDTH_1IN,HEIGHT_1IN))
 69         return resized_photo
 70     if choice == 2:
 71         resized_photo = photo.resize((WIDTH_2IN, HEIGHT_2IN))
 72         return resized_photo
 73 
 74 
 75 def layout_photo_5_1(photo):
 76     """
 77     在5寸照片上排版1寸照片
 78     :param photo: 待處理照片1寸
 79     :return: 處理后的照片
 80     """
 81     bk = Image.new("RGB", [WIDTH_5IN,HEIGHT_5IN], (255,255,255))
 82     draw = ImageDraw.Draw(bk)# 創建畫筆
 83     draw.line([(0,HEIGHT_5IN/2),(WIDTH_5IN,HEIGHT_5IN/2)],fill=128) # 橫線
 84     draw.line([(WIDTH_5IN*0.25,0),(WIDTH_5IN*0.25,HEIGHT_5IN)],fill=128) # 第1條豎線
 85     draw.line([(WIDTH_5IN*0.5,0),(WIDTH_5IN*0.5,HEIGHT_5IN)],fill=128) # 第2條豎線
 86     draw.line([(WIDTH_5IN*0.75,0),(WIDTH_5IN*0.75,HEIGHT_5IN)],fill=128) # 第3條豎線
 87 
 88     focus_point = [0.125 * WIDTH_5IN,0.25 * HEIGHT_5IN]
 89     start_point = [focus_point[0] - 0.5 * WIDTH_1IN, focus_point[1] - 0.5 * HEIGHT_1IN]
 90     for i in range(0,2):
 91         for k in range(0,4):
 92             bk.paste(photo, (int(start_point[0] + (k * WIDTH_5IN / 4)), int(start_point[1] + 0.5 * i * HEIGHT_5IN)))
 93     return bk
 94 
 95 
 96 def layout_photo_5_2(photo):
 97     """
 98     在5寸照片上排版2寸照片
 99     :param photo: 待處理照片2寸
100     :return: 處理后的照片
101     """
102     bk = Image.new("RGB", [HEIGHT_5IN,WIDTH_5IN], (255,255,255)) # 豎版排版
103     # 創建畫筆
104     draw = ImageDraw.Draw(bk)
105     draw.line([(0,WIDTH_5IN/2),(WIDTH_5IN,WIDTH_5IN/2)],fill=128) # 橫線
106     draw.line([(HEIGHT_5IN*0.5,0),(HEIGHT_5IN*0.5,WIDTH_5IN)],fill=128) # 豎線
107     focus_point = [0.25 * HEIGHT_5IN, 0.25 * WIDTH_5IN]
108     start_point = [focus_point[0] - 0.5 * WIDTH_2IN, focus_point[1] - 0.5 * HEIGHT_2IN]
109     #print(focus_point,start_point)
110     for i in range(0,2):
111         for k in range(0,2):
112             bk.paste(photo, (int(start_point[0] + (k * HEIGHT_5IN / 2)), int(start_point[1] + 0.5* i * WIDTH_5IN)))
113     return bk
114 
115 def layout_photo_5_mix(photo1,photo2):
116     """
117     在5寸照片上混合排版1寸、2寸照片
118     :param photo1: 待處理照片1寸
119     :param photo1: 待處理照片2寸
120     :return: 處理后的照片
121     """
122     bk = Image.new("RGB", [WIDTH_5IN,HEIGHT_5IN], (255,255,255))
123     # 創建畫筆
124     draw = ImageDraw.Draw(bk)
125     draw.line([(0,HEIGHT_5IN/2),(WIDTH_5IN,HEIGHT_5IN/2)],fill=128) # 橫線
126     draw.line([(WIDTH_5IN*0.25,0),(WIDTH_5IN*0.25,HEIGHT_5IN)],fill=128) # 第1條豎線
127     draw.line([(WIDTH_5IN*0.5,0),(WIDTH_5IN*0.5,HEIGHT_5IN)],fill=128) # 第2條豎線
128 
129     focus_point = [0.125 * WIDTH_5IN,0.25 * HEIGHT_5IN]
130     start_point = [focus_point[0] - 0.5 * WIDTH_1IN, focus_point[1] - 0.5 * HEIGHT_1IN]
131     focus_point2 = [0.75 * WIDTH_5IN, 0.25 * HEIGHT_5IN]
132     start_point2 = [focus_point2[0] - 0.5 * HEIGHT_2IN, focus_point2[1] - 0.5 * WIDTH_2IN]
133 
134     for i in range(0,2):
135         for k in range(0,2):
136             bk.paste(photo1, (int(start_point[0] + (k * WIDTH_5IN / 4)), int(start_point[1] + 0.5 * i * HEIGHT_5IN)))
137 
138     bk.paste(photo2,(int(start_point2[0]),int(start_point2[1])))
139     bk.paste(photo2,(int(start_point2[0]),int(start_point2[1] + 0.5 * HEIGHT_5IN)))
140     return bk
141 
142 def layout_photo_6_1(photo):
143     """
144     在6寸照片上排版2寸照片
145     :param photo: 待處理照片1寸
146     :return: 處理后的照片
147     """
148     bk = Image.new("RGB", [HEIGHT_6IN,WIDTH_6IN], (255,255,255)) # 豎版排版
149     # 創建畫筆
150     draw = ImageDraw.Draw(bk)
151     draw.line([(0,WIDTH_6IN*0.25),(WIDTH_6IN,WIDTH_6IN*0.25)],fill=128) # 橫線
152     draw.line([(0,WIDTH_6IN*0.5),(WIDTH_6IN,WIDTH_6IN*0.5)],fill=128) # 橫線
153     draw.line([(0,WIDTH_6IN*0.75),(WIDTH_6IN,WIDTH_6IN*0.75)],fill=128) # 橫線
154     draw.line([(HEIGHT_6IN*0.25,0),(HEIGHT_6IN*0.25,WIDTH_6IN)],fill=128) # 豎線
155     draw.line([(HEIGHT_6IN*0.5,0),(HEIGHT_6IN*0.5,WIDTH_6IN)],fill=128) # 豎線
156     draw.line([(HEIGHT_6IN*0.75,0),(HEIGHT_6IN*0.75,WIDTH_6IN)],fill=128) # 豎線
157     focus_point = [0.125 * HEIGHT_6IN, 0.125 * WIDTH_6IN]
158     start_point = [focus_point[0] - 0.5 * WIDTH_1IN, focus_point[1] - 0.5 * HEIGHT_1IN]
159     #print(focus_point,start_point)
160     for i in range(0,4):
161         for k in range(0,4):
162             bk.paste(photo, (int(start_point[0] + (k * HEIGHT_6IN / 4)), int(start_point[1] + i * 0.25 * WIDTH_6IN )))
163     return bk
164 
165 def layout_photo_6_2(photo):
166     """
167     在6寸照片上排版2寸照片
168     :param photo: 待處理照片2寸
169     :return: 處理后的照片
170     """
171     bk = Image.new("RGB", [WIDTH_6IN,HEIGHT_6IN], (255,255,255))
172     # 創建畫筆
173     draw = ImageDraw.Draw(bk)
174     draw.line([(0,HEIGHT_6IN/2),(WIDTH_6IN,HEIGHT_6IN/2)],fill=128) # 橫線
175     draw.line([(WIDTH_6IN*0.25,0),(WIDTH_6IN*0.25,HEIGHT_6IN)],fill=128) # 第1條豎線
176     draw.line([(WIDTH_6IN*0.5,0),(WIDTH_6IN*0.5,HEIGHT_6IN)],fill=128) # 第2條豎線
177     draw.line([(WIDTH_6IN*0.75,0),(WIDTH_6IN*0.75,HEIGHT_6IN)],fill=128) # 第3條豎線
178     focus_point = [0.125 * WIDTH_6IN,0.25 * HEIGHT_6IN]
179     start_point = [focus_point[0] - 0.5 * WIDTH_2IN, focus_point[1] - 0.5 * HEIGHT_2IN]
180     for i in range(0,2):
181         for k in range(0,4):
182             bk.paste(photo, (int(start_point[0] + (k * WIDTH_6IN / 4)), int(start_point[1] + 0.5 * i * HEIGHT_6IN)))
183     return bk
184 
185 
186 def layout_photo_6_mix1(photo1,photo2):
187     """
188     在6寸照片上混合排版1寸、2寸照片
189     :param photo1: 待處理照片1寸
190     :param photo1: 待處理照片2寸
191     :return: 處理后的照片
192     """
193     bk = Image.new("RGB", [WIDTH_6IN,HEIGHT_6IN], (255,255,255))
194     # 創建畫筆
195     draw = ImageDraw.Draw(bk)
196     draw.line([(0,HEIGHT_6IN*0.5),(WIDTH_6IN,HEIGHT_6IN/2)],fill=128) # 橫線
197     draw.line([(0,HEIGHT_6IN*0.25),(WIDTH_6IN*0.5,HEIGHT_6IN*0.25)],fill=128) # 短橫線
198     draw.line([(0,HEIGHT_6IN*0.75),(WIDTH_6IN*0.5,HEIGHT_6IN*0.75)],fill=128) # 短橫線
199     draw.line([(WIDTH_6IN*0.25,0),(WIDTH_6IN*0.25,HEIGHT_6IN)],fill=128) # 第1條豎線
200     draw.line([(WIDTH_6IN*0.5,0),(WIDTH_6IN*0.5,HEIGHT_6IN)],fill=128) # 第2條豎線
201     draw.line([(WIDTH_6IN*0.75,0),(WIDTH_6IN*0.75,HEIGHT_6IN)],fill=128) # 第3條豎線
202     focus_point = [0.125 * WIDTH_6IN, 0.125 * HEIGHT_6IN]
203     start_point = [focus_point[0] - 0.5 * HEIGHT_1IN, focus_point[1] - 0.5 * WIDTH_1IN]
204     for i in range(0,4):
205         for k in range(0,2):
206             bk.paste(photo1, (int(start_point[0] + (0.25 * k * WIDTH_6IN )), int(start_point[1] + 0.25 * i * HEIGHT_6IN)))
207     focus_point2 = [0.625 * WIDTH_6IN, 0.25 * HEIGHT_6IN]
208     start_point2 = [focus_point2[0] - 0.5 * WIDTH_2IN, focus_point2[1] - 0.5 * HEIGHT_2IN]
209     for i in range(0,2):
210         for k in range(0,2):
211             bk.paste(photo2,(int(start_point2[0] + (0.25 * k * WIDTH_6IN)), int(start_point2[1] + 0.5 * i * HEIGHT_6IN)))
212     bk.show()
213     return bk
214 
215 
216 
217 def layout_photo_6_mix2(photo1,photo2):
218     """
219     在6寸照片上混合排版1寸、2寸照片
220     :param photo1: 待處理照片1寸
221     :param photo1: 待處理照片2寸
222     :return: 處理后的照片
223     """
224     bk = Image.new("RGB", [HEIGHT_6IN,WIDTH_6IN], (255,255,255)) # 豎版排版
225     # 創建畫筆
226     draw = ImageDraw.Draw(bk)
227 
228     draw.line([(350,0),(350,WIDTH_6IN)],fill=128) # 豎線
229     draw.line([(700,0),(700,WIDTH_6IN)],fill=128) # 豎線
230 
231 
232     draw.line([(0,WIDTH_6IN*0.25),(700,WIDTH_6IN*0.25)],fill=128) # 橫線1
233     draw.line([(0,WIDTH_6IN*0.5),(700,WIDTH_6IN*0.5)],fill=128) # 橫線2
234     draw.line([(0,WIDTH_6IN*0.75),(700,WIDTH_6IN*0.75)],fill=128) # 橫線3
235     draw.line([(700,WIDTH_6IN/3),(HEIGHT_6IN,WIDTH_6IN/3)],fill=128) # 橫線4
236     draw.line([(700,WIDTH_6IN*2/3),(HEIGHT_6IN,WIDTH_6IN*2/3)],fill=128) # 橫線5
237 
238     focus_point = [0.5 * 350, 0.125 * WIDTH_6IN]
239     start_point = [focus_point[0] - 0.5 * WIDTH_1IN, focus_point[1] - 0.5 * HEIGHT_1IN]
240 
241     #print(focus_point,start_point)
242     for i in range(0,4):
243         for k in range(0,2):
244             bk.paste(photo1, (int(start_point[0] + (k * 350)), int(start_point[1] + i * 0.25 * WIDTH_6IN )))
245 
246     focus_point2 = [0.5 * HEIGHT_6IN+350,  WIDTH_6IN/6]
247     start_point2 = [focus_point2[0] - 0.5 * WIDTH_2IN, focus_point2[1] - 0.5 * HEIGHT_2IN]
248     for i in range(0,3):
249         bk.paste(photo2, (int(start_point2[0]), int(start_point2[1] + i  * WIDTH_6IN /3)))
250     return bk
251 
252 
253 im = Image.open('xzk2.jpg')
254 width = im.size[0]
255 height = im.size[1]
256 rate = height / width
257 layout_photo_5_1(resize_photo(cut_photo(im,1),1)).save('5_1.jpg')
258 layout_photo_5_2(resize_photo(cut_photo(im,2),2)).save('5_2.jpg')
259 layout_photo_6_1(resize_photo(cut_photo(im,1),1)).save('6_1.jpg')
260 layout_photo_6_2(resize_photo(cut_photo(im,2),2)).save('6_2.jpg')
261 layout_photo_5_mix(resize_photo(cut_photo(im,1),1),resize_photo(cut_photo(im,2),2).rotate(90,expand=True)).save('5_1_mix.jpg')
262 layout_photo_6_mix1(resize_photo(cut_photo(im,1),1).rotate(90,expand=True),resize_photo(cut_photo(im,2),2)).save('6_mix1.jpg')
263 layout_photo_6_mix2(resize_photo(cut_photo(im,1),1),resize_photo(cut_photo(im,2),2)).save('6_mix2.jpg')

 

 最后,感謝薛老板友情出鏡!

 

 


免責聲明!

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



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