AIが顔認識して自動でスタンプ貼るアプリ作って大儲けする
作ってみました。
※アプリ化までは出来てません
動作環境
動作環境・ライブラリ等は以下の通り。
Python 3.6.8
opencv-python==3.4.3.18 dlib==19.17.0
中間生成物も含め、フォルダ構成は大体こんな感じです。
├── data │ ├── img │ │ └── Green11_kessai20141123152655_TP_V.jpg │ ├── json │ │ └── Green11_kessai20141123152655_TP_V.json │ └── stamps │ ├── 01.png │ └── 02.png ├── prog │ ├── detector.py │ ├── predictor │ │ └── shape_predictor_68_face_landmarks.dat │ └── stamper.py └── result └── Green11_kessai20141123152655_TP_V._stamped.jpg
インタフェースはくそ雑設計です。data/img/
にある1つ目の画像に対して ①顔認識 ②スタンプ合成 を実行します。
画像はぱくたそとEmojipediaからお借りしています。
元画像:
Green11_kessai20141123152655_TP_V.jpg
スタンプ:
01.png
02.png
コード
つべこべ言わずにソースコード
まずはdetector.py
# coding:utf-8 import json import math import os import cv2 import dlib def main(): ## input path and read image img_path = "../data/img/" + os.listdir("../data/img/")[0] img_filename = os.path.basename(img_path) img_ext = img_filename.split(".")[-1] img = cv2.imread(img_path, cv2.IMREAD_COLOR) if img is None: print("Could not read input image") exit() ## instance detector/predictor detector = dlib.get_frontal_face_detector() predictor = dlib.shape_predictor("predictor/shape_predictor_68_face_landmarks.dat") ## detect faces faces = detector(img, 1) if len(faces) == 0: print("Could not detect any faces") exit() ## compute face properties face_properties = {} for i, f in enumerate(faces): ## get 68 points points = predictor(img, f).parts() ## Left/Right temples t_L = points[0] t_R = points[16] ## calc properties face_width = math.sqrt((t_L.x - t_R.x)**2 + (t_L.y - t_R.y)**2) face_center = [(t_L.x + t_R.x)/2, (t_L.y + t_L.y)/2] degree_acw = -1 * math.degrees(math.atan2((t_R.y - t_L.y), (t_R.x - t_L.x))) ## add result face_properties[i] = { "stamp": "01.png", "center": face_center, "width": face_width, "angle": degree_acw } ## save with open("../data/json/" + img_filename.replace(img_ext, "json"), "w") as f: json.dump(face_properties, f, ensure_ascii=False, indent=4, separators=(',', ': ')) if __name__ == "__main__": main()
data/img/
にある画像を読み込み、AIが顔認識して、顔の ①座標 ②幅 ③傾き をjson形式で出力します。
今回の画像に対してはこんな感じの結果を出力します。
{ "0": { "stamp": "01.png", "center": [ 158.0, 334.0 ], "width": 113.03096920755833, "angle": 13.29857033049428 }, "1": { "stamp": "01.png", "center": [ 374.0, 106.0 ], "width": 82.46211251235322, "angle": -14.036243467926479 }, "2": { "stamp": "01.png", "center": [ 1172.0, 134.0 ], "width": 92.04890004774636, "angle": -17.056965576617063 }, "3": { "stamp": "01.png", "center": [ 1314.0, 333.0 ], "width": 109.65856099730654, "angle": 9.977712620150575 } }
この結果を元に次のstamper.py
が、data/stamps/
にあるスタンプ画像を使って合成します。jsonを直いじりすれば、誰の顔にどのスタンプを使うかを変更することも出来る親切設計です。
次にstamper.py
# coding:utf-8 import json import os import cv2 def paste(img_back, img_front, center_xy): ## itiou img_ret = img_back.copy() ## set params c_x, c_y = center_xy f_h, f_w = img_front.shape[:2] ## get roi/mask roi = img_back[int(c_y-(f_h/2)):int(c_y+(f_h/2)), int(c_x-(f_w/2)):int(c_x+(f_w/2))] _, mask = cv2.threshold(cv2.cvtColor(img_front, cv2.COLOR_BGR2GRAY), 10, 255, cv2.THRESH_BINARY) mask_inv = cv2.bitwise_not(mask) img_back_bwa = cv2.bitwise_and(roi, roi, mask=mask_inv) img_front_bwa = cv2.bitwise_and(img_front, img_front, mask=mask) ## paste dst = cv2.add(img_back_bwa, img_front_bwa) img_ret[int(c_y-(f_h/2)):int(c_y+(f_h/2)), int(c_x-(f_w/2)):int(c_x+(f_w/2))] = dst return img_ret def main(): ## read image img_path = "../data/img/" + os.listdir("../data/img/")[0] img_filename = os.path.basename(img_path) img_ext = img_filename.split(".")[-1] img = cv2.imread(img_path, cv2.IMREAD_COLOR) if img is None: print("Could not read input image") exit() ## read properties(json) with open("../data/json/" + img_filename.replace(img_ext, "json")) as f: face_properties = json.load(f) ## stamp for f_id, f_prop in face_properties.items(): ## read stamp image img_stamp = cv2.imread("../data/stamps/" + f_prop["stamp"]) ## rotate s_h, s_w = img_stamp.shape[:2] s_center = (int(s_w / 2), int(s_h / 2)) M = cv2.getRotationMatrix2D(s_center, f_prop["angle"], 1.0) img_stamp = cv2.warpAffine(img_stamp, M, (s_w, s_h)) ## paste img = paste(img, img_stamp, center_xy=f_prop["center"]) ## save cv2.imwrite("../result/" + img_filename.replace(img_ext, "_stamped."+img_ext), img) if __name__ == "__main__": main()
処理は至ってシンプルで、元画像とdetector.py
が出力したjsonを読み込んで、顔それぞれに対してスタンプ画像を顔の傾きに合わせて回転させて合成するだけです。
結果
大体こんな感じ。
Green11_kessai20141123152655_TP_V._stamped.jpg
以上!