Python公众号开发(二)—颜值检测
上篇文章,我们把自己的程序接入了微信公众号,并且能把用户发送的文本及图片文件原样返回。今天我们把用户的图片通过腾讯的AI平台分析后再返回给用户。
为了防止我的文章被到处转载,贴一下我的公众号【智能制造社区】,欢迎大家关注。
github仓库地址https://github.com/injetlee/Python/tree/master/wechat
效果图
一. 接入腾讯AI平台
我们先看一下官方人脸检测与分析接口的描述:
检测给定图片(Image)中的所有人脸(Face)的位置和相应的面部属性。位置包括(x, y, w, h),面部属性包括性别(gender), 年龄(age), 表情(expression), 魅力(beauty), 眼镜(glass)和姿态(pitch,roll,yaw)。
请求参数包括下面几个:
- app_id 应用标识,我们在AI平台注册后就可以得到app_id
- time_stamp 时间戳
- nonce_str 随机字符串
- sign 签名信息,需要我们自己去计算
- image 需要检测的图片(上限1M)
- mode 检测模式
1.接口鉴权,构造请求参数
官方给了我们接口鉴权的计算方法。
- 将<key, value>请求参数对按key进行字典升序排序,得到有序的参数对列表N
- 将列表N中的参数对按URL键值对的格式拼接成字符串,得到字符串T(如:key1=value1&key2=value2),URL键值拼接过程value部分需要URL编码,URL编码算法用大写字母,例如%E8,而不是小写%e8
- 将应用密钥以app_key为键名,组成URL键值拼接到字符串T末尾,得到字符串S(如:key1=value1&key2=value2&app_key=密钥)
- 对字符串S进行MD5运算,将得到的MD5值所有字符转换成大写,得到接口请求签名
2.请求接口地址
请求接口信息,我们用 requests 发送请求,会得到返回的 json 格式的图像信息pip install requests
安装requests。
3.处理返回的信息
处理返回的信息,把信息展示在图片上,再把处理后的图片保存。这里我们用到 opencv ,和 pillow 两个库pip install pillow
和pip install opencv-python
来安装。
开始编写代码,我们新建一个face_id.py 文件来对接AI平台,并且返回检测后的图像数据。
import time
import random
import base64
import hashlib
import requests
from urllib.parse import urlencode
import cv2
import numpy as np
from PIL import Image, ImageDraw, ImageFont
import os
# 一.计算接口鉴权,构造请求参数
def random_str():
'''得到随机字符串nonce_str'''
str = 'abcdefghijklmnopqrstuvwxyz'
r = ''
for i in range(15):
index = random.randint(0,25)
r += str[index]
return r
def image(name):
with open(name, 'rb') as f:
content = f.read()
return base64.b64encode(content)
def get_params(img):
'''组织接口请求的参数形式,并且计算sign接口鉴权信息,
最终返回接口请求所需要的参数字典'''
params = {
'app_id': '1106860829',
'time_stamp': str(int(time.time())),
'nonce_str': random_str(),
'image': img,
'mode': '0'
}
sort_dict = sorted(params.items(), key=lambda item: item[0], reverse=False) # 排序
sort_dict.append(('app_key', 'P8Gt8nxi6k8vLKbS')) # 添加app_key
rawtext = urlencode(sort_dict).encode() # URL编码
sha = hashlib.md5()
sha.update(rawtext)
md5text = sha.hexdigest().upper() # 计算出sign,接口鉴权
params['sign'] = md5text # 添加到请求参数列表中
return params
# 二.请求接口URL
def access_api(img):
print(img)
frame = cv2.imread(img)
nparry_encode = cv2.imencode('.jpg', frame)[1]
data_encode = np.array(nparry_encode)
img_encode = base64.b64encode(data_encode) # 图片转为base64编码格式
url = 'https://api.ai.qq.com/fcgi-bin/face/face_detectface'
res = requests.post(url, get_params(img_encode)).json() # 请求URL,得到json信息
# 把信息显示到图片上
if res['ret'] == 0: # 0代表请求成功
pil_img = Image.fromarray(cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)) # 把opencv格式转换为PIL格式,方便写汉字
draw = ImageDraw.Draw(pil_img)
for obj in res['data']['face_list']:
img_width = res['data']['image_width'] # 图像宽度
img_height = res['data']['image_height'] # 图像高度
# print(obj)
x = obj['x'] # 人脸框左上角x坐标
y = obj['y'] # 人脸框左上角y坐标
w = obj['width'] # 人脸框宽度
h = obj['height'] # 人脸框高度
# 根据返回的值,自定义一下显示的文字内容
if obj['glass'] == 1: # 眼镜
glass = '有'
else:
glass = '无'
if obj['gender'] >= 70: # 性别值从0-100表示从女性到男性
gender = '男'
elif 50 <= obj['gender'] < 70:
gender = "娘"
elif obj['gender'] < 30:
gender = '女'
else:
gender = '女汉子'
if 90 < obj['expression'] <= 100: # 表情从0-100,表示笑的程度
expression = '一笑倾城'
elif 80 < obj['expression'] <= 90:
expression = '心花怒放'
elif 70 < obj['expression'] <= 80:
expression = '兴高采烈'
elif 60 < obj['expression'] <= 70:
expression = '眉开眼笑'
elif 50 < obj['expression'] <= 60:
expression = '喜上眉梢'
elif 40 < obj['expression'] <= 50:
expression = '喜气洋洋'
elif 30 < obj['expression'] <= 40:
expression = '笑逐颜开'
elif 20 < obj['expression'] <= 30:
expression = '似笑非笑'
elif 10 < obj['expression'] <= 20:
expression = '半嗔半喜'
elif 0 <= obj['expression'] <= 10:
expression = '黯然伤神'
delt = h // 5 # 确定文字垂直距离
# 写入图片
if len(res['data']['face_list']) > 1: # 检测到多个人脸,就把信息写入人脸框内
font = ImageFont.truetype('yahei.ttf', w // 8, encoding='utf-8') # 提前把字体文件下载好
draw.text((x + 10, y + 10), '性别 :' + gender, (76, 176, 80), font=font)
draw.text((x + 10, y + 10 + delt * 1), '年龄 :' + str(obj['age']), (76, 176, 80), font=font)
draw.text((x + 10, y + 10 + delt * 2), '表情 :' + expression, (76, 176, 80), font=font)
draw.text((x + 10, y + 10 + delt * 3), '魅力 :' + str(obj['beauty']), (76, 176, 80), font=font)
draw.text((x + 10, y + 10 + delt * 4), '眼镜 :' + glass, (76, 176, 80), font=font)
elif img_width - x - w < 170: # 避免图片太窄,导致文字显示不完全
font = ImageFont.truetype('yahei.ttf', w // 8, encoding='utf-8')
draw.text((x + 10, y + 10), '性别 :' + gender, (76, 176, 80), font=font)
draw.text((x + 10, y + 10 + delt * 1), '年龄 :' + str(obj['age']), (76, 176, 80), font=font)
draw.text((x + 10, y + 10 + delt * 2), '表情 :' + expression, (76, 176, 80), font=font)
draw.text((x + 10, y + 10 + delt * 3), '魅力 :' + str(obj['beauty']), (76, 176, 80), font=font)
draw.text((x + 10, y + 10 + delt * 4), '眼镜 :' + glass, (76, 176, 80), font=font)
else:
font = ImageFont.truetype('yahei.ttf', 20, encoding='utf-8')
draw.text((x + w + 10, y + 10), '性别 :' + gender, (76, 176, 80), font=font)
draw.text((x + w + 10, y + 10 + delt * 1), '年龄 :' + str(obj['age']), (76, 176, 80), font=font)
draw.text((x + w + 10, y + 10 + delt * 2), '表情 :' + expression, (76, 176, 80), font=font)
draw.text((x + w + 10, y + 10 + delt * 3), '魅力 :' + str(obj['beauty']), (76, 176, 80), font=font)
draw.text((x + w + 10, y + 10 + delt * 4), '眼镜 :' + glass, (76, 176, 80), font=font)
draw.rectangle((x, y, x + w, y + h), outline="#4CB050") # 画出人脸方框
cv2img = cv2.cvtColor(np.array(pil_img), cv2.COLOR_RGB2BGR) # 把 pil 格式转换为 cv
cv2.imwrite('faces/{}'.format(os.path.basename(img)), cv2img) # 保存图片到 face 文件夹下
return '检测成功'
else:
return '检测失败'
到这里我们的人脸检测接口接入及图片处理就完成了。之后在收到用户发送的图片信息后,调用这个函数,把处理后的图片返回给用户就可以。
返回图片给用户
当收到用户图片时,需要以下几个步骤:
保存图片
当接收到用户图片后,我们要先把图片保存起来,之后才能去调用人脸分析接口,把图片信息传递过去,我们需要编写一个 img_download 函数来下载图片。详见下方代码
调用人脸分析接口
图片下载后,调用 face_id.py 文件里的接口函数,得到处理后的图片。
上传图片
检测结果是一张新的图片,要把图片发送给用户我们需要一个 Media_ID,要获取Media_ID必须先把图片上传为临时素材,所以这里我们需要一个img_upload函数来上传图片,并且在上传时需要用到一个access_token,我们通过一个函数来获取.** 获取access_token必须要把我们自己的IP地址加入白名单,否则是获取不到的。请登录“微信公众平台-开发-基本配置”提前将服务器IP地址添加到IP白名单中,可以在http://ip.qq.com/查看本机的IP地址**
开始编写代码,我们新建一个 utils.py 来下载、上传图片
import requests
import json
import threading
import time
import os
token = ''
app_id = 'wxfc6adcdd7593a712'
secret = '429d85da0244792be19e0deb29615128'
def img_download(url, name):
r = requests.get(url)
with open('images/{}-{}.jpg'.format(name, time.strftime("%Y_%m_%d%H_%M_%S", time.localtime())), 'wb') as fd:
fd.write(r.content)
if os.path.getsize(fd.name) >= 1048576:
return 'large'
# print('namename', os.path.basename(fd.name))
return os.path.basename(fd.name)
def get_access_token(appid, secret):
'''获取access_token,100分钟刷新一次'''
url = 'https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid={}&secret={}'.format(appid, secret)
r = requests.get(url)
parse_json = json.loads(r.text)
global token
token = parse_json['access_token']
global timer
timer = threading.Timer(6000, get_access_token)
timer.start()
def img_upload(mediaType, name):
global token
url = "https://api.weixin.qq.com/cgi-bin/media/upload?access_token=%s&type=%s" % (token, mediaType)
files = {'media': open('{}'.format(name), 'rb')}
r = requests.post(url, files=files)
parse_json = json.loads(r.text)
return parse_json['media_id']
get_access_token(app_id, secret)
返回给用户
我们简单修改下收到图片后的逻辑,收到图片后经过人脸检测,上传获得Media_ID,我们要做的就是把图片返回给用户即可。直接看connect.py的代码
import falcon
from falcon import uri
from wechatpy.utils import check_signature
from wechatpy.exceptions import InvalidSignatureException
from wechatpy import parse_message
from wechatpy.replies import TextReply, ImageReply
from utils import img_download, img_upload
from face_id import access_api
class Connect(object):
def on_get(self, req, resp):
query_string = req.query_string
query_list = query_string.split('&')
b = {}
for i in query_list:
b[i.split('=')[0]] = i.split('=')[1]
try:
check_signature(token='lengxiao', signature=b['signature'], timestamp=b['timestamp'], nonce=b['nonce'])
resp.body = (b['echostr'])
except InvalidSignatureException:
pass
resp.status = falcon.HTTP_200
def on_post(self, req, resp):
xml = req.stream.read()
msg = parse_message(xml)
if msg.type == 'text':
print('hello')
reply = TextReply(content=msg.content, message=msg)
xml = reply.render()
resp.body = (xml)
resp.status = falcon.HTTP_200
elif msg.type == 'image':
name = img_download(msg.image, msg.source) # 下载图片
print(name)
r = access_api('images/' + name)
if r == '检测成功':
media_id = img_upload('image', 'faces/' + name) # 上传图片,得到 media_id
reply = ImageReply(media_id=media_id, message=msg)
else:
reply = TextReply(content='人脸检测失败,请上传1M以下人脸清晰的照片', message=msg)
xml = reply.render()
resp.body = (xml)
resp.status = falcon.HTTP_200
app = falcon.API()
connect = Connect()
app.add_route('/connect', connect)
至此我们的工作就做完了,我们的公众号可以进行颜值检测了。本来我打算用在自己公众号上的,但是还存在下面几个问题,所以没有使用。
微信的机制,我们的程序必须在5s内给出响应。不然就会报'公众号提供的服务出现故障'。然而处理图片有时会比较慢,经常会超过5s。所以正确的处理方式应该是拿到用户的请求后立即返回一个空字符串表示我们收到了,之后单独创建一个线程去处理图片,当图片处理完后通过客服接口发送给用户。可惜的是未认证的公众号没有客服接口,所以没办法,超过5s就会报错。
无法自定义菜单,一旦启用了自定义开发,菜单也需要自定义配置,但是未认证的公众号没有权限通过程序来配置菜单,只能在微信后台配置。
所以,我并没有在我的公众号上启用这个程序,但是如果有认证的公众号,可以尝试开发各种好玩的功能。
Python公众号开发(二)—颜值检测的更多相关文章
- Python微信公众号开发—小白篇
本文面向想通过Python学习公众号开发的同学.一站式解决新手开发微信公众号遇到的所有问题. 为了防止我的文章被到处转载,贴一下我的公众号[智能制造专栏],欢迎大家关注. github仓库地址http ...
- python之微信公众号开发(基本配置和校验)
前言 最近有微信公众号开发的业务,以前没有用python做过微信公众号开发,记录一下自己的学习和开发历程,共勉! 公众号类型 订阅号 普通订阅号 认证订阅号 服务号 普通服务号 认证服务号 服务方式 ...
- Python微信公众号开发—小白篇(1)
本文面向想通过Python学习公众号开发的同学.一站式解决新手开发微信公众号遇到的所有问题. 为了防止我的文章被到处转载,贴一下我的公众号[智能制造社区],欢迎大家关注. github仓库地址http ...
- C#微信公众号开发系列教程二(新手接入指南)
http://www.cnblogs.com/zskbll/p/4093954.html 此系列前面已经更新了两篇博文了,都是微信开发的前期准备工作,现在切入正题,本篇讲解新手接入的步骤与方法,大神可 ...
- NodeJs 开发微信公众号(二)测试环境部署
由于卤煮本人是做前端开发的,所以在做公众号过程中基本上没有遇到前端问题,在这方面花的时间是最少的.加上用了mui框架(纯css界面)和自己积累的代码,很快地开发出了界面来.接着是后台开发.卤煮选的是n ...
- 从Python爬虫到SAE云和微信公众号:二、新浪SAE上搭建微信服务
目的:用PHP在SAE上搭建一个微信公众号的服务器. 1.申请一个SAE云账号 SAE申请地址:http://sae.sina.com.cn/ 可以使用微博账号登陆,SAE是新浪的云服务,时间也比较 ...
- 微信公众号开发C#系列-11、生成带参数二维码应用场景
1.概述 我们在微信公众号开发C#系列-7.消息管理-接收事件推送章节有对扫描带参数二维码事件的处理做了讲解.本篇主要讲解通过微信公众号开发平台提供的接口生成带参数的二维码及应用场景. 微信公众号平台 ...
- Java开发微信公众号(二)---开启开发者模式,接入微信公众平台开发
接入微信公众平台开发,开发者需要按照如下步骤完成: 1.填写服务器配置 2.验证服务器地址的有效性 3.依据接口文档实现业务逻辑 资料准备: 1.一个可以访问的外网,即80的访问端口,因为微信公众号接 ...
- C#微信公众号开发-高级接口-之网页授权oauth2.0获取用户基本信息(二)
C#微信公众号开发之网页授权oauth2.0获取用户基本信息(一) 中讲解了如果通过微信授权2.0snsapi_base获取已经关注用户的基本信息,然而很多情况下我们经常需要获取非关注用户的信息,方法 ...
随机推荐
- centos7设置httpd
1.httpd开机自启动 systemctl enable httpd.service 2.httpd开机不自启动 systemctl disable httpd.service 3.启动httpd ...
- 关于DataTable 判断 列名是否存在的方法中英文符合不区分?
最近系统出现一个错误,排查了很久,发现判断DataTable 列名是否存在时,发现一个坑,居然不会区分中英文符合. 有谁知道其中的原理?先记录一下,免得以后忘记这个天坑. 一. 先初始化一个DataT ...
- Android Studio编译卡死
首先,用AS,你必须fanqiang,其它都是次要的. AS/bin/*.exe.vmoptions ## *DO NOT* modify this file directly. If there i ...
- Windows 2008 如何启用 TLS1.1 1.2
1.下载工具:下载>>> 2.在服务器运行本软件,点击“Bes”按钮,然后必须确保有TLS 1.2被选中,SSL2 SSL3可以不用选择,然后点击Apply按钮,这时系统提醒您重启, ...
- Linux中jdk的安装配置
1.下载jdk安装包 2.解压文件:tar -zxvf jdk-8u211-linux-x64.tar.gz 3.编辑环境变量:vi /etc/profile 4.在环境变量文末添加三行: expor ...
- 2018-2019-2 20165319 《网络对抗技术》 Exp5:MSF基础应用
实验内容 metasploit中有六个模块分别是 渗透攻击模块(Exploit Modules) 辅助模块(Auxiliary Modules 攻击载荷(Payload Modules) 空字段模块( ...
- 记录几个爬取动态网页时的问题(下拉框,旧的元素无法获取,获取的源代码和f12看到的不一致,爬取延迟)
更新.....这个动态网页其实直接抓取ajax请求就可以了,很简单,我之前想复杂了,虽然也实现了,但是效率极低,不过没关系,就当作是对Selenium的一次学习吧 1.最近在爬取一个动态网页,其中为了 ...
- SQL数据库的操作,表的操作
数据库定义语言(DDL):用于对数据库及数据库中的各种对象进行创建,删除,修改等操作 (1)create:用于创建数据库或数据库对象 (2)alter:用于对数据库或数据库对象进行修改 (3)drop ...
- PHP序列号生成函数和字符串替换函数代码
/** * 序列号生成器 */ function snMaker($pre = '') { $date = date('Ymd'); $rand = rand(1000000,9999999); $t ...
- [JZOJ3588]【中山市选2014】J语言(表达式解析+栈)
Description J语言作为一门编程语言,诞生于20世纪90年代.............. 好学的小H今天又学到了一种新东西——J语言.显然,J语言的背景已经被小H忘得一干二净了,但是小H仍然 ...