Python人脸检测(初级)

栏目: Python · 发布时间: 6年前

内容简介:忙了一段时间,少有时间写文,前连天公司的一个web项目需要用户上传自拍照然后检查自拍照里面的人脸状态(人脸数量,是否正脸,是否戴眼镜,是否戴帽子等等),如果状态合适就将人脸提取出来,最后与设置的前景图片进行融合。这其中涉及到的关键点在于人脸的检测,人脸的提取,至于人脸与前景的融合则可以使用前端js实现,对人脸的处理一般使用滤镜处理,前端可以使用腾讯AlloyTeam团队开源的项目AlloyImage(关于滤镜处理图片这部分不多说了,仔细研读腾讯的开源项目文档就能搞定,今天说说人脸检测和提取部分,注意是“检

前言

忙了一段时间,少有时间写文,前连天公司的一个web项目需要用户上传自拍照然后检查自拍照里面的人脸状态(人脸数量,是否正脸,是否戴眼镜,是否戴帽子等等),如果状态合适就将人脸提取出来,最后与设置的前景图片进行融合。

这其中涉及到的关键点在于人脸的检测,人脸的提取,至于人脸与前景的融合则可以使用前端js实现,对人脸的处理一般使用滤镜处理,前端可以使用腾讯AlloyTeam团队开源的项目AlloyImage( )处理图片,拥有丰富的滤镜。当然也可以在后端处理好传给前端,后端处理可以选择使用OpenCV,后端处理的缺点在于多用户同时上传的时候服务器压力较大,交给前端处理比较妥当。

关于滤镜处理图片这部分不多说了,仔细研读腾讯的开源项目文档就能搞定,今天说说人脸检测和提取部分,注意是“检测”,不是“识别”。

检测的意思是只需要检查是人脸和人脸的状态以及人脸的位置,识别则是根据人脸特征匹配到人的姓名,这是两种不同的概念,千万不要混淆。

分析

既然是入门级的人脸检测,当然是从易到难,首先考虑的是有没有第三方人脸检测API,直接调用第三方接口,也是一种快速实现的方法。说到第三方接口,自然要货比三家,谁好用用谁。

经过搜索引擎的筛选,博主选择了四家服务商的接口,分别是百度、腾讯、阿里云、Face++。当然了,如果这篇文章只是单纯的描述如何调用第三方接口的话,估计各位看官也不会买账,不就是一个requests请求的事,还需要写个文?

为了体现从易到难循序渐进的思想,博主增加了本地检测的方式进行对比,本地 Python 配合OpenCV也可以实现人脸的检测,把这几种进行对比,自然就能看出各自的优劣。

当然了,本地OpenCV检测的速度和效率是第三方服务商接口调用无法比的,这也是为下一篇进阶(监控流媒体画面实时检测)打基础。

好了,废话不多说,开始几种方案的对比。

接口

首先对四家的接口文档进行分析,对接难度从易到难分别是 Face++、百度、腾讯、阿里云,参考标准是实现相同功能需要的代码行数(手动滑稽!)。

给出四家服务商的接口文档地址供大家自行参考:

  • Face++ 文档地址:https://console.faceplusplus.com.cn/documents/4888373
  • 腾讯人脸检测 文档地址:https://cloud.tencent.com/document/product/867/17588
  • 百度 人脸检测 文档地址:http://ai.baidu.com/docs#/Face-Detect-V3/top
  • 阿里云 人脸检测 文档地址:https://help.aliyun.com/knowledge_detail/53399.html

Face++和百度云不需要生成签名,只需要传入key和id即可,腾讯和阿里云需要生成对应的签名做认证才能调用成功,其中阿里云的签名生成是对所有的请求内容进行签名,稍有错误就导致签名失败,所以调试起来稍微有点复杂,博主也是在这里被坑的很惨。

Face++ 支持三种图片的传递方式,url、二进制、base64,文件大小为最大2M。

腾讯 支持两种图片的传递方式,url和二进制。

百度 支持两种图片的传递方式,url和Base64。

阿里云 支持两种图片的传递方式,url和Base64。

阿里云返回的结果中数据量最小,很多检测结果是没有的,比如是否戴眼镜,戴帽子等,当然了,这里的重点是人脸检测,只需要检测到人脸的位置信息就OK了,其他的都是次要信息。

针对项目需求,url传图的方式因为有图片下载的时间不确定性,而且用户上传图片后台转换url也是一道程序,还不如直接传图,四家服务商除了腾讯不支持base64以外,其他三家都支持base64,而且前端调用微信的图片上传方法可以直接将图片在前端就转换成base64,通过接口上传给服务端处理,调用腾讯云的时候可以将base64编码的图片直接decode成二进制文件,这样腾讯云就可以支持了。

实战

博主首选选取一张带有明显人脸标志的图片进行测试,上测试图(在此感谢迪丽热巴)

Python人脸检测(初级)

然后根据需求编写代码,先给出测试结果的对比图,再来根据各自的结果进行分析

Python人脸检测(初级)

从对比图可以看出,Face++和百度的识别结果基本一致,腾讯和本地OpenCV识别出来的结果非常接近,而阿里云识别出来的结果则是相对比较完整的人脸。

当然了,仅仅依靠这一张图很难判断出来谁更厉害,那咱们多来几个测试,首先来一张胡歌的侧脸特写(在此感谢胡歌)

Python人脸检测(初级)

这组检测结果里面依然是阿里云表现最佳,而百度则有点离谱,OpenCV虽然检测到的位置是准确的,但是检测人脸的范围出现了失误,显示为正方形。

下面再来一张,这张测试图是一张面部被头发遮挡并且不是正面,看下各自的识别结果(在此感谢袁姗姗)

Python人脸检测(初级)

从这组测试结果来看还是阿里云最佳,其次是face++,而OpenCV直接识别失败了!! Python人脸检测(初级)

这里得说明一下,OpenCV识别失败并不是因为它就一定很垃圾,OpenCV的识别率是根据给定的人脸模型而来的,OpenCV自带了很多识别的模型,这些模型都是经过多次卷积网络算法训练得来的,而博主这里使用的是人脸最为严格的模型,也就是说识别的条件更为苛刻,所以在这次测试中OpenCV没有检测到人脸,比较尴尬。

所以,综上一些简单的测试后发现如果要做人脸的提取,还是选择阿里云的人脸检测接口比较合适,下面来看看各家API的收费情况,这里仅仅比对免费额度。

Face++提供免费的使用API key,不限制调用次数,但是一些高级的功能无法使用。

百度 对于个人用户提供QOS 为2 企业用户QOS 为10,调用次数不限的免费服务。

腾讯 提供不限次数的调用请求,但是高级功能需要付费。

阿里云 提供免费5000次的免费调用额度,5000次用完自动切换到付费模式。

这里放上一张前两天项目中的效果图吧

Python人脸检测(初级)

代码

根据各家提供的API文档和测试demo,编写各自的测试代码,下面提供代码供大家参考。

注意:这里代码仅仅作为快速调试的参考,没有做异常处理,正式开发请根据自身开发环境和生产环境自行考虑各种异常处理,请不要直接使用我的代码。

如果有不懂的地方可以发邮件询问。

Face++(图片以二进制上传)

# -*- coding: utf-8 -*-

import requests, sys
from PIL import Image, ImageDraw

source_path = sys.path[0] + '/shanshan.jpg'

with open(source_path, "rb") as f:
    file_data = f.read()
params = {
    "api_key": "xxxxxx",
    "api_secret": "xxxxxxx"
}

f = {"image_file": file_data}

request_url = "https://api-cn.faceplusplus.com/facepp/v3/detect"

decode_result = requests.post(url=request_url, data=params, files=f, timeout=30).json()

current_face = decode_result.get('faces')[0]
face_location = current_face.get('face_rectangle')

face_x = int(face_location.get("left"))
face_y = int(face_location.get("top"))
face_width = face_x + int(face_location.get("width"))
face_height = face_y + int(face_location.get("height"))

old_img = Image.open(source_path)
draw_instance = ImageDraw.Draw(old_img)
draw_instance.rectangle((face_x, face_y, face_width, face_height), outline=(255, 0, 0))
old_img.save(sys.path[0] + '/i-face++.jpg')

百度

# -*- coding: utf-8 -*-

import requests, sys, base64
from PIL import Image, ImageDraw

source_path = sys.path[0] + '/shanshan.jpg'

# client_id 为官网获取的AK, client_secret 为官网获取的SK
host = 'https://aip.baidubce.com/oauth/2.0/token?grant_type=client_credentials&client_id=*********&client_secret=**********'
session = requests.get(url=host, timeout=30).json()
access_token = session.get('access_token')

with open(source_path, "rb") as f:
    base64_data = base64.b64encode(f.read())
params = {
    "image": base64_data,
    "image_type": "BASE64",
    "face_field": "beauty,age,glasses,face_type,quality"
}

request_url = "https://aip.baidubce.com/rest/2.0/face/v3/detect"
request_url = request_url + "?access_token=" + access_token
decode_result = requests.post(url=request_url, data=params, timeout=30).json()

face_num = decode_result.get('result').get('face_num')
current_face = decode_result.get('result').get('face_list')[0]
face_location = current_face.get('location')
face_glasses = current_face.get('glasses')
face_real = current_face.get('face_type')
face_quality = current_face.get('quality')
face_probability = current_face.get('face_probability')
face_angel = current_face.get('angel')

face_x = int(face_location.get("left"))
face_y = int(face_location.get("top"))
face_width = face_x + int(face_location.get("width"))
face_height = face_y + int(face_location.get("height"))

old_img = Image.open(source_path)
draw_instance = ImageDraw.Draw(old_img)
draw_instance.rectangle((face_x, face_y, face_width, face_height), outline=(255, 0, 0))
old_img.save(sys.path[0] + '/i-baidu.jpg')

腾讯

# -*- coding: utf-8 -*-

import requests, sys, base64, time, random, hashlib, hmac
from PIL import Image, ImageDraw

source_path = sys.path[0] + '/shanshan.jpg'
orignal = "a=******&b=&k=*******&e=******&t=%d&r=%d&f=" % (int(time.time()), random.randint(1, 1000))

SignTmp = hmac.new("**********".encode('utf8'), orignal.encode('utf8'), hashlib.sha1).digest()

before_string = SignTmp + orignal.encode('utf8')
Sign = base64.b64encode(before_string).decode('utf8')

request_url = "https://recognition.image.myqcloud.com/face/detect"

headers = {"authorization": Sign, "host": "recognition.image.myqcloud.com"}

s = requests.session()


with open(source_path, "rb") as f:
    img_file = f.read()

params = {"appid": "*********", "mode": 1}

f = {"image": open(source_path, "rb").read()}

decode_result = s.post(url=request_url, headers=headers, data=params, files=f, timeout=30).json()

if decode_result.get('code') == 0:
    face_data = decode_result.get('data')

    current_face = face_data.get('face')[0]

    face_x = int(current_face.get("x"))
    face_y = int(current_face.get("y"))
    face_width = face_x + int(current_face.get("width"))
    face_height = face_y + int(current_face.get("height"))

    old_img = Image.open(source_path)
    draw_instance = ImageDraw.Draw(old_img)
    draw_instance.rectangle((face_x, face_y, face_width, face_height), outline=(255, 0, 0))
    old_img.save(sys.path[0] + '/i-tencent.jpg')

阿里云

# -*- coding: utf-8 -*-

import requests, sys, base64, hashlib, hmac, json, datetime
from PIL import Image, ImageDraw

source_path = sys.path[0] + '/shanshan.jpg'

id = "*******"
key = "***********"

request_url = "https://dtplus-cn-shanghai.data.aliyuncs.com/face/detect"

with open(source_path, "rb") as f:
    base64_data = base64.b64encode(f.read()).decode('utf8')

params = {"type": 1, "content": base64_data}


def get_current_date():
    date = datetime.datetime.strftime(datetime.datetime.utcnow(), "%a, %d %b %Y %H:%M:%S GMT")
    return date


def to_md5_base64(string_body):
    hash_obj = hashlib.md5()
    hash_obj.update(string_body.encode('utf8'))
    return base64.b64encode(hash_obj.digest()).decode('utf8')


def to_sha1_base64(string_to_sign, secret):
    hmac_sha1 = hmac.new(secret.encode('utf8'), string_to_sign.encode('utf8'), hashlib.sha1)
    return base64.b64encode(hmac_sha1.digest()).decode('utf8')


options = {
    'method': 'POST',
    'body': json.dumps(params),
    'headers': {
        'accept': 'application/json',
        'content-type': 'application/json',
        'date':  get_current_date()
    }
}

body_md5 = to_md5_base64(options.get('body'))


string_sign = '%s\n%s\n%s\n%s\n%s\n%s' % (
    options.get('method'),
    options.get('headers').get('accept'),
    body_md5,
    options.get('headers').get('content-type'),
    options.get('headers').get('date'),
    "/face/detect"
)

signature = to_sha1_base64(string_sign, key)


headers = {
    'Accept': 'application/json',
    'Content-type': 'application/json',
    "Date": options.get('headers').get('date'),
    "Authorization": "Dataplus %s:%s" % (id, signature)
}

s = requests.session()

decode_result = s.post(url=request_url, headers=headers, data=json.dumps(params), timeout=30).json()

if decode_result.get('errno') == 0:
    face_data = decode_result.get('face_rect')

    face_x = int(face_data[0])
    face_y = int(face_data[1])
    face_width = face_x + int(face_data[2])
    face_height = face_y + int(face_data[3])

    old_img = Image.open(source_path)
    draw_instance = ImageDraw.Draw(old_img)
    draw_instance.rectangle((face_x, face_y, face_width, face_height), outline=(255, 0, 0))
    old_img.save(sys.path[0] + '/i-aliyun.jpg')

OpenCV

# -*- coding: utf-8 -*-

import cv2, sys
from PIL import Image, ImageDraw


def test_face():
    source_path = sys.path[0] + '/shanshan.jpg'
    img = cv2.imread(source_path)
    face_cascade = cv2.CascadeClassifier("/usr/share/opencv/haarcascades/haarcascade_frontalface_default.xml")
    gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
    faces = face_cascade.detectMultiScale(gray, 1.3, 4)
    if len(faces) > 1:
        print("不止一个人脸")
    else:
        print("一个人脸")

        face_x = faces[0][0]
        face_y = faces[0][1]
        face_width = face_x + faces[0][2]
        face_height = face_y + faces[0][3]

        old_img = Image.open(source_path)
        draw_instance = ImageDraw.Draw(old_img)
        draw_instance.rectangle((face_x, face_y, face_width, face_height), outline=(255, 0, 0))
        old_img.save(sys.path[0] + '/i-opencv.jpg')

if __name__ == '__main__':
    test_face()

请根据自己的环境自行替换代码中的参数进行测试。

如果需要对图片中的人脸进行提取也非常简单,增加下面的代码即可

image_name = sys.path[0] + '/cut-opencv.jpg'
Image.open(source_path).crop((face_x, face_y, face_width, face_height)).save(image_name)

用途

写了这么多,到底都有啥用途呢,很常见的那种颜值测试就可以用这种api直接实现,api里面都可以直接返回颜值分数,非常简单。

本文链接:https://www.92ez.com/?action=show&id=23472

!!! 转载请先联系non3gov@gmail.com授权并在显著位置注明作者和原文链接 !!!小黑屋

提示:技术文章有一定的时效性,请先确认是否适用你当前的系统环境。


以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

只是为了好玩

只是为了好玩

Linus Torvalds、David Diamond / 陈少芸 / 人民邮电出版社 / 2014-7 / 49.00 元

本书是Linux之父Linus Torvalds的自传。 Linux之父Linus Torvalds的自传,也是Linus唯一一本书。Linus以调侃的语气讲述了自己的成长经历,在他看来,一切都是为了好玩儿,兴趣引发革命。书中内容共分为五章,一部分是Linus自己写的,一部分是合著者David Diamond的评论。 林纳斯•托瓦兹 当今世界最著名的程序员、黑客,开源操作系统Linux......一起来看看 《只是为了好玩》 这本书的介绍吧!

在线进制转换器
在线进制转换器

各进制数互转换器

随机密码生成器
随机密码生成器

多种字符组合密码

RGB HSV 转换
RGB HSV 转换

RGB HSV 互转工具