打印

连载:将人脸识别微服务化,容器化【原创】

[复制链接]
2911|8
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
keer_zu|  楼主 | 2019-2-21 10:40 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
本帖最后由 keer_zu 于 2019-3-1 15:48 编辑

@21ic小喇叭 @21ic小管家 @gaoyang9992006 @sherwin @海中水 @tyw @yyy71cj @紫剑

广告:最近在抽空学《微分几何》和《群论》

将人脸识别应用微服务化,进一步容器化是我当下的任务。
可以实现类似科大讯飞的云服务器一样的人脸识别服务,这一切从定义接口开始。
要定义接口,首先是选择通信方式:tcp or http。


代码依然参考:人脸识别及容器化项目

tcp效率高,交互方式灵活。但是因为进出服务器都有传输图片或者流的可能,所以tcp实现的接口很不友好,对接入者要求过高。先从两者对比开始吧。
首先是尝试采用tcp传输图片:trans_tcp.py

import socket
import threading

import time
import sys
import os
import struct


class _Session:
    m_conn = 0
    m_addr = 0
    m_tread = 0

    def __init__(self,conn,addr,on_session):
        self.m_conn = conn
        self.m_addr = addr

        self.m_tread = threading.Thread(target=on_session, args=(conn, addr))
        self.m_tread.start()


class TcpServer:

    def __init__(self,ip,port):
        try:
            s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
            s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
            s.bind((ip, port))
            s.listen(10)
        except socket.error as msg:
            print(msg)
            sys.exit(1)
        print('Waiting connection...')

        while 1:
            conn, addr = s.accept()
            #t = threading.Thread(target=deal_data, args=(conn, addr))
            #t.start()
            session = _Session(conn,addr,self.recv_image)


    def recv_image(self,conn, addr):
        print ('Accept new connection from {0}'.format(addr))
        #conn.settimeout(500)
        conn.send(('Hi, Welcome to the server!').encode())

        while 1:
            fileinfo_size = struct.calcsize('128sl')
            buf = conn.recv(fileinfo_size)
            if buf:
                    filename, filesize = struct.unpack('128sl', buf)
                    fn = filename.strip(('\00').encode())
                    #src_path,_ = os.path.split(os.path.realpath(__file__))
                    new_filename = os.path.join('./', 'new_' + fn.decode())
                    print('file new name is {0}, filesize if {1}'.format(new_filename, filesize))
                    recvd_size = 0 # 定义已接收文件的大小
                    fp = open(new_filename, 'wb')
                    print('start receiving...')

                    while not recvd_size == filesize:
                            if filesize - recvd_size > 1024:
                                    data = conn.recv(1024)
                                    recvd_size += len(data)
                            else:
                                    data = conn.recv(filesize - recvd_size)
                                    recvd_size = filesize
                            fp.write(data)
                    fp.close()
                    print('end receive...')
            conn.close()
            break

class TcpClient:
    def __init__(self,ip,port):
        try:
            s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
            s.connect((ip, port))
        except socket.error as msg:
            print(msg)
            sys.exit(1)

        print(s.recv(1024))

        while 1:
            filepath = input('please input file path: ')
            if os.path.isfile(filepath):
            # 定义定义文件信息。128s表示文件名为128bytes长,l表示一个int或log文件类型,在此为文件大小
                fileinfo_size = struct.calcsize('128sl')
            # 定义文件头信息,包含文件名和文件大小
                fhead = struct.pack(('128sl').encode(), os.path.basename(filepath.encode()), os.stat(filepath).st_size)
                s.send(fhead)
                print('client filepath: {0}'.format(filepath))

                fp = open(filepath, 'rb')
                while 1:
                    data = fp.read(1024)
                    if not data:
                        print('{0} file send over...'.format(filepath))
                        break
                    s.send(data)
            s.close()
            break

tcp_server = TcpServer("127.0.0.1",6666)
#tcp_client = TcpClient("127.0.0.1",6666)


可以看到:首先是通过tcp传输图片名字和大小信息,然后才传输图片。

===============================================================================

接下来尝试采用http的post方法一次传输图片和命令。。。
先分享一种尝试:

trans_http.py

=================================== 2019.2.22 =======================================

