喜欢就关注我们吧
【飞桨开发者说】刘建建,飞桨开发者,现工作于西部某厂,从事管理工作。
项目构想
最近飞桨PaddleHub大火,吸引无数开发者眼球,作为俗人的我也不例外,被看图写诗和艺术风格迁移两个模型吸引。假象一下,如果把AI和指物作诗的方仲永、江南四大才子之首的唐伯虎结合,会是啥样子?
在项目实施前,先给小伙伴们稍稍科普下本文中涉及的主人公。
  • 方仲永
金溪民方仲永,世隶耕。仲永生五年,未尝识书具,忽啼求之。父异焉,借旁近与之,即书诗四句,并自为其名。其诗以养父母、收族为意,传一乡秀才观之。自是指物作诗立就,其文理皆有可观者。
  • 唐伯虎
唐寅,出生于明朝庚寅年,故名唐寅,因排行老大,又称唐伯虎。二十五岁之前的唐伯虎,过着读书、游历、吟诗作画的单纯生活。据说,他画的虾,往水里一丢,就好像全都变得鲜活了。不仅如此,唐伯虎更擅长画山水和工笔人物,尤其是仕女。许多名家都称赞他的画法潇洒飘逸,冠绝一时。
我选择使用PaddleHub的 文本生成 — 看图写诗(reading_pictures_writing_poems) module,实现如金溪方仲永指物作诗、出口成章的效果;使用PaddleHub的 图像生成 — 艺术风格迁移(stylepro_artistic) module,实现看到唐伯虎的背影并达到他的画画效果。
项目地址:
https://aistudio.baidu.com/aistudio/projectdetail/920580
PaddleHub module地址:
https://www.paddlepaddle.org.cn/hublist?filter=en_category&value=ImageClassification
项目组成
二、
整个项目分3个部分:
  1. PaddleHub Serving服务,由于需要进行推理,需要内存较大或能力突出的显卡支持。
  2. 后台服务,使用flask提供后台进行数据输入预处理,以及serving处理后的资源调度等。
  3. 微信小程序,负责微信模块展示 。
项目效果
1. PaddleHub体验小程序效果:
选择模型效果→上传图片(等待20s)→ 输出AI写诗结果。
2、开发后台效果
详细代码解析
二、
1. PaddleHub Serving服务
hub serving start --config config.json

(base) root@iZ2ze74yt1daioe0s04o9kZ
:~/paddlehubweixinbox# cat config.json 
{

"modules_info"
: {

"stylepro_artistic"
: {

"init_args"
: {

"version"
"1.0.0"
      },

"predict_args"
: {

"use_gpu"
false
      }

    },

"reading_pictures_writing_poems"
: {

"init_args"
: {

"version"
"1.0.0"
      },

"predict_args"
: {

"use_gpu"
false
      }

    }

  },

"port"
8866
,

"use_multiprocess"
false
,

"workers"
1
}

该命令启用上述的两个PaddleHub module serving服务。
2. Web后台
Web后台的作用一是提供看图写诗图片上传、处理,生成的诗返回;二是提供风格画图片上传、风格选择、处理,生成风格画文件路径返回。
代码如下所示:
  • 看图写诗
# coding:utf-8
# author: Livingbody
# date: 2020.05.06

from
 flask 
import
 Flask, render_template, request, jsonify, Response

from
 werkzeug.utils 
import
 secure_filename

import
 os

import
 requests

import
 paddlehub 
as
 hub

import
 cv2

import
 time

from
 flask 
import
 Blueprint, render_template

import
 requests

import
 json

import
 cv2

import
 base64

from
 flask 
import
 json


index_reading_pictures = Blueprint(
"reading_pictures"
, __name__)

# 设置允许的文件格式
ALLOWED_EXTENSIONS = set([
'png'
'jpg'
'bmp'
'jpeg'
])

# 当前文件所在路径
basepath = os.path.dirname(__file__)



defcv2_to_base64(image):
    data = cv2.imencode(
'.jpg'
, image)[
1
]

return
 base64.b64encode(data.tostring()).decode(
'utf8'
)



defallowed_file(filename):
    filename = filename.lower()

return'.'in
 filename 
