Python 二次开发实现

RVS 支持用户使用 python 语言编写算子文件(即.py文件),但是为了实现 python 算子文件同 RVS 软件的数据交互,需要用到统一的函数接口以及数据类型,以下是操作步骤。

注意

RVS 1.5.1095 版本已废弃 GeneratePython 算子。现在,您可以直接使用 Python 生成器面板生成 Python 二次开发所需的文件。

在新版本中,之前的 XML 文件已不适用,需要使用 Python 生成器面板重新生成 JSON 文件。在迁移时,请务必备份原始文件。

Python 二次开发流程

实现功能: 将输入图像进行缩放后输出,并可以在参数面板中调整输出图像的宽度和高度。

定义数据交互端口

使用Python 生成器面板生成一个 JSON 文件,用于确定 python 算子文件同 RVS 软件之间的数据交互端口与参数。

在算子图中空白处右击,弹出菜单栏,选择Python 生成器面板

image-20240227151556647

Python 生成器面板中进行添加功能中需要的宽度和高度参数,以及输入图像和输出图像的端口。

请参照 Python 生成器面板,详细了解名称命名规范和类型定义格式,并进行相应添加。

image-20240228101954348

生成 Python 脚本文件

添加完成后将 JSON 命名为ResImg.json,点击生成

image-20240228102025813

生成成功后,在日志视图中显示导出配置文件(ResImg.json)和同名的 python 脚本文件(ResImg.py)的路径。

image-20240227152358807

runtime 目录中显示如下:

image-20240227152621747

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 函数被 processre_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 算子至算子图。

  1. 设置 Load 算子参数:

    • 类型→ Image

    • 文件 → icon_more→ 选择图像文件名 (example_data/images/bird.png)

    • 图片→icon_visOn 可视

  2. 设置 PythonSimple 算子参数:

    • Python配置文件 → ResImg.json

    • output_image→icon_visOn 可视

说明:当 PythonSimple 算子选择 Python配置文件后,自动刷新算子数据信号端口。

image-20240227160756504

算子连接

后面的使用同其他 RVS 算子一样,给该算子输入数据,并给定触发信号,算子内部会自动调用一次ResImg.json 对应的同名目录下的同名 python文件(即 ResImg.py)。

image-20240227160832746

运行

点击 RVS 的运行按钮,触发 Trigger 算子。

每一次 RVS 软件中的 PythonSimple 算子被触发后,都会在 python 文件内部按照下述顺序执行一次:

  • RVSPyClass 的初始化函数 _ init _

  • RVSPyClass 的数据输入函数 inputData(传入的函数内参即 RVS 软件中输入给 PythonSimple 算子的实际数据)

  • RVSPyClass 的可选执行函数 re_ini(当 RVS 软件中 PythonSimple 算子的 re_ini 属性勾选以后会执行该函数,执行后该 re_ini 属性会自动恢复非勾选状态),如下图所示:

    image-20240227162834655

  • RVSPyClass 的数据处理函数 process

  • RVSPyCLass 的数据输出 outputData

运行结果

  1. 点击 RVS 的运行按钮,触发 Trigger 算子。

  2. 如下图所示,2D 视图中分别显示 Load 、PythonSimple 算子运行成功后的结果。

    image-20240227161415064

  3. 在日志视图中显示 python_file。image-20240227161514661

至此,二次开发案例已完成。

Python 生成器面板

本章节将介绍 Python 生成器面板及其使用步骤,包括数据类型的定义格式和名称命名规范。

面板介绍

Python 生成器面板如下图所示:

image-20240227170435190

Python 生成器面板 中,可以添加参数、输入端口和输出端口。

  • 参数:设置算子属性面板中参数的名称、类型、最大值、最小值、默认值。

  • 输入端口:设置算子输入端口的名称、类型、是否为列表。

  • 输出端口:设置算子输出端口的名称、类型、是否为列表。

操作:

  • +:添加内容。

  • new.json:设置生成的 JSON 文件名,默认为 new.json。

  • 清空:清空当前所有已添加的内容。

  • 加载:加载已有 JSON 文件。

  • 生成:生成 JSON 文件和其同名的 python 文件。

  • 删除:右击已添加的内容,选择删除

    image-20240227170700988

名称命名规范

  1. 支持字母、数字、下划线(_),必须以字母开头。

  2. 参数、输入端口、输出端口,任意名称不能重复。

参数数据类型格式定义

