概述

Playground API 是为配合 MNN 工作台提供的一套面向算法快速验证和调试的上层调用能力,可以使用 Python 快速搭建一个端侧视觉场景的验证应用,能在“一端编写,三端(Mac/Android/iOS)调试和运行。
4.9 Playground API 使用手册 - 图1

使用场景

算法在开发&测试阶段快速构建 UI,线上建议不使用。

提供能力

  • 摄像头基础功能,数据回调
  • 相册选择、拍照,数据回调
  • 环境参数注册,如模型路径等
  • 结果渲染,画框、画点、画文本
  • Android / iOS / Mac三端API一致

    依赖模块

    1. import DebugUI

Mac工作台请配置requirements.txt

  1. DebugUI==0.2.2

DebugUI 使用指南

1. 输入模式

支持两种模式的输入页面,视频和图像。视频来源于摄像头实时数据,图像来源于用户交互选择相册图片。

1.1 视频模式

打开原生的摄像头预览界面,不断回调视频数据。

  1. DebugUI.open("camera")

回调数据字段

  • _image:视频帧数据,numpy array格式
  • _format:数据格式,int类型,见图像格式对照
  • _width:图像宽
  • _height:图像高
  • _inAngle:输入图像需要修正的角度
  • _outAngle:输出角度

    1.2 图像模式

    打开相册选择页面,等待用户交互,选择成功后回调图片数据。

    1. DebugUI.open("image")

    回调数据字段

  • _image:视频帧数据,numpy array格式

  • _format:数据格式,int类型,见图像格式对照
  • _width:数据宽
  • _height:数据高
  1. DebugUI.open("images", 2)

回调数据字段

  • _images:多个数据的数组,每个元素为一个字典,字典的格式:
    • _image:视频帧数据,numpy array格式
    • _format:数据格式,int类型,见图像格式对照
    • _width:数据宽
    • _height:数据高

2. 交互组件

交互组件在视频/图片模式打开的时候配置需要哪些组件,组件的触发由用户交互产生,交互事件触发后会产生数据回调。

2.1 selector

image.png
单选选择器,可配置项:

  • name:选择器按钮显示名称
  • items:弹出选项的名称
  • index:初始选择项的索引
  • title:弹出选择框的标题

    1. options = {
    2. 'selector':{
    3. 'name':'风格迁移',
    4. 'items':('11','22','33'),
    5. 'index':0,
    6. 'title':"请选择",
    7. }
    8. }
    9. DebugUI.open("camera", options)

    回调数据字段

  • _index:选择项的索引值

  • _value:选择项的内容

3. 渲染组件

渲染组件用来显示或绘制结果,由算法主动调用。

3.1 文本渲染

使用setLabel显示文本信息:

  • index:目前最多支持3条文本,代表渲染哪一个,0、1、2
  • text:文本内容字符串
  • 配置:控件的样式

    • color:文字颜色,#开头16进制字符串
      1. DebugUI.setLabel(0, "text", {"color":"#FF0000"})


      3.2 关键点渲染

      使用drawPoints绘制关键点:
  • 点序列:tuple类型,每个元素也是一个tuple,((x,y), (x,y), …)

  • 点颜色:#开头16进制字符串
  • 点大小
    1. DebugUI.drawPoints(((imgw/2,imgh/2), (imgw/2-50,imgh/2-50)), "#FF0000", 20.0);
    注意:这里的坐标您根据图像坐标系设置就行了,SDK会自动映射到渲染坐标系上。

3.3 矩形渲染

使用drawRects绘制矩形框:

  • 框坐标列表:支持多个矩形框,tuple类型,每个元素也是一个tuple,((x,y,w,h), (x,y,w,h), …)
  • 跟随文本:框左上角跟随显示的文字,如得分值。列表大小要和框个数一致
  • 线条颜色:#开头16进制字符串
  • 线宽
    1. # 带跟随文字显示
    2. DebugUI.drawRects(((10,20,100,200),(20,40,100,100)), ("0.986776","0.896536"), "#00FF00", 5.0)
    3. # 不带跟随文字显示
    4. DebugUI.drawRects(((10,20,100,200),), "#00FF00", 5.0)
    注意:这里的坐标根据图像坐标系设置就行了,DebugUI会自动映射到渲染坐标系上。

3.4 图像渲染

使用drawImage绘制一张图片,图片会从屏幕的左上角开始显示:

  • 图像数据:numpy数组格式
    1. DebugUI.drawImage(image_data)

3.5 多关键点渲染

机制等同于 drawPoints,但是可以同时绘制多组不同样式的点效果

  • 最外层是一个数组 List,数组里面的每个元素都是一个 tuple ,具体如下

    • 点序列:tuple类型,每个元素也是一个tuple,((x,y), (x,y), …)
    • 点颜色:#开头16进制字符串
    • 点大小
      1. DebugUI.drawPoints([
      2. ((imgw/2,imgh/2), (imgw/2-50,imgh/2-50)), "#FF0000", 20.0),
      3. ((imgw/2,imgh/2), (imgw/2-20,imgh/2-20)), "#FFFFFF", 50.0)
      4. ])

      4. 数据回调

      摄像头/相册的数据,交互组件产生的数据,会统一通过playground封装类中的process方法回调:
  • type:回调组件的类型,有如下几种

    • camera:摄像头数据回调
    • image:相册选择数据回调
    • selector:选择器交互回调
  • id:组件的id,如果有多个相同组件,可以使用id来区分。组件id在open的时候由算法设置(TODO)。
  • batch_data:回调的数据,字典类型,每个组件的数据包含的字段不一样,见组件中的回调字段说明
    1. class Playground(DebugUIBaseClass):
    2. ...
    3. def process(self, type, id, batch_data):
    4. if type == "image":
    5. data = batch_data['_image']
    6. format = batch_data['_format']
    7. elif type == "camera":
    8. pass
    9. elif type == "selector":
    10. pass

