zoukankan      html  css  js  c++  java
  • 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')

     最后,感谢薛老板友情出镜!

  • 相关阅读:
    centos6安装部署ntp
    pttablechecksum和pttablesync修复主从不一致的数据
    clickhouse数据导入导出
    mongodb执行js结果输出文件
    rman验证备份有效性
    使用expdp和impdp进行goldengate初始化
    安装goldengate软件(ogg)
    sql ltrim rtrim
    2022
    Mqtt服务部署
  • 原文地址:https://www.cnblogs.com/aby321/p/11805789.html
Copyright © 2011-2022 走看看