概述
Playground API 是为配合 MNN 工作台提供的一套面向算法快速验证和调试的上层调用能力,可以使用 Python 快速搭建一个端侧视觉场景的验证应用,能在“一端编写,三端(Mac/Android/iOS)调试和运行。
使用场景
算法在开发&测试阶段快速构建 UI,线上建议不使用。
提供能力
Mac工作台请配置requirements.txt
DebugUI==0.2.2
DebugUI 使用指南
1. 输入模式
支持两种模式的输入页面,视频和图像。视频来源于摄像头实时数据,图像来源于用户交互选择相册图片。
1.1 视频模式
打开原生的摄像头预览界面,不断回调视频数据。
DebugUI.open("camera")
回调数据字段
- _image:视频帧数据,numpy array格式
- _format:数据格式,int类型,见图像格式对照
- _width:图像宽
- _height:图像高
- _inAngle:输入图像需要修正的角度
-
1.2 图像模式
打开相册选择页面,等待用户交互,选择成功后回调图片数据。
DebugUI.open("image")
回调数据字段
_image:视频帧数据,numpy array格式
- _format:数据格式,int类型,见图像格式对照
- _width:数据宽
- _height:数据高
DebugUI.open("images", 2)
回调数据字段
- _images:多个数据的数组,每个元素为一个字典,字典的格式:
- _image:视频帧数据,numpy array格式
- _format:数据格式,int类型,见图像格式对照
- _width:数据宽
- _height:数据高
2. 交互组件
交互组件在视频/图片模式打开的时候配置需要哪些组件,组件的触发由用户交互产生,交互事件触发后会产生数据回调。
2.1 selector
单选选择器,可配置项:
- name:选择器按钮显示名称
- items:弹出选项的名称
- index:初始选择项的索引
title:弹出选择框的标题
options = {
'selector':{
'name':'风格迁移',
'items':('11','22','33'),
'index':0,
'title':"请选择",
}
}
DebugUI.open("camera", options)
回调数据字段
_index:选择项的索引值
- _value:选择项的内容
3. 渲染组件
渲染组件用来显示或绘制结果,由算法主动调用。
3.1 文本渲染
使用setLabel显示文本信息:
- index:目前最多支持3条文本,代表渲染哪一个,0、1、2
- text:文本内容字符串
配置:控件的样式
点序列:tuple类型,每个元素也是一个tuple,((x,y), (x,y), …)
- 点颜色:#开头16进制字符串
- 点大小
注意:这里的坐标您根据图像坐标系设置就行了,SDK会自动映射到渲染坐标系上。DebugUI.drawPoints(((imgw/2,imgh/2), (imgw/2-50,imgh/2-50)), "#FF0000", 20.0);
3.3 矩形渲染
使用drawRects绘制矩形框:
- 框坐标列表:支持多个矩形框,tuple类型,每个元素也是一个tuple,((x,y,w,h), (x,y,w,h), …)
- 跟随文本:框左上角跟随显示的文字,如得分值。列表大小要和框个数一致
- 线条颜色:#开头16进制字符串
- 线宽
注意:这里的坐标根据图像坐标系设置就行了,DebugUI会自动映射到渲染坐标系上。# 带跟随文字显示
DebugUI.drawRects(((10,20,100,200),(20,40,100,100)), ("0.986776","0.896536"), "#00FF00", 5.0)
# 不带跟随文字显示
DebugUI.drawRects(((10,20,100,200),), "#00FF00", 5.0)
3.4 图像渲染
使用drawImage绘制一张图片,图片会从屏幕的左上角开始显示:
- 图像数据:numpy数组格式
DebugUI.drawImage(image_data)
3.5 多关键点渲染
机制等同于 drawPoints,但是可以同时绘制多组不同样式的点效果
最外层是一个数组 List,数组里面的每个元素都是一个 tuple ,具体如下
type:回调组件的类型,有如下几种
- camera:摄像头数据回调
- image:相册选择数据回调
- selector:选择器交互回调
- …
- id:组件的id,如果有多个相同组件,可以使用id来区分。组件id在open的时候由算法设置(TODO)。
- batch_data:回调的数据,字典类型,每个组件的数据包含的字段不一样,见组件中的回调字段说明
class Playground(DebugUIBaseClass):
...
def process(self, type, id, batch_data):
if type == "image":
data = batch_data['_image']
format = batch_data['_format']
elif type == "camera":
pass
elif type == "selector":
pass
5. 环境变量
Python中有时需要获取当前应用的一些环境变量,比如某个文件的路径,设备的版本等等,这些由工程同学在应用中主动设置:
// MNNCVDebug
+ (void)registerEnvVars:(NSDictionary*)vars;
然后Python中可以这样获取
- key:字符串key
vars = DebugUI.getEnvVars()
modelpath = vars["modelpath"]
6. 传感器数据「新增」
Python 中可以获取手机的加速度数据以及陀螺仪数据,受限于设备性能,可能存在一定的延迟以及精度偏差。获取方式如下:
- 获取加速度,若返回结果都为 0,则代表获取失败或者设备还未更新。 ```python // 返回数据是一个 size 为 3 的 Tuple accelerations = DebugUI.getAcceleration()
x = accelerations[0] y = accelerations[1] z = accelerations[2]
- 获取陀螺仪,若返回结果都为 0,则代表获取失败或者设备还未更新。
```python
// 返回数据是一个 size 为 3 的 Tuple
gryos = DebugUI.getGryo()
x = gryos[0]
y = gryos[1]
z = gryos[2]
附
图像格式对照
图像格式 | 索引值 |
---|---|
RGBA | 0 |
RGB | 1 |
BGR | 2 |
GRAY | 3 |
BGRA | 4 |
NV21 | 11 |
NV12 | 12 |
Playground 模板
DebugUI为一套视觉调用的Python API,Playground就是一个Python搭建的“应用”。如果你需要用DebugUI去搭建一个视觉场景的应用,那么就需要使用Playground模板,这样就能方便的获取输入或组件的回调数据,进行异步断点调试等等。
1. Playground开发规范
算法需要按照如下规范开发Playground
第一步:封装模块
playground视觉算法工程中,一般都会实现一种算法能力,比如图像分类。首先需要把这些算法能力的代码都放在在一个类里面,这个类继承自DebugUIBaseClass,然后实现三个方法init、process、del
...
from debug_ui_base_class import DebugUIBaseClass
class DetectionPlayground(DebugUIBaseClass):
def __init__(self):
# playground启动的时候只会调用一次
def process(self, type, id, batch_data):
# 所有的异步回调
def __del__(self):
# 页面退出的回调
这三个方法代表创建、推理、释放的三个生命周期:
- init:启动的时候会且只会调用一次,可以做网络初始化等操作
- process:组件事件回调,如视频/图像数据回调、按钮点击等等。(各个组件的回调数据见DebugUI使用)
- type:回调组件的类型,有如下几种
- camera:摄像头数据回调
- image:相册选择数据回调
- selector:选择器交互回调
- …
- id:组件的id,如果有多个相同组件,可以使用id来区分。组件id在open的时候由算法设置。
- type:回调组件的类型,有如下几种
- del:页面退出时触发,可以在里面做一些对象销毁等操作
第二步:入口方法
算法的入口方法仍然是main方法,但是入口方法里需要主动调用一下上一步封装类的init方法。if __name__ == '__main__':
DetectionPlayground.init();
# DebugUI调用
DebugUI.open("camera")
代码示例
代码示例了一个从摄像头打开的视频检测的视觉Playground应用。
objectDetect.py
#!/usr/bin/python
# -*- coding: UTF-8 -*-
import MNN
import DebugUI
import cv2
import os
import numpy as np
from net import Net
import time
F = MNN.expr
vars = DebugUI.getEnvVars()
work_path = vars['workPath']
from debug_ui_base_class import DebugUIBaseClass
class DetectionPlayground(DebugUIBaseClass):
def __init__(self):
#To replace MNN_PROJECT_NAME with related project name
MNN_PROJECT_NAME = 'MyObjectDetection'
model_path = os.path.join(work_path, MNN_PROJECT_NAME + '.mnn')
global_label_path = os.path.join(work_path, MNN_PROJECT_NAME + '_label.txt')
#first is box, second is scores, third is class_index
output_layer_names = ['TFLite_Detection_PostProcess','TFLite_Detection_PostProcess:2', 'TFLite_Detection_PostProcess:1']
input_layer_name = 'normalized_input_image_tensor'
self.global_net = Net(model_path,
output_layer_names,
input_layer_name)
def process(self, type, id, batch_data):
# print "child process"
if type == "camera":
image = batch_data['_image']
format = batch_data['_format']
elif type == "selector":
pass
def __del__(self):
print "mian del"
pass
if __name__ == '__main__':
DetectionPlayground.init();
DebugUI.open("camera")
debug_ui_base_class.py
__metaclass__ = type
handle = None
class DebugUIBaseClass():
@classmethod
def __pg_init__(cls):
global handle
handle = cls()
return {"_errCode":0}
@classmethod
def __pg_process__(cls, batch_data):
global handle
if handle == None:
return {"_errCode": -1, "msg": "instance is None"}
if not batch_data.has_key('_debug_ui_component_type'):
raise Exception, "Invalid DebugUI Component Type!"
debug_type = batch_data["_debug_ui_component_type"]
batch_data.pop("_debug_ui_component_type")
component_id = 0
if batch_data.has_key('_debug_ui_component_id'):
component_id = batch_data['_debug_ui_component_id']
batch_data.pop("_debug_ui_component_id")
handle.process(debug_type, component_id, batch_data)
return {"_errCode":0}
def process(self, batch_data):
pass
@classmethod
def __pg_destroy__(cls):
global handle
handle = None
return {"_errCode":0}
@classmethod
def init(cls):
cls.__pg_init__()