5. 环境变量

Python中有时需要获取当前应用的一些环境变量,比如某个文件的路径,设备的版本等等,这些由工程同学在应用中主动设置:

  1. // MNNCVDebug
  2. + (void)registerEnvVars:(NSDictionary*)vars;

然后Python中可以这样获取

  • key:字符串key
    1. vars = DebugUI.getEnvVars()
    2. modelpath = vars["modelpath"]

6. 传感器数据「新增」

Python 中可以获取手机的加速度数据以及陀螺仪数据,受限于设备性能,可能存在一定的延迟以及精度偏差。获取方式如下:

  • 获取加速度,若返回结果都为 0,则代表获取失败或者设备还未更新。 ```python // 返回数据是一个 size 为 3 的 Tuple accelerations = DebugUI.getAcceleration()

x = accelerations[0] y = accelerations[1] z = accelerations[2]

  1. - 获取陀螺仪,若返回结果都为 0,则代表获取失败或者设备还未更新。
  2. ```python
  3. // 返回数据是一个 size 为 3 的 Tuple
  4. gryos = DebugUI.getGryo()
  5. x = gryos[0]
  6. y = gryos[1]
  7. 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

  1. ...
  2. from debug_ui_base_class import DebugUIBaseClass
  3. class DetectionPlayground(DebugUIBaseClass):
  4. def __init__(self):
  5. # playground启动的时候只会调用一次
  6. def process(self, type, id, batch_data):
  7. # 所有的异步回调
  8. def __del__(self):
  9. # 页面退出的回调

这三个方法代表创建、推理、释放的三个生命周期:

  • init:启动的时候会且只会调用一次,可以做网络初始化等操作
  • process:组件事件回调,如视频/图像数据回调、按钮点击等等。(各个组件的回调数据见DebugUI使用)
    • type:回调组件的类型,有如下几种
      • camera:摄像头数据回调
      • image:相册选择数据回调
      • selector:选择器交互回调
    • id:组件的id,如果有多个相同组件,可以使用id来区分。组件id在open的时候由算法设置。
  • del:页面退出时触发,可以在里面做一些对象销毁等操作

    第二步:入口方法

    算法的入口方法仍然是main方法,但是入口方法里需要主动调用一下上一步封装类的init方法。
    1. if __name__ == '__main__':
    2. DetectionPlayground.init();
    3. # DebugUI调用
    4. DebugUI.open("camera")

代码示例

代码示例了一个从摄像头打开的视频检测的视觉Playground应用。
objectDetect.py

  1. #!/usr/bin/python
  2. # -*- coding: UTF-8 -*-
  3. import MNN
  4. import DebugUI
  5. import cv2
  6. import os
  7. import numpy as np
  8. from net import Net
  9. import time
  10. F = MNN.expr
  11. vars = DebugUI.getEnvVars()
  12. work_path = vars['workPath']
  13. from debug_ui_base_class import DebugUIBaseClass
  14. class DetectionPlayground(DebugUIBaseClass):
  15. def __init__(self):
  16. #To replace MNN_PROJECT_NAME with related project name
  17. MNN_PROJECT_NAME = 'MyObjectDetection'
  18. model_path = os.path.join(work_path, MNN_PROJECT_NAME + '.mnn')
  19. global_label_path = os.path.join(work_path, MNN_PROJECT_NAME + '_label.txt')
  20. #first is box, second is scores, third is class_index
  21. output_layer_names = ['TFLite_Detection_PostProcess','TFLite_Detection_PostProcess:2', 'TFLite_Detection_PostProcess:1']
  22. input_layer_name = 'normalized_input_image_tensor'
  23. self.global_net = Net(model_path,
  24. output_layer_names,
  25. input_layer_name)
  26. def process(self, type, id, batch_data):
  27. # print "child process"
  28. if type == "camera":
  29. image = batch_data['_image']
  30. format = batch_data['_format']
  31. elif type == "selector":
  32. pass
  33. def __del__(self):
  34. print "mian del"
  35. pass
  36. if __name__ == '__main__':
  37. DetectionPlayground.init();
  38. DebugUI.open("camera")

debug_ui_base_class.py

  1. __metaclass__ = type
  2. handle = None
  3. class DebugUIBaseClass():
  4. @classmethod
  5. def __pg_init__(cls):
  6. global handle
  7. handle = cls()
  8. return {"_errCode":0}
  9. @classmethod
  10. def __pg_process__(cls, batch_data):
  11. global handle
  12. if handle == None:
  13. return {"_errCode": -1, "msg": "instance is None"}
  14. if not batch_data.has_key('_debug_ui_component_type'):
  15. raise Exception, "Invalid DebugUI Component Type!"
  16. debug_type = batch_data["_debug_ui_component_type"]
  17. batch_data.pop("_debug_ui_component_type")
  18. component_id = 0
  19. if batch_data.has_key('_debug_ui_component_id'):
  20. component_id = batch_data['_debug_ui_component_id']
  21. batch_data.pop("_debug_ui_component_id")
  22. handle.process(debug_type, component_id, batch_data)
  23. return {"_errCode":0}
  24. def process(self, batch_data):
  25. pass
  26. @classmethod
  27. def __pg_destroy__(cls):
  28. global handle
  29. handle = None
  30. return {"_errCode":0}
  31. @classmethod
  32. def init(cls):
  33. cls.__pg_init__()