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 (特徵),會讓比對變得簡單而且成本很低。
缺點是,這樣的比對準確度和如何計算特徵有很大的關係。
缺點是,這樣的比對準確度和如何計算特徵有很大的關係。