今回は、Microsoft Cognitive ServicesのFace APIから取得した情報をOpenCVで画像に描き込んでみます。
OpenCVの概要
OpenCV(正式名称: Open Source Computer Vision Library)は、名称の通りオープンソースの画像処理、解析用のライブラリです。OpenCVではフィルター処理や特徴点抽出、物体認識など様々な機能が利用可能であるため、作り込めば顔認識などの機能を作ることが可能です。ただし今回は、顔認識をFace APIで行うため、OpenCVでは画像加工のみを行います。
OpenCVの利用
OpenCVを利用するにはNumPy、OpenCVをそれぞれインストールする必要があります。まずは、下記のコードでそれぞれインストールを行っていきます。
※今回は「Bash on Ubuntu on Windows」の環境で検証をしています。
・NumPy
$ sudo apt-get install python-numpy
・OpenCV
$ sudo apt-get install python-opencv
インストールしたら、前回使用したPythonのコードにOpenCVによる画像加工を追加します。
まずは、顔の枠(faceRectangle)と特徴点(faceLandmarks)を描き込んでみました。長方形、点(円)はそれぞれcv2.rectangle、cv2.circleで描き込むことができます。顔の枠に関しては、faceAttributesのgenderを基に性別で色分けするようにしています。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 |
#!/usr/bin/env python #coding:utf-8 import httplib, urllib, base64 import json import cv2, numpy headers = { # Request headers 'Content-Type': 'application/json', 'Ocp-Apim-Subscription-Key': '***********{APIキー}', } params = urllib.urlencode({ # Request parameters 'returnFaceId': 'true', 'returnFaceLandmarks': 'true', 'returnFaceAttributes': 'age,gender,smile,facialHair,headPose,glasses', }) image_url = '{画像のURL}' dir_arr = image_url.split('/') for file in dir_arr: if file.find(".jpg") >= 0: filename = file[0:file.find(".jpg")] form = '.jpg' elif file.find(".png") >= 0: filename = file[0:file.find(".png")] form = '.png' body = { # Request body 'url': image_url } try: conn = httplib.HTTPSConnection('api.projectoxford.ai') conn.request("POST", "/face/v1.0/detect?%s" % params, json.dumps(body), headers) response = conn.getresponse() data = json.loads(response.read()) #print(json.dumps(data)) conn.close() # 画像の読み込み img_req = urllib.urlopen(image_url) arr = numpy.asarray(bytearray(img_req.read()), dtype=numpy.uint8) img = cv2.imdecode(arr,-1) # 顔ごとの処理 for face in data: # 性別ごとに色分け if face["faceAttributes"]["gender"] == 'female': color = {"r":255,"g":63,"b":255} elif face["faceAttributes"]["gender"] == 'male': color = {"r":63,"g":255,"b":255} # 顔の枠追加 cv2.rectangle(img, (face["faceRectangle"]["left"], face["faceRectangle"]["top"]), (face["faceRectangle"]["left"] + face["faceRectangle"]["width"], face["faceRectangle"]["top"] + face["faceRectangle"]["height"]), (color["b"], color["g"], color["r"]), 2, cv2.CV_AA) # 特徴点の描画 for landmark in face["faceLandmarks"].values(): cv2.circle(img, (int(round(landmark["x"])), int(round(landmark["y"]))), 2, (255, 0, 0), -1, cv2.CV_AA) # 画像の保存 cv2.imwrite(filename + "_points" + form, img) except Exception as e: print("[Errno {0}] {1}".format(e.errno, e.strerror)) |
これを男女2人の画像で実行すると、男性には水色、女性にはピンク色の枠が付き、それぞれの目、鼻、口に特徴点を表す青い点が描き込まれました。
黒目線の挿入
今度は、取得できる目の特徴点を基にして画像の顔に黒目線を挿入する加工をしてみます。
目の特徴点は概ね直線的に並ぶことから、今回は目、瞳の特徴点から線形近似曲線を算出して、特徴点を隠すように直線を挿入するようにします。
以下が、黒目線を挿入するコードになります。先ほどの特徴点の描画の代わりに下記の4つの処理を追記し、目を隠すように直線を描画します。
① 目、瞳に関する特徴点座標の抽出
② 特徴点の線形近似を算出
③ 線形近似曲線と各特徴点の距離の算出
④ ③の最大値を基に線幅を決定し、黒目線を挿入
・Python 2.7
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 |
#!/usr/bin/env python #coding:utf-8 import httplib, urllib, base64 import json import cv2, numpy headers = { # Request headers 'Content-Type': 'application/json', 'Ocp-Apim-Subscription-Key': '***********{APIキー}', } params = urllib.urlencode({ # Request parameters 'returnFaceId': 'true', 'returnFaceLandmarks': 'true', 'returnFaceAttributes': 'age,gender,smile,facialHair,headPose,glasses', }) image_url = '{画像のURL}' dir_arr = image_url.split('/') for file in dir_arr: if file.find(".jpg") >= 0: filename = file[0:file.find(".jpg")] form = '.jpg' elif file.find(".png") >= 0: filename = file[0:file.find(".png")] form = '.png' body = { # Request body 'url': image_url } try: conn = httplib.HTTPSConnection('api.projectoxford.ai') conn.request("POST", "/face/v1.0/detect?%s" % params, json.dumps(body), headers) response = conn.getresponse() data = json.loads(response.read()) #print(json.dumps(data)) conn.close() # 画像の読み込み img_req = urllib.urlopen(image_url) arr = numpy.asarray(bytearray(img_req.read()), dtype=numpy.uint8) img = cv2.imdecode(arr,-1) # 顔ごとの処理 for face in data: # 性別ごとに色分け if face["faceAttributes"]["gender"] == 'female': color = {"r":255,"g":63,"b":255} elif face["faceAttributes"]["gender"] == 'male': color = {"r":63,"g":255,"b":255} # 顔の枠追加 cv2.rectangle(img, (face["faceRectangle"]["left"], face["faceRectangle"]["top"]), (face["faceRectangle"]["left"] + face["faceRectangle"]["width"], face["faceRectangle"]["top"] + face["faceRectangle"]["height"]), (color["b"], color["g"], color["r"]), 2) # 線形近似(最小二乗法) x = [] y = [] for landmark in face["faceLandmarks"].items(): if (landmark[0].find('eye') >= 0 or landmark[0].find('pupil') >= 0) and landmark[0].find('brow') < 0: x.append(landmark[1]["x"]) y.append(landmark[1]["y"]) A = numpy.array([x,numpy.ones(len(x))]) A = A.T a,b = numpy.linalg.lstsq(A,y)[0] # 目線の両端座標算出 eyeLeft = [min(x) * 1.1 - max(x) * 0.1] eyeRight = [max(x) * 1.1 - min(x) * 0.1] eyeLeft.append(a * eyeLeft[0] + b) eyeRight.append(a * eyeRight[0] + b) # 目線幅の決定(点と直線の距離算出) width = [] for cnt in range(0, len(x) - 1): u = numpy.array([eyeRight[0] - eyeLeft[0], eyeRight[1] - eyeLeft[1]]) v = numpy.array([x[cnt] - eyeLeft[0], y[cnt] - eyeLeft[1]]) width.append(abs(numpy.cross(u, v) / numpy.linalg.norm(u))) # 黒目線の挿入 cv2.line(img, (int(eyeLeft[0]), int(eyeLeft[1])), (int(eyeRight[0]), int(eyeRight[1])), (0, 0, 0), int(max(width) * 5), cv2.CV_AA) # 画像の保存 cv2.imwrite(filename + "_eyeline.jpg", img) except Exception as e: print("[Errno {0}] {1}".format(e.errno, e.strerror)) |
これで、画像を処理したところ、顔に黒目線を描き込むことができいました。
同様に、5人の子供が写る画像でも試してみたところ、5人の顔を認識して的確な位置に黒目線を入れることができました。
ちなみにfaceRectangleを基準に処理をすれば、顔のモザイク処理も簡単に行うことができます。
いかがでしたか?
Face APIとOpenCVを使うと、テキストを描きこむことも容易にできるため、Face APIの出力内容を簡易的に可視化するなどの応用もできそうです。
次回は、Face APIに対して様々な画像を入力して検出の精度を検証していきます。
次回もお楽しみに!