今天看了腾讯和百度的api定义:

人脸验证(腾讯)
人脸检测(百度)

感觉百度过于繁杂,还是比较喜欢腾讯的接口。于是着手学习之,自己实现类似接口。就从接口定义开始吧。

================================== 2019.2.26 =========================================
对比了若干个python的http框架,最好决定使用flask来实现我的http接口。
新的代码:
facecloud

首先定义接口,然后一个一个实现。
接口定义详情请参考下面帖子。

第一个接口的实现请参考下面对应帖子。



==================================== 2019.3.1 =============================================

这两天致力于将之前python做的http服务容器化。

问题是这样的:首先,tensorflow的hub.docker.com tensorflow dockerhub镜像
只有在具备nvidia显卡的机器上才能运行python3的环境,而且虚拟机不支持对显卡的虚拟化,所以必须在一台机器上安装nvidia的显卡,
然后运行linux系统(我是centos),接着docker pull 拉取 上面的镜像,再将其运行为支持nvidia环境的容器。

在centos上安装nvidia显卡颇费周折,因为是找来的旧显卡,而且我的显示器有比较古董,所以启动安装好的linux后就显示没有信号,找了很多解决办法,最后、
在nvidia的论坛得知新的显示器可以支持,无奈在公司地位卑微,用的都是淘汰的设备,于是斗胆接了一台新的三星显示器,可以显示。
按照别人试验过的方法,安装好驱动。再切换至之前的显示器,发现可以显示了。

于是拉取docker镜像,采用以下方式:
Start a GPU container with Python 3, using the Python interpreter.
$ docker run -it --rm -v $(realpath ~/notebooks):/tf/notebooks -p 8888:8888 tensorflow/tensorflow:latest-py3-jupyter


OK! 容器跑起来了。
然后将我的faceclude代码用git克隆到共享文件夹中,又是一大波依赖包安装,终于跑起来了。
后边的开发就在容器里面做了。客户端demo(sdk)跑在其它地方,包括宿主机。

继续。

@gaoyang9992006 @sherwin @21ic小喇叭




评分
参与人数 1威望 +15 收起 理由
tyw + 15 很给力!

相关帖子

沙发
keer_zu|  楼主 | 2019-2-21 10:47 | 只看该作者
trans_http.py

#!/usr/bin/python 
# -*-coding=utf-8 -*-

import sys
import traceback
#from StringIO
try:
    from StringIO import StringIO
except ImportError:
        from io import StringIO
import io
import requests
import tornado
import tornado.ioloop
import tornado.web
from tornado.escape import json_encode
from PIL import Image
import imp


imp.reload(sys)
#sys.setdefaultencoding('utf8')

class Handler(tornado.web.RequestHandler):
    def post(self):
        result = {}
        image_url = self.get_argument("image_url", default="")
        print (image_url)
        if not image_url:
            result["msg"] = "no image url"
        else:
            result["msg"] = self.process(image_url)

        self.write(json_encode(result))
    def process(self, image_url):
        """
        根据image_url获取图片进行处理
        :param image_url: 图片url
        """
        try:
            response = requests.get(image_url)
            if response.status_code == requests.codes.ok:
                obj = Image.open(
                        io.BytesIO(response.content))
                # TODO 根据obj进行相应的操作
                return "ok"
            else:
                return "get image failed."
        except Exception as e:
            print (traceback.format_exc() )
            return str(e)


class ImageServer(object):
    def __init__(self, port):
        self.port = port
    def process(self):
        app = tornado.web.Application([(r"/image?", Handler)], )
        app.listen(self.port)
        tornado.ioloop.IOLoop.current().start()








if __name__ == "__main__":
    server_port = "6060"
    server = ImageServer(server_port)
    print ("begin server" )
server.process()

使用特权

评论回复
板凳
keer_zu|  楼主 | 2019-2-21 13:48 | 只看该作者

使用特权

评论回复
地板
keer_zu|  楼主 | 2019-2-22 14:05 | 只看该作者
百度人脸检测概述:

人脸检测与属性分析

