[微笑偵測器] 微軟辨識服務搭配Arduino

本文要介紹微軟辨識服務的Face API,可以偵測照片中的臉孔以及相關參數,包含年齡、性別、情緒、眼鏡、鬍子以及五官座標等。

作者/攝影  曾吉弘
時間   3小時
成本
難度  * * * * *
材料表
  • 個人電腦
  • Arduino Uno 或其他可執行 StandFirmata 之 Arduino相容板
  • LED x 3
  • 線材
  • 麵包板

本範例修改自微軟辨識服務Face API教學而來,您也可以在該頁面找到其他程式語言的教學。

微軟辨識服務

根據官方網站說法:Microsoft 辨識服務具備多種程式語言的API,可以透過自然的溝通方式看、聽、說,以及理解和解讀各種媒體素材,包然文字、語音、照片、影片與搜尋建議等等。分成五大類,每項又各自分成不同細項,本範例將使用辨識下的臉部API:

  1. 辨識
  2. 語音
  3. 語言
  4. 知識
  5. 搜尋

 

註冊Face API

註冊一個新的帳號,需要手機號碼認證與信用卡認證…

  • 這些步驟完成之後選擇您要的API,即可取得金鑰(subscription key),每一個API各自會有自己的流量與次數限制,請注意。
  • 下圖是 Face API 的兩組金鑰,使用任何一組都可以

 

在其頁面就可以看到效果,有一些範例圖片或者您可以自行上傳照片,右側就是偵測結果,格式是JSON,各主要程式語言對於JSON都有現成的函式庫來爬取。

範例程式

本範例是修改自 Face API python 範例程式而來,使用 python 2.7。本範例會使用一張網路圖片(#37)送到 Face API之後取得其辨識結果。

程式#33可指定我們所要偵測的臉孔參數,包含以下:

  • age:年齡
  • gender:性別
  • headPose:頭部姿態,回傳XYZ軸傾斜狀態
  • smile:微笑(0~1之間小數,數字愈高代表微笑愈明顯)
  • facialHair:臉部髮型,例如山羊鬍、八字鬍、鬢角
  • glasses:偵測有無戴眼鏡
  • emotion:情緒,包含生氣、滿足、害怕、驚訝與悲傷等等
  • hair:髮型,包含禿頭、髮色等等
  • makeup:偵測是否化妝
  • occlusion:偵測是否閉合,例如眼睛、嘴巴
  • accessories:配件
  • blur:偵測圖片是否模糊,以及模糊程度參數
  • exposure:偵測圖片曝光程度與曝光程參數
  • noise:偵測圖片中雜訊與雜訊程度參數

 

在此用到的圖片是這位可愛的女生,您可以換成其他的圖片來測試

import httplib, urllib, base64, json

###############################################
#### Update or verify the following values. ###
###############################################

# Replace the subscription_key string value with your valid subscription key.
subscription_key = 'XXXX'   #填入Face API金鑰,任一組都可以

# Replace or verify the region.
#
# You must use the same region in your REST API call as you used to obtain your subscription keys.
# For example, if you obtained your subscription keys from the westus region, replace 
# "westcentralus" in the URI below with "westus".
#
# NOTE: Free trial subscription keys are generated in the westcentralus region, so if you are using
# a free trial subscription key, you should not need to change this region.
uri_base = 'westcentralus.api.cognitive.microsoft.com'

# Request headers.
headers = {
    'Content-Type': 'application/json',
    'Ocp-Apim-Subscription-Key': subscription_key,
}

# Request parameters.
params = urllib.urlencode({
    'returnFaceId': 'true',
    'returnFaceLandmarks': 'false',
    'returnFaceAttributes': 'age,gender,headPose,smile,facialHair,glasses,emotion,hair,makeup,occlusion,accessories,blur,exposure,noise',
})

# The URL of a JPEG image to analyze.
body = "{'url':'https://how-old.net/Images/faces2/main0011.jpg'}"

try:
    # Execute the REST API call and get the response.
    conn = httplib.HTTPSConnection('westcentralus.api.cognitive.microsoft.com')
    conn.request("POST", "/face/v1.0/detect?%s" % params, body, headers)
    response = conn.getresponse()
    data = response.read()

    # 'data' contains the JSON data. The following formats the JSON data for display.
    parsed = json.loads(data)
    print ("Response:")
    print (json.dumps(parsed, sort_keys=True, indent=2))
    print parsed[0]["faceAttributes"]["smile"]   #第一張臉的 smile 強度
    conn.close()

except Exception as e:
    print("[Errno {0}] {1}".format(e.errno, e.strerror))

 

根據Face API,一張照片最多可以辨識到64張臉,但這樣真的太多了… 在基礎練習最好使用單一臉孔來測試。熟悉之後可以加入更多臉孔甚至連續偵測

參考:[微軟認知服務] 串流影像之臉孔與年齡辨識

執行結果如下,本範例就是把所有JSON結果顯示出來(#50),但為了後續微笑偵測,我們在#51獨立把smile的強度抓出來了,如下圖的 1.0

print parsed[0][“faceAttributes”][“smile”] #取得第一張臉的 smile 強度

上圖可愛小妹妹的完整偵測JSON結果如下,您可以慢慢檢視:

[
  {
    "faceAttributes": {
      "accessories": [], 
      "age": 21.6, 
      "blur": {
        "blurLevel": "low", 
        "value": 0.1
      }, 
      "emotion": {
        "anger": 0.0, 
        "contempt": 0.0, 
        "disgust": 0.0, 
        "fear": 0.0, 
        "happiness": 1.0, 
        "neutral": 0.0, 
        "sadness": 0.0, 
        "surprise": 0.0
      }, 
      "exposure": {
        "exposureLevel": "goodExposure", 
        "value": 0.73
      }, 
      "facialHair": {
        "beard": 0.0, 
        "moustache": 0.0, 
        "sideburns": 0.0
      }, 
      "gender": "female", 
      "glasses": "NoGlasses", 
      "hair": {
        "bald": 0.03, 
        "hairColor": [
          {
            "color": "brown", 
            "confidence": 1.0
          }, 
          {
            "color": "black", 
            "confidence": 0.77
          }, 
          {
            "color": "other", 
            "confidence": 0.46
          }, 
          {
            "color": "red", 
            "confidence": 0.2
          }, 
          {
            "color": "blond", 
            "confidence": 0.06
          }, 
          {
            "color": "gray", 
            "confidence": 0.02
          }
        ], 
        "invisible": false
      }, 
      "headPose": {
        "pitch": 0.0, 
        "roll": -11.5, 
        "yaw": -5.6
      }, 
      "makeup": {
        "eyeMakeup": true, 
        "lipMakeup": true
      }, 
      "noise": {
        "noiseLevel": "low", 
        "value": 0.08
      }, 
      "occlusion": {
        "eyeOccluded": false, 
        "foreheadOccluded": false, 
        "mouthOccluded": false
      }, 
      "smile": 1.0
    }, 
    "faceId": "d87447bd-a4b3-4c87-b92d-736871e8e6d9", 
    "faceRectangle": {
      "height": 166, 
      "left": 175, 
      "top": 197, 
      "width": 166
    }
  }
]
1.0   #獨立抓出來的 smile 強度

您可以使用 jsoneditoronline.org 來檢視 json 結構,會比直接看原始資料來的清楚明暸。

 

pyfirmata

取得臉孔資訊之後就有很多東西可以玩了,例如本範例的微笑偵測器。或是找找看畫面中誰有戴眼鏡。如果是連續偵測的話,還能做到讓攝影鏡頭跟著你的臉孔中心移動(PTZ平台),總之太多應用啦!

pyfirmata 是python透過序列埠來與Arduino溝通的模組,Arduino端只要上傳(請先確認)StandardFirmata 這個程式就可以了,安裝完成請關閉Arduino IDE,後續用不到了~

 

請在 terminal 中使用以下指令來安裝 pyfirmata

pip install pyfirmata

 

Arduino端設定

python透過序列埠控制Arduino LED

 

本範例就是 python 版的 LED 閃爍,您可以比較一下兩者的差異,您只要確定COM port 邊與您電腦上的一致即可。如果是 MAC/ Linux,應該是 /dev/ttyUSB0 這樣的東西。如果是 Raspberry Pi 則應該是 /dev/ttyACM0

#!/usr/bin/python
 
import pyfirmata
import time
pin = 13
port = 'COM5'
board = pyfirmata.Arduino(port)   #對指定序列埠開啟通訊
while True:
    	board.digital[pin].write(1)  #設定指定腳位高電位
    	time.sleep(1)                    #等候1秒
    	print "ON : %s" % time.ctime()  #顯示相關訊息與時間
    	board.digital[pin].write(0)
    	time.sleep(1)
    	print "OFF : %s" % time.ctime()

執行畫面如下圖,除了Arduino D13 會亮暗之外,在console上也有對應的訊息

Python結合Arduino微笑偵測應用

來看一下綜合運用吧!本範例會根據畫面中臉孔的微笑程度來決定LED亮起的數目。

import httplib, urllib, base64, json, pyfirmata
from time import sleep
port = 'COM5'

# Replace the subscription_key string value with your valid subscription key.
subscription_key = 'XXX'   #填入Face API金鑰,任一組都可以

# NOTE: Free trial subscription keys are generated in the westcentralus region, so if you are using
# a free trial subscription key, you should not need to change this region.
uri_base = 'westcentralus.api.cognitive.microsoft.com'

# Request headers.
headers = {
    'Content-Type': 'application/json',
    'Ocp-Apim-Subscription-Key': subscription_key,
}

# Request parameters.
params = urllib.urlencode({
    'returnFaceId': 'true',
    'returnFaceLandmarks': 'false',
    'returnFaceAttributes': 'age,gender,smile,emotion,occlusion,accessories,exposure,noise',
    #'returnFaceAttributes': 'age,gender,headPose,smile,facialHair,glasses,emotion,hair,makeup,occlusion,accessories,blur,exposure,noise',
})

# The URL of a JPEG image to analyze.
body = "{'url':'https://how-old.net/Images/faces2/main002.jpg'}"

#connect Arduino
board = pyfirmata.Arduino(port)    #對指定序列埠開啟連線
sleep(3)      #等候3秒來開啟序列連線

try:
    # Execute the REST API call and get the response.
    conn = httplib.HTTPSConnection('westcentralus.api.cognitive.microsoft.com')
    conn.request("POST", "/face/v1.0/detect?%s" % params, body, headers)
    response = conn.getresponse()
    data = response.read()
    # 'data' contains the JSON data. The following formats the JSON data for display.
    parsed = json.loads(data)
    print ("Response:")
    print (json.dumps(parsed, sort_keys=True, indent=2))  #顯示所有資料
    a = parsed[0]["faceAttributes"]["smile"]   #顯示微笑程度
    conn.close()

except Exception as e:
    print("[Errno {0}] {1}".format(e.errno, e.strerror))

print "smile value is"
print a                                #顯示微笑程度
if a > 0.75:                         #以下分成三個區間來控制LED,您可以自行修改
    board.digital[9].write(1)
    board.digital[10].write(1)
    board.digital[11].write(1)
    print 111
elif a < 0.25:
    board.digital[9].write(1)
    board.digital[10].write(0)
    board.digital[11].write(0)
    print 100
else:
    board.digital[9].write(1)
    board.digital[10].write(1)
    board.digital[11].write(0)
    print 110

 

執行!

執行時,Moto GP 傳奇車手 Valentino Rossi~ https://moto7.tw/imgs/valentino-rossi-2015.jpg,也可以看到100代表只會亮起一個LED代表 Rossi 現在只有微笑或是沒表情。您可以切分更多條件或是修改Arduino端的呈現效果,一起來微笑吧!

延伸

 

請改用以下語法來將本機端圖檔上傳到FACE API來做辨識,請注意這個圖檔需要與 .py 放在同一個資料夾中,不然就需要指定絕對路徑。為了程式精簡,我們把 Arduino 端拿掉,您可以根據上續範例修改即可。

 

f = open(‘7688.jpg’, ‘rb’)

jpgdata = f.read()

f.close()

 

完整程式如下:

import httplib, urllib, base64
import json

f = open('a006.jpg', 'rb')
jpgdata = f.read()
f.close()

# Request header
headers = {
# Request headers
'Content-Type': 'application/octet-stream',
'Ocp-Apim-Subscription-Key': 'd3d138ceb1f4470286dcaba79f7d2de9',
}

params = urllib.urlencode({
# Request parameters
'returnFaceId': 'true',
'returnFaceLandmarks': 'false',
'returnFaceAttributes': 'age,gender,smile,emotion,glasses',
})   #只需要上述五個參數


body = {
}

try:
    conn = httplib.HTTPSConnection('api.projectoxford.ai')
    #conn.request("POST", "/face/v1.0/detect?%s" % params, json.dumps(body), headers)
    conn.request("POST", "/face/v1.0/detect?%s" % params, jpgdata, headers)
    response = conn.getresponse()
    data = json.loads(response.read())
    print(json.dumps(data, indent=2))   #顯示所有資料
    print data[0]["faceAttributes"]["smile"]   #顯示微笑程度
    conn.close()
except Exception as e:
    print("[Errno {0}] {1}".format(e.errno, e.strerror))

 

相關文章:

 

 

 

Leave a comment

Your email address will not be published.


*