上篇文章,我们把自己的程序接入了微信公众号,并且能把用户发送的文本及图片文件原样返回。今天我们把用户的图片通过腾讯的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.接口鉴权,构造请求参数

官方给了我们接口鉴权的计算方法。

  1. 将<key, value>请求参数对按key进行字典升序排序,得到有序的参数对列表N
  2. 将列表N中的参数对按URL键值对的格式拼接成字符串,得到字符串T(如:key1=value1&key2=value2),URL键值拼接过程value部分需要URL编码,URL编码算法用大写字母,例如%E8,而不是小写%e8
  3. 将应用密钥以app_key为键名,组成URL键值拼接到字符串T末尾,得到字符串S(如:key1=value1&key2=value2&app_key=密钥)
  4. 对字符串S进行MD5运算,将得到的MD5值所有字符转换成大写,得到接口请求签名

2.请求接口地址

请求接口信息,我们用 requests 发送请求,会得到返回的 json 格式的图像信息pip install requests安装requests。

3.处理返回的信息

处理返回的信息,把信息展示在图片上,再把处理后的图片保存。这里我们用到 opencv ,和 pillow 两个库pip install pillowpip 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)

至此我们的工作就做完了,我们的公众号可以进行颜值检测了。本来我打算用在自己公众号上的,但是还存在下面几个问题,所以没有使用。

  1. 微信的机制,我们的程序必须在5s内给出响应。不然就会报'公众号提供的服务出现故障'。然而处理图片有时会比较慢,经常会超过5s。所以正确的处理方式应该是拿到用户的请求后立即返回一个空字符串表示我们收到了,之后单独创建一个线程去处理图片,当图片处理完后通过客服接口发送给用户。可惜的是未认证的公众号没有客服接口,所以没办法,超过5s就会报错。

  2. 无法自定义菜单,一旦启用了自定义开发,菜单也需要自定义配置,但是未认证的公众号没有权限通过程序来配置菜单,只能在微信后台配置。

所以,我并没有在我的公众号上启用这个程序,但是如果有认证的公众号,可以尝试开发各种好玩的功能。

Python公众号开发(二)—颜值检测的更多相关文章

  1. Python微信公众号开发—小白篇

    本文面向想通过Python学习公众号开发的同学.一站式解决新手开发微信公众号遇到的所有问题. 为了防止我的文章被到处转载,贴一下我的公众号[智能制造专栏],欢迎大家关注. github仓库地址http ...

  2. python之微信公众号开发(基本配置和校验)

    前言 最近有微信公众号开发的业务,以前没有用python做过微信公众号开发,记录一下自己的学习和开发历程,共勉! 公众号类型 订阅号 普通订阅号 认证订阅号 服务号 普通服务号 认证服务号 服务方式 ...

  3. Python微信公众号开发—小白篇(1)

    本文面向想通过Python学习公众号开发的同学.一站式解决新手开发微信公众号遇到的所有问题. 为了防止我的文章被到处转载,贴一下我的公众号[智能制造社区],欢迎大家关注. github仓库地址http ...

  4. C#微信公众号开发系列教程二(新手接入指南)

    http://www.cnblogs.com/zskbll/p/4093954.html 此系列前面已经更新了两篇博文了,都是微信开发的前期准备工作,现在切入正题,本篇讲解新手接入的步骤与方法,大神可 ...

  5. NodeJs 开发微信公众号(二)测试环境部署

    由于卤煮本人是做前端开发的,所以在做公众号过程中基本上没有遇到前端问题,在这方面花的时间是最少的.加上用了mui框架(纯css界面)和自己积累的代码,很快地开发出了界面来.接着是后台开发.卤煮选的是n ...

  6. 从Python爬虫到SAE云和微信公众号:二、新浪SAE上搭建微信服务

    目的:用PHP在SAE上搭建一个微信公众号的服务器. 1.申请一个SAE云账号 SAE申请地址:http://sae.sina.com.cn/  可以使用微博账号登陆,SAE是新浪的云服务,时间也比较 ...

  7. 微信公众号开发C#系列-11、生成带参数二维码应用场景

    1.概述 我们在微信公众号开发C#系列-7.消息管理-接收事件推送章节有对扫描带参数二维码事件的处理做了讲解.本篇主要讲解通过微信公众号开发平台提供的接口生成带参数的二维码及应用场景. 微信公众号平台 ...

  8. Java开发微信公众号(二)---开启开发者模式,接入微信公众平台开发

    接入微信公众平台开发,开发者需要按照如下步骤完成: 1.填写服务器配置 2.验证服务器地址的有效性 3.依据接口文档实现业务逻辑 资料准备: 1.一个可以访问的外网,即80的访问端口,因为微信公众号接 ...

  9. C#微信公众号开发-高级接口-之网页授权oauth2.0获取用户基本信息(二)

    C#微信公众号开发之网页授权oauth2.0获取用户基本信息(一) 中讲解了如果通过微信授权2.0snsapi_base获取已经关注用户的基本信息,然而很多情况下我们经常需要获取非关注用户的信息,方法 ...

随机推荐

  1. python 对Excel表格的写入

    python对Excel表格写入需要导入xlrd ,和xlutils两个库 from xlrd import open_workbook from xlutils.copy import copy o ...

  2. Flask简介&入门

    1 了解框架 Flask作为Web框架,它的作用主要是为了开发Web应用程序.那么我们首先来了解下Web应用程序.Web应用程序 (World Wide Web)诞生最初的目的,是为了利用互联网交流工 ...

  3. 运行maven命令的时候出现jre不正确

    出错详情: Unable to locate the Javac Compiler in: C:\Program Files\Java\jre1.6.0_07\..\lib\tools.jar Ple ...

  4. Map,HashMap,LinkedHashMap,TreeMap比较和理解

    /* * 获取功能: * V get(Object key):根据键获取值 * Set<K> keySet():获取集合中所有键的集合 * Collection<V> valu ...

  5. python学习-Pillow图像处理

    Pillow中文文档:https://pillow-cn.readthedocs.io/zh_CN/latest/handbook/tutorial.html 安装:pip install pillo ...

  6. HDU 1060  Leftmost Digit

    Leftmost Digit Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others) T ...

  7. Ionic2生成的main.js执行时间10s+

    写个随笔记录下. 经过一周多各种莫名其妙的研究与分析,然后在论坛中大神给一个命令完美解决之. ionic build android --prod --release

  8. Metaphor of quotient space

    In James Munkres "Topology" Section 22, the quotient space is defined as below. Definition ...

  9. nginx学习.第一部分

    1.nginx的版本发布历史 2015年支持thread pool提供stream四层反向代理支持reuseport特性,支持http v2协议.完全可以替代LVS 2016年支持动态模块 2.ngi ...

  10. 分布式缓存技术之Redis_01数据结构分析

    目录 官网 1. 分布式缓存技术的应用 2. Redis的安装指引 官网  redis.io  redisDoc 1. 分布式缓存技术的应用 2. Redis的安装指引 官网下载安装包 redis-5 ...