ホームページ > WeChat アプレット > WeChatの開発 > あなたがどれほど素敵か見てみましょう! Pythonをベースに開発されたパブリックアカウント

あなたがどれほど素敵か見てみましょう! Pythonをベースに開発されたパブリックアカウント

php是最好的语言
リリース: 2018-07-25 13:56:17
オリジナル
2476 人が閲覧しました

これは、外観検出のための Python ベースの WeChat パブリック アカウントの開発であり、現在、Tencent の AI プラットフォームを通じてユーザーの写真を分析し、ユーザーに返します。公開アカウントの額面検出を一緒に体験しましょう

レンダリング

あなたがどれほど素敵か見てみましょう! Pythonをベースに開発されたパブリックアカウント

あなたがどれほど素敵か見てみましょう! Pythonをベースに開発されたパブリックアカウント

あなたがどれほど素敵か見てみましょう! Pythonをベースに開発されたパブリックアカウント

1. Tencent AI プラットフォームに接続します

まず、公式の顔検出および分析インターフェイスの説明を見てみましょう。

指定された画像内のすべての顔の位置と対応する顔属性を検出します。位置には (x、y、w、h) が含まれ、顔の属性には性別、年齢、表情、美しさ、眼鏡、姿勢 (ピッチ、ロール、ヨー) が含まれます。

リクエストパラメータには以下が含まれます:

  • app_id アプリケーション識別、AIプラットフォームに登録した後にapp_idを取得できます

  • time_stampタイムスタンプ

  • nonce_strランダム文字列

  • 署名署名情報, 自分で計算する必要があります

  • 検出するイメージ画像(上限1M)

  • モード検出モード

1.インターフェイス認証、リクエストパラメータを構築します

公式が計算方法を教えてくれました。インターフェース認証。

  1. リクエストパラメータペアをキーごとに辞書の昇順に並べ替えて、パラメータペアの順序付けされたリスト N を取得します

  2. リスト N 内のパラメータペアを URL の形式に従って文字に結合しますキーと値のペア文字列を使用して文字列 T を取得します (例: key1=value1&key2=value2)。URL キーと値の結合プロセスの値部分では、URL エンコード アルゴリズムでは、代わりに %E8 などの大文字が使用されます。小文字の %e8

  3. アプリケーションのパスワードは、キー名として app_key を使用し、URL キーの値が文字列 T の末尾に結合されて文字列 S が取得されます (例: key1=value1&key2) =value2&app_key=key)

  4. 文字列 S に対して MD5 操作を実行し、MD5 を取得します。 値のすべての文字を大文字に変換し、インターフェース要求署名を取得します

2. を要求するインターフェース アドレス

を要求します。インターフェイス情報。リクエストを送信するためにリクエストを使用し、返されたイメージ情報を json 形式で取得します。pip installrequestsインストール リクエスト。 pip install requests安装requests。

3.处理返回的信息

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

3. 返された情報を処理します

返された情報を処理し、画像上に情報を表示し、処理された画像を保存します。ここでは、opencv ライブラリとpillow ライブラリ pip installpillow および pip install opencv-python をインストールに使用します。