and
 filename.rsplit(
'.'
1
)[
1
in
 ALLOWED_EXTENSIONS



# 上传并写诗
@index_reading_pictures.route('/reading_pictures', methods=['POST', 'GET'])  # 添加路由
defupload():
    print(request)

if
 request.method == 
'POST'
:

try
:

            f = request.files[
'file'
]

            print(f.filename)

ifnot
 (f 
and
 allowed_file(f.filename)):

# return jsonify({"error": 1001, "msg": "请检查上传的图片类型,仅限于png、jpg、bmp"})
return
 render_template(
'404.html'
)

            sourcefile = os.path.join(
'static/images/source'
, secure_filename(f.filename))

            print(
'sourcefile: %s'
 % sourcefile)

            upload_path = os.path.join(basepath, sourcefile)  
# 注意:没有的文件夹一定要先创建,不然会提示没有该路径
            f.save(upload_path)

            print(upload_path)

            print(
'upload_path: %s'
 % upload_path)

            results = reading_pictures(sourcefile)

            headers = {
"Content-type"
"application/json"
"charset"
"gbk"
}

# results: [{'Poetrys': '山隈山坳山,海滨岭颠海。中有无底渊,千古不可改。'}]
# return Response(json.dumps(results), content_type='application/json')
return
 jsonify(results)

except
 Exception:

return
 render_template(
'404.html'
)

return
 render_template(
'poem.html'
)



# 读取图片
defreading_pictures(upload_path):
    print(
'upload_path: %s'
 % upload_path)

# 指定图片分割方法为deeplabv3p_xception65_humanseg并发送post请求
    data = {
'images'
: [cv2_to_base64(cv2.imread(upload_path))]}

    headers = {
"Content-type"
"application/json"
}

    url = 
"http://localhost:8861/predict/reading_pictures_writing_poems"
    r = requests.post(url=url, headers=headers, data=json.dumps(data))

    print(
'request: %s'
 % r)

    t = time.time()

    results = r.json()[
"results"
]

    number = results[
0
][
'Poetrys'
].count(
"。"
) - 
1
    results[
0
][
'Poetrys'
] = results[
0
][
'Poetrys'
].replace(
"。"
"。\n"
, number)

    print(
'results: %s'
 % results)

return
 results
  • 风格画
# coding:utf-8
# author: Livingbody
# date: 2020.05.06

from
 flask 
import
 Flask, render_template, request, jsonify, Response

from
 werkzeug.utils 
import
 secure_filename

import
 os

import
 requests

import
 paddlehub 
as
 hub

import
 cv2

import
 time

import
 numpy 
as
 np

from
 flask 
import
 Blueprint, render_template

import
 requests

import
 json

import
 cv2

import
 base64

from
 flask 
import
 json


index_stylepro_artistic = Blueprint(
"stylepro_artistic"
, __name__)

# 设置允许的文件格式
ALLOWED_EXTENSIONS = set([
'png'
'jpg'
'bmp'
'jpeg'
])

# 当前文件所在路径
basepath = os.path.dirname(__file__)



defcv2_to_base64(image):
    data = cv2.imencode(
'.jpg'
, image)[
1
]

return
 base64.b64encode(data.tostring()).decode(
'utf8'
)



defbase64_to_cv2(b64str):
    data = base64.b64decode(b64str.encode(
'utf8'
))

    data = np.fromstring(data, np.uint8)

    data = cv2.imdecode(data, cv2.IMREAD_COLOR)

return
 data



defallowed_file(filename):
    filename = filename.lower()

return'.'in
 filename 
and
 filename.rsplit(
'.'
1
)[
1
in
 ALLOWED_EXTENSIONS



@index_stylepro_artistic.route('/stylepro_artistic', methods=['POST', 'GET'])  # 添加路由
defupload():
    print(request)

if
 request.method == 
'POST'
:

try
:

            f = request.files[
'file'
]

            print(f.filename)

            print(request.form.get(
'mystyle'
))

            mystyle = request.form.get(
'mystyle'
)

ifnot
 (f 
and
 allowed_file(f.filename)):

# return jsonify({"error": 1001, "msg": "请检查上传的图片类型,仅限于png、jpg、bmp"})
return
 render_template(
'404.html'
)

            sourcefile = os.path.join(
'static/images/source'
, secure_filename(f.filename))

            print(
'sourcefile: %s'
 % sourcefile)

            upload_path = os.path.join(basepath, sourcefile)  
# 注意:没有的文件夹一定要先创建,不然会提示没有该路径
            f.save(upload_path)

            results = change_pictures(sourcefile, mystyle)

            headers = {
"Content-type"
"application/json"
"charset"
"gbk"
}

return
 jsonify(results)

except
 Exception:

return
 render_template(
'404.html'
)

return
 render_template(
'style.html'
)



defchange_pictures(upload_path, style):
    style = 
'static/images/style/'
 + str(style) + 
'.jpg'
    data = {
'images'
: [

        {

'content'
: cv2_to_base64(cv2.imread(upload_path)),

'styles'
: [cv2_to_base64(cv2.imread(style))],

# 'use_gpu': False,
# 'visualization': True,
# 'output_dir': 'static/images/stylepro_artistic'
        }

    ]}

    headers = {
"Content-type"
"application/json"
}

    url = 
"http://localhost/predict/stylepro_artistic"
    r = requests.post(url=url, headers=headers, data=json.dumps(data))

    print(
'*'
 * 
50
)

    print(
'stylepro_artistic OK...........'
)



    t = time.time()

    filename = str(t) + 
'.jpg'
    mypath = os.path.join(basepath, 
'static/images/stylepro_artistic'
, filename)

    cv2.imwrite(mypath, base64_to_cv2(r.json()[
"results"
][
0
][
'data'
]))

    filepath = {
'save_path'
: os.path.join(
'static/images/stylepro_artistic'
, filename)}

    print(
'filepath: %s'
 % filepath)

return
 filepath
3. 微信小程序
微信小程序不难,只是流程比较繁琐,需要下面四个关键步骤:
  1. 申请小程序账号
  2. 本地开发小程序
  3. 上传小程序,并申请审核
  4. 审核通过,申请上线
小程序开发主要把握界面布局设计和API调用,然说小程序设计也是界面、js、css这三类,但是和web略有不同,有时候难以下手。
4. 其他注意事项
https配置:
微信小程序上传图片等文件仅支持SSL域名,HTTP或IP地址都不行,要注意flask的https配置,特别是证书配置。
(base) root@iZ2ze74yt1daioe0s04o9kZ:~/paddlehubweixinbox
# cat app.py
from
 flask 
import
 Flask, render_template

from
 flask_bootstrap 
import
 Bootstrap


# from datetime import timedelta

app = Flask(__name__)

from
 reading_pictures 
import
 *


app.register_blueprint(index_reading_pictures)

from
 stylepro_artistic 
import
 *


app.register_blueprint(index_stylepro_artistic)


Bootstrap(app)


@app.route('/', methods=['POST', 'GET'])
defindex():
return
 render_template(
'index.html'
)



@app.route('/error', methods=['POST', 'GET'])
deferror():
return
 render_template(
'404.html'
)



if
 __name__ == 
'__main__'
:

    app.config[
'JSON_AS_ASCII'
] = 
False
    app.run(host=
'0.0.0.0'
, port=
8080
, debug=
True
,

            ssl_context=(
"4543112_www.livingbody.xyz.pem"
"4543112_www.livingbody.xyz.key"
))

负载问题:
在开始开发过程中经常莫名其妙的掉服务,究其原因是服务器比较单薄,只有2G内存,无显卡,在推理时内存溢出,后台服务挂掉,所以出问题。解决办法是将serving单独起在条件较好的服务器上。
界面设计问题:
此次小程序是我第一次接触,所以开发经验基本为“零”。在开发时,一直查文档,原生写界面。界面布局、数据传递、处理、样式表等,都是一步一步写,有的特别难看,直到后来才发现原来有界面WeUI,可以加速设计,成品可用,初步使用效果不错,感觉在小程序领域相当于web界的bootstrap,可以省去很多麻烦。
# 如果需要进行持久化安装, 需要使用持久化路径, 如下方代码示例:
# If a persistence installation is required, you need to use the persistence path as the following:
!mkdir /home/aistudio/external-libraries

!pip 
install
 beautifulsoup4 -t /home/aistudio/
external
-libraries


# 同时添加如下代码, 这样每次环境(kernel)启动的时候只要运行下方代码即可:
# Also add the following code, so that every time the environment (kernel) starts, just run the following code:
importsys
sys.path.append(
'/home/aistudio/external-libraries'
)
如果您想详细了解更多飞桨的相关内容,请参阅以下文档。


·飞桨官网地址·
https://www.paddlepaddle.org.cn/
·飞桨PaddleHub项目地址·
GitHub: 
https://github.com/PaddlePaddle/PaddleHub 
Gitee: 
https://gitee.com/paddlepaddle/PaddleHub
▼ 
往期精彩回顾 ▼

LibreOffice:爸爸你太慢了
微软不会将 Windows 迁移到 Linux
基于飞桨Res-Unet网络实现肝脏肿瘤分割任务
国产芯片项目迎“烂尾潮”,六个百亿级项目停摆
WordPress 已过时?创始人与新架构拥护者开战
觉得不错,请点个在看
继续阅读
阅读原文