AWS在2016年底釋出的圖片辨識服務(Rekognition)其實是非常非常昂貴。除了前5000次影像辨識不收費之外,接下來每一千次影像處理會收1美金。
乍看之下不多,但實務上,公開使用的影像辨識,通常無意中就暴增。
以之前LINE聊天機器人影像辨識為例,由於會當辨識到女性的照片時,會特別額外辨識內建的臉孔比對(40個亞洲女星照片)。等於是每收到一個女性照片,會進行42次臉孔辨識:40次照片比對+1次特徵比對+一次名人資料庫比對。就LINE聊天機器人數百的好友而言,該功能開放不到7天,就已經超過四萬次比對,換算價格約35美金。
35美金其實足以開啟維持t2.medium (EC2 VM)一整個月。這個VM甚至還有4G的記憶體。這樣的VM絕對能支撐每秒2-5次的臉孔比對,換言之,一整個月可以比對超過7百萬次。而這7百萬次也才略高於35美金。
然而,不應該因為成本的增加,就直接使用EC2 VM。而是應該考慮在符合serverless的架構下,如何解決這個問題。畢竟,當使用了VM,未來在擴增(scale-out)上也會有些麻煩。其實,我們目的很簡單清楚:只是要比對兩張臉孔的相似度。因此,應該使用輕量化Lambda即可。
原本做法
當使用者透過LINE上傳照片給聊天機器人之後,後端系統會執行下列事情:
(1) 先利用AWS Rekognition (detect)查詢基本臉孔資料,例如性別,年紀等等。
(2) 假如判斷是女性,就到AWS S3上選取所有要比對的臉孔,進行比對分析。在這裡,如果有40張臉孔,表示每一次上傳圖片,都要在這個階段額外送出40次分析。即便AWS允許先行儲存圖片特徵,但在比對階段仍然是看次數。
參考程式節錄如下:
rclient = boto3.client('rekognition')
s3 = boto3.resource('s3')
bucket = s3.Bucket('sandyifamousface')
for o in bucket.objects.all():
#print(o.key)
response = rclient.compare_faces(
SourceImage={
'Bytes': byteArray
},
TargetImage={
'S3Object': {
'Bucket': 'sandyifamousface',
'Name': o.key,
}
},
SimilarityThreshold = 60
)
if len(response['FaceMatches'] ) > 0:
# DO things if match..
(3) 最後把判斷之後的結果,送回給LINE
改良做法
先將40張圖做臉孔分析,並且把特徵值Landmarks挑出來,儲存在檔案中。未來數量大的話當然可以存在dynamodb。
在這個範例是儲存於json文字檔中。
(1) 與上一段相同
(2) 在Lambda被載入
時,就先讀取文字檔,成為python的dictionary。原本要利用Rekognition做比對,改為使用自己寫的比對函數。在範例中,這個函數是利用landmark的相對距離變化,來判對臉孔相似與否。當然這樣的比對其實很粗糙,而且也沒有考慮臉孔的前側傾角度。不過,和aws本身所附帶的臉孔比對的結果其實已經很接近。
參考程式節錄如下:
def compareLandMark(landmarkList1, landmarkList2):
distList = []
compareList = [
('eyeRight','nose') ,
('eyeLeft','nose'),
('mouthLeft','nose'),
('mouthRight','nose'),
('mouthUp','mouthDown'),
('mouthLeft','mouthDown'),
('mouthRight','mouthDown'),
('noseRight','eyeRight'),
('leftPupil','rightPupil'),
('nose','rightPupil'),
('leftPupil','nose'),
('noseRight','noseLeft'),
('eyeRight','eyeLeft') ,
('mouthRight','mouthLeft') ,
('mouthRight','eyeRight') ,
('mouthLeft','eyeRight') ,
('mouthRight','eyeLeft') ,
]
for (m1,m2) in compareList:
d1 = getDistanceFromType(landmarkList1, m1, m2)
d2 = getDistanceFromType(landmarkList2, m1, m2)
distance = (abs(d1-d2)/d1)
distList.append(distance)
lenD = len(distList)
mD = statistics.mean(distList)
# stdev and variance could be used in the future.
mStd = statistics.stdev(distList)
mV = statistics.variance(distList)
conf = (1-mD)**2
return conf*100
(3) 最後把判斷之後的結果,送回給LINE
結果:
在Lambda自行撰寫比對程式,但是其實是利用AWS Rekognition 所給出的landmark (特徵),會讓比對變得簡單而且成本很低。
缺點是,這樣的比對準確度和如何計算特徵有很大的關係。
* 關於LINE聊天機器人,請參考這篇
* 專案程式碼放在這裡。
* google的vision api其實價格更貴,請參考這裡。