Pythonでブラウザから画像を受け取って、処理して返す。
クライアントがHTML、サーバーがPythonのWebアプリの勉強。ブラウザから画像を選択し、Pythonで微分画像にしてそれをブラウザでまた表示する、という内容。
結果
初期画面。左側に処理前の画像を表示し、右側に処理後の画像を表示する。
画像のアップロード(Pythonで処理)後の画面。
※入力フォームで画像を選んだ瞬間に左側に選択画像が表示されるが、サーバーから処理画像を返す時に、HTMLをすべて読み込ませ直すため、選択画像が保持できない。今見えてるのはサーバーに保存された選択画像。なんとかスマートに作れないものか…
プログラム
全体
# 15_cgi_image_001.py # python 3.8.1 # coding: utf-8 import cgi import sys import io import cv2 import numpy as np sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding = 'utf-8') #printで出力する内容をutf-8にする print('Content-Type: text/html; charset=UTF-8\n') #ファイルの種類を定義 #ブラウザに表示する内容を記述 ## HTMLを変数html_bodyに代入している html_body = """ <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>Python CGI Image</title> <style> .box{ margin : left; box-sizing : border-box; border : 1px solid #666; width : 620px; max-height : auto; display : flex; justify-content: center; flex-wrap : wrap; } .box div img{ box-sizing : border-box; border : 2px solid #35557f; width : 300px; height : auto; margin : 2px; } </style> </head> <body> <form name="fm" action="15_cgi_image_001.py" method="post" enctype="multipart/form-data"> <input type="file" id="imagefile" name="imgfile"> <input type="submit" value="変換する"> </form> <br> <div class="box" id="makeImg"> <div> <img id="orgImg" src="%s" alt="Original Image"> </div> <div> <img id="rsltImg" src="%s" alt="Result Image"> </div> </div> <p>%s</p> <script type="text/javascript"> var elem = document.getElementById("orgImg"); <!-- console.log(elem.src); ---> document.getElementById("imagefile").addEventListener("change", function(){ var fileList = this.files ; <!-- console.log(fileList); ---> <!-- console.log(fileList[0]); ---> var blobUrl = window.URL.createObjectURL(fileList[0]); <!-- console.log(blobUrl); ---> elem.src = blobUrl; }); </script> </body> </html>""" form = cgi.FieldStorage() img = form.getvalue("imgfile") #form.getvalue(name)なので注意。idだとダメ。 txt1 = "初期化されました" txt2 = "保存しました" txt3 = "http://localhost:8000/NoImage.jpg" #初期用のダミー画像。./NoImage.jpg だと何故かダメだったのでフルパス txt4 = "http://localhost:8000/NoImage.jpg" try: #読み込み一発目はエラーになる。一発目はexceptの処理が走る。 arr = np.asarray(bytearray(img), dtype=np.uint8) #バイナリデータを一度nparrayで受ける d_img = cv2.imdecode(arr, -1) #cv2でデコードする cv2.imwrite('OriginalImage.jpg', d_img) #アップロードされた画像をドキュメントルートに保存する txt3 = "http://localhost:8000/OriginalImage.jpg" #このやり方だと、アップロード前に表示できてたorg画像は消えてしまうので、改めてorg画像を表示させる edge = cv2.Canny(d_img,100,200) #ここにやりたい画像処理を入れる(今回は微分処理だけ) cv2.imwrite('ResultImage.jpg', edge) txt4 = "http://localhost:8000/ResultImage.jpg" #処理後の画像をhtmlに表示するためのパス print(html_body % (txt3, txt4, txt2)) #HTML文中にある%sにそれぞれ代入してからHTML表示する except: print(html_body % (txt3, txt3, txt1))
HTML部分のコメント
HTML部分はソースにコメント書きにくいので以下にまとめる。
<form name="fm" action="15_cgi_image_001.py" method="post" enctype="multipart/form-data"> <input type="file" id="imagefile" name="imgfile"> <input type="submit" value="変換する"> </form>
注意点はenctype
で、ファイルのアップロードがある場合は、とりあえずmultipart/form-data
にしておけば間違いない、らいしい。
また、入力フォームの"id"と"name"は似てるけど、省略できない。それぞれgetElementById、form.getvalueに使われている。
<div class="box" id="makeImg"> <div> <img id="orgImg" src="%s" alt="Original Image"> </div> <div> <img id="rsltImg" src="%s" alt="Result Image"> </div> </div>
処理前の画像を左に、処理後の画像を右に、横並びするためにdiv
の入れ子にしている。
<script type="text/javascript"> var elem = document.getElementById("orgImg"); <!-- console.log(elem.src); ---> document.getElementById("imagefile").addEventListener("change", function(){ var fileList = this.files ; <!-- console.log(fileList); ---> <!-- console.log(fileList[0]); ---> var blobUrl = window.URL.createObjectURL(fileList[0]); <!-- console.log(blobUrl); ---> elem.src = blobUrl; }); </script>
画像をアップロードする前にブラウザに表示させるためのスクリプト。
ローカル画像はタグで取り扱うために、パスをBlobしないといけないらしい。詳細は未勉強。入力フォームの状態が変化したら、その値をBlob urlに変換し、それをタグに渡す、という処理。