V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
推荐学习书目
Learn Python the Hard Way
Python Sites
PyPI - Python Package Index
http://diveintopython.org/toc/index.html
Pocoo
值得关注的项目
PyPy
Celery
Jinja2
Read the Docs
gevent
pyenv
virtualenv
Stackless Python
Beautiful Soup
结巴中文分词
Green Unicorn
Sentry
Shovel
Pyflakes
pytest
Python 编程
pep8 Checker
Styles
PEP 8
Google Python Style Guide
Code Style from The Hitchhiker's Guide
xiaolinjia
V2EX  ›  Python

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

  •  
  •   xiaolinjia · 2019-08-08 14:57:32 +08:00 · 2001 次点击
    这是一个创建于 1963 天前的主题,其中的信息可能已经有所发展或是发生改变。

    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。

    1 条回复    2019-12-20 11:59:56 +08:00
    xiaolinjia
        1
    xiaolinjia  
    OP
       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,
    )
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   919 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 22ms · UTC 19:41 · PVG 03:41 · LAX 11:41 · JFK 14:41
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.