本文要介紹微軟辨識服務的Face API,可以偵測照片中的臉孔以及相關參數,包含年齡、性別、情緒、眼鏡、鬍子以及五官座標等。
作者/攝影 |
曾吉弘 |
時間 |
3小時 |
成本 |
無 |
難度 |
* * * * * |
材料表 |
- 個人電腦
- Arduino Uno 或其他可執行 StandFirmata 之 Arduino相容板
- LED x 3
- 線材
- 麵包板
|
本範例修改自微軟辨識服務Face API教學而來,您也可以在該頁面找到其他程式語言的教學。
微軟辨識服務
根據官方網站說法:Microsoft 辨識服務具備多種程式語言的API,可以透過自然的溝通方式看、聽、說,以及理解和解讀各種媒體素材,包然文字、語音、照片、影片與搜尋建議等等。分成五大類,每項又各自分成不同細項,本範例將使用辨識下的臉部API:
- 辨識
- 語音
- 語言
- 知識
- 搜尋
註冊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))
相關文章: