我已经在Rust中为相机库编写了一个包装器,该包装器可以命令和操作相机,并使用bindgen将图像保存到文件中。一旦我命令开始曝光(基本上是告诉相机拍摄图像),我就可以使用以下形式的函数获取图像:
pub fn GetQHYCCDSingleFrame(
handle: *mut qhyccd_handle,
w: *mut u32,
...,
imgdata: &mut [u8],) -> u32 //(u32 is a retval)
在c++中,这个函数是:
uint32_t STDCALL GetQHYCCDSingleFrame(qhyccd_handle: *handle, ..., uint8_t *imgdata)
在c++中,我可以传入一个形式为imgdata = new unsigned char[length_buffer]
的缓冲区,该函数将用来自相机的图像数据填充缓冲区。
在Rust中,类似地,我可以以Vec:let mut buffer: Vec<u8> = Vec::with_capacity(length_buffer)
的形式传入缓冲区。
目前,我构建代码的方式是,有一个主结构体,设置如图像的宽度和高度,相机手柄,和其他,包括图像缓冲区。该结构体已初始化为mut
,如:
let mut main_settings = MainSettings {
width: 9600,
...,
buffer: Vec::with_capacity(length_buffer),
}
我写了一个单独的函数,它将主结构体作为参数,并调用GetQHYCCDSingleFrame函数:
fn grab_image(main_settings: &mut MainSettings) {
let retval = unsafe { GetQHYCCDSingleFrame(main_settings.cam_handle, ..., &mut main_settings.image_buffer) };
}
在调用这个函数之后,如果我检查main_settings.image_buffer的长度和容量:
println!("Elements in buffer are {}, capacity of buffer is {}.", main_settings.image_buffer.len(), main_settings.image_buffer.capacity());
的长度为0,而buffer_length为容量。类似地,输出任何索引(如main_settings.image_buffer[0]
或1)将导致紧急退出,显示len is 0
。
这会让我认为GetQHYCCDSingleFrame
代码不能正常工作,但是,当我使用fitsio
和hdu.write_region
保存image_buffer到文件时(这里链接的fitsio文档),我使用:
let ranges = [&(x_start..(x_start + roi_width)), &(y_start..(y_start+roi_height))];
hdu.write_region(&mut fits_file, &ranges, &main_settings.image_buffer).expect("Could not write to fits file");
这将一个实际的图像保存到具有合适大小的文件中,并且是一个完美的图像(如果我使用c++程序,它看起来正是这样)。然而,当我试图打印缓冲区时,由于某种原因是空的,然而hdu.write_region
代码能够以某种方式访问数据。
目前,我的(不太好的)解决方法是创建另一个向量,从保存的文件中读取数据并保存到缓冲区中,然后缓冲区中有正确数量的元素:
main_settings.new_buffer = hdu.read_region(&mut fits_file, &ranges).expect("Couldn't read fits file");
为什么我不能访问原始缓冲区,为什么它报告长度0,当hdu.write_region
函数可以从某个地方访问数据?它究竟从哪里访问数据,我怎样才能正确地访问它?我对借用和引用有点陌生,所以我相信我可能在借用/引用缓冲区时做错了什么,或者是别的什么?
很抱歉说来话长,但细节可能对这里的一切都很重要。谢谢!
首先,您需要知道Vec<u8>
和&mut [u8]
与C或c++的uint8_t *
并不完全相同。主要区别在于Vec<u8>
和&mut [u8]
保存了数组或切片的大小,而uint8_t *
没有。与C/c++指针等价的Rust指针是原始指针,如*mut [u8]
。构建原始指针是安全的,但需要使用unsafe
。然而,即使它们是不同的类型,像&mut [u8]
这样的智能指针也可以毫无问题地转换为原始指针。
其次,Vec
的容量与它的大小不同。实际上,为了获得良好的性能,Vec
会分配比您使用的更多的内存,以避免在每个添加到vector中的新元素上重新分配内存。然而,长度是所用部件的尺寸。在您的示例中,您要求Vec
分配一个长度为length_buffer
的堆空间,但是您没有告诉它们考虑使用任何已分配的空间,因此初始长度为0。由于c++不知道Vec
并且只使用原始指针,因此它无法改变写入Vec
内部的长度,它保持为0。这就是恐慌。
要解决这个问题,我看到了多个解决方案:
-
将
Vec::with_capacity(length_buffer)
更改为vec![0; length_buffer]
,明确要求从一开始length_buffer
的长度 -
使用
unsafe
代码显式设置Vec
的长度,而不触及里面的内容(使用Vec::from_raw_parts
)。这个可能比第一个解决方案快,但我不确定。 -
使用
Box<[u8; length_buffer]>
,它类似于Vec
,但没有重新分配,其长度是容量 -
如果您的
length_buffer
在编译时是恒定的,使用[u8; length_buffer]
将更有效,因为不需要分配,但它也有缺点,正如您可能知道的