人脸识别接口分为V2和V3两个版本,本文档为V3版本接口的说明文档,请确认您在百度云后台获得的是V3版本接口权限,再来阅读本文档。

    辨别接口版本的方法是:在百度云后台进入【应用列表】,点击【应用名称】,在【API列表】中可以看到【请求地址】,若请求地址中带有【v3】标识,则您具有的是v3权限,可以阅读本文档;若请求地址中带有【v2】标识,则您具有的是v2权限,应该去阅读v2文档。 如果您对文档内容有任何疑问,可以通过以下几种方式联系我们:

    在百度云控制台内 提交工单,咨询问题类型请选择人工智能服务;
    如有需要讨论的疑问,欢迎进入 AI社区 与其他开发者们一同交流。

能力介绍

接口能力

    人脸检测:检测图片中的人脸并标记出位置信息;
    人脸关键点:展示人脸的核心关键点信息,及150个关键点信息。
    人脸属性值:展示人脸属性信息,如年龄、性别等。
    人脸质量信息:返回人脸各部分的遮挡、光照、模糊、完整度、置信度等信息。

业务应用

典型应用场景:如人脸属性分析,基于人脸关键点的加工分析,人脸营销活动等。

    说明:检测响应速度,与图片中人脸数量相关,人脸数量较多时响应时间会有些许延长。

质量检测

如果需要判断一张图片中的人脸,是否符合后续识别或者对比的条件,可以使用此接口,在请求时在face_field参数中请求quality。基于返回结果quality中,以下字段及对应阈值,进行质量检测的判断,以保证人脸质量符合后续业务操作要求。

使用特权

评论回复
5
keer_zu|  楼主 | 2019-2-22 14:06 | 只看该作者
腾讯人脸验证概述:
1. 接口描述

接口请求域名: iai.tencentcloudapi.com 。

给定一张人脸图片和一个 PersonId,判断图片中的人和 PersonId 对应的人是否为同一人。PersonId 请参考人员库管理相关接口。 和人脸比对接口不同的是,人脸验证用于判断 “此人是否是此人”,“此人”的信息已存于人员库中,“此人”可能存在多张人脸图片;而人脸比对用于判断两张人脸的相似度。

默认接口请求频率限制:50次/秒。

使用特权

评论回复
6
keer_zu|  楼主 | 2019-2-26 09:27 | 只看该作者
接口定义:

人员管理库接口定义


使用特权

评论回复
7
keer_zu|  楼主 | 2019-2-26 10:55 | 只看该作者
第一个接口的实现:


import os
from flask import Flask, request, redirect, url_for
from werkzeug import secure_filename

UPLOAD_FOLDER = '/root/src/test/img'
ALLOWED_EXTENSIONS = set(['txt', 'pdf', 'png', 'jpg', 'jpeg', 'gif'])

base_route = '/v1'

app = Flask(__name__)
app.config['UPLOAD_FOLDER'] = UPLOAD_FOLDER
def allowed_file(filename):
    return '.' in filename and \
           filename.rsplit('.', 1)[1] in ALLOWED_EXTENSIONS




# api: PersonnelDataEntry
@app.route(base_route + '/PersonnelDataEntry', methods=['GET', 'POST'])
def upload_file():
    if request.method == 'POST':
        print(request.args)
        print(request.files)
        file = request.files['file']
        print(file.filename)
        if file and allowed_file(file.filename):
            filename = secure_filename(file.filename)
            file.save(os.path.join(app.config['UPLOAD_FOLDER'], filename))
            return '''redirect(url_for('uploaded_file',
                                    filename=filename))
                                    '''
    return '''
    <!doctype html>
    <title>Upload new File</title>
    <h1>Upload new File</h1>
    <form action="" method=post enctype=multipart/form-data>
      <p><input type=file name=file>
         <input type=submit value=Upload>
    </form>
    '''

if __name__ == '__main__':
app.run()

使用特权

评论回复
8
烟花丶易逝| | 2019-2-27 18:17 | 只看该作者
谢谢楼主分享

使用特权

评论回复
9
keer_zu|  楼主 | 2019-3-1 15:51 | 只看该作者
小地方做这些,其实什么都要自己弄,包括N次装系统。

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

个人签名:qq群:49734243 Email:zukeqiang@gmail.com

1350

主题

12427

帖子

53

粉丝