您的位置:首页 > 其它

韩国女星长一个样儿?Rekognition治好你的脸盲症

2020-12-18 21:19 453 查看


韩国女团Twice

对很多程序猿/媛来说,脸盲症绝对是个要命的短板,尤其是那些青春靓丽但偏偏又彼此相似的韩国明星……这时候,就需要图像识别中的面部识别技术来助你一臂之力了。

Amazon Rekognition网站提供了许多有用的功能,例如“物体和场景检测”、“面部识别”、“面部分析”和“名人识别”。不过,在针对韩国明星的“名人识别”功能体验中,Amazon Rekognition偶尔会遇到麻烦:有时系统无法识别,还有时识别结果是错误的。


图为女团Twice中的周子瑜,但Amazon把她识别成了金雪炫(另一个韩国女团AOA成员)

因此,本文将用Amazon Rekognition编写一个简单的Python脚本,从而来准确地识别Twice的成员。

用Amazon Rekognition进行人脸检测

为了能在Jupyter Notebook上运行本文中的代码,你需要满足以下几个条件:

  1. 拥有亚马逊AWS账户
  2. 使用AWS命令行界面(CLI)配置的AWS凭证
  3. 更新Boto3至最新版本

首先,导入一些package,这些package可用于下一步。

import boto3
from PIL import Image
%matplotlib inline

现在需要找到一张想要处理的图片。我们以上文出现过的图片为例。把图片发送到Rekognition API,获取图像识别结果。

display(Image.open('Tzuyu.jpeg'))

我们可以把最基本的任务,也就是面部识别交给Rekognition来做。只需几行代码就能完成这个任务。

import io
rekognition = boto3.client('rekognition')
image = Image.open("Tzuyu.jpeg")
stream = io.BytesIO()
image.save(stream,format="JPEG")
image_binary = stream.getvalue()
rekognition.detect_faces(
Image={'Bytes':image_binary},
Attributes=['ALL']
)

你可以直接把图片作为内存中的二进制文件对象,从本地计算机发送到Rekogntion,也可以上传图片到S3,并在调用rekognition.detect_faces()时将存储桶和密钥作为参数给出。在这里,把图片作为二进制对象发出。上述调用会花费很长时间。你可以从Rekognition的detect_faces功能中获得所有信息。

