解决了下 flask+imageai 的集成问题。

2019-08-08 14:57:32 +08:00
 xiaolinjia

www.v2ex.com/t/588188#reply0 那天发了上面这个,各位见笑了。主要是我那天想,就一个全局变量这么简单的问题,居然还报错,花了我一整天。 不过最终还是解决了,那下面给出解决方案供大家参考吧。

场景是,我想用 flask+imageai 来分析处理一张图片。

一开始把 imageai 的加载 model 和处理都放在一个函数里了,然后别人访问这个 url,就调用分析处理函数,处理这图片(这一步是没有出现问题的)。

然后发现了一个缺点是每次都要 loadModel 一次,效率大打折扣。既然每次都用的同一个训练模型(目前 imageai 的图像识别只提供了两种模型),没必要每次访问 url 都 load 一次模型吧。

之后理所当然得将初始化( loadModel 之类)的步骤都放全局里,想着就是刚启动服务器就直接加载模型,之后访问 url 就直接执行分析操作就好。

结果发现迷之报错,加上我本身也不做机器学习这块,还不懂报错的原因是什么。。。去 imageai 的 issue 里看 OlafenwaMoses/ImageAI/issues/159 说是设置成 global 可以防止多次加载 model 就行了,但是也没解决。

没法,只好从根源错误找起。 根源好像是 tensorflow 模块(毕竟 imageai 也依赖了这个库)报的错,ValueError: Tensor Tensor("keras_learning_phase:0", shape=(), dtype=bool) is not an element of this graph. 然后又去 Stack Overflow 里搜了一堆 TensorFlow 的问题,说起来也没怎么懂,不过大概看出来以上错误有不少情况都是因为在 tensorflow 中用了多线程 /多进程导致的。 既然如此,突然想起,那就把 flask 的多线程、多进程禁用了吧。 结果,居然成了。反正就公司内部里用用,不用多线程就不用吧,也没啥。

下面的是我的成功了的代码。

from flask import Flask, Response, jsonify
app = Flask(__name__)
import os
from imageai.Detection import ObjectDetection
import time
import json

execution_path = os.getcwd()
st = time.time()
detector = ObjectDetection()
detector.setModelTypeAsRetinaNet()
detector.setModelPath(os.path.join(execution_path, "model", "resnet50_coco_best_v2.0.1.h5"))
# detector.setModelTypeAsTinyYOLOv3()
# detector.setModelPath(os.path.join(execution_path, "model", "yolo-tiny.h5"))
detector.loadModel()
# detector.loadModel(detection_speed="fastest")
print(f'Init Timer: {time.time()-st}')

@app.route('/detect/<pic_name>')
def boat_detection(pic_name):
    st = time.time()
    results = getDetections(pic_name)
    print(f'Sum Timer: {time.time()-st}')

    msg = {}
    for i, result in enumerate(results, 1):
        result['percentage_probability'] = float(result['percentage_probability'])
        result['box_points'] = list(result['box_points'])
        for index in range(len(result['box_points'])):
            result['box_points'][index] = int(result['box_points'][index])
        result['box_points'] = tuple(result['box_points'])
        msg[str(i)] = json.dumps(result)
    return jsonify(msg)


def getDetections(file_name):
    start = time.time()

    image_folder = os.path.join(execution_path, 'data\\ship2\\')
    output_folder = os.path.join(execution_path, 'data\\output\\')

    st1 = time.time()
    image_file = os.path.join(image_folder, file_name)
    new_image_file = os.path.join(output_folder, file_name)
    print(image_file, "-->", new_image_file)
    if not os.path.exists(image_file):
        print("not exist.")
        return

    # global detector
    custom_objects = detector.CustomObjects(boat=True)
    detections = detector.detectCustomObjectsFromImage(
        custom_objects=custom_objects,
        input_image=image_file,
        output_image_path=new_image_file,
        minimum_percentage_probability=30)
    print(f'[Info]识别到 boat{len(detections)}艘')
    for eachObject in detections:
        print(eachObject.items())

    end = time.time()
    print(f'Excute Timer: {end-st1}')
    print ("耗时: ",end-start)
    return detections

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

重点就是在 app.run()里设置成禁用多线程、多进程,global 有没有都无所谓。毕竟没涉及到修改 detector。

1826 次点击
所在节点    Python
1 条回复
xiaolinjia
2019-12-20 11:59:56 +08:00
最近又学习了一些机器识别的问题,发现 keras 有个 session.graph 存放了这个 model,而 imageai 有一个参数可以设置用 session.graph。也就是多线程共用一个 model。
因此,如果 flask 开启多线程也没事。只需要在该方法里添加个参数 thread_safe=True 即可,如下:
detections = detector.detectCustomObjectsFromImage(
custom_objects=custom_objects,
input_image=image_file,
output_image_path=new_image_file,
minimum_percentage_probability=30,
thread_safe=True,
)

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/590160

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX