Webアプリ~リア充と非リア~
前書き
この記事は,SLP KBIT Advent Calendar 2021 の5日目の記事です。
前回の記事で男女を区別するmodelを作成しました。 今回は、作成したmodelとFlaskを使用し、リア充と非リアの判断を行うWebアプリを製作していきます。
目次
全体の概要
- ファイル構造
. ├── F_M.h5 ├── demo_app.py ├── haarcascade_frontalface_default.xml ├── resize ├── stock └── templates ├── main_page.html └── uploads_page.html
demo_app.py
がバックエンドのメイン処理を行っています。F_M.h5
が前回したmodel,haarcascade_frontalface_default.xml
は顔検知の際に使用するもの、resize
とstock
は画像の処理を行う際に使用するディレクトリーです。
templates
は、フロントエンドのコードです。
バックエンド
処理の流れが以下のようになってます。
1. 画像から顔部分を抜き取る
1. 抜き取った画像サイズを変更する
1. 加工した画像から、写真に写ってる人が男性か女性か判断
1. 写真に写ってる人数や男女比からリア充か判断
今回は、LGBTも尊重しているため、ホモっプルかレズップルかも判断することにしました。これで、桜Trickのようなカップルを見つけれるかもしれません。
demo_app.py
from flask import Flask, request, render_template import os import cv2 import numpy as np from keras.models import load_model from PIL import Image import glob from werkzeug.utils import secure_filename # 画像をアップロードするフォルダー upload_folder = './uploads' # 画像の拡張子の制限 # set()で重複した要素を取り除く allowed_extenstions = set(["png","jpg","jpeg"]) # お決まり app = Flask(__name__) app.secret_key = "hogehoge" # 設定の保存 # upload_folderの設定を保存 UPLOAD_FOLDER = './uploads/' app.config['UPLOAD_FOLDER'] = UPLOAD_FOLDER # configの読み込み app.config.from_object(__name__) face_cascade_path = './haarcascade_frontalface_default.xml' face_cascade = cv2.CascadeClassifier(face_cascade_path) #バウンディングボックス座標データを取得 def Get_Bounding_Box(image): #画像を読み込む img_b = cv2.imread(image) if img_b is None: print("No No Object") return -1 #ここでオブジェクトを検出 #バウンディングボックスもろもろ検出してる img_b_gray = cv2.cvtColor(img_b, cv2.COLOR_BGR2GRAY) #バウンディングボックスの座標情報をゲット face = face_cascade.detectMultiScale(img_b_gray) bbox = [] for x, y, w, h in face: bbox.append([x,y,x+w,y+h]) #print(face) #バウンディングボックス座標データを返却 #print(bbox) return bbox #画像を抜き取り、リサイズ def detect_face(img, bbox_Coordinate): #一時保存先を指定 save_path = "./stock/stock.jpg" #リサイズしたものの出力先 out_dir = "./resize/stock_" i = 0 #配列の中が空かどうか判断 #空だったら、実行しない if len(bbox_Coordinate) == 0: #print("no object") return -1 else: #画像読み込み img_o = Image.open(img) #画像を切り取る #座標を一個ずつ読み取る for item_position in bbox_Coordinate: #取得した座標のところだけを抜き取る img_crop = img_o.crop(item_position) #画像出力 img_crop.save(save_path) #座標リストを空にする #item_position.clear() #サイズ変更 img =Image.open(save_path) #64×64サイズにしてる img_rot = img.resize((64,64)) img_rot.save(out_dir + str(i) + ".jpg") i += 1 return 0 def detect_who(img_url): model = load_model('./F_M.h5') img = cv2.imread(img_url) img_set = np.expand_dims(img,axis=0) #予測 name="" #print("predict:",model.predict(img_set)) nameNumLabel=np.argmax(model.predict(img_set)) if nameNumLabel== 0: name="男性" elif nameNumLabel==1: name="女性" print(name) return name # ボッチかやその顔が男性か女性など判断 def predict_Type(): fm_dic = {"男性":0, "女性":0} img_list = glob.glob("./resize/*.jpg") if len(img_list) == 1: return "ボッチですね!悲しい人" # 各顔写真がどっちか判断 for img in img_list: if detect_who(img) == "男性": fm_dic["男性"] += 1 elif detect_who(img) == "女性": fm_dic["女性"] += 1 #リア充か非リアかホモっプルか判断 if fm_dic["男性"] == 2 and fm_dic["女性"] == 0: return "ホモップル。やりますねー" if fm_dic["男性"] == 0 and fm_dic["女性"] == 2: return "レズップル!最高!!" if fm_dic["男性"] == 0 or fm_dic["女性"] == 0: return "リア充に見せかけた非リア" if fm_dic["男性"] == 1 and fm_dic["女性"] == 1: return "素晴らしいリア充" return "お前らは何や?" # resizeのファルダーを削除 def remove_image(): img_list = glob.glob("./resize/*.jpg") for img in img_list: os.remove(img) # リア充か非リアか判断 def riajuu_or_hiria(image_url): remove_image() image=cv2.imread(image_url) if image is None: return "Not open" # b,g,r = cv2.split(image) # image = cv2.merge([r,g,b]) bbox_Coordinate = Get_Bounding_Box(image_url) detect_face(image_url,bbox_Coordinate) return predict_Type() # 拡張子の確認 def allwed_file(filename): # .があるかどうかのチェックと、拡張子の確認 # OKなら1、だめなら0 return '.' in filename and filename.rsplit('.', 1)[1].lower() in allowed_extenstions # main画面 @app.route("/main_page",methods = ["GET", "POST"]) def main_page(): text = "アップロードテストです" return render_template("main_page.html",text = text) # アップロードしたファイルの処理 @app.route('/uploads_page',methods = ["GET", "POST"]) # ファイルを表示する def uploaded_file(): text = "アップしました" #データが届いたら if request.method == "POST": # ファイルを読み込む img_file = request.files['img_file'] # ファイル名を取得する filename = secure_filename(img_file.filename) # 画像のアップロード先URLを生成する img_url = os.path.join(app.config['UPLOAD_FOLDER'], filename) # 画像をアップロード先に保存する img_file.save(img_url) # 結果を表示 result = riajuu_or_hiria(img_url) return render_template('uploads_page.html', text=text, result=result) ## おまじない if __name__ == "__main__": app.run(debug=True)
フロントエンド
とりあえず、画像をアップロードするページと結果を表示するページの作成しました。
main_page.html
<!doctype html> <html lang="ja"> <head> <!-- 文字をutf-8に設定 --> <meta charset="utf-8"> <!-- どのデバイスで見ても見え方を一緒にする = レスポンシブデザイン --> <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"> <title>アップロードテスト</title> <!-- cssの読み込み --> <!-- link rel="stylesheet" href="static/css/style.css" --> </head> <body class="text-center"> <h1>{{text}}</h1> <form action="/uploads_page" method=post enctype="multipart/form-data"> <p><input type=file id="img_file" name=img_file> <input type = submit value = Upload> </p> </form> </body> </html>
uploads_page.html
<!doctype html> <html lang="ja"> <head> <!-- 文字をutf-8に設定 --> <meta charset="utf-8"> <!-- どのデバイスで見ても見え方を一緒にする = レスポンシブデザイン --> <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"> <title>アップロードテスト</title> <!-- cssの読み込み --> <!-- link rel="stylesheet" href="static/css/style.css" --> </head> <body class="text-center"> <h1>{{text}}</h1><br> {% if result %} <!--img src={{result_img}}--> <p>{{result}}</p> {% endif %} </body> </html>
私はサボっていますが、cssとかもっとしかっり書けば、GUIは豪華になります。
カップル検証
実際にうまくいってるか試してみます。
ネットで無作為にこの画像を選びました。
結果がこちらです。
あれ...?
なぜか、男の娘カップルと判断されましたね('ω')
おそらく、画像解析部分がうまくいってません。頑張って、この部分を改良しようと思います。