Python - Keras API 服务器在经过训练的自定义模型上处理图像
Python - Keras API server to process image on trained custom model
我已经按照本教程创建了一个自定义对象 YoloV3 Keras 模型:
https://momoky.space/pythonlessons/YOLOv3-object-detection-tutorial/tree/master/YOLOv3-custom-training
模型工作得很好,我的下一个目标是创建一个 Python Flask API 能够在上传后处理图像。
我已经开始修改 Code here for image detection
这是我添加的代码:
@app.route('/api/test', methods=['POST'])
def main():
img = request.files["image"].read()
img = Image.open(io.BytesIO(img))
npimg=np.array(img)
image=npimg.copy()
image=cv2.cvtColor(image,cv2.COLOR_BGR2RGB)
#cv2.imshow("Image", image)
#cv2.waitKey()
cv2.imwrite('c:\yolo\temp.jpg', image)
image = 'c:\yolo\temp.jpg'
yolo = YOLO()
r_image, ObjectsList = yolo.detect_img(image)
#response = {ObjectsList}
response_pikled = jsonpickle.encode(ObjectsList)
#yolo.close_session()
return Response(response=response_pikled, status=200, mimetype="application/json")
app.run(host="localhost", port=5000)
所以我的问题是它只适用于第一次迭代,当我上传新图片时,我收到以下错误:
File "C:\Users\xxx\Anaconda3\envs\yolo\lib\site-packages\tensorflow\python\client\session.py", line 929, in run
run_metadata_ptr)
File "C:\Users\xxx\Anaconda3\envs\yolo\lib\site-packages\tensorflow\python\client\session.py", line 1095, in _run
'Cannot interpret feed_dict key as Tensor: ' + e.args[0])
TypeError: Cannot interpret feed_dict key as Tensor: Tensor Tensor("Placeholder:0", shape=(3, 3, 3, 32), dtype=float32) is not an element of this graph.
这是代码的原始静态部分:
if __name__=="__main__":
yolo = YOLO()
image = 'test.png'
r_image, ObjectsList = yolo.detect_img(image)
print(ObjectsList)
#cv2.imshow(image, r_image)
cv2.imwrite('detect.png', r_image)
yolo.close_session()
真正让我困惑的是如何在应用程序启动时加载模型,并在每次发布新图像时执行检测。
谢谢
更新
在构造器部分有一个引用的 Keras 后端会话:
**def __init__(self, **kwargs):
self.__dict__.update(self._defaults) # set up default values
self.__dict__.update(kwargs) # and update with user overrides
self.class_names = self._get_class()
self.anchors = self._get_anchors()
self.sess = K.get_session()
self.boxes, self.scores, self.classes = self.generate()**
添加 K.clear_session 后,它适用于多个系列请求:
@app.route('/api/test', methods=['POST'])
def main():
img = request.files["image"].read()
img = Image.open(io.BytesIO(img))
npimg=np.array(img)
image=npimg.copy()
image=cv2.cvtColor(image,cv2.COLOR_BGR2RGB)
#cv2.imshow("Image", image)
#cv2.waitKey()
cv2.imwrite('c:\yolo\temp.jpg', image)
image = 'c:\yolo\temp.jpg'
yolo = YOLO()
r_image, ObjectsList = yolo.detect_img(image)
#response = {ObjectsList}
response_pikled = jsonpickle.encode(ObjectsList)
#yolo.close_session()
K.clear_session()
return Response(response=response_pikled, status=200, mimetype="application/json")
将有可能避免模型、锚点和 类 需要在每次计算时加载以避免这种情况:
ogs/000/trained_weights_final.h5 model, anchors, and classes loaded.
127.0.0.1 - - [27/Dec/2019 22:58:49] "?[37mPOST /api/test HTTP/1.1?[0m" 200 -
logs/000/trained_weights_final.h5 model, anchors, and classes loaded.
127.0.0.1 - - [27/Dec/2019 22:59:08] "?[37mPOST /api/test HTTP/1.1?[0m" 200 -
logs/000/trained_weights_final.h5 model, anchors, and classes loaded.
127.0.0.1 - - [27/Dec/2019 22:59:33] "?[37mPOST /api/test HTTP/1.1?[0m" 200
-
在 YOLO 构造函数中尝试添加:
from keras import backend as K
K.clear_session()
我已经设法把它弄好并 运行宁作为原型。我已经上传了一个 repo:vulcan25/image_processor 实现了这一切。
我调查的第一件事是教程中该代码中方法 YOLO.detect_img
的功能。此方法接受一个文件名,它立即由原始代码中的 cv2.imread
处理:#L152-L153。然后返回的数据由 self.detect_image
(注意拼写差异)在内部处理,结果显示为 cv2.show
。
这种行为对 Web 应用程序不利,我想将所有内容都保存在内存中,因此认为更改该功能的最佳方法是子类化 YOLO
并覆盖 detect_img
方法,使其表现不同。所以在 processor/my_yolo.py
我做了类似的事情:
from image_detect import YOLO as stock_yolo
class custom_yolo(stock_yolo):
def detect_img(self, input_stream):
image = cv2.imdecode(numpy.fromstring(input_stream, numpy.uint8), 1)
original_image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
original_image_color = cv2.cvtColor(original_image, cv2.COLOR_BGR2RGB)
r_image, ObjectsList = self.detect_image(original_image_color)
is_success, output_stream = cv2.imencode(".jpg", r_image)
return is_success, output_stream
Note: In a later decision, I pulled image_detect.py
into my repo to append K.clear_session()
. It would have been possible to put the above mod in that file also, but I've stuck with subclassing for that part.
它接受一个流,然后使用 cv2.imencode
and cv2.imdecode
分别代替 imshow
和 imread
。
我们现在可以定义一个单独的函数,它反过来 运行 所有图像处理的东西。这将那部分代码(和依赖项)与你的 Flask 应用程序分开,这很好:
yolo = custom_yolo() # create an object from the custom class defined above.
def process(intput_stream):
start_time = time.time()
is_success, output_stream = yolo.detect_img(input_stream)
io_buf = io.BytesIO(output_stream)
print("--- %s seconds ---" % (time.time() - start_time))
return is_success, io_buf.read()
在 Flask 中,我们可以用相同的方式调用它,我们已经有可用的上传文件流:request.files['file'].read()
(这实际上是 werkzeug.datastructures.FileStorage
对象的一个方法, 正如我 ).
作为旁注,此功能可能来自终端 运行。如果你要启动我的 repo,你需要在 processor
容器中执行此操作(请参阅我的自述文件末尾的 docker exec
语法)...
from my_yolo import process
with f.open('image.jpg', 'rb') as f:
is_sucess, processed_data = process(f.read())
然后将结果写入文件:
with f.open('processed_image.jpg', 'wb' as f):
f.write(processed_data)
请注意,我的存储库实际上有两个独立的 Flask 应用程序(基于我放在一起的另一个 upload script,它在前端实现了 dropzone.js)。
我可以运行两种模式:
processor/src/app.py
:可在端口 5001 上访问,此 运行s process
直接(传入请求阻塞,直到处理的数据返回)。
flask/src/app.py
:可在端口 5000 上访问,这会为每个传入请求创建一个 rq
作业,processor
容器然后 运行 作为工作程序来处理这些请求来自队列的请求。
每个应用程序都有自己的 index.html
,它在前端执行自己独特的实现。模式 (1) 将图像直接写入页面,模式 (2) 将 link 添加到页面,单击该页面会导致一个单独的端点为图像提供服务(处理时)。
主要区别在于 process
的调用方式。使用模式 (1) processor/src/app.py
:
from my_yolo import process
if file and allowed_file(file.filename):
# process the upload immediately
input_data = file.read()
complete, data = process(input_data)
如评论中所述,我发现使用此模式的转换非常快:~1.6s per image
on CPU。此脚本还使用一个 redis 集来维护上传文件的列表,可用于进一步向下 view
端点进行验证。
模式 (2) flask/src/app.py
:
from qu import image_enqueue
if file and allowed_file(file.filename):
input_data = file.read()
job = img_enqueue(input_data)
return jsonify({'url': url_for('view', job_id=job.id)})
我已经实现了一个单独的文件 flask/src/qu.py
,它实现了这个 img_enqueue
函数,它最终从 flask/src/my_yolo.py
加载了 process
函数,它被定义为:
def process(data): pass
这是一个重要的归宿。通常使用 rq
时,此函数的内容将在与 flask
服务相同的代码库中定义。事实上,我实际上已经将业务逻辑放在 processor/src/my_yolo.py
中,这允许我们分离具有图像处理依赖项的容器,并最终将其托管在其他地方,只要它与以下内容共享 redis
连接flask
服务。
请查看存储库中的代码以获取更多信息,并随时记录针对该存储库的问题以进行任何进一步的查询(或者如果您遇到困难)。请注意,我可能会引入重大更改,因此您可能希望分叉。
我尽量保持简单。从理论上讲,这可以稍微编辑以支持不同的 processor/Dockerfile
来处理任何处理工作负载,但相同的前端允许您从流中提交任何类型的数据:图像、CSV、其他文本等。
Things that really confuse me is how to load the model when the application start, and execute detection every time a new image is posted. Thank you
当你 运行 这种模式 (1) 时你会注意到它是完美的,因为依赖项在 flask 服务器启动时加载 (~17.s
) 并且单个图像处理需要 ~1s
.这是理想的,尽管可能会导致服务器上的整体内存使用率更高,因为每个 WSGI worker 都需要加载所有依赖项。
当 运行 处于模式 (2) 时 - 将处理传递给 rq worker,每次处理图像时都会加载库,因此速度要慢得多。我会尝试解决这个问题,我只需要研究如何在 rq
部署中预加载库;我之前接近这个,但那是我偶然发现 K.clear_session()
问题的时候,所以还没有时间重新测试这个修复程序(还)。
我已经按照本教程创建了一个自定义对象 YoloV3 Keras 模型: https://momoky.space/pythonlessons/YOLOv3-object-detection-tutorial/tree/master/YOLOv3-custom-training
模型工作得很好,我的下一个目标是创建一个 Python Flask API 能够在上传后处理图像。 我已经开始修改 Code here for image detection
这是我添加的代码:
@app.route('/api/test', methods=['POST'])
def main():
img = request.files["image"].read()
img = Image.open(io.BytesIO(img))
npimg=np.array(img)
image=npimg.copy()
image=cv2.cvtColor(image,cv2.COLOR_BGR2RGB)
#cv2.imshow("Image", image)
#cv2.waitKey()
cv2.imwrite('c:\yolo\temp.jpg', image)
image = 'c:\yolo\temp.jpg'
yolo = YOLO()
r_image, ObjectsList = yolo.detect_img(image)
#response = {ObjectsList}
response_pikled = jsonpickle.encode(ObjectsList)
#yolo.close_session()
return Response(response=response_pikled, status=200, mimetype="application/json")
app.run(host="localhost", port=5000)
所以我的问题是它只适用于第一次迭代,当我上传新图片时,我收到以下错误:
File "C:\Users\xxx\Anaconda3\envs\yolo\lib\site-packages\tensorflow\python\client\session.py", line 929, in run
run_metadata_ptr)
File "C:\Users\xxx\Anaconda3\envs\yolo\lib\site-packages\tensorflow\python\client\session.py", line 1095, in _run
'Cannot interpret feed_dict key as Tensor: ' + e.args[0])
TypeError: Cannot interpret feed_dict key as Tensor: Tensor Tensor("Placeholder:0", shape=(3, 3, 3, 32), dtype=float32) is not an element of this graph.
这是代码的原始静态部分:
if __name__=="__main__":
yolo = YOLO()
image = 'test.png'
r_image, ObjectsList = yolo.detect_img(image)
print(ObjectsList)
#cv2.imshow(image, r_image)
cv2.imwrite('detect.png', r_image)
yolo.close_session()
真正让我困惑的是如何在应用程序启动时加载模型,并在每次发布新图像时执行检测。 谢谢
更新
在构造器部分有一个引用的 Keras 后端会话:
**def __init__(self, **kwargs):
self.__dict__.update(self._defaults) # set up default values
self.__dict__.update(kwargs) # and update with user overrides
self.class_names = self._get_class()
self.anchors = self._get_anchors()
self.sess = K.get_session()
self.boxes, self.scores, self.classes = self.generate()**
添加 K.clear_session 后,它适用于多个系列请求:
@app.route('/api/test', methods=['POST'])
def main():
img = request.files["image"].read()
img = Image.open(io.BytesIO(img))
npimg=np.array(img)
image=npimg.copy()
image=cv2.cvtColor(image,cv2.COLOR_BGR2RGB)
#cv2.imshow("Image", image)
#cv2.waitKey()
cv2.imwrite('c:\yolo\temp.jpg', image)
image = 'c:\yolo\temp.jpg'
yolo = YOLO()
r_image, ObjectsList = yolo.detect_img(image)
#response = {ObjectsList}
response_pikled = jsonpickle.encode(ObjectsList)
#yolo.close_session()
K.clear_session()
return Response(response=response_pikled, status=200, mimetype="application/json")
将有可能避免模型、锚点和 类 需要在每次计算时加载以避免这种情况:
ogs/000/trained_weights_final.h5 model, anchors, and classes loaded.
127.0.0.1 - - [27/Dec/2019 22:58:49] "?[37mPOST /api/test HTTP/1.1?[0m" 200 -
logs/000/trained_weights_final.h5 model, anchors, and classes loaded.
127.0.0.1 - - [27/Dec/2019 22:59:08] "?[37mPOST /api/test HTTP/1.1?[0m" 200 -
logs/000/trained_weights_final.h5 model, anchors, and classes loaded.
127.0.0.1 - - [27/Dec/2019 22:59:33] "?[37mPOST /api/test HTTP/1.1?[0m" 200
-
在 YOLO 构造函数中尝试添加:
from keras import backend as K
K.clear_session()
我已经设法把它弄好并 运行宁作为原型。我已经上传了一个 repo:vulcan25/image_processor 实现了这一切。
我调查的第一件事是教程中该代码中方法 YOLO.detect_img
的功能。此方法接受一个文件名,它立即由原始代码中的 cv2.imread
处理:#L152-L153。然后返回的数据由 self.detect_image
(注意拼写差异)在内部处理,结果显示为 cv2.show
。
这种行为对 Web 应用程序不利,我想将所有内容都保存在内存中,因此认为更改该功能的最佳方法是子类化 YOLO
并覆盖 detect_img
方法,使其表现不同。所以在 processor/my_yolo.py
我做了类似的事情:
from image_detect import YOLO as stock_yolo
class custom_yolo(stock_yolo):
def detect_img(self, input_stream):
image = cv2.imdecode(numpy.fromstring(input_stream, numpy.uint8), 1)
original_image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
original_image_color = cv2.cvtColor(original_image, cv2.COLOR_BGR2RGB)
r_image, ObjectsList = self.detect_image(original_image_color)
is_success, output_stream = cv2.imencode(".jpg", r_image)
return is_success, output_stream
Note: In a later decision, I pulled
image_detect.py
into my repo to appendK.clear_session()
. It would have been possible to put the above mod in that file also, but I've stuck with subclassing for that part.
它接受一个流,然后使用 cv2.imencode
cv2.imdecode
imshow
和 imread
。
我们现在可以定义一个单独的函数,它反过来 运行 所有图像处理的东西。这将那部分代码(和依赖项)与你的 Flask 应用程序分开,这很好:
yolo = custom_yolo() # create an object from the custom class defined above.
def process(intput_stream):
start_time = time.time()
is_success, output_stream = yolo.detect_img(input_stream)
io_buf = io.BytesIO(output_stream)
print("--- %s seconds ---" % (time.time() - start_time))
return is_success, io_buf.read()
在 Flask 中,我们可以用相同的方式调用它,我们已经有可用的上传文件流:request.files['file'].read()
(这实际上是 werkzeug.datastructures.FileStorage
对象的一个方法, 正如我
作为旁注,此功能可能来自终端 运行。如果你要启动我的 repo,你需要在 processor
容器中执行此操作(请参阅我的自述文件末尾的 docker exec
语法)...
from my_yolo import process
with f.open('image.jpg', 'rb') as f:
is_sucess, processed_data = process(f.read())
然后将结果写入文件:
with f.open('processed_image.jpg', 'wb' as f):
f.write(processed_data)
请注意,我的存储库实际上有两个独立的 Flask 应用程序(基于我放在一起的另一个 upload script,它在前端实现了 dropzone.js)。
我可以运行两种模式:
processor/src/app.py
:可在端口 5001 上访问,此 运行sprocess
直接(传入请求阻塞,直到处理的数据返回)。flask/src/app.py
:可在端口 5000 上访问,这会为每个传入请求创建一个rq
作业,processor
容器然后 运行 作为工作程序来处理这些请求来自队列的请求。
每个应用程序都有自己的 index.html
,它在前端执行自己独特的实现。模式 (1) 将图像直接写入页面,模式 (2) 将 link 添加到页面,单击该页面会导致一个单独的端点为图像提供服务(处理时)。
主要区别在于 process
的调用方式。使用模式 (1) processor/src/app.py
:
from my_yolo import process
if file and allowed_file(file.filename):
# process the upload immediately
input_data = file.read()
complete, data = process(input_data)
如评论中所述,我发现使用此模式的转换非常快:~1.6s per image
on CPU。此脚本还使用一个 redis 集来维护上传文件的列表,可用于进一步向下 view
端点进行验证。
模式 (2) flask/src/app.py
:
from qu import image_enqueue
if file and allowed_file(file.filename):
input_data = file.read()
job = img_enqueue(input_data)
return jsonify({'url': url_for('view', job_id=job.id)})
我已经实现了一个单独的文件 flask/src/qu.py
,它实现了这个 img_enqueue
函数,它最终从 flask/src/my_yolo.py
加载了 process
函数,它被定义为:
def process(data): pass
这是一个重要的归宿。通常使用 rq
时,此函数的内容将在与 flask
服务相同的代码库中定义。事实上,我实际上已经将业务逻辑放在 processor/src/my_yolo.py
中,这允许我们分离具有图像处理依赖项的容器,并最终将其托管在其他地方,只要它与以下内容共享 redis
连接flask
服务。
请查看存储库中的代码以获取更多信息,并随时记录针对该存储库的问题以进行任何进一步的查询(或者如果您遇到困难)。请注意,我可能会引入重大更改,因此您可能希望分叉。
我尽量保持简单。从理论上讲,这可以稍微编辑以支持不同的 processor/Dockerfile
来处理任何处理工作负载,但相同的前端允许您从流中提交任何类型的数据:图像、CSV、其他文本等。
Things that really confuse me is how to load the model when the application start, and execute detection every time a new image is posted. Thank you
当你 运行 这种模式 (1) 时你会注意到它是完美的,因为依赖项在 flask 服务器启动时加载 (~17.s
) 并且单个图像处理需要 ~1s
.这是理想的,尽管可能会导致服务器上的整体内存使用率更高,因为每个 WSGI worker 都需要加载所有依赖项。
当 运行 处于模式 (2) 时 - 将处理传递给 rq worker,每次处理图像时都会加载库,因此速度要慢得多。我会尝试解决这个问题,我只需要研究如何在 rq
部署中预加载库;我之前接近这个,但那是我偶然发现 K.clear_session()
问题的时候,所以还没有时间重新测试这个修复程序(还)。