用Django加PIL作一個證件照模板生成器網頁

最近在整理本身的簡歷,發現簡歷上面的ID照有些太老了,因此就準備從新準備一些證件照,恰好最近在弄本身的博客網站,想着直接作一個網頁工具出來,直接生成證件照模板,這樣還能夠省去PS的麻煩。並且照片涉及到我的隱私,把照片存儲到服務器後端會有諸多問題,因此我就直接所有在內存中處理了javascript

下面是個人處理過程html

上傳ID照,選擇須要的尺寸和底板的大小,而後保存到本地,直接打印便可。前端

後臺主要用到的是PIL和Django。java

PIL這塊,我用了比較原始的方法,先處理原始照片爲對應的尺寸,而後加邊框,算出總的長寬,再用地板的長寬相除,算出橫豎版本的張數,再用單張乘以張數算出寬高,再粘貼到背景板上。而後就是完整的照片了python

我單獨寫了一個函數處理照片生成的過程,下面附上代碼:ajax

#!/usr/bin/env python
# -*- coding: utf-8 -*-
from PIL import Image

def preview_pic(img,photo_size,bk_size,bk_color,photo_size_background_dict,photo_size_dict):
    try:
        # id照尺寸
        idsize = photo_size
        # 底板尺寸
        bgsize = bk_size
        # 底板顏色
        bgcol = bk_color
        # 原始照片
        photopath = img
        photo = Image.open(photopath)
        #處理底板和照片的長寬,將CM轉化爲像素,我是以300dpi轉化的,這個基本就是照片的標準了
        back_ground_hight = round(
            photo_size_background_dict[bgsize]["h"] *
            round(
                photo_size_background_dict[bgsize]["dpi"] /
                2.54))
        back_ground_width = round(
            photo_size_background_dict[bgsize]["w"] *
            round(
                photo_size_background_dict[bgsize]["dpi"] /
                2.54))
        photo_hight = round(
            photo_size_dict[idsize]["h"] *
            round(
                photo_size_dict[idsize]["dpi"] /
                2.54))
        photo_width = round(
            photo_size_dict[idsize]["w"] *
            round(
                photo_size_dict[idsize]["dpi"] /
                2.54))
        #建立空白底板
        img_bk = Image.new(
            "RGB", (back_ground_width, back_ground_hight), bgcol)
        #處理原始照片,將原始照片處理成ID照尺寸
        img_photo = photo.resize((photo_width, photo_hight))
        img_photo_width = img_photo.size[0]
        img_photo_hight = img_photo.size[1]
        #給id照加邊框,左右上下各10像素
        img_photo_with_side = Image.new(
            "RGB", (img_photo_width + 20, img_photo_hight + 20), bgcol)
        img_photo_with_side.paste(img_photo, (10, 10))
        img_photo_with_side_width = img_photo_with_side.size[0]
        img_photo_with_side_hight = img_photo_with_side.size[1]
        #算出底板橫向和縱向的id照張數
        width_num = divmod(back_ground_width, img_photo_with_side_width)
        hight_num = divmod(back_ground_hight, img_photo_with_side_hight)
        #建立帶邊框的id照的總長和總寬的空白照
        img_photo_with_side_total = Image.new(
            "RGB",
            (img_photo_with_side_width *
             width_num[0],
             img_photo_with_side_hight *
             hight_num[0]),
            bgcol)
        #開始往空白照上面粘貼id照
        for i in range(0, width_num[0]):
            for k in range(0, hight_num[0]):
                img_photo_with_side_total.paste(
                    img_photo_with_side, (img_photo_with_side_width * i, img_photo_with_side_hight * k))
        #以前已經粘貼好了id照的模板,如今要往總底板上面粘貼了,粘貼好以後會在上下左右空出空間來
        img_bk.paste(
            img_photo_with_side_total, (int(width_num[1] / 2), int(hight_num[1] / 2)))
        return img_bk

    except BaseException as e:
        print(e)


def Hex_to_RGB(hex):
    r = int(hex[1:3],16)
    g = int(hex[3:5],16)
    b = int(hex[5:7], 16)
    rgb =(r,g,b)
    return rgb
  

還有前端傳遞到後端的代碼:django

<div class="jumbotron">
        <div class="container">
            <div class="row">
                <div class="col-12">
                    <h3 class="text-center">照片生成工具</h3>
                </div>
                <div class="col-12 mt-3">
                    <p class="text-wrap">該工具用於批量生成證件照,只須要上傳須要生成模板的照片,而後選擇想要的參數,系統會自動排版照片,
                        生成新的照片,模板背景顏色也能夠自由選擇,目前支持的格式僅限於1寸和2寸,背景照片爲A4,6寸和Letter。</p>
                    <p>照片模板</p>
                    <span>1寸證件照6寸底片</span>
                    <img class="img-fluid w-25" alt="" src={% static 'images/pic_tem1.jpg' %} >
                    <span>1寸證件照A4底片</span>
                    <img class="img-fluid w-25" alt="" src={% static 'images/pic_tem2.jpg' %} >
                    <p>使用方法:</p>
                    <form id="photo_info" role="form" enctype="multipart/form-data" action="">
                        <ol>
                            <li class="m-3">
                                <div class="col-4">
                                    <input class="btn form-control" type="file" accept=".jpg" id="photo">
                                </div>
                            </li>
                            <li class="m-3">
                                <div class="col-4">
                                    <div class="input-group">
                                        <div class="input-group-prepend">
                                            <span class="input-group-text bg-light">選擇照片大小</span>
                                        </div>
                                        <select class="form-control" id="photo_size" name="photo_size">
                                            {% for p_size in photo_size %}
                                                <option value="{{ p_size }}">{{ p_size }}</option>
                                            {% endfor %}
                                        </select>
                                    </div>
                                </div>
                            </li>
                            <li class="m-3">
                                <div class="col-4">
                                    <div class="input-group">
                                        <div class="input-group-prepend">
                                            <span class="input-group-text bg-light">選擇背景大小</span>
                                        </div>
                                        <select class="form-control" id="bk_size" name="bk_size">
                                            {% for p_s_b_k,p_s_b_v in photo_size_background.items %}
                                                <option value="{{ p_s_b_k}}">{{ p_s_b_k }}</option>
                                            {% endfor %}
                                        </select>
                                    </div>
                                </div>
                            </li>
                            <li class="m-3">
                                <div class="col-4">
                                    <span>選擇背景顏色:</span>
                                    <label for="w">白色</label>
                                    <input id="w" type="radio" name="bk_color" value="#ffffff" checked="checked">
                                    <label for="g">灰色</label>
                                    <input id="g" type="radio" name="bk_color" value="#808080">
                                </div>
                            </li>
                            <li class="m-3">
                                <div class="col-4">
                                    <button type="button" class="btn-primary rounded" onclick="build_photo()">預覽</button>
                                </div>
                            </li>
                            <li class="m-3">
                                <div id="pic" class="col-6">

                                </div>
                            </li>
                        </ol>
                    </form>
                </div>
            </div>
        </div>
    </div>

