4

我目前正在参与一个图像量非常大的项目。这些卷必须非常快速地处理(加法、减法、阈值处理等)。此外,大多数卷都非常大,以至于它们无法放入系统的内存中。出于这个原因,我创建了一个抽象体积类 (VoxelVolume),它承载体积和图像数据并重载运算符,以便可以对体积执行常规数学运算。因此,另外两个问题打开了,我将把它们放入 stackoverflow 到另外两个线程中。

这是我的第一个问题。我的卷以只能包含浮点数组数据的方式实现,但大部分包含的数据来自 UInt16 图像源。只有对卷的操作才能创建浮点数组图像。

当我开始实现这样一个卷时,该类如下所示:

public abstract class VoxelVolume<T> 
{
...
}

但后来我意识到重载运算符或返回值会变得更加复杂。一个例子是:

public abstract class VoxelVolume<T>
{
...
    public static VoxelVolume<T> Import<T>(param string[] files) 
    {
    } 
}

还添加两个重载运算符会更复杂:

...
public static VoxelVolume<T> operator+(VoxelVolume<T> A, VoxelVolume<T> B)
{
...
}    

假设我可以克服上述问题,但我有不同类型的包含图像数据的数组。由于我已将卷中的类型固定为浮动,所以没有问题,并且在添加两个图像卷数组的内容时可以执行不安全的操作。我在这里阅读了一些主题并浏览了网络,但没有找到真正好的解释当我想快速添加两个不同类型的数组时该怎么做。不幸的是,不可能对泛型进行所有数学运算,因为 C# 无法计算基础数据类型的大小。当然,可以通过使用 C++/CLR 来解决这个问题,但目前我所做的一切都是在 32 位和 64 位下运行而无需做任何事情。切换到 C++/CLR 在我看来(如果我错了,请纠正我)我' m 绑定到某个平台(32 位),当我让应用程序在另一个平台(64 位)上运行时,我必须编译两个程序集。这是真的?

所以很快问:如何快速添加两个不同类型的两个数组。是不是C#的开发者没有想到这一点。切换到另一种语言(C# -> C++)似乎不是一种选择。

我意识到只需执行此操作

float []A = new float[]{1,2,3};  
byte  []B = new byte[]{1,2,3};

float []C = A+B;

是不可能的和不必要的,尽管如果它可以工作会很好。我正在尝试的解决方案如下:

public static class ArrayExt
{
    public static unsafe TResult[] Add<T1, T2, TResult>(T1 []A, T2 []B)
    {
       // Assume the length of both arrays is equal
       TResult[] result = new TResult[A.Length];

       GCHandle h1 = GCHandle.Alloc (A, Pinned);
       GCHandle h2 = GCHandle.Alloc (B, Pinned);
       GCHandle hR = GCHandle.Alloc (C, Pinned);

       void *ptrA = h1.ToPointer();
       void *ptrB = h2.ToPointer();
       void *ptrR = hR.ToPointer();

       for (int i=0; i<A.Length; i++)
       {
          *((TResult *)ptrR) = (TResult *)((T1)*ptrA + (T2)*ptrB));
       }

       h1.Free();
       h2.Free();
       hR.Free();

       return result;
    }
}

如果上面的代码不是很正确,请原谅,我没有使用 C# 编辑器编写它。上面显示的这种解决方案是否可行?请随时询问我是否犯了错误或描述了一些不完整的内容。

谢谢你的帮助
马丁

4

4 回答 4

1

作为泛型类的成员,导入可能也不需要本身是泛型的。如果是这样,您绝对不应该T对类的泛型参数和函数的泛型参数使用相同的名称。

您可能正在寻找的是 Marc Gravell 的通用运算符

至于您关于 C++/CLI 的问题,是的,如果您使用模板而不是泛型,这可能会有所帮助,因为所有可能的值typename T都在编译时控制,编译器会查找每个的运算符。此外,您可以使用/clr:pure/clr:safe在这种情况下您的代码将是 MSIL,AnyCPU可以像 C# 一样运行。

于 2010-04-11T16:48:44.313 回答
1

这似乎是“为什么我们没有 Inumerical 接口”的(复杂)版本。

最后一个问题的简短回答是:不,不安全的指针不是解决方案,编译器仍然无法找出+in ((T1)*ptrA + (T2)*ptrB))

于 2010-04-11T16:18:59.950 回答
1

如果您只有几个类型,例如floatand UInt32,请提供所有需要的转换函数,例如 from VoxelVolume<UInt32>toVoxelVolume<float>和对 进行数学运算VoxelVolume<float>。对于大多数实际情况,这应该足够快。您甚至可以提供从VoxelVolume<T1>to的通用转换函数VoxelVolume<T2>(如果 T1 可转换为 T2)。另一方面,如果你真的需要一个

public static VoxelVolume<T2> operator+(VoxelVolume<T1> A,VoxelVolume<T2> B)

T1对于T2每个数组元素的类型转换,是什么阻碍了您编写此类运算符?

于 2010-04-11T16:36:15.580 回答
0

诚然,我没有阅读整个问题(它有点太长了),但是:

  1. VoxelVolume<T> where T : ISummand ... T a; a.Add(b)
  2. static float Sum (this VoxelVolume<float> self, VoxelVolume<float> other) {...}
  3. 要以任何有意义的方式将浮点数添加到字节,您必须将字节转换为浮点数。因此,将字节数组转换为浮点数组然后添加它们,您只会丢失一些内存。
于 2010-04-11T16:49:26.327 回答