我正在使用一种名为 rerun 的工具来可视化图像。我认为我应该使我的函数通用,而不是针对不同的数据类型使用多个函数,例如
f32
、u8
等。
这是我的尝试:
use num::Zero;
use opencv::core;
use opencv::prelude::*;
use rerun::external::ndarray;
use shared::Point3d;
trait AllowedTypes {}
impl AllowedTypes for f32 {}
impl AllowedTypes for u8 {}
impl AllowedTypes for u16 {}
pub fn visualize_image<T: AllowedTypes>(
cv_image: &core::Mat,
frame_idx: usize,
rec: &rerun::RecordingStream,
name: &str,
) where
T: Zero + std::clone::Clone + std::fmt::Debug + core::DataType,
{
let mat = cv_image.clone();
let (width, height) = (mat.cols(), mat.rows());
let size = (width * height * mat.channels() as i32) as usize;
let mut image_data = vec![T::zero(); size];
let data_typed = match mat.data_typed::<T>() {
Ok(data_u8) => data_u8,
Err(e) => panic!("Error extracting data: {}", e),
};
image_data.copy_from_slice(data_typed);
let img_array =
match ndarray::Array2::from_shape_vec((height as usize, width as usize), image_data) {
Ok(img_array) => img_array,
Err(e) => panic!("Error creating Array from vec<u8>: {}", e),
};
let rgb_image =
match rerun::Image::from_color_model_and_tensor(rerun::ColorModel::L, img_array.clone()) {
Ok(rgb_image) => rgb_image,
Err(e) => panic!("Could not create image: {}", e),
};
rec.set_time_sequence("frame_idx", frame_idx as i64);
// RGB image
match rec.log(name, &rgb_image) {
Ok(_) => (),
Err(e) => panic!("Could not log image: {}", e),
}
}
错误是:
error[E0277]: the trait bound `rerun::TensorData: From<ArrayBase<OwnedRepr<T>, Dim<[usize; 2]>>>` is not satisfied
--> libs/visualization/src/visualizer.rs:39:15
|
39 | match rerun::Image::from_color_model_and_tensor(rerun::ColorModel::L, img_array.clone()) {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `From<ArrayBase<OwnedRepr<T>, Dim<[usize; 2]>>>` is not implemented for `rerun::TensorData`, which is required by `rerun::TensorData: TryFrom<ArrayBase<OwnedRepr<T>, Dim<[usize; 2]>>>`
|
= help: the following other types implement trait `From<T>`:
<rerun::TensorData as From<&[f32]>>
<rerun::TensorData as From<&[f64]>>
<rerun::TensorData as From<&[i16]>>
<rerun::TensorData as From<&[i32]>>
<rerun::TensorData as From<&[i64]>>
<rerun::TensorData as From<&[i8]>>
<rerun::TensorData as From<&[rerun::external::re_arrow2::types::f16]>>
<rerun::TensorData as From<&[u16]>>
and 14 others
= note: required for `ArrayBase<OwnedRepr<T>, Dim<[usize; 2]>>` to implement `Into<rerun::TensorData>`
= note: required for `rerun::TensorData` to implement `TryFrom<ArrayBase<OwnedRepr<T>, Dim<[usize; 2]>>>`
我以为我可以通过限制 AllowedTypes
使用我感兴趣的数字类型来解决这个问题。但这似乎不起作用。货物.toml
[package]
name = "visualization"
version = "0.1.0"
edition = "2021"
[dependencies]
rerun = "0.19.0"
opencv = "0.92.3"
num = "0.4"
无法通过将 AllowedTypes
限制为
TensorData
最初接受的数字类型来解决,因为这就是 Rust 类型系统的工作方式 - 所有特征边界都在本地检查,这意味着编译器将一无所知类型参数
T
除了它的边界和它们的方法。具体来说,编译器不知道函数端类型参数的“底层类型”是什么;这与 C++ 世界有很大的不同。
TryFrom
会导致失败,人们自然会想要实现
TryFrom<ArrayBase<OwnedRepr<T>, D>> for TensorData
,其中
T: AllowedTypes
模仿
rerun
中预先存在的实现,试图满足
from_color_model_and_tensor
的界限,但是不幸的是,这会失败,因为“孤儿规则”要求:我们无法在外部类型上实现外部特征。这里,
ArrayBase
和 TensorData
都是外部类型,
TryFrom
是一个标准特征,它们都来自我们的板条箱外部。为了解决前一个问题,您可以使用“动态调度”,编译器几乎肯定会对其进行优化,通过将类型信息记录在
Vec<T>
T
表示的枚举,并使用宏有效地扩展
AllowedTypes
的impl内所有可能的类型:
TryFrom
并且,绕过后一个的一种简单方法是定义一个
macro_rules! impl_allowed {
( $( $t:tt ),* ) => {
#[allow(non_camel_case_types)]
pub enum Type {
$( $t ),*
}
$(
impl AllowedTypes for $t {
fn actual_type() -> Type {
Type::$t
}
}
)*
};
}
// ...
// at the end of `try_from` impl
paste! {
let buffer = match T::actual_type() {
$(
Type::$t => unsafe {
let (ptr, len, cap) = vec.into_raw_parts();
let vec = Vec::from_raw_parts(ptr as *mut $t, len, cap);
// use paste to format the typename to match `TensorBuffer`'s variant names
TensorBuffer::[< $t:camel >](vec.into())
}
)*
};
}
,它简单地将任何值包装在其中,impl Local<T>
,并将其作为参数传递。
TryFrom<Local<ArrayBase<OwnedRepr<T>, D>>> for TensorData
完整代码如下:
#[repr(transparent)]
struct Local<T>(pub T);
macro_rules! impl_try_from {
( $( $t:ident ),* ) => {
impl<T: AllowedTypes, D: Dimension> TryFrom<Local<ArrayBase<OwnedRepr<T>, D>>> for TensorData {
type Error = TensorCastError;
fn try_from(value: Local<ArrayBase<OwnedRepr<T>, D>>) -> Result<Self, Self::Error> {
let value = value.0;
// ...
// at call site
let rgb_image = match rerun::Image::from_color_model_and_tensor(
rerun::ColorModel::L,
Local(img_array.clone()),
) {
// ...