Python 二次开发实现
RVS 支持用户使用 python 语言编写算子文件(即.py文件),但是为了实现 python 算子文件同 RVS 软件的数据交互,需要用到统一的函数接口以及数据类型,以下是操作步骤。
注意
RVS 1.5.1095 版本已废弃 GeneratePython 算子。现在,您可以直接使用 Python 生成器面板生成 Python 二次开发所需的文件。
在新版本中,之前的 XML 文件已不适用,需要使用 Python 生成器面板重新生成 JSON 文件。在迁移时,请务必备份原始文件。
Python 二次开发流程
实现功能: 将输入图像进行缩放后输出,并可以在参数面板中调整输出图像的宽度和高度。
定义数据交互端口
使用Python 生成器面板
生成一个
JSON 文件,用于确定 python 算子文件同 RVS 软件之间的数据交互端口与参数。
在算子图中空白处右击,弹出菜单栏,选择Python 生成器面板
。
在 Python 生成器面板
中进行添加功能中需要的宽度和高度参数,以及输入图像和输出图像的端口。
请参照 Python 生成器面板,详细了解名称命名规范和类型定义格式,并进行相应添加。
生成 Python 脚本文件
添加完成后将 JSON 命名为ResImg.json
,点击生成
。
生成成功后,在日志视图中显示导出配置文件(ResImg.json)和同名的 python 脚本文件(ResImg.py)的路径。
runtime 目录中显示如下:
Python 文件代码解析
使用 python 脚本编辑器(比如 VSCode、PyCharm 等)打开 ResImg.py
,其内容如下所述:
-
该模板中自动导入了 RVS_LOG 模块: import RVS_LOG 使用方法是 RVS_LOG.write(”any string”) 在该 python 脚本被 python 算子调用时,每执行到一次 RVS_LOG.write(”any string”) 均会在RVS程序的 log 栏高亮显示这条 “any string” 语句,同时写入到本地 RVS 的日志文件中。
-
在 python 脚本中手动添加 ”import cv2”,使用 OpenCV 视觉库进行图像处理。
# Note: this file is a template python-file and it was generated from config_file :/home/zmt/rvs-installed/projects/runtime/ResImg.json. ############################################ import RVS_LOG # write log to RVS_log_file, for example: # RVS_LOG.write('your own log string') import numpy from enum import Enum # ToDo: import the moudle you need import cv2
-
同时在这个 python 模板文件中,还自动创建了接口类 RVSClass,在这个类中包含了
_ init _ 函数
、inputData 函数
、outputData 函数
、process 函数
和re_ini
函数。注意:接口类名和所有的函数类名都不可以进行修改。
所有的数据端口都在该类的
_ init _ 函数
中进行了初始定义,Python 文件中的_ init _ 函数
中的初始内容仅仅作为一个显示效果,作用是告诉使用者对应的输入输出数值的格式,这些初始内容都是可以删除的。# The following class 'RVSPyClass' is the interface-class between your python_moudle and RVS. class RVSPyClass: def __init__(self): # all io_port_name and para_name have been list as below. self.input_image = numpy.ndarray(shape=(0,0,0)) # dtype=numpy.uint8 or numpy.uint16 self.output_image = None self.width = 400 self.height = 200 ########################################## # ToDo: remove 'pass' and add your code for initial pass
在实际运行时,通过
inputData 函数
,input_image、width、heigh 会分别去接收 RVS 中传输的数据。def inputData(self,input): self.input_image = input[0] self.width = int(input[1]) self.height = int(input[2]) pass
outputData 函数
将计算结果返回到 output_image 端口,并通过这两个数据端口传输给下个算子。def outputData(self): return [ self.output_image ]
用户可以修改
process
函数和re_ini 函数
的主体内容,同时也可以在 Class 类中自定义用户 Function 函数被process
和re_ini
函数调用。def re_ini(self): ########################################## # ToDo: remove 'pass' and add your code pass def process(self): ########################################## # ToDo: remove 'pass' and add your code pass ############################################ # ToDo: add your own function ############################################## # ToDo: add your own code
在此案例中,将
process
函数的 pass 语句删除,修改为:RVS_LOG.write("start resize ...") self.output_image = cv2.resize(self.input_image, (self.width,self.height), interpolation=cv2.INTER_AREA) RVS_LOG.write("finished!")
代码的功能是将图像
self.input_image
调整为指定的宽度和高度,并使用cv2.INTER_AREA
插值方法进行缩放。cv2.resize()
函数用于调整图像的大小,interpolation
参数用于指定插值方法。
代码测试
已完成了二次开发代码的编写,进入到 RVS 软件当中测试已完成的代码。
算子准备
添加 Trigger、Load、PythonSimple 算子至算子图。
-
设置 Load 算子参数:
-
类型→ Image
-
文件 → → 选择图像文件名 (example_data/images/bird.png)
-
图片→ 可视
-
-
设置 PythonSimple 算子参数:
-
Python配置文件 → ResImg.json
-
output_image→ 可视
-
算子连接
后面的使用同其他 RVS 算子一样,给该算子输入数据,并给定触发信号,算子内部会自动调用一次ResImg.json
对应的同名目录下的同名
python文件(即 ResImg.py)。
运行
点击 RVS 的运行按钮,触发 Trigger 算子。
每一次 RVS 软件中的 PythonSimple 算子被触发后,都会在 python 文件内部按照下述顺序执行一次:
-
RVSPyClass 的初始化函数 _ init _
-
RVSPyClass 的数据输入函数 inputData(传入的函数内参即 RVS 软件中输入给 PythonSimple 算子的实际数据)
-
RVSPyClass 的可选执行函数 re_ini(当 RVS 软件中 PythonSimple 算子的 re_ini 属性勾选以后会执行该函数,执行后该 re_ini 属性会自动恢复非勾选状态),如下图所示:
-
RVSPyClass 的数据处理函数 process
-
RVSPyCLass 的数据输出 outputData
运行结果
-
点击 RVS 的运行按钮,触发 Trigger 算子。
-
如下图所示,2D 视图中分别显示 Load 、PythonSimple 算子运行成功后的结果。
-
在日志视图中显示 python_file。
至此,二次开发案例已完成。
Python 生成器面板
本章节将介绍 Python 生成器面板及其使用步骤,包括数据类型的定义格式和名称命名规范。
面板介绍
Python 生成器面板
如下图所示:
在 Python 生成器面板
中,可以添加参数、输入端口和输出端口。
-
参数
:设置算子属性面板中参数的名称、类型、最大值、最小值、默认值。 -
输入端口
:设置算子输入端口的名称、类型、是否为列表。 -
输出端口
:设置算子输出端口的名称、类型、是否为列表。
操作:
-
+
:添加内容。 -
new.json
:设置生成的 JSON 文件名,默认为 new.json。 -
清空
:清空当前所有已添加的内容。 -
加载
:加载已有 JSON 文件。 -
生成
:生成 JSON 文件和其同名的 python 文件。 -
删除
:右击已添加的内容,选择删除
。
名称命名规范
-
支持字母、数字、下划线(_),必须以字母开头。
-
参数、输入端口、输出端口,任意名称不能重复。
参数数据类型格式定义
数据类型 | 最大值 | 最小值 | 默认值 |
---|---|---|---|
Bool | / | / | false/true |
Int | MIN | MAX | int 数值 |
Float | MIN | MAX | float 数值 |
Double | MIN | MAX | double 数值 |
String | / | / | 字符串 |
File | / | / | 拓展名:文件名中的后缀部分,如 *.txt 文件夹:选择指定的文件 |
Directory | / | / | 目录路径 |
Enum | / | / | 参数的枚举列表(必须填写) 每填写完一个枚举后,按回车键确认 |
Pose | / | / | / |
Joint | / | / | / |
输入/输出端口数据类型格式定义
数据类型 | 格式定义 |
---|---|
Pose | 6 个 float 数据构成的 list,即 [x y z rx ry rz] |
Cube | 9 个 float 数据构成的 list,即[x y z rx ry rz width height depth] |
Image | Numpy 的 Ndarray对象,输入数据的数据类型可以是 numpy.uint8 或 numpy.uint16(对应图漾相机的深度图), uint8 格式下支持灰度图和 3 通道彩色图, numpy.uint16 格式下仅支持灰度图。 输出数据只能是 numpy.uint8,可以是单通道灰度图或 3 通道彩色图。 |
ImagePoints | 如 [x1 y1 x2 y2...] 的 2d 像素点列表 list |
String | 字符串 |
Joint | 6 个 float 数据构成的 list,即[j1 j2 j3 j4 j5 j6] |
RotatedRect | 5 个 float 数据构成的 list,即 [center_x,center_y,width,height,angle] |
操作步骤
在算子图中空白处右击,弹出菜单栏,选择Python 生成器面板
。
打开后,如下图所示:
添加参数、输入端口、输出端口。
参数添加:点击+
按钮→命名名称→选择类型→双击设置最小值/最大值→填写默认值。
添加输入端口:点击+
按钮→命名名称→选择类型→设置是否为列表。
添加输出端口:点击+
按钮 →
命名名称→选择类型→设置是否为列表。
添加完成后,点击生成
。生成成功后,在日志视图中显示生成的 JSON
和和其同名的 python 脚本文件路径。
补充说明
本章内容为补充部分,主要讲述 PythonThread 算子的使用、常见的错误操作,由于 PythonThread 与PythonSimple 使用的流程一致,这里只阐述一下 PythonThread 算子的意义和使用过程中的注意事项。
PythonThread算子
-
除了PythonSimple算子,RVS中还提供了PythonThread算子。其使用方式同PythonSimple算子相同,不过PythonThread算子是额外开辟个新线程来运行python算子。由于PythonThread算子从属于Thread类算子,所以多个并列的PythonThread算子可以同时运行;而PythonSimple算子从属于普通算子,运行会占用主线程,所以多个PythonSimple算子即便并行连接,实际执行时还是先后依次运行。
-
无论是PythonSimple算子还是PythonThread算子,都无法实现python文件的在线更新;如果您在运行了RVS以后,重新更改了自己的python算子文件,对应的PythonSimple/PythonThread均无法重新捕获该更改,必须重启RVS软件或者删掉该算子并重新加载新的Python算子,才能实现python算子文件的重新加载。另外,为了解决这个“需要临时更新python文件中某些参数的问题”,建议您将这些需要更新的参数作为String类型的数据输入,通过合理使用re_ini函数来达到这种更新效果。
PythonThread算子
-
RVS中以ThreadNode结尾的算子都是线程类算子,它们的运行时间较长。(下文描述,基于普通算子的运行时间较短的情况)
-
thread算子在被触发之后,会极短暂的占用主线程,然后会进入到分线程执行功能函数,同时释放主线程,所以此时RVS可以继续触发执行下一个算子,这也是多个并列的thread算子可以近似同时运行的原因。
-
所有的算子被拖动到RVS的算子图区域时会被自动赋予一个算子名,RVS底层会记录这些算子名的创建顺序作为算子顺序。
-
在某一个并行连接结构中,比如算子A触发算子B,算子B触发算子C;且算子A触发算子D,算子D触发算子E。无论B或者D两者中是否含有thread算子,RVS的执行都是等两者全部运行完成,才会去考虑执行C或者E(此时C/E虽然不是源自同一个起点,但是执行的先后顺序也是按照并行连接来考虑)。
-
当B以及D全部都是thread算子时,因为两个并行的thread可以同时运行,所以此时能有效节省运行时间。比如B运行3秒,D运行5秒,则两者的并行连接只需要运行5s就可以执行到C/E算子,而串形连接需要运行8s才能执行到C/E。
-
如果将一个thread算子同一个普通算子并行连接,则理论上只能节省该普通算子的运行时间,意义不大。RVS不建议进行这种连接方式,而且底层做了处理,即便这样连接了,也是先触发普通算子,执行完成后再执行thread算子,之后再考虑执行C/E。
-
如果多个普通算子同一个thread算子并行连接,第一个运行的一定是普通算子,执行完成后执行的第二个算子是否是thread算子则需要根据算子顺序排序。如果第二个算子是thread算子,则由于thread算子的特性,在该算子被触发之后,不用等到该thread算子执行完成,就会立刻触发执行第三个算子;如果第二个算子是普通算子,则必须等该算子执行完成后才可以执行第三个算子,依此类推。
-
多个普通算子并行连接,按照算子顺序,依次执行。
常见的错误操作
-
更改了python模板文件中接口类的类名 RVSPyclass、函数名 inputData、outputData、re_ini、process。或者不恰当的修改了 inputData、outputData 的函数体。
-
在 process 函数中写了大量代码,但是最后忘记将需要返回的实际变量赋值给 outputData 函数中的返回变量
-
修改了 python 模板文件的文件名,或者将该模板文件移动到其他位置。
注意:python 模板文件可以移动,但是一定要保证同 python_json 文件一起移动,保证这个两个文件在同一个目录即可。
-
在任何需要输入文件路径的地方,如果需要使用/或者\,必须只能使用/(该说明仅限定 linux 版本,在 windows 版本待定)
-
outputData 函数中返回的数据格式不合法