4

私は現在、非常に大量の画像を扱うプロジェクトに携わっています。このボリュームは非常に高速に処理する必要があります (加算、減算、しきい値処理など)。さらに、ほとんどのボリュームは非常に大きいため、システムのメモリに収まりません。そのため、ボリュームとイメージ データをホストし、オペレータをオーバーロードする抽象ボリューム クラス (VoxelVolume) を作成して、ボリュームに対して通常の数学演算を実行できるようにしました。これにより、さらに 2 つの質問が開かれ、stackoverflow に追加の 2 つのスレッドに入れます。

これが私の最初の質問です。私のボリュームは、float 配列データのみを含むことができるように実装されていますが、含まれているデータのほとんどは UInt16 イメージ ソースからのものです。float 配列イメージを作成できるのは、ボリュームに対する操作のみです。

このようなボリュームの実装を開始したとき、クラスは次のようになりました。

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

しかしその後、演算子や戻り値をオーバーロードするとさらに複雑になることに気付きました。例は次のとおりです。

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

また、2 つのオーバーロード演算子を追加すると、より複雑になります。

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

上記の問題を克服できると仮定しましょう。ただし、画像データを含むさまざまなタイプの配列があります。ボリュームのタイプを float に修正したので問題ありません。2 つのイメージ ボリューム配列の内容を追加するときに安全でない操作を行うことができます。ここでいくつかのスレッドを読み、Web を見回しましたが、異なるタイプの 2 つの配列をすばやく追加したい場合に何をすべきかについての適切な説明が見つかりませんでした。残念ながら、C# は基礎となるデータ型のサイズを計算できないため、ジェネリックに対するすべての数学演算は不可能です。もちろん、C++/CLR を使用してこの問題を回避することもできますが、現在のところ、これまでに行ったことはすべて、何もしなくても 32 ビットと 64 ビットで実行できます。C++/CLR への切り替えは、私には次のように思えました (間違っていたら訂正してください)。m は特定のプラットフォーム (32 ビット) にバインドされており、アプリケーションを別のプラットフォーム (64 ビット) で実行するには、2 つのアセンブリをコンパイルする必要があります。これは本当ですか?

簡単に尋ねたところ、2 つの異なるタイプの 2 つの配列をすばやく追加するにはどうすればよいでしょうか。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

floatやのような型が数個しかない場合は、必要なすべての変換関数を提供します。UInt32たとえば、 fromや do the math on などです。これは、ほとんどの実際のケースでは十分に高速です。からへの一般的な変換関数を提供することもできます(T1 が T2 に変換可能な場合)。一方、本当に必要な場合は、VoxelVolume<UInt32>VoxelVolume<float>VoxelVolume<float>VoxelVolume<T1>VoxelVolume<T2>

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

配列要素ごとに からT1への型変換を行う場合、そのような演算子を書くのを妨げるものは何ですか?T2

于 2010-04-11T16:36:15.580 に答える
1

これは、「INumerical インターフェイスがない理由」の (複雑な) バージョンのようです。

最後の質問に対する短い答えは次のとおりです。いいえ、安全でないポインターに行くことは解決策ではありません。コンパイラーはまだ+in を 理解できません((T1)*ptrA + (T2)*ptrB))

于 2010-04-11T16:18:59.950 に答える
1

ジェネリック クラスのメンバーであるインポートは、おそらくそれ自体もジェネリックである必要はありません。Tその場合、クラスのジェネリック パラメーターと関数のジェネリック パラメーターの両方に同じ名前を使用しないでください。

おそらく探しているのは、Marc Gravell のGeneric Operatorsです。

C++/CLI に関する質問については、はい、ジェネリックの代わりにテンプレートを使用するtypename Tと、コンパイル時にすべての可能な値が制御され、コンパイラがそれぞれの演算子を検索するため、これが役立ちます。また、/clr:pureor/clr:safeを使用することもできます。この場合、コードは MSIL になり、AnyCPUC# と同じように実行できます。

于 2010-04-11T16:48:44.313 に答える
0

確かに、私は質問全体を読んでいませんでした (かなり長すぎます) が、:

  1. VoxelVolume<T> where T : ISummand ... T a; a.Add(b)
  2. static float Sum (this VoxelVolume<float> self, VoxelVolume<float> other) {...}
  3. 意味のある意味で float を byte に追加するには、byte を float に変換する必要があります。したがって、バイト配列をフロート配列に変換してから追加すると、メモリがいくらか失われるだけです。
于 2010-04-11T16:49:26.327 に答える