当代方仲出口成章指物作诗与唐寅再现之微信小程序

我正在参加「创意开发 投稿大赛」详情请看:掘金创意开发大赛来了!

当代方仲出口成章指物作诗与唐寅再现之微信小程序

伤仲永

金溪民方仲永,世隶耕。仲永生五年,未尝识书具,忽啼求之。父异焉,借旁近与之,即书诗四句,并自为其名。其诗以养父母、收族为意,传一乡秀才观之。自是指物作诗立就,其文理皆有可观者。邑人奇之,稍稍宾客其父,或以钱币乞之。父利其然也,日扳仲永环谒于邑人,不使学。

余闻之也久。明道中,从先人还家,于舅家见之,十二三矣。令作诗,不能称前时之闻。又七年,还自杨州,复到舅家,问焉,曰“泯然众人矣。”

唐伯虎

唐寅,出生于明朝庚寅年,故名唐寅,因排行老大,又称唐伯虎。二十五岁之前的唐伯虎,过着读书、游历、吟诗作画的单纯生活。

据说,他画的虾,往水里一丢,就好像全都变得鲜活了。不仅如此,唐伯虎更擅长画山水和工笔人物,尤其是仕女。许多名家都称赞他的画法潇洒飘逸,冠绝一时。

1.俺也想有才艺

俺也想有方仲永的才艺以及唐伯虎的风流,为此终日终日昏昏醉梦间,梦中忽闻几位大佬什么凭借PaddleHub意见写诗作画,遂有此项目,又得浮生半日闲。 在此特别感谢我们亲爱的小伙伴,没有你们的支持、关心、帮助,就没有这个小程序。

2.项目构想

最近PaddleHub大火,夺得无数美男俊女眼球。当然作为俗人的我也不例外,被文本生成 看图写诗(reading_pictures_writing_poems)图像生成艺术风格迁移(stylepro_artistic) 两个模型深深的吸引住了。

  • 为了能达到如金溪民方仲永 指物作诗立就,出口成章的水平,在此使用了PaddleHub的 看图写诗(reading_pictures_writing_poems) module
  • 为了能看到唐寅的后背,达到他的画画水平,在此,使用了PaddleHub的 图像生成艺术风格迁移(stylepro_artistic) module

3.项目架构

如下所示,该小程序分为3块:

  • 第一块:PaddleHub Serving服务,由于需要进行推理,需要内存较大,或者能力突出显卡支持
  • 第二块:后台服务,在此我用flask提供后台,分别进行数据输入预处理,以及serving处理后,资源调度等
  • 第三块:微信小程序模块,在此负责微信模块展示

4.项目效果

4.1小程序截图

4.2开发后台截图

5.代码解析

5.1 PaddleHub Serving服务

config.json如下所示 启动命令如下

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服务。

5.2 Web后台

后台3个作用

  • 提供看图写诗图片上传、处理,生成的诗返回
  • 提供风格画图片上传、风格选择、处理,生成风格画文件路径返回

代码如下所示:

看图写诗

# 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__)


def cv2_to_base64(image):
    data = cv2.imencode('.jpg', image)[1]
    return base64.b64encode(data.tostring()).decode('utf8')


def allowed_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'])  # 添加路由
def upload():
    print(request)
    if request.method == 'POST':
        try:
            f = request.files['file']
            print(f.filename)
            if not (f and allowed_file(f.filename)):
                # return jsonify({"error": 1001, "msg": "请检查上传的图片类型,仅限于png、PNG、jpg、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')


# 干活
def reading_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__)


def cv2_to_base64(image):
    data = cv2.imencode('.jpg', image)[1]
    return base64.b64encode(data.tostring()).decode('utf8')


def base64_to_cv2(b64str):
    data = base64.b64decode(b64str.encode('utf8'))
    data = np.fromstring(data, np.uint8)
    data = cv2.imdecode(data, cv2.IMREAD_COLOR)
    return data


def allowed_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'])  # 添加路由
def upload():
    print(request)
    if request.method == 'POST':
        try:
            f = request.files['file']
            print(f.filename)
            print(request.form.get('mystyle'))
            mystyle = request.form.get('mystyle')
            if not (f and allowed_file(f.filename)):
                # return jsonify({"error": 1001, "msg": "请检查上传的图片类型,仅限于png、PNG、jpg、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')


def change_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
复制代码

5.3微信小程序

微信小程序不难,只是流程麻烦,先一一道来。

  • 1.申请小程序账号
  • 2.本地开发小程序
  • 3.上传小程序,并申请审核
  • 4.审核通过,申请上线

小程序开发,主要把握两个。

  • 一个是界面布局设计
  • 一个是api调用

虽然说小程序设计也是界面、js、css这三类,但是和web略有不同,有时候难以下手。

5.4其他注意事项

5.4.1 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'])
def index():
    return render_template('index.html')


@app.route('/error', methods=['POST', 'GET'])
def error():
    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"))
复制代码

5.4.2 负载问题

在开始开发过程中,经常莫名其妙的掉服务,究其原因,是服务器比较单薄,只有2g内存,无显卡,在推理时内存溢出,后台服务挂掉,所以出问题。

解决办法: 将serving单独起在条件较好的服务器上。

5.4.3 界面设计问题

此次小程序,是我第一次接触,所以开发经验0, 在开发的时候,一味查文档,原生写界面。界面布局、数据传递、处理、样式表等,都是一步一步写,有的特别难看,知道今晚才发现原来有界面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:
import sys
sys.path.append('/home/aistudio/external-libraries')
复制代码

6.迭代记录

6.1 2020.9.29更新

主要是美化了小程序界面,熟悉了若干组件。后续代码上传github,在此就不再更新代码了。

6.2 2020.10.2更新记录

  • 小程序界面改了点
  • 审核过程比较漫长,经过多次提交以及据理力争,终于通过了审核。。。。。。

猜你喜欢

转载自juejin.im/post/7127580693269315591