<script type="text/javascript">
    function build_photo() {
        var photo_size = document.getElementById("photo_size").value;
        var bk_size = document.getElementById("bk_size").value;
        var photo = document.getElementById("photo").files[0];
        var bk_color = $("input[name='bk_color']:checked").val();
        var formdata = new FormData();
        formdata.append("photo",photo);
        formdata.append("photo_size",photo_size);
        formdata.append("bk_size",bk_size);
        formdata.append("bk_color",bk_color);
        $.ajax({
            url:"/tools/photo_builder/",
            type:"POST",
            data:formdata,
            processData:false,
            contentType:false,
            success:function (data) {
                document.getElementById("pic").innerHTML = '<img class="img-thumbnail" src="data:image/jpg;base64,'+data["data"]+'">\n'+ //img直接用二進制表示,不指定路徑
                                                            '<a class="btn btn-primary m-3 float-right" href="/download_photo" download>下載</a>'
            }
        })
    }

</script>

主要是上傳圖片的處理,我用了ajax上傳,另外注意,form必定要加 enctype="multipart/form-data",否則django會報錯json

以後就是後端Django的處理了,分爲照片生成和照片下載後端

from io import StringIO,BytesIO
from base64 import b64encode
from django.views.decorators.csrf import csrf_exempt
from Web_Demo.photo_builder import preview_pic,Hex_to_RGB
from django.shortcuts import HttpResponse, render, redirect,HttpResponseRedirect

@csrf_exempt
def photo_builder(request):
  #聲明一個全局變量,後面會用到
global byte_data
  #底板和id照類型的字典,能夠本身加,我用了通用模板,加了以後會直接傳遞到前端 photo_size_background_dict
= { "6寸": {"h": 10.16, "w": 15.24, "dpi": 300}, "A4": {"h": 21.0, "w": 29.7, "dpi": 300}, } photo_size_dict = {"1寸": {"h": 3.21, "w": 2.4, "dpi": 300}, "2寸": {"h": 4.9, "w": 3.4, "dpi": 300} } if request.method == "GET": return render(request,'../templates/photo_builder.html',{"photo_size_background":photo_size_bac "photo_size":photo_size_dict}) else:
     #獲取ajax傳遞到後端的信息 photo
= request.FILES.get("photo") photo_size = request.POST.get("photo_size") bk_size = request.POST.get("bk_size") bk_color_hex = request.POST.get("bk_color")
     #這裏把16進制顏色處理成RGB模式的,後續會用到 bk_color
= Hex_to_RGB(bk_color_hex)
     #這個用BytesIO把前端傳遞過來的數據轉化爲二進制,方便PIL讀取 img
= BytesIO(photo.read())
     #引入以前建立的函數,參數都已經給定,返回一個Image對象,過程參考前面的代碼 pic
= preview_pic(img,photo_size,bk_size,bk_color,photo_size_background_dict,photo_size_dict)
     #把返回的Image對象轉化爲二進制代碼,再轉化爲base64 output_buffer
= BytesIO() pic.save(output_buffer, format='JPEG') byte_data = output_buffer.getvalue() base64_bytes = b64encode(byte_data)
     #把base64代碼轉化爲utf8的字符串,並用Json序列化傳遞到前端,前端img直接引用base64碼顯示 base64_string
= base64_bytes.decode('utf-8') raw = {"data":base64_string} json_data = json.dumps(raw,ensure_ascii=False) return HttpResponse(json_data,content_type='application/json; charset=utf-8')

下載照片代碼,這裏處理起來很坑,我試了下,用數據流處理的話,下載下來的會提示文件損壞,服務器

                                                                                 
def download_photo(request): 
  #仍是聲明全局變量,調用以前生成的二進制數據,這樣省掉了讀取本地文件的過程
global byte_data try: response = HttpResponse(byte_data)
     #這裏必定要注意,千萬不能用FileResponse和StreamingHttpResponse,並且type直接指定jpg就能夠了,由於不是從後端讀取的文件,是直接獲取的二進制代碼,用數據流的話必定會報錯 response[
'Content-Type'] = 'application/jpg' response['Content-Disposition'] = 'attachment;filename="ID.JPG"' except: return HttpResponse("Not Found the File") return response

最後就是處理以後的顯示了

 

 而後是下載到本地的照片,大功告成,把模板保存起來打印的時候選擇對應的底板尺寸就能夠大了,不再須要去用PS花一堆時間貼圖了

相關文章
相關標籤/搜索