简介
GXCI是基于Rust的大恒相机API上层封装
模块
主要分为四大模块:
- HAL API:基于RAW Wrapper的为具体功能而作高级封装
- RAW Wrapper:GxIAPI.dll的直接Rust封装,提供原生API调用
- Utils:一些工具函数
- Error Handling:统一错误处理
特性
目前有四个Features:
- solo:单相机模式
- use-opencv:使用opencv库
- (开发中)multi模式:多相机模式
- (开发中)use-imageproc:使用imageproc库
默认开启的是solo和use-opencv
说白了
raw就是用大恒相机给的C语言开发用的GXIAPI.dll,结合开发文档,用libloading这个库把dll的所有函数加载进来封装了一层。然后此外每个对应的类型、结构体、枚举、常量等等都重新用rust写了一遍。特别是枚举描述部分还遇到过结构体内存没对齐的问题。
hal最主要做的其实就是给GX_INSTANCE、DEVICE_HANDLE这些在库内部用全局变量封装了一层,在调库的时候就不用每次let一个gx出来然后调gx的方法。
但其实全局变量封装目前用的还是std的Mutex(典中典LazyLock<Arc<Mutex<T>>>
),暂时还没引入tokio(毕竟手头也只有一个相机,前端那边也还没有多相机的业务)
utils写的一坨狗屎,乱成一堆,开始写的时候项目水平还很差是怎样的,但也暂时懒得改了
error部分挺好的,还是喜欢原生Error Handling
快速开始
主要分为
- 从旧版本迁移
- 预配置项
- 安装
- 列举你的设备
- 采一张图
- 推流
- 调整相机参数
这样七个部分,搭配examples快速上手GXCI
从旧版本迁移
如果你是第一次使用GXCI那么,你可以跳过这一节。这一节主要是为了帮助0.3.6之前的GXCI用户迁移到新版本。
GXCI在0.3.5及此前的版本一直使用的是4.9.0版本的OpenCV,但GXCI从0.3.6开始使用4.10.0版本的OpenCV。所以用户也需要相应的升级一下自己的OpenCV版本
通过Choco升级OpenCV
choco upgrade chocolatey
choco upgrade opencv
更改系统环境变量
把系统变量的OPENCV_LINK_LIBS
更新为opencv_world4100
即可
以后如果报错STATUS_DLL_NOT_FOUND
,就复制一份opencv_world4100.dll
到exe的同级文件夹就行
清理旧版本Target
如果你之前使用过GXCI,那么你可能需要清理一下之前的target文件夹,因为之前的target文件夹可能会有一些旧的依赖库,导致编译错误
cargo clean
预配置项
大恒相机官方驱动
GXCI库基于大恒相机官方提供的C语言dll(即GxIAPI.dll)进行开发,默认位于C:\Program Files\Daheng Imaging\GalaxySDK\APIDll\Win64\GxIAPI.dll
直接前往大恒官网下载中心下载你所使用的相机的驱动即可。
OpenCV
GXCI默认依赖的图像处理库为opencv,即OpenCV的Rust绑定库。
这部分配置可以参照如下几个文章,但都是英文
当然我们这里也提供了一份中文版的opencv-rust配置方法
安装LLVM和OpenCV 4.10.0
在Windows 10/11中,我建议使用Chocolatey来安装LLVM和OpenCV 4.10.0:
choco install llvm opencv
以下是一些会用到的官方网站:
添加Path环境变量(系统变量)
将以下路径添加到相应的系统的Path环境变量中:
- OpenCV的bin路径
- OpenCV的x64 bin路径
- Chocolatey的bin路径
- LLVM的bin路径
请根据你的安装路径自行调整,下面是一个示例:
C:\tools\opencv\build\bin
C:\tools\opencv\build\x64\vc16\bin
C:\ProgramData\chocolatey\bin
C:\Program Files\LLVM\bin
添加OpenCV环境变量(系统变量)
新建三个系统变量:
- OPENCV_INCLUDE_PATHS
- OPENCV_LINK_LIBS
- OPENCV_LINK_PATHS
请根据你的安装路径自行调整,下面是一个示例: |变量名|变量值| |---|---| |OPENCV_INCLUDE_PATHS|C:\tools\opencv\build\include| |OPENCV_LINK_LIBS|opencv_world410| |OPENCV_LINK_PATHS|C:\tools\opencv\build\x64\vc16\lib|
复制opencv_world410.dll到目标目录(如果需要)
有时候你需要将opencv_world4100.dll复制到目标目录,即与exe文件同级的目录。
重启电脑(如果需要)
有时候你需要重启电脑才能使环境变量生效。
Galaxy Viewer (可选)
Galaxy Viewer是大恒相机官方提供的相机调试工具,可以用来查看相机的参数、调整相机的参数等。
难绷碎碎念
OpenCV库在这里用于方便地将图像矩阵化并提供GUI显示图像,说白了就用来保存图片和显示推流的,实际上GXCI目前并没有用到太多的图像处理算法。
怎么说呢,我认为OpenCV在图像处理领域确实是一个必要的库,所以我们也re-export了这个库,依赖启用了use-opencv
的gxci
就可以直接用use gxci::opencv
来使用opencv。
但是OpenCV的缺点是配置和编译时间都有点难绷,所以这里还提供了一个use-imageproc
特性,通过使用imageproc
库来避免使用OpenCV。
但是在我测试的过程中imageproc
的编译时间也很长(或者说就没编译成果过),特别是在nalgebra部分。有一天我编译imageproc
的nalgebra花了3个小时都还没完成,绝对是卡在那了,尝试被迫终止。所以我也只能认为OpenCV仍然是一个不错的选择了,所以GXCI默认启用的也就是use-opencv
这个特性了。
安装
在Cargo.toml中添加依赖:
[dependencies]
gxci = "0.3.6"
# 默认并且目前也只支持solo和use-opencv两个特性
就OK啦,开始调库吧
列举你的设备
HAL代码示例
下面是列举已链接设备的示例代码。该示例代码使用 gxi_count_devices
和 gxi_list_devices
函数来获取设备数量和设备信息,然后使用 print_device_info
函数打印设备信息。
use gxci::hal::base::*; use gxci::hal::device::{gxi_count_devices,gxi_list_devices}; use gxci::utils::debug::print_device_info; fn main()->Result<()> { gxci_init_default()?; let device_num = gxi_count_devices(1000)?; println!("Device number: {}", device_num); let base_info = gxi_list_devices()?; for device in &base_info { print_device_info(&device); } gxci_close()?; Ok(()) }
HAL注意点
hal
部分的base
模块推荐还是直接use gxci::hal::base::*
全部引入,一是里面的函数绝对都会用到,而是这里还re-export了error
模块的Result
类型,方便错误处理。gxci_init_default()
默认的初始化地址是"C:\\Program Files\\Daheng Imaging\\GalaxySDK\\APIDll\\Win64\\GxIAPI.dll"
,如果你的dll文件不在默认路径,可以使用gxci_init("your_dll_path")
来初始化。(暂时只支持绝对路径,请自行拼接适配)gxi_count_devices
函数的参数是超时时间,通常就传1000
,单位是毫秒。gxi_list_devices
函数返回的是一个Vec<GX_DEVICE_BASE_INFO>
,GX_DEVICE_BASE_INFO
的具体内容参看GXCI的Docs.rs文档的struct部分或者大恒的C语言SDK文档,文档是SDK自带的,默认位置在C:\Program Files\Daheng Imaging\GalaxySDK\Doc
,自己根据安装目录找一找就行。- 在程序结束时,记得调用
gxci_close()
来释放dll。
RAW代码示例
下面是列举已链接设备的示例代码。首先new一个GXInstance,然后调用 gx_init_lib
函数来初始化库,接着调用 gx_update_device_list
和 gx_get_all_device_base_info
函数来获取设备数量和设备信息,然后使用 print_device_info
函数打印设备信息,最后调用 gx_close_lib
函数来关闭库。
use std::mem::size_of; use gxci::{ raw::{ gx_interface::*, gx_struct::*, }, utils::{ debug::print_device_info, builder::GXDeviceBaseInfoBuilder, }, }; fn main() { // You can change the library path as you need let gx = GXInstance::new("C:\\Program Files\\Daheng Imaging\\GalaxySDK\\APIDll\\Win64\\GxIAPI.dll").expect("Failed to load library"); gx.gx_init_lib().expect("Failed to initialize library"); let mut device_num = 0; gx.gx_update_device_list(&mut device_num, 1000) .expect("Failed to update device list"); if device_num > 0 { let mut base_info: Vec<GX_DEVICE_BASE_INFO> = (0..device_num).map(|_| { GXDeviceBaseInfoBuilder::new().build() }).collect(); // or you can use the following code to initialize the vector without using the builder // let mut base_info = vec![ // GX_DEVICE_BASE_INFO { // szVendorName: [0; GX_INFO_LENGTH_32_BYTE], // szModelName: [0; GX_INFO_LENGTH_32_BYTE], // szSN: [0; GX_INFO_LENGTH_32_BYTE], // szDisplayName: [0; GX_INFO_LENGTH_128_BYTE], // szDeviceID: [0; GX_INFO_LENGTH_64_BYTE], // szUserID: [0; GX_INFO_LENGTH_64_BYTE], // accessStatus: GX_ACCESS_STATUS_CMD::Unknown, // deviceClass: GX_DEVICE_CLASS::Unknown, // reserved: [0; 300], // }; // device_num as usize // ]; let mut size = (device_num as usize) * size_of::<GX_DEVICE_BASE_INFO>(); let status = gx .gx_get_all_device_base_info(base_info.as_mut_ptr(), &mut size) .expect("Failed to get all device base info"); if status == 0 { // Assuming 0 is GX_STATUS_SUCCESS println!( "Device base info retrieved successfully. Number of devices: {}", device_num ); for device in &base_info { print_device_info(&device); } } else { println!("Failed to retrieve device base info, status: {}", status); } } else { println!("No Devices found."); } gx.gx_close_lib().expect("Failed to close library"); println!("Library closed.") }
RAW注意点
- 谨慎使用吧,平常开发用hal还是更舒服一些(主要还是懒得写文档了)
采一张图
HAL代码示例
采图首先要用gxi_open_device
打开设备,然后调用gxi_get_image
获取图像,最后调用gxi_save_image_as_png
保存缓存区中的图像,然后调用gxi_close_device
关闭设备就走完一遍流程了
当然,除了gxi_get_image
把图像保存在缓存区这个返回Resulf<()>
函数,还有gxi_get_image_as_raw
和gxi_get_image_as_bytes
两个函数可以返回拿到的图像数据,前者返回的是Result<&[u8]>
,后者返回的是Result<Vec<u8>>
。
use gxci::hal::device::*; use gxci::hal::base::*; use gxci::utils::debug::print_device_info; fn main()->Result<()> { gxci_init_default()?; let device_num = gxi_count_devices( 1000)?; println!("Device number: {}", device_num); let base_info = gxi_list_devices()?; for device in &base_info { print_device_info(&device); } gxi_open_device()?; gxi_get_image()?; gxi_save_image_as_png("test.png")?; gxi_close_device()?; gxci_close()?; Ok(()) }
HAL注意点
- 当然偷懒直接把
device
的全部都引入也是可以的23333 gxi_open_device
在solo
模式下默认打开第一个设备,多设备暂时没写gxi_get_image
函数返回的是Result<()>
,因为图像数据是保存在缓存区的,所以这个函数只是把图像数据保存在缓存区,如果要获取图像数据,可以使用gxi_get_image_as_raw
和gxi_get_image_as_bytes
两个函数gxi_save_image_as_png
函数的参数是保存的文件名,保存在当前目录下
RAW代码示例
采图首先要用gx_open_device_by_index
打开设备,然后发送GX_COMMAND_ACQUISITION_START
命令开始采集,接着调用gx_get_image
获取图像,采图成功后调用opencv
库把image_buffer
转换成Mat
对象,然后调用imwrite
函数保存图像,最后调用gx_send_command
发送GX_COMMAND_ACQUISITION_STOP
命令停止采集,然后调用gx_close_device
关闭设备就走完一遍流程了
use std::mem::size_of; use std::slice; use opencv::{ imgcodecs, core, }; use gxci::{ raw::{ gx_interface::*, gx_enum::*, gx_struct::*, gx_handle::*, }, utils::{ debug::print_device_info, builder::GXDeviceBaseInfoBuilder, facade::*, }, }; fn main() -> Result<()> { unsafe { let gx = GXInstance::new("C:\\Program Files\\Daheng Imaging\\GalaxySDK\\APIDll\\Win64\\GxIAPI.dll").expect("Failed to load library"); gx.gx_init_lib().expect("Failed to initialize library"); // Update the device list let mut device_num = 0; gx.gx_update_device_list(&mut device_num, 1000) .expect("Failed to update device list"); if device_num > 0 { let mut base_info: Vec<GX_DEVICE_BASE_INFO> = (0..device_num).map(|_| { GXDeviceBaseInfoBuilder::new().build() }).collect(); let mut size = (device_num as usize) * size_of::<GX_DEVICE_BASE_INFO>(); let status = gx .gx_get_all_device_base_info(base_info.as_mut_ptr(), &mut size) .expect("Failed to get all device base info"); if status == 0 { println!( "Device base info retrieved successfully. Number of devices: {}", device_num ); for device in &base_info { print_device_info(&device); } let first_device_sn = std::str::from_utf8(&base_info[0].szSN).unwrap_or(""); let mut device_handle: GX_DEV_HANDLE = std::ptr::null_mut(); let open_status = gx .gx_open_device_by_index(1, &mut device_handle) .expect("Failed to open device with index"); if open_status == 0 { println!( "Successfully opened device index 1 with SN: {}", first_device_sn.trim_end_matches(char::from(0)) ); gx.gx_send_command(device_handle, GX_FEATURE_ID::GX_COMMAND_ACQUISITION_START) .expect("Failed to send command"); // 这种写法在所有权机制下是错误的,因为image_buffer在返回的时候就已经被释放了 // let frame_data_facade = fetch_frame_data(&gx, device_handle); // let mut frame_data = convert_to_frame_data(&frame_data_facade.unwrap()); // 这种写法是正确的,因为image_buffer被返回到了当前作用域 #[allow(unused_variables)] let (frame_data_facade, image_buffer) = fetch_frame_data(&gx, device_handle).unwrap(); let mut frame_data = convert_to_frame_data(&frame_data_facade); let result = gx.gx_get_image(device_handle, &mut frame_data, 100); match result { Ok(_) => { println!("Image captured successfully."); if frame_data.nStatus == 0 { let data = slice::from_raw_parts(frame_data.pImgBuf as *const u8, (frame_data.nWidth * frame_data.nHeight) as usize); let mat = core::Mat::new_rows_cols_with_data( frame_data.nHeight, frame_data.nWidth, data ).unwrap(); let vec = core::Vector::<i32>::new(); if imgcodecs::imwrite("right.png", &mat, &vec).unwrap() { println!("Image saved successfully."); } else { println!("Failed to save the image."); } } } Err(e) => eprintln!("Failed to capture image: {:?}", e), } gx.gx_send_command(device_handle, GX_FEATURE_ID::GX_COMMAND_ACQUISITION_STOP) .expect("Failed to send command"); // Close the device gx.gx_close_device(device_handle) .expect("Failed to close device"); println!("Device closed.") } else { println!( "Failed to open device with SN: {}", first_device_sn.trim_end_matches(char::from(0)) ); } } else { println!("Failed to retrieve device base info, status: {}", status); } } else { println!("No Devices found."); } gx.gx_close_lib().expect("Failed to close library"); println!("Library closed."); Ok(()) } }
RAW注意点
- 谨慎使用吧,平常开发用hal还是更舒服一些(主要还是懒得写文档了)
- 这里还玩了会所有权游戏,image_buffer如果不返回到当前作用域,就会在facade的那个函数中被释放掉,导致下文直接就拿不到图像数据了(送客不接客,客死他乡了)
推流
HAL代码示例
核心在于gxi_use_stream
,这个0.3.4
版本新增的函数
use gxci::hal::device::*; use gxci::hal::base::*; use gxci::utils::debug::print_device_info; use gxci::utils::extract::{extract_callback_img_buf,extract_frame_callback_param}; use gxci::raw::gx_struct::GX_FRAME_CALLBACK_PARAM; use gxci::raw::gx_interface::Result; use gxci::opencv::{core, highgui}; extern "C" fn frame_callback(p_frame_callback_data: *mut GX_FRAME_CALLBACK_PARAM) { let frame_callback_data = extract_frame_callback_param(p_frame_callback_data); let data = extract_callback_img_buf(frame_callback_data); let mat = core::Mat::new_rows_cols_with_data( frame_callback_data.nHeight, frame_callback_data.nWidth, data ).unwrap(); highgui::imshow("Camera Frame", &mat).unwrap(); if highgui::wait_key(10).unwrap() > 0 { highgui::destroy_window("Camera Frame").unwrap(); } } fn main()->Result<()> { gxci_init_default()?; let device_num = gxi_count_devices( 1000)?; println!("Device number: {}", device_num); let base_info = gxi_list_devices()?; for device in &base_info { print_device_info(&device); } gxi_open_device()?; gxi_use_stream(frame_callback)?; gxi_close_device()?; gxci_close()?; Ok(()) }
HAL注意点
- 最闪亮的函数当属
gxi_use_stream
,这个函数的参数是一个类型为pub type GXCaptureCallBack = extern "C" fn(pFrameData: *mut GX_FRAME_CALLBACK_PARAM);
的回调函数,允许你自定义回调函数来处理推流采集到的图像数据。此外,在utils模块中还提供了extract_callback_img_buf
和extract_frame_callback_param
两个函数来帮助你提取回调函数中的图像数据。 - 这里的
frame_callback
就仅仅只是把图像用opencv显示出来罢了,你可以根据自己的需求来处理图像数据。例如通过全局变量来暴露图像数据,然后在主线程中处理图像数据等等
RAW代码示例
use std::mem::size_of; use std::slice; use std::thread::sleep; use std::time::Duration; use opencv::{ highgui, core, }; use gxci::{ raw::{ gx_interface::*, gx_enum::*, gx_struct::*, gx_handle::*, }, utils::{ debug::print_device_info, builder::GXDeviceBaseInfoBuilder, }, }; extern "C" fn frame_callback(p_frame_callback_data: *mut GX_FRAME_CALLBACK_PARAM) { // 避免刷屏 // println!("Frame callback triggered."); // println!("Frame status: {:?}", unsafe { (*p_frame_callback_data).status }); // println!("Frame All: {:?}", unsafe { *p_frame_callback_data }); unsafe { let frame_callback_data = &*p_frame_callback_data; if frame_callback_data.status == 0 { let data = slice::from_raw_parts(frame_callback_data.pImgBuf as *const u8, (frame_callback_data.nWidth * frame_callback_data.nHeight) as usize); let mat = core::Mat::new_rows_cols_with_data( frame_callback_data.nHeight, frame_callback_data.nWidth, // core::CV_8UC1, data ).unwrap(); highgui::imshow("Camera Frame", &mat).unwrap(); if highgui::wait_key(10).unwrap() > 0 { highgui::destroy_window("Camera Frame").unwrap(); } } } } fn main()->Result<()> { // You can change the library path as you need let gx = GXInstance::new("C:\\Program Files\\Daheng Imaging\\GalaxySDK\\APIDll\\Win64\\GxIAPI.dll").expect("Failed to load library"); gx.gx_init_lib().expect("Failed to initialize library"); // Update the device list let mut device_num = 0; gx.gx_update_device_list(&mut device_num, 1000) .expect("Failed to update device list"); if device_num > 0 { let mut base_info: Vec<GX_DEVICE_BASE_INFO> = (0..device_num).map(|_| { GXDeviceBaseInfoBuilder::new().build() }).collect(); let mut size = (device_num as usize) * size_of::<GX_DEVICE_BASE_INFO>(); let status = gx .gx_get_all_device_base_info(base_info.as_mut_ptr(), &mut size) .expect("Failed to get all device base info"); if status == 0 { // Assuming 0 is GX_STATUS_SUCCESS println!( "Device base info retrieved successfully. Number of devices: {}", device_num ); for device in &base_info { print_device_info(&device); } // Attempt to open the first device using its SN let first_device_sn = std::str::from_utf8(&base_info[0].szSN).unwrap_or(""); let mut device_handle: GX_DEV_HANDLE = std::ptr::null_mut(); let open_status = gx .gx_open_device_by_index(1, &mut device_handle) .expect("Failed to open device with index"); if open_status == 0 { println!( "Successfully opened device index 1 with SN: {}", first_device_sn.trim_end_matches(char::from(0)) ); gx.gx_set_float(device_handle, GX_FEATURE_ID::GX_FLOAT_GAIN, 20.0)?; let reg_result = gx.gx_register_capture_callback(device_handle, frame_callback); match reg_result { Ok(_) => println!("Capture callback registered successfully."), Err(e) => eprintln!("Failed to register capture callback: {:?}", e), } gx.gx_send_command(device_handle, GX_FEATURE_ID::GX_COMMAND_ACQUISITION_START) .expect("Failed to send command"); highgui::named_window("Camera", highgui::WINDOW_AUTOSIZE).unwrap(); loop { sleep(Duration::from_secs(60)); break; } gx.gx_send_command(device_handle, GX_FEATURE_ID::GX_COMMAND_ACQUISITION_STOP) .expect("Failed to send command"); let unregeister_result = gx.gx_unregister_capture_callback(device_handle); match unregeister_result { Ok(_) => println!("Capture callback unregistered successfully."), Err(e) => eprintln!("Failed to unregister capture callback: {:?}", e), } // Close the device gx.gx_close_device(device_handle) .expect("Failed to close device"); println!("Device closed.") } else { println!( "Failed to open device with SN: {}", first_device_sn.trim_end_matches(char::from(0)) ); } } else { println!("Failed to retrieve device base info, status: {}", status); } } else { println!("No Devices found."); } gx.gx_close_lib().expect("Failed to close library"); println!("Library closed."); Ok(()) }
RAW注意点
- 谨慎使用吧,平常开发用hal还是更舒服一些(主要还是懒得写文档了)
调整相机参数
HAL代码示例
这里我们就承接着上面推流部分的代码进行了,这里我们调整了相机的一些参数,例如设置了相机的分辨率、自动增益等等,分别是通过gxi_set_width
、gxi_set_height
、gxi_set_gain_auto_continuous
、gxi_set_gain
函数。
use gxci::hal::device::*; use gxci::hal::base::*; use gxci::hal::control::analog::*; use gxci::hal::control::image_format::*; use gxci::utils::debug::print_device_info; use gxci::utils::extract::{extract_callback_img_buf,extract_frame_callback_param}; use gxci::raw::gx_struct::GX_FRAME_CALLBACK_PARAM; use gxci::raw::gx_interface::Result; use gxci::opencv::{core, highgui}; extern "C" fn frame_callback(p_frame_callback_data: *mut GX_FRAME_CALLBACK_PARAM) { let frame_callback_data = extract_frame_callback_param(p_frame_callback_data); let data = extract_callback_img_buf(frame_callback_data); let mat = core::Mat::new_rows_cols_with_data( frame_callback_data.nHeight, frame_callback_data.nWidth, data ).unwrap(); highgui::imshow("Camera Frame", &mat).unwrap(); if highgui::wait_key(10).unwrap() > 0 { highgui::destroy_window("Camera Frame").unwrap(); } } fn main()->Result<()> { gxci_init_default()?; let device_num = gxi_count_devices( 1000)?; println!("Device number: {}", device_num); let base_info = gxi_list_devices()?; for device in &base_info { print_device_info(&device); } gxi_open_device()?; gxi_set_width(4024)?; gxi_set_height(3036)?; gxi_set_gain_auto_continuous()?; // gxi_set_gain(1.0)?; gxi_use_stream(frame_callback)?; gxi_close_device()?; gxci_close()?; Ok(()) }
HAL注意点
- 注意参数设置范围,例如分辨率的设置范围,增益的设置范围等等,超出相机限制范围会导致函数返回错误
- 实际上
control
这个模块就是按照Galaxy Viewer
的设置界面来对应编写了,涵盖了大部分的相机参数设置需求。但是底层都是通过gxi_get_feature_value
和gxi_set_feature_value
函数来设置的,所以如果有特殊的参数设置需求,可以参考具体文档来直接调用这两个函数
RAW代码示例
哦豁,这个还没写
RAW注意点
- 有时间再写
更改日志
0.1
raw
模块从gxi_hako
库迁移过来,然后gxi_hako
库进入deprecated
状态- 初步实现了
hal
模块,用于相机的基本操作
0.2
0.2.0
- 安全化了
raw
模块,现在所有对raw
模块的调用都不需要unsafe
块 - 初步实现了全局错误处理,但是是在
raw
模块的gx_interface
中 - md文档中的图片上云,减少体积
0.2.3
- 重新加入了
solo
feature gxci_init_default()
可以通过默认路径加载GxIAPI.dll,gxci_check_device_handle()
可以检查设备句柄是否有效
0.2.4
gxi_get_device_handle()
用于获取设备句柄- 初始化配置了
config
模块,但是没有实现任何功能
0.3
0.3.0
- 为
hal
添加了check
模块,用于检查常见错误 - 为
hal
添加了config
模块,用于配置相机参数,但是某些FeatureID缺失,导致部分功能未实现 - 为
hal
添加了control
模块,用于控制相机,基于Galaxy Viewer
的侧边栏
0.3.2
gxi_use_stream()
允许使用自定义流回调函数处理图像数据。您可以在hal_use_stream
示例中查看用法
0.3.3
- re-export了
opencv
和imageproc
,您可以通过gxci::opencv
和gxci::imageproc
访问这两个模块
0.3.4
gxi_get_image_as_frame_data()
、gxi_get_image_as_raw()
和gxi_get_image_as_bytes()
提供了使用图像数据的接口,并提供了示例
0.3.5
- 独立的
error.rs
模块和优化的错误处理部分
0.3.6
- 升级到OpenCV 4.10.0,依赖
opencv
和升级到最新的0.93.5
- 基于
mdbook
搭建了中文与英文的文档站,提供了快速上手的教程,网址为https://hakochest.github.io/gxci/
这啥,好像是之前写的,不过还是留着吧
旧版是一个叫做gxi_hako的crate库,里面raw部分和utils部分的不完全实现,现在已经暂时弃用了;
新版也就是这一款gxci,里面包含了raw、HAL、utils三个部分的实现;
截至目前,2024年7月11日23点45分,已经完成了features=["solo"]
部分的HAL库编写,多相机的feature还未实现,等再次闲下来再来更新吧(๑˃ᴗ˂)ﻭ
2024年9月8日21点15分,2.0更新!主要是错误处理和安全化,现在所有的库函数都是安全的了,同时建立了非常规范和健壮的错误处理。此外也更新了所有例子,消除了所有的Warning。
2024年9月19日16点09分,3.0更新!主要是增加了config模块,现在所有的HAL和raw-binding的config模块都已经实现了,你可以调节宽高、增益、白平衡以及一切已实现的相机参数!但是由于inc中部分FeatureID的缺失,所以config模块还有一些函数没有实现。此外,增加了control模块,这是基于Galaxy Viewer的侧边栏常用部分的封装。
路线图
0.4计划更新多相机支持,尝试解决imageproc
的依赖问题
附录
GxIAPI.dll的方法实现情况
- 302 0 0001C020 GXCloseDevice
- 101 1 0001BBC0 GXCloseLib
- 700 2 0001E9E0 GXExportConfigFile
- 707 3 0001EA50 GXExportConfigFileW ?在开发文档里面没介绍这个函数
- 602 4 0001E920 GXFlushEvent
- 505 5 0001E6E0 GXFlushQueue
- 201 6 0001BDE0 GXGetAllDeviceBaseInfo
- 414 7 0001D5F0 GXGetBool
- 419 8 0001E080 GXGetBuffer
- 418 9 0001DF50 GXGetBufferLength
- 205 A 0001BE80 GXGetDeviceIPInfo
- 423 B 0001C0B0 GXGetDevicePersistentIpAddress
- 411 C 0001D3C0 GXGetEnum
- 410 D 0001CF50 GXGetEnumDescription
- 409 E 0001CE20 GXGetEnumEntryNums
- 506 F 0001E970 GXGetEventNumInQueue
- 422 10 0001C1E0 GXGetFeatureName
- 408 11 0001CCF0 GXGetFloat
- 406 12 0001C960 GXGetFloatRange
- 504 13 0001E670 GXGetImage
- 404 14 0001C730 GXGetInt
- 403 15 0001C590 GXGetIntRange
- 204 16 0001BC40 GXGetLastError
- 709 17 0001F370 GXGetOptimalPacketSize (Windows Only)
- 416 18 0001DAA0 GXGetString
- 415 19 0001D820 GXGetStringLength
- 425 1A 0001D970 GXGetStringMaxLength
- 705 1B 0001EEF0 GXGigEForceIp
- 704 1C 0001ECC0 GXGigEIpConfiguration
- 706 1D 0001F170 GXGigEResetDevice
- 701 1E 0001EAC0 GXImportConfigFile
- 708 1F 0001EB40 GXImportConfigFileW ?在开发文档里面没介绍这个函数
- 100 20 0001BB70 GXInitLib
- 400 21 0001C260 GXIsImplemented
- 401 22 0001C370 GXIsReadable
- 402 23 0001C480 GXIsWritable
- 301 24 0001BFB0 GXOpenDevice
- 300 25 0001BF10 GXOpenDeviceByIndex
- 702 26 0001EBC0 GXReadRemoteDevicePort
- 710 27 0001F3E0 GXReadRemoteDevicePortStacked
- 500 28 0001E5B0 GXRegisterCaptureCallback
- 600 29 0001E730 GXRegisterDeviceOfflineCallback
- 603 2A 0001E820 GXRegisterFeatureCallback
- 421 2B 0001E480 GXSendCommand
- 507 2C 0001F100 GXSetAcqusitionBufferNumber
- 413 2D 0001D720 GXSetBool
- 420 2E 0001E350 GXSetBuffer
- 424 2F 0001C160 GXSetDevicePersistentIpAddress
- 412 30 0001D4F0 GXSetEnum
- 407 31 0001CBE0 GXSetFloat
- 405 32 0001C860 GXSetInt
- 417 33 0001DDC0 GXSetString
- 501 34 0001E620 GXUnregisterCaptureCallback
- 601 35 0001E7B0 GXUnregisterDeviceOfflineCallback
- 604 36 0001E8B0 GXUnregisterFeatureCallback
- 206 37 0001BD70 GXUpdateAllDeviceList
- 200 38 0001BD00 GXUpdateDeviceList
- 703 39 0001EC40 GXWriteRemoteDevicePort
- 711 3A 0001F450 GXWriteRemoteDevicePortStacked (Windows Only)
HAL模块函数实现情况
- base
- gxi_check()
- gxci_init()
- gxci_init_default()
- gxci_close()
- device
- gxi_count_devices()
- gxi_list_devices()
- gxi_open_device() // solo feature
- gxi_close_device() // solo feature
- gxi_check_device_handle() // solo feature
- gxi_send_command() // solo feature
- gxi_get_image() // solo feature
- gxi_open_stream() // solo feature
- gxi_open_stream_interval() // solo feature
- gxi_close_stream() // solo feature
- check
- check_status()
- check_status_with_ok_fn()
- check_gx_status()
- check_gx_status_with_ok_fn()
- config
- Here are the HAL functions
- gxi_get_feature_value()
- gxi_set_feature_value()
- Following are the raw-wapper functions
- gxi_get_feature_name()
- gxi_get_int_range()
- gxi_get_int()
- gxi_set_int()
- gxi_get_float_range()
- gxi_get_float()
- gxi_set_float()
- gxi_get_enum_entry_nums()
- gxi_get_enum_description()
- gxi_get_enum()
- gxi_set_enum()
- gxi_get_bool()
- gxi_set_bool()
- gxi_get_string_length()
- gxi_get_string_max_length()
- gxi_get_string()
- gxi_set_string()
- gxi_get_buffer_length()
- gxi_get_buffer()
- gxi_set_buffer()
- control (这里只列出了函数的数量,这部分的列表太长了,所以请查看ControlList markdown)
- device 17
- image_format 27+
- acquisition 46+
- digital_io 0 (But MISSING this module's FEATURE_ID)
- analog 40+
- transport_layer 1
- user_set 10
- chunk_data 8
- event
- todo!()
- network
- todo!()
控制函数列表
这部分的列表太长了,所以我把它分成了这个文档。
某些枚举选择函数将在未来实现,但应该不是在0.3。
并且很多FeatureID都缺失了,所以很多函数都没有实现。
- control
- device
- gxi_get_device_vendor_name()
- gxi_get_device_model_name()
- gxi_get_device_version()
- gxi_get_device_firmware_version()
- gxi_get_device_serial_number()
- gxi_get_factory_setting_version()
- gxi_get_device_user_id()
- gxi_set_device_user_id()
- gxi_get_device_link_selector()
- gxi_set_device_link_selector()
- gxi_get_device_link_throughput_limit_mode()
- gxi_set_device_link_throughput_limit_mode()
- gxi_set_device_link_throughput_limit_mode_off()
- gxi_set_device_link_throughput_limit_mode_on()
- gxi_get_device_link_throughput_limit()
- gxi_set_device_link_throughput_limit()
- gxi_get_device_link_current_throughput()
- todo!()
- image_format
- gxi_get_sensor_width()
- gxi_get_sensor_height()
- gxi_get_max_width()
- gxi_get_max_height()
- gxi_get_width()
- gxi_set_width()
- gxi_get_height()
- gxi_set_height()
- gxi_get_offset_x()
- gxi_set_offset_x()
- gxi_get_offset_y()
- gxi_set_offset_y()
- gxi_get_region_selector()
- gxi_set_region_selector()
- gxi_set_region_selector_region0()
- gxi_get_pixel_format()
- gxi_set_pixel_format()
- gxi_set_pixel_format_bayer_rgb()
- gxi_set_pixel_format_bayer_rg10()
- gxi_get_pixel_size()
- gxi_get_pixel_color_filter()
- gxi_get_test_pattern_generator_selector()
- gxi_set_test_pattern_generator_selector()
- gxi_set_test_pattern_generator_selector_region0()
- gxi_get_test_pattern()
- gxi_set_test_pattern()
- gxi_set_test_pattern_off()
- gxi_set_test_pattern_gray_frame_ramp_moving()
- gxi_set_test_pattern_slant_line_moving()
- gxi_set_test_pattern_slant_line()
- todo!()
- acquisition
- gxi_get_acquisition_mode()
- gxi_set_acquisition_mode()
- gxi_set_acquisition_mode_continuous()
- gxi_acquisition_start()
- gxi_acquisition_stop()
- gxi_get_trigger_selector()
- gxi_set_trigger_selector()
- gxi_set_trigger_selector_frame_start()
- gxi_get_trigger_mode()
- gxi_set_trigger_mode()
- gxi_set_trigger_mode_off()
- gxi_get_trigger_source()
- gxi_set_trigger_source()
- gxi_set_trigger_source_software()
- gxi_set_trigger_source_line0()
- gxi_set_trigger_source_line2()
- gxi_set_trigger_source_line3()
- gxi_get_trigger_activation()
- gxi_set_trigger_activation()
- gxi_set_trigger_activation_falling_edge()
- gxi_set_trigger_activation_rising_edge()
- gxi_get_trigger_delay()
- gxi_set_trigger_delay()
- gxi_get_trigger_filter_rasing_edge()
- gxi_set_trigger_filter_rasing_edge()
- gxi_get_trigger_filter_falling_edge()
- gxi_set_trigger_filter_falling_edge()
- gxi_get_exposure_mode()
- gxi_set_exposure_mode()
- gxi_set_exposure_mode_timed()
- gxi_get_exposure_time()
- gxi_set_exposure_time()
- gxi_get_exposure_auto()
- gxi_set_exposure_auto()
- gxi_set_exposure_auto_off()
- gxi_set_exposure_auto_continuous()
- gxi_set_exposure_auto_once()
- gxi_get_auto_exposure_time_min()
- gxi_set_auto_exposure_time_min()
- gxi_get_auto_exposure_time_max()
- gxi_set_auto_exposure_time_min()
- gxi_get_aaroi_width()
- gxi_set_aaroi_width()
- gxi_get_aaroi_height()
- gxi_set_aaroi_height()
- gxi_get_aaroi_offset_x()
- gxi_set_aaroi_offset_x()
- gxi_get_aaroi_offset_y()
- gxi_set_aaroi_offset_y()
- gxi_get_expected_gray_value()
- gxi_set_expected_gray_value()
- todo!()
- digital_io (But MISSING this module's FEATURE_ID)
- gxi_get_line_selector()
- gxi_set_line_selector()
- gxi_set_line_selector_line0()
- gxi_set_line_selector_line1()
- gxi_set_line_selector_line2()
- gxi_set_line_selector_line3()
- gxi_get_line_mode()
- gxi_set_line_mode()
- gxi_set_line_mode_input()
- gxi_get_line_inverter()
- gxi_set_line_inverter()
- gxi_get_line_source()
- gxi_get_line_status()
- gxi_get_line_status_all()
- gxi_get_user_output_selector()
- gxi_set_user_output_selector()
- gxi_set_user_output_selector_user_output0()
- gxi_set_user_output_selector_user_output1()
- gxi_set_user_output_selector_user_output2()
- gxi_get_user_output_value()
- gxi_set_user_output_value()
- todo!()
- analog
- gxi_get_gain_selector()
- gxi_set_gain_selector()
- gxi_set_gain_selector_analog_all()
- gxi_get_gain()
- gxi_set_gain()
- gxi_get_gain_auto()
- gxi_set_gain_auto()
- gxi_set_gain_auto_off()
- gxi_set_gain_auto_continuous()
- gxi_set_gain_auto_once()
- gxi_get_auto_gain_min()
- gxi_set_auto_gain_min()
- gxi_get_auto_gain_max()
- gxi_set_auto_gain_max()
- gxi_get_balance_ratio_selector()
- gxi_set_balance_ratio_selector()
- gxi_set_balance_ratio_selector_red()
- gxi_set_balance_ratio_selector_green()
- gxi_set_balance_ratio_selector_blue()
- gxi_get_balance_ratio()
- gxi_get_balance_white_auto()
- gxi_set_balance_white_auto()
- gxi_set_balance_white_auto_off()
- gxi_set_balance_white_auto_continuous()
- gxi_set_balance_white_auto_once()
- gxi_get_awb_lamp_house()
- gxi_set_awb_lamp_house()
- gxi_set_awb_lamp_house_adaptive()
- todo!("more variants")
- gxi_get_awbroi_width()
- gxi_set_awbroi_width()
- gxi_get_awbroi_height()
- gxi_set_awbroi_height()
- gxi_get_awbroi_offset_x()
- gxi_set_awbroi_offset_x()
- gxi_get_awbroi_offset_y()
- gxi_set_awbroi_offset_y()
- todo!()
- transport_layer
- gxi_get_payload_size()
- todo!()
- user_set
- gxi_get_user_set_selector()
- gxi_set_user_set_selector()
- gxi_set_user_set_selector_default()
- gxi_set_user_set_selector_user_set0()
- gxi_user_set_load()
- gxi_user_set_save()
- gxi_get_user_set_default()
- gxi_set_user_set_default()
- gxi_set_user_set_default_default()
- gxi_set_user_set_default_user_set0()
- todo!()
- chunk_data
- gxi_get_chunk_mode_active()
- gxi_set_chunk_mode_active()
- gxi_get_chunk_selector()
- gxi_set_chunk_selector()
- gxi_set_chunk_selector_frame_id()
- gxi_set_chunk_selector_timestamp()
- gxi_get_chunk_enable()
- gxi_set_chunk_enable()
- gxi_get_chunk_timestamp()
- todo!()
- device
问题反馈
有任何问题,直接提 issue 即可
如果你想贡献,可以 fork 后提交 pull request
如果你想联系我,可以发邮件到zoneherobrine@gmail.com
或者恰个 QQ:2212540603(好友请求中附带相关信息,比如说是gxci问题~)