python使用dir()函数获取对象中可用的属性和方法(看不到python源码又想知道怎么调用,DLL调用分析,SDK二次开发技巧)

news2024/12/30 3:22:45

有时候调用一些SDK,但是人家又是封装成dll文件形式调用的,这时没法看源码,也不想看其对应的开发文档(尤其有些开发文档写得还很难懂,或者你从某个开源社区拿过来,就根本没找到开发文档),

一.动态链接库之为啥你看不到源码

DLL(Dynamic Link Library)和 SO(Shared Object)都是可执行文件的一种形式,用于在运行时动态链接到程序中。它们的意义是为了实现代码的模块化和共享,提高代码的复用性和可维护性。
也就是说他们相当于打包好了的模块,你是看不到内部的代码的(当然逆向或许可以)

他所暴露的就只有调用的python接口,当然这个暴露的接口实际上也是通过python的ctypes模块调用编写的py文件中来看的,不过有些项目写得很混乱或易读性不够好,不论是结构还是命名,因此,本文是给出通过python的dir()模块来获取对象中可用的属性和方法并讨论说明在实际开发过程中如何玄学使用。

DLL(Windows 操作系统):

DLL 是 Windows 操作系统中的动态链接库文件,以 .dll 扩展名结尾。 DLL
文件包含函数、数据和资源等可供程序在运行时动态链接的代码。 多个程序可以共享使用同一个 DLL 文件,避免了重复编写和存储相同的代码。
DLL 的优势在于实现代码的动态链接,程序在运行时才将所需的函数和资源链接到程序中,而不是在编译时静态链接。 SO(Linux 操作系统):

SO 是 Linux 操作系统中的共享对象文件,以 .so 扩展名结尾。 SO 文件也是包含可供程序在运行时动态链接的代码、数据和资源等。
在 Linux 系统中,SO 文件可以被多个程序共享使用,实现代码的模块化和共享。 SO 文件的概念和作用与 DLL 文件相似,但在
Linux 系统下使用。

1.1简单dir()例子(不用dll的情况)

下面我给出一个简单的例子,这个例子我们可以清晰的看到 名为A的类的结构

# 定义一个类
class A():
    a_num = 1111
    a_string = "AAAAAAAAA"

    def A_fun(self):
        print("这是A函数")

    def A_add_1(self, num):
        return num+1

# 创建一个对象
a = A()

# 使用 dir() 显示当前作用域中的所有名称列表
name_list = dir(a)
print(name_list)

输出:

['A_add_1', 'A_fun', '__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'a_num', 'a_string']

这里可以看到,‘A_add_1’, ‘A_fun’,‘a_num’, ‘a_string’ 这几个我们自定义的类函数与变量都存在了

1.2 简单dir()例子(调用DLL的情况)

A.dll 零积分下载链接
或者看 下文 四章节 自己将打包一个等效刚才A类功能的dll文件,

import ctypes

# 加载 DLL 文件
dll = ctypes.CDLL("A.dll")

# 定义函数的返回类型和参数类型
dll.A_add_1.restype = ctypes.c_int
dll.A_add_1.argtypes = [ctypes.c_void_p, ctypes.c_int]

# 创建 A 类的实例
class A(ctypes.Structure):
    _fields_ = [("a_num", ctypes.c_int),
                ("a_string", ctypes.c_char * 10)]

# 调用 A_new 函数创建实例
dll.A_new.restype = ctypes.POINTER(A)
a_ptr = dll.A_new()
a = a_ptr.contents

# 调用 A_add_1 函数
num = 5
result = dll.A_add_1(ctypes.byref(a), num)
print("Result:", result)

# 获取 a_num 的值
a_num = a.a_num
print("a_num:", a_num)

# 释放实例
dll.A_del(a_ptr)

print(dir(A))
['__class__', '__ctypes_from_outparam__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__setstate__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', '_b_base_', '_b_needsfree_', '_fields_', '_objects', 'a_num', 'a_string']

这里实现的功能是和之前的A类是相同的,不过这里只能看到 ‘a_num’, ‘a_string’*,
至于为什么,因为python没法直接与获得dll文件的数据,只能得到一堆指向某个函数或者某个变量的指针,所以你想使用dll调用的方法复刻 A类 的各个函数(方法),变量。

那你可以下面这样写(当然这个,存python开发者不用掌握,我都用python了还要管变量类型与输出定义?能看懂已经很不错了)

import ctypes

# 加载 DLL 文件
dll = ctypes.CDLL("A.dll")

# 定义函数的返回类型和参数类型
dll.A_add_1.restype = ctypes.c_int
dll.A_add_1.argtypes = [ctypes.c_void_p, ctypes.c_int]

dll.A_fun.restype = ctypes.c_char
dll.A_fun.argtypes = [ctypes.c_void_p]


# dll.not_exist.restype = ctypes.c_char
# dll.not_exist.argtypes = [ctypes.c_void_p]

# 创建 A 类的实例
class A(ctypes.Structure):
    _fields_ = [("a_num", ctypes.c_int),
                ("a_string", ctypes.c_char * 10),
                ("not_exist_val", ctypes.c_char * 10),

                ]

    def A_add_1(self, num):
        return dll.A_add_1(ctypes.byref(self), num)
    def A_fun(self):
        return dll.A_fun(ctypes.byref(self))

# 调用 A_new 函数创建实例
dll.A_new.restype = ctypes.POINTER(A)
a_ptr = dll.A_new()
a = a_ptr.contents

# 调用 A_fun方法
result = a.A_fun()

print("*"*10)
# 调用 A_add_1 方法
num = 5
result = a.A_add_1(num)
print("A_add_1 Result:", result)


# 获取 a_num 的值
a_num = a.a_num
print("a_num:", a_num)

# 获取 not_exist_val 的值(实际上该值并不存在,但是程序依然后随机分配一个数据地址指针给你,
# 你可以看到每次运行的结果都是空,但不会报错)
a_not_exist_val= a.not_exist_val
print("a_not_exist_val:", a_not_exist_val)

# 释放实例
dll.A_del(a_ptr)

print(dir(A))

输出

**********
A_add_1 Result: 6
a_num: 1111
a_not_exist_val: b''
['A_add_1', 'A_fun', '__class__', '__ctypes_from_outparam__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__setstate__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', '_b_base_', '_b_needsfree_', '_fields_', '_objects', 'a_num', 'a_string', 'not_exist_val']
这是A函数

提示: 在上面代码中我还多加了一个 名叫 not_exist_val 的值,这个值在A.dll文件中是不存在的,但是作为变量依然不会报错,只是返回了个空(也有可能随机一个十六进制数),这里主要是想说明,如果看到一个从dll调用的变量值是空或者乱码,极有可能这个调用的 名字错了,比如我这里的 not_exist_val ,而不是这个dll文件中真的存在 not_exist_val 这个变量。

二.实例分析

尤其是针对一些硬件的sdk的开发,要么没技术支持,要么连手册都没有,要么有手册但是和python没关系,但是又想用python来进行调用。

比如下面的例子,我要使用海康的MV-DLS600P深度相机做手眼标定的开发, 我想获取深度相机的视频流,就2023年而言海康给了一部分调用代码,但又没有完全给,成功运行上了示例代码,相机是亮了,但是只得到了一个 stFrameData.stImageData[i] 的变量,然后你就可以从这个变量上把深度图和点云图求出来了。如下图:
开发手册是c++的,看起来只能所有定义参考作用,鉴定为不如直接用dir()自己看,而且总感觉代码更新了,手册没更新…
在这里插入图片描述
但是不知道视频流的返回函数是什么(即stFrameData.stImageData[i]这个对象到底应该调用什么才能返回视频流),例如下面代码中 stFrameData.stImageData[i].nWidth是返回宽度,那么返回视频流是什么呢?

def work_thread(camera=0,pdata=0,nDataSize=0):
    while True:
        stFrameData=MV3D_RGBD_FRAME_DATA()
        ret=camera.MV3D_RGBD_FetchFrame(pointer(stFrameData), 1000)
        if ret==0:
            for i in range(0, stFrameData.nImageCount):
                # print("MV3D_RGBD_FetchFrame[%d]:nFrameNum[%d],nDataLen[%d],nWidth[%d],nHeight[%d]" % (
                # i, stFrameData.stImageData[i].nFrameNum, stFrameData.stImageData[i].nDataLen, stFrameData.stImageData[i].nWidth, stFrameData.stImageData[i].nHeight))
                print("MV3D_RGBD_FetchFrame[%d]:帧号[%d],数据长度[%d],宽度[%d],高度[%d]" % (
                    i, stFrameData.stImageData[i].nFrameNum, stFrameData.stImageData[i].nDataLen,
                    stFrameData.stImageData[i].nWidth, stFrameData.stImageData[i].nHeight))

1.通过dir查看

dir() 是一个内置函数,用于获取对象的所有属性和方法的列表。它返回一个包含字符串的列表,这些字符串表示对象拥有的属性和方法的名称。

dir() 函数有以下两种常见的用法:

无参数使用:当不传入任何参数时,dir() 返回当前作用域中的所有名称列表,包括内置的名称。

python Copy print(dir()) # 返回当前作用域中的所有名称列表 有参数使用:当传入一个对象作为参数时,dir()
返回该对象的属性和方法列表。

python Copy my_list = [1, 2, 3] print(dir(my_list)) # 返回 my_list
对象的属性和方法列表 注意:参数可以是任何对象,包括内置对象(如列表、字典、字符串等)和自定义对象。

dir()
函数返回的列表中的字符串代表对象的属性和方法名称。属性名称以字符串的形式表示,而方法名称则以函数对象的形式表示。您可以通过访问对象的属性(如
obj.attribute)或调用对象的方法(如 obj.method())来使用它们。

需要注意的是,dir()
函数只返回对象中可见的属性和方法名称。有些属性和方法可能以双下划线开头,表示为特殊属性或私有属性,这些在列表中是不可见的。但是,您仍然可以通过直接访问这些属性和方法来使用它们。

加上下面,代码即可看到

print("对象中可用的属性和方法名称: " ,dir(stFrameData.stImageData[i]))

输出:

对象中可用的属性和方法名称:
[‘class’, ‘ctypes_from_outparam’, ‘delattr’, ‘dict’, ‘dir’, ‘doc’, ‘eq’, ‘format’, ‘ge’, ‘getattribute’, ‘gt’, ‘hash’, ‘init’, ‘init_subclass’, ‘le’, ‘lt’, ‘module’, ‘ne’, ‘new’, ‘reduce’, ‘reduce_ex’, ‘repr’, ‘setattr’, ‘setstate’, ‘sizeof’, ‘str’, ‘subclasshook’, ‘weakref’, ‘b_base’, ‘b_needsfree’, ‘fields’, ‘_objects’, ‘enImageType’, ‘nDataLen’, ‘nFrameNum’, ‘nHeight’, ‘nReserved’, ‘nTimeStamp’, ‘nWidth’, ‘pData’]

这里通过英文名判断大概就是,pData ,这里加上去然后调用看看是什么类型

                print("en图像类型",stFrameData.stImageData[i].enImageType)
                print("en图像类型十六进制:", hex(stFrameData.stImageData[i].enImageType))
                print("n数据长度", stFrameData.stImageData[i].nDataLen)
                print("n帧数", stFrameData.stImageData[i].nFrameNum)
                print("n高度", stFrameData.stImageData[i].nHeight)
                print("n保留(类型)", type(stFrameData.stImageData[i].nReserved))
                print("n保留", stFrameData.stImageData[i].nReserved)
                print("n时间戳", stFrameData.stImageData[i].nTimeStamp)
                print("n宽度", stFrameData.stImageData[i].nWidth)
                print( "p数据(类型)",type(stFrameData.stImageData[i].pData))
                print("p数据", stFrameData.stImageData[i].pData)

输出:

en图像类型 35127329
en图像类型十六进制: 0x2180021
n数据长度 18874368
n帧数 1
n高度 2048
n保留(类型) <class 'Mv3dRgbdImport.Mv3dRgbdDefine.c_byte_Array_16'>
n保留 <Mv3dRgbdImport.Mv3dRgbdDefine.c_byte_Array_16 object at 0x000001F6F8ED8AC8>
n时间戳 1057938808
n宽度 3072
p数据(类型) <class 'Mv3dRgbdImport.Mv3dRgbdDefine.LP_c_ubyte'>
p数据 <Mv3dRgbdImport.Mv3dRgbdDefine.LP_c_ubyte object at 0x000001F6F8ED8AC8>

2.根据类型进行分析

基础知识:

C语言中有多种数据类型,以下是一些常见的C类型的定义示例:

整数类型:

int: 用于表示整数,通常为32位或64位(取决于编译器和平台)。 short: 用于表示短整数,通常为16位。 long:
用于表示长整数,通常为32位或64位。 char: 用于表示字符,通常为8位。 浮点数类型:

float: 用于表示单精度浮点数,通常为32位。 double: 用于表示双精度浮点数,通常为64位。 指针类型:

int*: 用于表示指向整数的指针。 char*: 用于表示指向字符的指针。 void*: 用于表示通用指针,可以指向任意类型的数据。
结构体类型:

struct: 用于定义自定义的结构体类型,可以包含多个成员变量,每个成员变量可以是任意类型。 枚举类型:

enum: 用于定义枚举类型,可以列出一组具名的常数值。

根据打印输出结果,stFrameData.stImageData[i]的成员变量(例如enImageType、nDataLen、nFrameNum等)的值和类型都表明它们是C类型的数据。C语言中的数据类型,例如整数、枚举、指针等(通常在Python中使用ctypes库进行封装和访问)

而我要获得点云图和深度图,那么根据stFrameData.stImageData[i] 对象打印的结果,以及给出的英文名来判断,深度相机返回数据最有可能出现的位置应该是 nReserved或者 pData的函数(方法)的返回值中。(有些类型的深度相机返回值有可能直接是一个很长的字符串,海康则一般是一个 C类型数据 ,反正就是一个结构体变量(一个指向储存着特定二维结构信息的指针))

print("en图像类型",stFrameData.stImageData[i].enImageType)
print("en图像类型十六进制:", hex(stFrameData.stImageData[i].enImageType))
print("n数据长度", stFrameData.stImageData[i].nDataLen)
print("n帧数", stFrameData.stImageData[i].nFrameNum)
print("n高度", stFrameData.stImageData[i].nHeight)
print("n保留(类型)", type(stFrameData.stImageData[i].nReserved))
print("n保留", stFrameData.stImageData[i].nReserved)
print("n时间戳", stFrameData.stImageData[i].nTimeStamp)
print("n宽度", stFrameData.stImageData[i].nWidth)
print( "p数据(类型)",type(stFrameData.stImageData[i].pData))
print("p数据", stFrameData.stImageData[i].pData)

现在让我们来看看这两个的返回值分别是什么

2.1nReserved:

stFrameData.stImageData[i].nReserved

n保留(类型) <class ‘Mv3dRgbdImport.Mv3dRgbdDefine.c_byte_Array_16’>
n保留 <Mv3dRgbdImport.Mv3dRgbdDefine.c_byte_Array_16 object at 0x00000253BF6A8BC8>

可观察到关键词 Array16,那么可能就是 可能是一个长度为 16 的字节数组类型,至于前面的 cbyte,那可能是c语言开发,返回字节。
因此以解析字节的方式进行类型解析

nReserved_data = np.array(stFrameData.stImageData[i].nReserved)
print("nReserved_data:" ,nReserved_data)
nReserved_data = list(stFrameData.stImageData[i].nReserved)
print("nReserved_data2 :", nReserved_data)

输出如下

nReserved_data: [0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0]
nReserved_data2 : [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]

可以看见还真是16个字节的数组,但是很明显不是我要的深度图数据

2.2pData:

stFrameData.stImageData[i].pData

p数据(类型) <class ‘Mv3dRgbdImport.Mv3dRgbdDefine.LP_c_ubyte’>
p数据 <Mv3dRgbdImport.Mv3dRgbdDefine.LP_c_ubyte object at 0x00000253BF6A8BC8>
关于LP_c_ubyte 是什么类型资料如下

LP_c_ubyte 是一个指针类型,通常在与 C 语言交互的过程中使用。它表示指向 c_ubyte 类型数据的指针。

c_ubyte 是 ctypes 库中定义的一种数据类型,它对应于 C 语言中的 unsigned char 类型,即无符号字节类型。

LP_c_ubyte 是 ctypes 库中的一个别名,它表示一个指向 c_ubyte 类型数据的指针。在与 C 语言进行交互时,可以使用
LP_c_ubyte 类型来表示指向字节数组的指针。

翻译一些,说人话就是 stFrameData.stImageData[i].pData 是个指针(不了解指针的同学,可以理解成 是一个储存有目标数据储存地址号的一个特殊变量,我们可以通过该变量找到目标数据 )

于是我们可以这样写,使用 contents 方法

在Python中,contents 是ctypes库中指针对象的属性之一。contents属性用于访问指针所指向的内存区域中的值。

pData_data = np.array(stFrameData.stImageData[i].pData.contents)
print("pData_data:",pData_data)

输出:

pData_data: 0

然后很明显,0 是个屁的深度相机的返回数据,根据经验,返回的深度数据应该是一串字符有或者是一个很大的二维矩阵,然后才能处理成深度图或者点云图,所以这里应该是使用 **np.ctypeslib.as_array()**方法

我这里说下两者的区别: contents 方法只返回指针变量下的数据区第一个地址的数据,而np.ctypeslib.as_array方法会顺着第一个往下遍历,遍历的长度则由之前获得的 width 和 height 决定,如下

width = stFrameData.stImageData[i].nWidth
height = stFrameData.stImageData[i].nHeight
print("-"*20)
print(type(stFrameData.stImageData[i].pData))
print(stFrameData.stImageData[i].pData)
data = np.ctypeslib.as_array(stFrameData.stImageData[i].pData, shape=(height, width))
print("获得转换后的图像数据 data : ", data)
image = cv2.cvtColor(data, cv2.COLOR_GRAY2BGR)
# 保存图像
now = datetime.now()
timestamp = now.strftime("%Y_%m_%d_%H_%M_%S")
timestamp = timestamp + "_" + str(i)
cv2.imwrite("img_out/" + timestamp + ".jpg", image)
print("保存图像名称: ", timestamp)
print("当前图像类型(十六进制格式): ", hex(stFrameData.stImageData[i].enImageType) )

print("*"*50)

输出:
在这里插入图片描述
深度图和rgb图还都是这个接口一起发出来的,只能说易读性很不好。

np.ctypeslib.as_array()
函数将指针转换为NumPy数组时,它会根据指针所指向的地址找到NumPy数组的起始位置,并从该地址开始遍历,将连续的内存块解释为NumPy数组的元素。

而 stFrameData.stImageData[i].pData.contents 返回的是指针
stFrameData.stImageData[i].pData 指向的地址上存储的数据。它提供了指针所指向的内存位置的内容,通常是一个
C/C++ 数据类型的对象。

因此,在正确配置和使用的情况下,stFrameData.stImageData[i].pData.contents
应该返回指针所指向的内存位置上存储的数据。

通过使用 np.ctypeslib.as_array() 函数,我们可以将指针直接转换为NumPy数组,并正确解释指针所指向的内存数据。

四.其他

4.1 dll文件的生成

通过将C++文件或者C文件编译后获得即可,例如之前的

class A():
    a_num = 1111
    a_string = "AAAAAAAAA"

    def A_fun(self):
        print("这是A函数")

    def A_add_1(self, num):
        return num+1

等效上面的在C语言下的 A.c 文件代码如下

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#ifdef _WIN32
#define DLL_EXPORT __declspec(dllexport)
#else
#define DLL_EXPORT
#endif

typedef struct {
    int a_num;
    char a_string[10];
} A;

DLL_EXPORT void A_fun(A* self) {
    printf("这是A函数\n");
}

DLL_EXPORT int A_add_1(A* self, int num) {
    return num + 1;
}

DLL_EXPORT A* A_new() {
    A* self = (A*)malloc(sizeof(A));
    self->a_num = 1111;
    strcpy(self->a_string, "AAAAAAAAA");
    return self;
}

DLL_EXPORT void A_del(A* self) {
    free(self);
}

然后使用gcc编译成dll或者so文件

gcc -shared -o A.dll A.c

在这里插入图片描述

然后再调用即可

import ctypes

# 加载 DLL 文件
dll = ctypes.CDLL("A.dll")

# 定义函数的返回类型和参数类型
dll.A_add_1.restype = ctypes.c_int
dll.A_add_1.argtypes = [ctypes.c_void_p, ctypes.c_int]

# 创建 A 类的实例
class A(ctypes.Structure):
    _fields_ = [("a_num", ctypes.c_int),
                ("a_string", ctypes.c_char * 10)]

# 调用 A_new 函数创建实例
dll.A_new.restype = ctypes.POINTER(A)
a_ptr = dll.A_new()
a = a_ptr.contents

# 调用 A_add_1 函数
num = 5
result = dll.A_add_1(ctypes.byref(a), num)
print("Result:", result)

# 获取 a_num 的值
a_num = a.a_num
print("a_num:", a_num)

# 释放实例
dll.A_del(a_ptr)

4.2 海康MV-DLS600P 深度图与黑白图采集主代码

# -- coding: utf-8 --
import threading
from Mv3dRgbdImport.Mv3dRgbdApi import *
from Mv3dRgbdImport.Mv3dRgbdDefine import *
import msvcrt
import ctypes
import time
import os
import numpy as np
from Mv3dRgbdImport.Mv3dRgbdDefine import DeviceType_Ethernet, DeviceType_USB, MV3D_RGBD_FLOAT_EXPOSURETIME, \
    ParamType_Float, CoordinateType_Depth
import cv2
from datetime import datetime

g_bExit = False
def work_thread(camera=0,pdata=0,nDataSize=0):

    while True:
        stFrameData=MV3D_RGBD_FRAME_DATA()
        ret=camera.MV3D_RGBD_FetchFrame(pointer(stFrameData), 1000)
        if ret == 0:
            for i in range(0, stFrameData.nImageCount):
                # if stFrameData.stImageData[i].enImageType == MV3D_RGBD_ImageType.Normal:
                width = stFrameData.stImageData[i].nWidth
                height = stFrameData.stImageData[i].nHeight
                print("-"*20)
                print(type(stFrameData.stImageData[i].pData))
                print(stFrameData.stImageData[i].pData)
                data = np.ctypeslib.as_array(stFrameData.stImageData[i].pData, shape=(height, width))
                print("获得转换后的图像数据 data : ", data)
                image = cv2.cvtColor(data, cv2.COLOR_GRAY2BGR)
                # 保存图像
                now = datetime.now()
                timestamp = now.strftime("%Y_%m_%d_%H_%M_%S")
                timestamp = timestamp + "_" + str(i)
                cv2.imwrite("img_out/" + timestamp + ".jpg", image)
                print("保存图像名称: ", timestamp)
                print("当前图像类型(十六进制格式): ", hex(stFrameData.stImageData[i].enImageType) )

                print("*"*50)

                # cv2.imshow("Image", image)
                # cv2.waitKey(1)
        else:
            print("no data[0x%x]" % ret)
        if g_bExit == True:
            break

# 触发线程  5s触发一次
def work_thread_trigger(cam=0, pData=0, nDataSize=0):
    while True:
        time.sleep(5)
        ret = cam.MV3D_RGBD_SoftTrigger()
        if 0 == ret:
            print ("MV3D_RGBD_SoftTrigger success")
        else:
            print ("MV3D_RGBD_SoftTrigger failed[0x%x]" % ret)

        if g_bExit == True:
            break

if __name__ == "__main__":
    nDeviceNum=ctypes.c_uint(0)
    nDeviceNum_p=byref(nDeviceNum)
    ret=Mv3dRgbd.MV3D_RGBD_GetDeviceNumber(DeviceType_Ethernet | DeviceType_USB, nDeviceNum_p) #获取设备数量
    if  ret!=0:
        print("MV3D_RGBD_GetDeviceNumber fail! ret[0x%x]" % ret)
        os.system('pause')
        sys.exit()
    if  nDeviceNum==0:
        print("find no device!")
        os.system('pause')
        sys.exit()
    print("Find devices numbers:", nDeviceNum.value)

    stDeviceList = MV3D_RGBD_DEVICE_INFO_LIST();
    net = Mv3dRgbd.MV3D_RGBD_GetDeviceList(DeviceType_Ethernet | DeviceType_USB, pointer(stDeviceList.DeviceInfo[0]), 20, nDeviceNum_p)
    for i in range(0, nDeviceNum.value):
        print("\ndevice: [%d]" % i)
        strModeName = ""
        for per in stDeviceList.DeviceInfo[i].chModelName:
            strModeName = strModeName + chr(per)
        print("device model name: %s" % strModeName)

        strSerialNumber = ""
        for per in stDeviceList.DeviceInfo[i].chSerialNumber:
            strSerialNumber = strSerialNumber + chr(per)
        print("device SerialNumber: %s" % strSerialNumber)
    # 创建相机示例
    camera=Mv3dRgbd()
    nConnectionNum = 0
    # 打开设备
    ret = camera.MV3D_RGBD_OpenDevice(pointer(stDeviceList.DeviceInfo[int(nConnectionNum)]))
    if ret != 0:
        print ("MV3D_RGBD_OpenDevice fail! ret[0x%x]" % ret)
        os.system('pause')
        sys.exit()

    # 开始取流
    ret=camera.MV3D_RGBD_Start()
    if ret != 0:
        print ("start fail! ret[0x%x]" % ret)
        camera.MV3D_RGBD_CloseDevice()
        os.system('pause')
        sys.exit()

    # 获取图像线程
    try:
        hthreadhandle=threading.Thread(target=work_thread,args=(camera,None,None))
        hthreadhandle.start()
    except:
        print("error: unable to start thread")

    try:
        hthreadhandle_trigger= threading.Thread(target=work_thread_trigger, args=(camera, None, None))
        hthreadhandle_trigger.start()
    except:
        print("error: unable to start thread")

    #msvcrt.getch()
    os.system('pause')
    g_bExit = True

    hthreadhandle.join()
    hthreadhandle_trigger.join()

    # 停止取流
    ret=camera.MV3D_RGBD_Stop()
    if ret != 0:
        print ("stop fail! ret[0x%x]" % ret)
        os.system('pause')
        sys.exit()

    # 销毁句柄
    ret=camera.MV3D_RGBD_CloseDevice()
    if ret != 0:
        print ("CloseDevice fail! ret[0x%x]" % ret)
        os.system('pause')
        sys.exit()
    
    sys.exit()

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/899763.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

回归预测 | MATLAB实现IPSO-SVM改进粒子群优化算法优化支持向量机多输入单输出回归预测(多指标,多图)

回归预测 | MATLAB实现IPSO-SVM改进粒子群优化算法优化支持向量机多输入单输出回归预测&#xff08;多指标&#xff0c;多图&#xff09; 目录 回归预测 | MATLAB实现IPSO-SVM改进粒子群优化算法优化支持向量机多输入单输出回归预测&#xff08;多指标&#xff0c;多图&#xf…

回归预测 | MATLAB实现WOA-BP鲸鱼优化算法优化BP神经网络多输入单输出回归预测(多指标,多图)

回归预测 | MATLAB实现WOA-BP鲸鱼优化算法优化BP神经网络多输入单输出回归预测&#xff08;多指标&#xff0c;多图&#xff09; 目录 回归预测 | MATLAB实现WOA-BP鲸鱼优化算法优化BP神经网络多输入单输出回归预测&#xff08;多指标&#xff0c;多图&#xff09;效果一览基本…

运动恢复结构(SfM)_OpenMVG_代码的核心逻辑

参考: 深蓝学院,基于图像的三维重建 课程 1. 输入输出 数据集 fountain,选⾃Lund University开源的三维重建数据集 (http://www.maths.lth.se/matematiklth/personal/calle/dataset/dataset.html ) 2. 特征提取与特征匹配

优于立方复杂度的 Rust 中矩阵乘法

优于立方复杂度的 Rust 中矩阵乘法 迈克克维特 跟随 发表于 更好的编程 6 分钟阅读 7月 <> 143 中途&#xff1a;三次矩阵乘法 一、说明 几年前&#xff0c;我在 C 年编写了 Strassen 矩阵乘法算法的实现&#xff0c;最近在 Rust 中重新实现了它&#xff0c;因为我继续…

TCP报文段的首部格式

TCP传送的数据单元称为报文段&#xff0c;既可以用来运载数据&#xff0c;又可以用来建立连接、释放连接和应答 一个TCP报文段可以分为首部和数据部分&#xff0c;整个TCP报文段作为IP数据报的数据部分封装在IP数据报中。首部的固定长度为20B&#xff0c;因此最短为20B&#xf…

17-工程化开发 脚手架 Vue CLI

开发Vue的两种方式: 1.核心包传统开发模式: 基于 html/css /js 文件&#xff0c;直接引入核心包&#xff0c;开发 Vue。 2.工程化开发模式: 基于构建工具 (例如: webpack)的环境中开发 Vue。 问题: 1. webpack 配置不简单 2. 雷同的基础配置 3. 缺乏统…

2023.8 - java - 对象和类

public class Dog {String breed;int size;String colour;int age;void eat() {}void run() {}void sleep(){}void name(){} } 一个类可以包含以下类型变量&#xff1a; 局部变量&#xff1a;在方法、构造方法或者语句块中定义的变量被称为局部变量。变量声明和初始化都是在方…

01-关于new Object()的问题

美团面试题关于Object o = new Object()的几个问题。 1、对象在内存中的存储布局? 实例化一个对象,在堆区开辟一段空间。 堆区由markword、类型指针(class point)、实例数据、对齐组成。 markword:由8个字节组成。 类型指针(class point):就是指向某class文件的指针,…

外网连接局域网的几种方式?快解析内网穿透安全便利吗?

外网连接局域网是一项网络连接中的关键技术&#xff0c;它能够让远程用户通过互联网访问内部局域网中的资源和服务。外网连接局域网为企业提供了更大的灵活性和便捷性&#xff0c;但也需要严格的安全措施来防止未经授权的访问。 外网连接局域网的几种方式 在将外网连接到局域…

数组详解

1. 一维数组的创建和初始化 1.1 数组的创建 数组是一组相同类型元素的集合。 数组的创建方式&#xff1a; type_t arr_name [const_n]; //type_t 是指数组的元素类型 //const_n 是一个常量表达式&#xff0c;用来指定数组的大小 数组创建的实例&#xff1a; //代码1 int a…

SpringMVC之入门搭建框架

文章目录 前言一、SpringMVC简介1.什么是MVC2.什么是SpringMVC3.SpringMVC的特点 二、搭建框架——HelloWorld1.创建maven工程&#xff08;web项目&#xff09;2.配置web.xml3.配置springMVC.xml4.创建请求控制器 总结 前言 基础小白第一次走进SpringMVC&#xff1a;了解什么是…

@mouseover不起作用,并没有触发

我的错误代码如下&#xff1a; <el-rowv-for"version in item.version_list":key"version.id":class"{ blue-background: versionItem.id version.id }"mouseover.native"version.isHovered true"mouseleave.native"version…

C++11并发与多线程笔记(11) std::atomic续谈、std::async深入谈

C11并发与多线程笔记&#xff08;11&#xff09; std::atomic续谈、std::async深入谈 1、std::atomic续谈2、std::async深入理解2.1 std::async参数详述2.2 std::async和std::thread()区别&#xff1a;2.3 async不确定性问题的解决 1、std::atomic续谈 #include <iostream&…

核能的发展与应用

目录 1.核能的概念 2.核能的实现原理 3.核能的利与弊 4.核能未来的发展趋势 1.核能的概念 核能是指利用核反应过程中释放出的能量来产生电力或其他形式能量的能源形式。核能主要通过核裂变和核聚变两种方式产生。 1. 核裂变&#xff1a;核裂变是指重核&#xff08;通常是铀、…

智能电视与win10电脑后续无法实现DLNA屏幕共享

问题背景&#xff1a; 我用的是TCL电视&#xff0c;但是并不是最新&#xff0c;打开的方式是U盘->电脑&#xff0c;各位看自己情况&#xff0c;很多问题都大概率是智能电视问题。 情景假设&#xff1a; 假设你已经完成原先智能电视该有的步骤&#xff0c;通过DLNA&#xf…

什么是springMVC 视图和视图解析器

&#x1f600;前言 本篇讲解了什么是springMVC 视图和视图解析器 &#x1f3e0;个人主页&#xff1a;尘觉主页 &#x1f9d1;个人简介&#xff1a;大家好&#xff0c;我是尘觉&#xff0c;希望我的文章可以帮助到大家&#xff0c;您的满意是我的动力&#x1f609;&#x1f60…

【C# 基础精讲】LINQ to XML查询

LINQ to XML 是 C# 中用于查询和操作 XML 数据的强大工具。它允许您使用 LINQ 查询语法对 XML 文档进行查询、过滤、投影等操作&#xff0c;从而更加方便地处理 XML 数据。本文将详细介绍 LINQ to XML 的基本概念、常见操作以及示例&#xff0c;帮助您了解如何在 C# 中使用 LIN…

内核编译bpf

1 cp /boot/config-4.15.0-213-generic . 2 make menuconfig 报错缺少库 2.1 安装flex库 https://github.com/westes/flex/releases/download/v2.6.4/flex-2.6.4.tar.gz ./configure CFLAGS-D_GNU_SOURCE make sudo make install 再次编译 缺少 bison库 2.2 下载 …

图片合成动图怎么弄?gif图制作的简单方法

许多鬼畜的表情包其实是用图片合成gif完成的&#xff0c;那么怎么将图片转gif呢&#xff1f;使用GIF中文网的gif合成&#xff08;https://www.gif.cn&#xff09;功能&#xff0c;打开浏览器就可以完成gif图片制作&#xff0c;非常简单方便&#xff0c;一起来了解一下吧。 打开…

C++笔记之Class A a = b调用的什么构造函数(引例)

C笔记之Class A a b调用的什么构造函数(引例) code review! 文章目录 C笔记之Class A a b调用的什么构造函数(引例)1.C笔记之Class A a b调用的什么构造函数&#xff1f;2.常见的三种形式3.等价的两种形式 1.C笔记之Class A a b调用的什么构造函数&#xff1f; 2.常见的三…