数据类型 最大值 最小值 默认值
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 roll pitch yaw]
Cube 9 个 float 数据构成的 list,即[x y z roll pitch yaw 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 生成器面板

image-20240227151556647

打开后,如下图所示:

image-20240227171650025

添加参数、输入端口、输出端口。

参数添加:点击+按钮→命名名称→选择类型→双击设置最小值/最大值→填写默认值。

添加输入端口:点击+按钮→命名名称→选择类型→设置是否为列表。

添加输出端口:点击+按钮 → 命名名称→选择类型→设置是否为列表。

image-20240227171943972

添加完成后,点击生成。生成成功后,在日志视图中显示生成的 JSON 和和其同名的 python 脚本文件路径。

image-20240227172343549

补充说明

本章内容为补充部分,主要讲述 PythonThread 算子的使用、常见的错误操作,由于 PythonThread 与PythonSimple 使用的流程一致,这里只阐述一下 PythonThread 算子的意义和使用过程中的注意事项。

PythonThread算子

  1. 除了PythonSimple算子,RVS中还提供了PythonThread算子。其使用方式同PythonSimple算子相同,不过PythonThread算子是额外开辟个新线程来运行python算子。由于PythonThread算子从属于Thread类算子,所以多个并列的PythonThread算子可以同时运行;而PythonSimple算子从属于普通算子,运行会占用主线程,所以多个PythonSimple算子即便并行连接,实际执行时还是先后依次运行。

  2. 无论是PythonSimple算子还是PythonThread算子,都无法实现python文件的在线更新;如果您在运行了RVS以后,重新更改了自己的python算子文件,对应的PythonSimple/PythonThread均无法重新捕获该更改,必须重启RVS软件或者删掉该算子并重新加载新的Python算子,才能实现python算子文件的重新加载。另外,为了解决这个“需要临时更新python文件中某些参数的问题”,建议您将这些需要更新的参数作为String类型的数据输入,通过合理使用re_ini函数来达到这种更新效果。

PythonThread算子

  1. RVS中以ThreadNode结尾的算子都是线程类算子,它们的运行时间较长。(下文描述,基于普通算子的运行时间较短的情况)

  2. thread算子在被触发之后,会极短暂的占用主线程,然后会进入到分线程执行功能函数,同时释放主线程,所以此时RVS可以继续触发执行下一个算子,这也是多个并列的thread算子可以近似同时运行的原因。

  3. 所有的算子被拖动到RVS的算子图区域时会被自动赋予一个算子名,RVS底层会记录这些算子名的创建顺序作为算子顺序。

  4. 在某一个并行连接结构中,比如算子A触发算子B,算子B触发算子C;且算子A触发算子D,算子D触发算子E。无论B或者D两者中是否含有thread算子,RVS的执行都是等两者全部运行完成,才会去考虑执行C或者E(此时C/E虽然不是源自同一个起点,但是执行的先后顺序也是按照并行连接来考虑)。

  5. 当B以及D全部都是thread算子时,因为两个并行的thread可以同时运行,所以此时能有效节省运行时间。比如B运行3秒,D运行5秒,则两者的并行连接只需要运行5s就可以执行到C/E算子,而串形连接需要运行8s才能执行到C/E。

  6. 如果将一个thread算子同一个普通算子并行连接,则理论上只能节省该普通算子的运行时间,意义不大。RVS不建议进行这种连接方式,而且底层做了处理,即便这样连接了,也是先触发普通算子,执行完成后再执行thread算子,之后再考虑执行C/E。

  7. 如果多个普通算子同一个thread算子并行连接,第一个运行的一定是普通算子,执行完成后执行的第二个算子是否是thread算子则需要根据算子顺序排序。如果第二个算子是thread算子,则由于thread算子的特性,在该算子被触发之后,不用等到该thread算子执行完成,就会立刻触发执行第三个算子;如果第二个算子是普通算子,则必须等该算子执行完成后才可以执行第三个算子,依此类推。

  8. 多个普通算子并行连接,按照算子顺序,依次执行。

常见的错误操作

  1. 更改了python模板文件中接口类的类名 RVSPyclass、函数名 inputData、outputData、re_ini、process。或者不恰当的修改了 inputData、outputData 的函数体。

  2. 在 process 函数中写了大量代码,但是最后忘记将需要返回的实际变量赋值给 outputData 函数中的返回变量

  3. 修改了 python 模板文件的文件名,或者将该模板文件移动到其他位置。

注意:python 模板文件可以移动,但是一定要保证同 python_json 文件一起移动,保证这个两个文件在同一个目录即可。

  1. 在任何需要输入文件路径的地方,如果需要使用/或者\,必须只能使用/(该说明仅限定 linux 版本,在 windows 版本待定)

  2. outputData 函数中返回的数据格式不合法