又到了一年一度辦公園年卡的日子了,每年需要一張一寸照片,庫存今年告罄
如果拿着一寸照片去沖印,商家那個沖印的價格可比沖印普通照片不知道貴了多少唄(目測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')
最后,感謝薛老板友情出鏡!