{'FaceDetails': [{'AgeRange': {'High': 38, 'Low': 20},
'Beard': {'Confidence': 99.98848724365234, 'Value': False},
'BoundingBox': {'Height': 0.1584049016237259,
'Left': 0.4546355605125427,
'Top': 0.0878104418516159,
'Width': 0.09999311715364456},
'Confidence': 100.0,
'Emotions': [{'Confidence': 37.66959762573242, 'Type': 'SURPRISED'},
{'Confidence': 29.646778106689453, 'Type': 'CALM'},
{'Confidence': 3.8459930419921875, 'Type': 'SAD'},
{'Confidence': 3.134934186935425, 'Type': 'DISGUSTED'},
{'Confidence': 2.061260938644409, 'Type': 'HAPPY'},
{'Confidence': 18.516468048095703, 'Type': 'CONFUSED'},
{'Confidence': 5.1249613761901855, 'Type': 'ANGRY'}],
'Eyeglasses': {'Confidence': 99.98339080810547, 'Value': False},
'EyesOpen': {'Confidence': 99.9864730834961, 'Value': True},
'Gender': {'Confidence': 99.84709167480469, 'Value': 'Female'},
'Landmarks': [{'Type': 'eyeLeft',
'X': 0.47338899970054626,
'Y': 0.15436244010925293},
{'Type': 'eyeRight', 'X': 0.5152773261070251, 'Y': 0.1474122554063797},
{'Type': 'mouthLeft', 'X': 0.48312342166900635, 'Y': 0.211111381649971},
{'Type': 'mouthRight', 'X': 0.5174261927604675, 'Y': 0.20560002326965332},
{'Type': 'nose', 'X': 0.4872787892818451, 'Y': 0.1808750480413437},
{'Type': 'leftEyeBrowLeft',
'X': 0.45876359939575195,
'Y': 0.14424000680446625},
{'Type': 'leftEyeBrowRight',
'X': 0.4760720133781433,
'Y': 0.13612663745880127},
{'Type': 'leftEyeBrowUp',
'X': 0.4654795229434967,
'Y': 0.13559915125370026},
{'Type': 'rightEyeBrowLeft',
'X': 0.5008187890052795,
'Y': 0.1317606270313263},
{'Type': 'rightEyeBrowRight',
'X': 0.5342025756835938,
'Y': 0.1317359358072281},
{'Type': 'rightEyeBrowUp',
'X': 0.5151524543762207,
'Y': 0.12679456174373627},
{'Type': 'leftEyeLeft', 'X': 0.4674917757511139, 'Y': 0.15510375797748566},
{'Type': 'leftEyeRight',
'X': 0.4817998707

从上面detect_faces的响应示例中可以看出,识别结果不仅包含人脸在图片中的边界框位置,还包含更高级的特征,如情感、性别、年龄段等。

人脸比较

Amazon Rekognition可以帮助对比两张图片中的人脸。例如,如果将子瑜的图片设为源图片,然后发送一张Twice成员的照片作为目标图片,Rekognition能够从目标图片中找到同源图片最相像的脸。我们要用到的Twice集体照就是下面这张。

如果你既不是亚洲人也不是Twice粉的话,可能连作为人类的你也无法完成这项任务。你可以猜猜这张图片里谁是子瑜。现在让我们来看看Rekognition是怎么完成任务的。

sourceFile='Tzuyu.jpeg'
targetFile='twice_group.jpg'

imageSource=open(sourceFile,'rb')
imageTarget=open(targetFile,'rb')
response = rekognition.compare_faces(SimilarityThreshold=80,
SourceImage={'Bytes': imageSource.read()},
TargetImage={'Bytes': imageTarget.read()})
response['FaceMatches']

输入compare_faces作为指令,系统会分析团体照中所有不匹配人脸的信息,这会花费大量时间。因此,我们将指令特殊化成[‘FaceMatches’],要求Rekognition只输入匹配的人脸信息。看上去,Rekognition从这张团体照中找出了一张相似度为97%的人脸。让我们通过边界框信息来检查一下Rekognition有没有在集体照中正确识别出周子瑜。

顺便一提,BoundingBox给出的值是人脸占整个图像大小的比率。所以,为了用BoundingBox的值来绘制边框,你需要将图像的真实长度或宽度乘以比率,计算人脸框的大小。利用下面的代码可以做到这一点。

from PIL import ImageDraw
image = Image.open("twice_group.jpg")
imgWidth,imgHeight  = image.size
draw = ImageDraw.Draw(image)
box = response['FaceMatches'][0]['Face']['BoundingBox']
left = imgWidth * box['Left']
top = imgHeight * box['Top']
width = imgWidth * box['Width']
height = imgHeight * box['Height']
points = (
(left,top),
(left + width, top),
(left + width, top + height),
(left , top + height),
(left, top)
)
draw.line(points, fill='#00d400', width=2)
display(image)

Rekognition,干得漂亮!这确实就是子瑜!

创建集合

现在我们能够识别图片中的面部,并且可以从目标图像中识别出源图像的人脸。但是以上功能都是一次性的,我们还需要存储女团中每个成员的面部信息和名字。这样的话,每发送一张Twice的新照片,系统都可以检索数据,识别成员面部并且显示成员名。

为达到该目的,需要应用亚马逊的“基于存储的API操作(Storage-Based API Operations)”。该操作包括两种亚马逊特有的专有术语。“集合”指的是Rekognition存储已识别人脸信息的虚拟数据库。通过使用集合,可以进行“索引”。这里指的是检测图像中的人脸,然后将信息存储在指定的集合中。需要提到的是,Rekognition存储的信息不是实际的图像,而是由算法提取的特征向量。接下来让我们学习如何创建集合并且添加索引。

collectionId='test-collection'
rekognition.create_collection(CollectionId=collectionId)

是的,就是这么简单。由于这是刚刚创建的新集合,因此里面没有存储任何信息。但是,让我们再看一下。

rekognition.describe_collection(CollectionId=collectionId)

在上面的响应中可以看到“FaceCount”的值是0。如果加入人脸索引并将其存储在集合中,这个值就会改变。

人脸索引

同样的,人脸索引也很简单,只需要在Rekognition中添加一行代码。

sourceFile='Tzuyu.jpeg'
imageSource=open(sourceFile,'rb')
rekognition.index_faces(Image={'Bytes':imageSource.read()},ExternalImageId='Tzuyu',CollectionId=collectionId)

你可以看到,上面的代码中设置了参数ExternalImageId,并把值命名为“子瑜”。当你尝试从新图片中识别出子瑜的脸时,Rekognition就会搜索索引中的面部信息。每当有新的面部被编入索引时,Rekognition就会给它一个独有的face ID。但是我们想在匹配出来的子瑜的脸旁边显示出她的名字。因此,需要用到参数ExternalImageId。现在,如果查看集合的话,会发现一个人脸信息已经被加到该集合中了。

rekognition.describe_collection(CollectionId=collectionId)

按图像搜索人脸

现在子瑜的面部信息已被加入到集合中,可以将一张新的图片发送到Rekognition进行匹配。但是search_faces_by_image功能有一个问题,那就是它每次只能检测一张人脸(图像中面积最大的脸)。因此,如果想在Twice的集体照中找到子瑜,还需要再添加一步。在执行detect_faces前,应利用每张脸的边界框信息,逐个调用search_faces_by_image。首先,让我们检测每个成员的面部信息。

imageSource=open('twice_group.jpg','rb')
resp = rekognition.detect_faces(Image={'Bytes':imageSource.read()})
all_faces = resp['FaceDetails']
len(all_faces)

Rekognition从集体照中识别出了9张脸。不错。接下来裁剪照片,用serach_faces_by_image逐个搜索。

image = Image.open("twice_group.jpg")
image_width,image_height  = image.size
for face in all_faces:
box=face['BoundingBox']
x1 = box['Left'] * image_width
y1 = box['Top'] * image_height
x2 = x1 + box['Width'] * image_width
y2 = y1 + box['Height']  * image_height
image_crop = image.crop((x1,y1,x2,y2))

stream = io.BytesIO()
image_crop.save(stream,format="JPEG")
image_crop_binary = stream.getvalue()
response = rekognition.search_faces_by_image(
CollectionId=collectionId,
Image={'Bytes':image_crop_binary}
)
print(response)
print('-'*100)

在search_faces_by_image的9个结果中,Rekognition发现了一张能够和集合中的面部匹配的人脸。我们只建立了子瑜的索引,所以匹配到的人脸就是子瑜的。在图片上显示边界框和名字吧。有关名字的部分,会用到把人脸编入索引时设置的ExternalImageId。

顺便说一下,在所有search_faces_by_image的响应中,“FaceMatches”是一个数组。也就是说,如果集合中有多个面部被匹配,系统则会显示所有的匹配结果。根据亚马逊的说法,这个数组是按照相似性得分排序的,相似性最高的优先。将数组中的第一项作为匹配度最高的结果。

from PIL import ImageFont
import io
image = Image.open("twice_group.jpg")
image_width,image_height  = image.size

for face in all_faces:
box=face['BoundingBox']
x1 = box['Left'] * image_width
y1 = box['Top'] * image_height
x2 = x1 + box['Width'] * image_width
y2 = y1 + box['Height']  * image_height
image_crop = image.crop((x1,y1,x2,y2))

stream = io.BytesIO()
image_crop.save(stream,format="JPEG")
image_crop_binary = stream.getvalue()
response = rekognition.search_faces_by_image(
CollectionId=collectionId,
Image={'Bytes':image_crop_binary}
)

if len(response['FaceMatches']) > 0:
draw = ImageDraw.Draw(image)
points = (
(x1,y1),
(x2, y1),
(x2, y2),
(x1 , y2),
(x1, y1)
)
draw.line(points, fill='#00d400', width=2)
fnt = ImageFont.truetype('/Library/Fonts/Arial.ttf', 15)

draw.text((x1,y2),response['FaceMatches'][0]['Face']['ExternalImageId'], font=fnt, fill=(255, 255, 0))
display(image)

太棒了!答案又是正确的!

识别Twice的所有成员

现在,让我们开发更多的项目功能,使之能够识别团体照中的所有成员。为了实现这个目标,首先需要把所有人的面部信息编入索引(一共有9个成员)。在Christian Petters编写的亚马逊教程中提到:“为每个人添加多个参考图像会大大提高匹配的成功率。”他的建议简单易懂。因此,我们为每个成员都准备了四张照片,并添加了同一个成员的多张照片。

collectionId='twice'
rekognition.create_collection(CollectionId=collectionId)

import os
path = 'Twice'
for r, d, f in os.walk(path):
for file in f:
if file != '.DS_Store':
sourceFile = os.path.join(r,file)
imageSource=open(sourceFile,'rb')

rekognition.index_faces(Image={'Bytes':imageSource.read()},ExternalImageId=file.split('_')[0],CollectionId=collectionId)
rekognition.describe_collection(CollectionId=collectionId)

好的。现在所有36张照片都被编入了“Twice”集合的索引。现在是时候检验成果了。升级后的Rekognition能够识别Twice的全部成员吗?

from PIL import ImageFont
image = Image.open("twice_group.jpg")
image_width,image_height  = image.size

for face in all_faces:
box=face['BoundingBox']
x1 = box['Left'] * image_width
y1 = box['Top'] * image_height
x2 = x1 + box['Width'] * image_width
y2 = y1 + box['Height']  * image_height
image_crop = image.crop((x1,y1,x2,y2))

stream = io.BytesIO()
image_crop.save(stream,format="JPEG")
image_crop_binary = stream.getvalue()
response = rekognition.search_faces_by_image(
CollectionId=collectionId,
Image={'Bytes':image_crop_binary}
)

if len(response['FaceMatches']) > 0:
draw = ImageDraw.Draw(image)
points = (
(x1,y1),
(x2, y1),
(x2, y2),
(x1 , y2),
(x1, y1)
)
draw.line(points, fill='#00d400', width=2)
fnt = ImageFont.truetype('/Library/Fonts/Arial.ttf', 15)

draw.text((x1,y2),response['FaceMatches'][0]['Face']['ExternalImageId'], font=fnt, fill=(255, 255, 0))
display(image)

是的,它做到了!它正确识别了所有成员!

Jupyter Notebook和所有照片传送门:
https://github.com/tthustla/twice_recognition

留言 点赞 发个朋友圈

我们一起分享AI学习与发展的干货

编译组:刘贺、梁晶晶
相关链接:
https://towardsdatascience.com/building-k-pop-idol-identifier-with-amazon-rekognition-92302442d763

如需转载,请后台留言,遵守转载规范

推荐文章阅读

ACL2018论文集50篇解读
EMNLP2017论文集28篇论文解读
2018年AI三大顶会中国学术成果全链接
ACL2017 论文集:34篇解读干货全在这里
10篇AAAI2017经典论文回顾

长按识别二维码可添加关注

读芯君爱你

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: