Skip to content

Commit c91f005

Browse files
Tutorial02 (#24)
* improvement(simple_classification): add py client * feat(examples): add tutorial02 Co-authored-by: MegEngine <megengine@megvii.com>
1 parent c0e9a88 commit c91f005

16 files changed

Lines changed: 653 additions & 16 deletions

File tree

README.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,9 @@ MegFlow 提供快速视觉应用落地流程,最快 15 分钟搭建起视频
2020
* [build on win10](docs/how-to-build-and-run/build-on-win10.zh.md)
2121
* [generate rtsp](docs/how-to-build-and-run/generate-rtsp.zh.md)
2222
* how to use
23-
* [add my first service](docs/how-to-add-my-service/01-single-classification-model.zh.md)
24-
* [how to optimize and debug](docs/how-to-debug.zh.md)
23+
* [tutorial01: image classification service](docs/how-to-add-my-service/01-single-classification-model.zh.md)
24+
* [tutorial02: detect and classify on video stream](docs/how-to-add-my-service/02-single-det-classify.zh.md)
25+
* [how to debug](docs/how-to-debug.zh.md)
2526
* [how to contribute](docs/how-to-contribute.zh.md)
2627
* [FAQ](docs/FAQ.zh.md)
2728

ci/run_pylint_check.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
python -m pip install pylint==2.5.2
2-
CHECK_DIR="flow-python/examples/simple_classification flow-python/examples/cat_finder flow-python/examples/electric_bicycle"
2+
CHECK_DIR="flow-python/examples/simple_classification flow-python/examples/simple_det_classify flow-python/examples/cat_finder flow-python/examples/electric_bicycle"
33
pylint $CHECK_DIR || pylint_ret=$?
44
if [ "$pylint_ret" ]; then
55
exit $pylint_ret

docs/how-to-add-my-service/01-single-classification-model.zh.md

Lines changed: 36 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ $ python3 dump.py
1717
$ ls -lah model.mge
1818
...
1919
```
20-
`dump.py` 正在 PR 到 MegEngine/models
20+
`dump.py` 已经 PR 到 [MegEngine/models 分类模型目录](https://github.com/MegEngine/Models/tree/master/official/vision/classification)
2121
```bash
2222
$ cat dump.py
2323
...
@@ -125,20 +125,49 @@ class Classify:
125125
* `__init__` 里加载模型,做个 warmup 防止首次推理太慢
126126
* 解码成 BGR 的 data 在 `envelope.msg['data']`,推理,send 返回 json string
127127
128-
[更多 node 说明](appendix-B-python-plugin.zh.md)
128+
[classify.py 各参数说明](appendix-B-python-plugin.zh.md)
129129
130130
## 运行测试
131131
132132
运行服务
133133
```bash
134134
$ cd flow-python/examples
135-
$ run_with_plugins -c simple_classification/image_cpu.toml -p simple_classification # 源码/docker 编译方式用这条命令
135+
$ run_with_plugins -c simple_classification/image_cpu.toml -p simple_classification
136136
```
137137
138-
浏览器打开 8084 端口服务(例如 http://10.122.101.175:8084/docs ),选择一张图“try it out”即可。
138+
### WebUI 方式
139+
浏览器打开 8084 端口服务(例如 http://127.0.0.1:8084/docs ),选择一张图“try it out”即可。
139140
140-
## 其他
141+
### 命令行方式
142+
```bash
143+
$ curl http://127.0.0.1:8081/analyze/image_name -X POST --header "Content-Type:image/*" --data-binary @test.jpeg
144+
```
145+
146+
`image_name` 是用户自定义参数,用在需要 POST 内容的场景。这里随便填即可;`test.jpeg` 是测试图片
147+
148+
### Python Client
149+
150+
```Python
151+
$ cat ${MegFlow_DIR}/flow-python/examples/simple_classification/client.py
141152

142-
一、http 客户端开发
153+
import requests
154+
import cv2
155+
156+
def test():
157+
ip = 'localhost'
158+
port = '8084'
159+
url = 'http://{}:{}/analyze/any_content'.format(ip, port)
160+
img = cv2.imread("./test.jpg")
161+
_, data = cv2.imencode(".jpg", img)
162+
data = data.tobytes()
163+
164+
headers = {'Content-Length': '%d' % len(data), 'Content-Type': 'image/*'}
165+
res = requests.post(url, data=data, headers=headers)
166+
print(res.content)
167+
168+
if __name__ == "__main__":
169+
test()
170+
```
143171
144-
rweb/Swagger 提供了 http RESTful API 描述文件,例如在 http://10.122.101.175:8084/openapi.json 。`swagger_codegen` 可用描述文件生成各种语言的调用代码。更多教程见 [swagger codegen tutorial ](https://swagger.io/tools/swagger-codegen/)。
172+
### 其他语言
173+
rweb/Swagger 提供了 http RESTful API 描述文件,例如在 http://127.0.0.1:8084/openapi.json 。`swagger_codegen` 可用描述文件生成 java/go 等语言的调用代码。更多教程见 [swagger codegen tutorial ](https://swagger.io/tools/swagger-codegen/)。
Lines changed: 147 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,147 @@
1+
# 串连检测和分类
2+
3+
本文将在 [tutorial01](01-single-classification-model.zh.md) 的基础上扩展计算图:先检测、再扣图分类。对外提供视频解析服务。完整的代码在 [simple_det_classify](../../flow-python/examples/simple_det_classify)
4+
5+
## 移除分类预处理
6+
7+
之前提到过:MegEngine 除了不需要转模型,还能消除预处理。我们修改 `dump.py` 把预处理从 SDK/业务代码提到模型内。这样的好处是:**划清工程和算法的边界**,预处理本来就应该由 scientist 维护,每次只需要 release mge 文件,减少交接内容
8+
9+
```bash
10+
$ cat ${MegFlow}/flow-python/examples/simple_det_classify/dump.py
11+
...
12+
data = mge.Tensor(np.ones(shape, dtype=np.uint8))
13+
14+
@jit.trace(capture_as_const=True)
15+
def pred_func(data):
16+
out = data.astype(np.float32)
17+
# resnet18 预处理
18+
output_h, output_w = 224, 224
19+
# resize
20+
M = mge.tensor(np.array([[1,0,0], [0,1,0], [0,0,1]], dtype=np.float32).reshape((1,3,3)))
21+
out = F.vision.warp_perspective(out, M, (output_h, output_w), format='NHWC')
22+
# mean
23+
_mean = mge.Tensor(np.array([103.530, 116.280, 123.675], dtype=np.float32))
24+
out = F.sub(out, _mean)
25+
# div
26+
_div = mge.Tensor(np.array([57.375, 57.120, 58.395], dtype=np.float32))
27+
out = F.div(out, _div)
28+
# dimshuffile
29+
out = F.transpose(out, (0,3,1,2))
30+
31+
outputs = model(out)
32+
return outputs
33+
...
34+
```
35+
具体实现是在 trace inference 里增加预处理动作,fuse opr 优化加速的事情交给 MegEngine 即可。更多 cv 操作参照 [MegEngine API 文档](https://megengine.org.cn/doc/stable/zh/reference/api/megengine.functional.vision.warp_perspective.html?highlight=warp_perspective)
36+
37+
因为推理输入变成了 BGR,所以 dump 模型的时候参数也应该跟着变
38+
```bash
39+
$ python3 dump.py -a resnet18 -s 1 224 224 3
40+
```
41+
42+
## 准备检测模型
43+
这里直接用现成的 YOLOX mge 模型。复用 [cat_finder 的检测](../../flow-python/examples/cat_finder/det.py) 或者从 [YOLOX 官网](https://github.com/Megvii-BaseDetection/YOLOX/tree/main/demo/MegEngine/python) 下载最新版。
44+
45+
## 配置计算图
46+
`flow-python/examples` 增加 `simple_det_classify/video_cpu.toml`
47+
48+
```bash
49+
$ cat flow-python/examples/simple_det_classify/video_cpu.toml
50+
51+
main = "tutorial_02"
52+
53+
# 重资源结点要先声明
54+
[[nodes]]
55+
name = "det"
56+
ty = "Detect"
57+
model = "yolox-s"
58+
conf = 0.25
59+
nms = 0.45
60+
tsize = 640
61+
path = "models/simple_det_classify_models/yolox_s.mge"
62+
interval = 5
63+
visualize = 1
64+
device = "cpu"
65+
device_id = 0
66+
67+
[[nodes]]
68+
name = "classify"
69+
ty = "Classify"
70+
path = "models/simple_det_classify_models/resnet18_preproc_inside.mge"
71+
device = "cpu"
72+
device_id = 0
73+
74+
[[graphs]]
75+
name = "subgraph"
76+
inputs = [{ name = "inp", cap = 16, ports = ["det:inp"] }]
77+
outputs = [{ name = "out", cap = 16, ports = ["classify:out"] }]
78+
# 描述连接关系
79+
connections = [
80+
{ cap = 16, ports = ["det:out", "classify:inp"] },
81+
]
82+
83+
...
84+
# ty 改成 VdieoServer
85+
[[graphs.nodes]]
86+
name = "source"
87+
ty = "VideoServer"
88+
port = 8085
89+
90+
...
91+
```
92+
想对上一期的配置,需要关注 3 点:
93+
* 视频流中的重资源结点,需要声明在 `[[graphs]]` 之外,因为多路视频需要复用这个结点。如果每一路都要启一个 det 结点,资源会爆掉
94+
* `connections` 不再是空白,因为两个结点要描述连接关系
95+
* Server 类型改成 `VideoServer`,告诉 UI 是要处理视频的
96+
97+
## 实现细节
98+
* 可以看到此时 [resnet18 的 lite.py](../../flow-python/examples/simple_det_classify/lite.py) 已经删除了 preprocess 函数
99+
* det.py 可以直接用 `cat_finder`
100+
101+
## 运行测试
102+
103+
运行服务
104+
```bash
105+
$ cd flow-python/examples
106+
$ run_with_plugins -c simple_det_classify/video_cpu.toml -p simple_det_classify
107+
```
108+
109+
### WebUI 方式
110+
浏览器打开 8085 端口服务(例如 http://127.0.0.1:8085/docs )
111+
112+
* 参照 [如何生成 rtsp](../how-to-build-and-run/generate-rtsp.zh.md),提供一个 rtsp 流地址
113+
* 或者干脆给 .mp4 文件的绝对路径(文件和 8085 服务在同一台机器上)
114+
115+
### 命令行方式
116+
```bash
117+
$ curl -X POST 'http://127.0.0.1:8085/start/rtsp%3A%2F%2F127.0.0.1%3A8554%2Ftest1.ts' # start rtsp://127.0.0.1:8554/test1.ts
118+
start stream whose id is 2%
119+
$ curl 'http://127.0.0.1:8085/list' # list all stream
120+
[{"id":1,"url":"rtsp://10.122.101.175:8554/test1.ts"},{"id":0,"url":"rtsp://10.122.101.175:8554/test1.ts"}]%
121+
```
122+
路径中的 `%2F``%3A` 是 [URL](https://www.ietf.org/rfc/rfc1738.txt) 的转义字符
123+
124+
### Python Client
125+
126+
```Python
127+
$ cat ${MegFlow_DIR}/flow-python/examples/simple_det_classify/client.py
128+
129+
import requests
130+
import urllib
131+
132+
133+
def test():
134+
ip = 'localhost'
135+
port = '8085'
136+
video_path = 'rtsp://127.0.0.1:8554/vehicle.ts'
137+
video_path = urllib.parse.quote(video_path, safe='')
138+
url = 'http://{}:{}/start/{}'.format(ip, port, video_path)
139+
140+
res = requests.post(url)
141+
ret = res.content
142+
print(ret)
143+
144+
145+
if __name__ == "__main__":
146+
test()
147+
```

docs/how-to-build-and-run/build-from-source.zh.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ P.S. 默认 ffmpeg 依赖自动从 github 上拉取源码构建,这会使得
8080
```bash
8181
$ cd examples
8282
$ cargo build --example run_with_plugins --release # 编译出 megflow bin
83-
$ ln -s ../../target/example/run_with_plugins
83+
$ ln -s ../../target/release/examples/run_with_plugins
8484
$ ./run_with_plugins -p logical_test
8585
```
8686
`logical_test` 是 examples 下最基础的计算图测试用例,运行能正常结束表示 MegFlow 编译成功、基本语义无问题。

docs/how-to-debug.zh.md

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,12 @@
1-
#
1+
# 如何 Debug 常见问题
2+
3+
一、`run_with_plugins` 无法启动服务,直接 core dump 报错退出
4+
5+
如果“Python 开机自检”的 `run_with_plugins -p logical_test` 能够正常结束,排查方向应该是 Python import error。调试方法举例
6+
```bash
7+
$ gdb --args ./run_with_plugins -c electric_bicycle/electric_bicycle_cpu.toml -p electric_bicycle
8+
...
9+
illegal instruction
10+
...
11+
```
12+
可以看到 crash 发生在哪个 import

flow-python/examples/cat_finder/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# 猫猫围栏
22

33
## 一、功能概述
4-
注册的猫猫离开围栏,会收到一条告警信息。未注册的不会报警。
4+
注册的猫猫离开围栏,会收到一条告警信息。未注册的不会报警。 CPU 配置已提供,没有 GPU 也可以运行。
55

66
## 二、模型和自测数据下载
77

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
# MegFlow is Licensed under the Apache License, Version 2.0 (the "License")
2+
#
3+
# Copyright (c) 2019-2021 Megvii Inc. All rights reserved.
4+
#
5+
# Unless required by applicable law or agreed to in writing,
6+
# software distributed under the License is distributed on an
7+
# "AS IS" BASIS, WITHOUT ARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
8+
9+
#!/usr/bin/env python
10+
# coding=utf-8
11+
import cv2
12+
import requests
13+
14+
15+
def test():
16+
ip = 'localhost'
17+
port = '8084'
18+
user_define_string = 'content'
19+
url = f'http://{ip}:{port}/analyze/{user_define_string}'
20+
img = cv2.imread("./test.jpg")
21+
_, data = cv2.imencode(".jpg", img)
22+
data = data.tobytes()
23+
24+
headers = {'Content-Length': f'{len(data)}', 'Content-Type': 'image/*'}
25+
res = requests.post(url, data=data, headers=headers)
26+
print(res.content)
27+
28+
29+
if __name__ == "__main__":
30+
test()

flow-python/examples/simple_classification/lite.py

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,13 @@
1-
#!/usr/bin/env python3
2-
# -*- coding:utf-8 -*-
3-
# Copyright (c) Megvii, Inc. and its affiliates.
1+
# MegFlow is Licensed under the Apache License, Version 2.0 (the "License")
2+
#
3+
# Copyright (c) 2019-2021 Megvii Inc. All rights reserved.
4+
#
5+
# Unless required by applicable law or agreed to in writing,
6+
# software distributed under the License is distributed on an
7+
# "AS IS" BASIS, WITHOUT ARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
8+
9+
#!/usr/bin/env python
10+
# coding=utf-8
411

512
import argparse
613
import cv2
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
# MegFlow is Licensed under the Apache License, Version 2.0 (the "License")
2+
#
3+
# Copyright (c) 2019-2021 Megvii Inc. All rights reserved.
4+
#
5+
# Unless required by applicable law or agreed to in writing,
6+
# software distributed under the License is distributed on an
7+
# "AS IS" BASIS, WITHOUT ARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
8+
9+
#!/usr/bin/env python
10+
# coding=utf-8

0 commit comments

Comments
 (0)