AI プラットフォームに接続し、検出された画像データを返すための新しい face_id.py ファイルを作成します。

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):
    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[&#39;gender&#39;] < 70:
                gender = "娘"
            elif obj[&#39;gender&#39;] < 30:
                gender = &#39;女&#39;
            else:
                gender = &#39;女汉子&#39;
            if 90 < obj[&#39;expression&#39;] <= 100:  # 表情从0-100,表示笑的程度
                expression = &#39;一笑倾城&#39;
            elif 80 < obj[&#39;expression&#39;] <= 90:
                expression = &#39;心花怒放&#39;
            elif 70 < obj[&#39;expression&#39;] <= 80:
                expression = &#39;兴高采烈&#39;
            elif 60 < obj[&#39;expression&#39;] <= 70:
                expression = &#39;眉开眼笑&#39;
            elif 50 < obj[&#39;expression&#39;] <= 60:
                expression = &#39;喜上眉梢&#39;
            elif 40 < obj[&#39;expression&#39;] <= 50:
                expression = &#39;喜气洋洋&#39;
            elif 30 < obj[&#39;expression&#39;] <= 40:
                expression = &#39;笑逐颜开&#39;
            elif 20 < obj[&#39;expression&#39;] <= 30:
                expression = &#39;似笑非笑&#39;
            elif 10 < obj[&#39;expression&#39;] <= 20:
                expression = &#39;半嗔半喜&#39;
            elif 0 <= obj[&#39;expression&#39;] <= 10:
                expression = &#39;黯然伤神&#39;
            delt = h // 5  # 确定文字垂直距离
            # 写入图片
            if len(res[&#39;data&#39;][&#39;face_list&#39;]) > 1:  # 检测到多个人脸,就把信息写入人脸框内
                font = ImageFont.truetype(&#39;yahei.ttf&#39;, w // 8, encoding=&#39;utf-8&#39;)  # 提前把字体文件下载好
                draw.text((x + 10, y + 10), &#39;性别 :&#39; + gender, (76, 176, 80), font=font)
                draw.text((x + 10, y + 10 + delt * 1), &#39;年龄 :&#39; + str(obj[&#39;age&#39;]), (76, 176, 80), font=font)
                draw.text((x + 10, y + 10 + delt * 2), &#39;表情 :&#39; + expression, (76, 176, 80), font=font)
                draw.text((x + 10, y + 10 + delt * 3), &#39;魅力 :&#39; + str(obj[&#39;beauty&#39;]), (76, 176, 80), font=font)
                draw.text((x + 10, y + 10 + delt * 4), &#39;眼镜 :&#39; + glass, (76, 176, 80), font=font)
            elif img_width - x - w < 170:  # 避免图片太窄,导致文字显示不完全
                font = ImageFont.truetype(&#39;yahei.ttf&#39;, w // 8, encoding=&#39;utf-8&#39;)
                draw.text((x + 10, y + 10), &#39;性别 :&#39; + gender, (76, 176, 80), font=font)
                draw.text((x + 10, y + 10 + delt * 1), &#39;年龄 :&#39; + str(obj[&#39;age&#39;]), (76, 176, 80), font=font)
                draw.text((x + 10, y + 10 + delt * 2), &#39;表情 :&#39; + expression, (76, 176, 80), font=font)
                draw.text((x + 10, y + 10 + delt * 3), &#39;魅力 :&#39; + str(obj[&#39;beauty&#39;]), (76, 176, 80), font=font)
                draw.text((x + 10, y + 10 + delt * 4), &#39;眼镜 :&#39; + glass, (76, 176, 80), font=font)
            else:
                font = ImageFont.truetype(&#39;yahei.ttf&#39;, 20, encoding=&#39;utf-8&#39;)
                draw.text((x + w + 10, y + 10), &#39;性别 :&#39; + gender, (76, 176, 80), font=font)
                draw.text((x + w + 10, y + 10 + delt * 1), &#39;年龄 :&#39; + str(obj[&#39;age&#39;]), (76, 176, 80), font=font)
                draw.text((x + w + 10, y + 10 + delt * 2), &#39;表情 :&#39; + expression, (76, 176, 80), font=font)
                draw.text((x + w + 10, y + 10 + delt * 3), &#39;魅力 :&#39; + str(obj[&#39;beauty&#39;]), (76, 176, 80), font=font)
                draw.text((x + w + 10, y + 10 + delt * 4), &#39;眼镜 :&#39; + 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(&#39;faces/{}&#39;.format(os.path.basename(img)), cv2img)  # 保存图片到 face 文件夹下
            return &#39;检测成功&#39;
    else:
        return &#39;检测失败&#39;
ログイン後にコピー

この時点で、顔検出インターフェースへのアクセスと画像処理が完了しました。ユーザーから送信された写真情報を受信した後、この関数を呼び出して、処理された写真をユーザーに返します。

ユーザーに画像を返す

ユーザー画像を受け取るときは、次の手順が必要です:

画像を保存する

ユーザー画像を受け取った後、顔分析インターフェースを呼び出す前に、まず画像を保存する必要があります。画像情報を取得するには、画像をダウンロードするための img_download 関数を記述する必要があります。詳細については、以下のコードを参照してください

顔分析インターフェイスを呼び出します

画像をダウンロードした後、face_id.py ファイル内のインターフェイス関数を呼び出して、処理された画像を取得します。

画像をアップロード

検出結果は新しい画像です。画像をユーザーに送信するには Media_ID が必要です。Media_ID を取得するには、まず画像を一時素材としてアップロードする必要があります。そのため、ここでは img_upload 関数が必要です。写真をアップロードするには、access_token が必要であり、関数を通じて取得します。 access_token を取得するには、自分の IP アドレスをホワイトリストに追加する必要があります。そうしないと、取得できません。事前に「WeChat パブリック プラットフォーム - 開発 - 基本設定」にログインしてサーバー IP アドレスを IP ホワイトリストに追加してください。このマシンの IP は http://ip.qq.com/...

で確認できます。

コードの作成を開始し、写真をダウンロードおよびアップロードするための新しい utils.py を作成します

import requests
import json
import threading
import time
import os

token = &#39;&#39;
app_id = &#39;wxfc6adcdd7593a712&#39;
secret = &#39;429d85da0244792be19e0deb29615128&#39;


def img_download(url, name):
    r = requests.get(url)
    with open(&#39;images/{}-{}.jpg&#39;.format(name, time.strftime("%Y_%m_%d%H_%M_%S", time.localtime())), &#39;wb&#39;) as fd:
        fd.write(r.content)
    if os.path.getsize(fd.name) >= 1048576:
        return &#39;large&#39;
    # print(&#39;namename&#39;, os.path.basename(fd.name))
    return os.path.basename(fd.name)


def get_access_token(appid, secret):
    &#39;&#39;&#39;获取access_token,100分钟刷新一次&#39;&#39;&#39;

    url = &#39;https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid={}&secret={}&#39;.format(appid, secret)
    r = requests.get(url)
    parse_json = json.loads(r.text)
    global token
    token = parse_json[&#39;access_token&#39;]
    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 = {&#39;media&#39;: open(&#39;{}&#39;.format(name), &#39;rb&#39;)}
    r = requests.post(url, files=files)
    parse_json = json.loads(r.text)
    return parse_json[&#39;media_id&#39;]

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(&#39;&&#39;)
        b = {}
        for i in query_list:
            b[i.split(&#39;=&#39;)[0]] = i.split(&#39;=&#39;)[1]

        try:
            check_signature(token=&#39;lengxiao&#39;, signature=b[&#39;signature&#39;], timestamp=b[&#39;timestamp&#39;], nonce=b[&#39;nonce&#39;])
            resp.body = (b[&#39;echostr&#39;])
        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 == &#39;text&#39;:
            reply = TextReply(content=msg.content, message=msg)
            xml = reply.render()
            resp.body = (xml)
            resp.status = falcon.HTTP_200
        elif msg.type == &#39;image&#39;:
            name = img_download(msg.image, msg.source)  # 下载图片
            r = access_api(&#39;images/&#39; + name)
            if r == &#39;检测成功&#39;:
                media_id = img_upload(&#39;image&#39;, &#39;faces/&#39; + name)  # 上传图片,得到 media_id
                reply = ImageReply(media_id=media_id, message=msg)
            else:
                reply = TextReply(content=&#39;人脸检测失败,请上传1M以下人脸清晰的照片&#39;, message=msg)
            xml = reply.render()
            resp.body = (xml)
            resp.status = falcon.HTTP_200

app = falcon.API()
connect = Connect()
app.add_route(&#39;/connect&#39;, connect)
ログイン後にコピー
🎜 これで作業は完了し、公式アカウントの外観をテストできるようになりました。当初は公式アカウントで使用する予定でしたが、いくつか問題があったため使用しませんでした。 🎜
  1. WeChat のメカニズムでは、プログラムは 5 秒以内に応答する必要があります。それ以外の場合は、「公式アカウントが提供するサービスに欠陥があります」と報告されます。ただし、画像処理が遅くなる場合があり、5 秒を超えることもよくあります。したがって、これを処理する正しい方法は、ユーザーのリクエストを受信した後、すぐに空の文字列を返して、それを受け取ったことを示し、画像を処理する別のスレッドを作成する必要があります。画像が処理されると、画像は に送信されます。カスタマーサービスインターフェースを通じてユーザーに伝えます。残念ながら、認証されていないパブリック アカウントにはカスタマー サービス インターフェイスがないため、5 秒以上かかるとエラーが報告されます。

  2. カスタム開発を有効にすると、メニューもカスタマイズする必要があります。ただし、認定されていない公式アカウントにはプログラムを通じてメニューを構成する権限がなく、WeChat バックグラウンドでのみ構成できます。

ということで、私の公式アカウントではこのプログラムを有効にしていませんが、認定された公式アカウントをお持ちであれば、さまざまな楽しい機能の開発を試すことができます。

関連する推奨事項:

WeChat パブリック プラットフォーム開発 ワンクリックで WeChat パブリック プラットフォーム アカウントをフォロー

WeChat パブリック プラットフォーム開発の試み、WeChat パブリック プラットフォーム

ビデオ: Chuanzhi、Dark Horse WeChat パブリック プラットフォーム開発ビデオ チュートリアル

以上があなたがどれほど素敵か見てみましょう! Pythonをベースに開発されたパブリックアカウントの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

ソース:php.cn
このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。
最新の問題
人気のチュートリアル
詳細>
最新のダウンロード
詳細>
ウェブエフェクト
公式サイト
サイト素材
フロントエンドテンプレート