2010-04-11 6 views
3

Je suis actuellement impliqué dans un projet où j'ai de très grands volumes d'images. Ces volumes doivent être traités très rapidement (ajout, soustraction, seuillage, etc.). De plus, la plus grande partie du volume est si grande que les événements ne rentrent pas dans la mémoire du système. Pour cette raison, j'ai créé une classe de volume abstrait (VoxelVolume) qui héberge les données de volume et d'image et surcharge les opérateurs de sorte qu'il est possible d'effectuer les opérations mathématiques normales sur les volumes. Par la suite, deux autres questions s'ouvriront que je mettrai dans stackoverflow dans deux threads supplémentaires.C# Generic Arrays et opérations mathématiques

Voici ma première question. Mon volume est implémenté d'une manière qui ne peut contenir que des données de type float, mais la plupart des données contenues proviennent d'une source d'image UInt16. Seules les opérations sur le volume peuvent créer des images flottantes.

Lorsque j'ai commencé à mettre en œuvre un tel volume de la classe ressemblait à ce qui suit:

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

mais je réalise que la surcharge des opérateurs ou de retour des valeurs obtiendraient plus compliquées. Un exemple serait:

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

également l'ajout de deux opérateurs surcharge seraient plus compliquées:

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

Supposons que je peux surmonter les problèmes décrits ci-dessus, néanmoins, j'ai différents types de tableaux qui contiennent l'image Les données. Depuis que j'ai fixé mon type dans les volumes pour flotter le n'est pas un problème et je peux faire une opération dangereuse lors de l'ajout du contenu de deux tableaux de volume d'image. J'ai lu quelques fils ici et ai jeté un coup d'oeil autour du Web, mais n'ai trouvé aucune vraie bonne explication de ce que faire quand je veux ajouter deux rangées de types différents d'une manière rapide. Malheureusement, chaque opération mathématique sur les génériques n'est pas possible, car C# n'est pas capable de calculer la taille du type de données sous-jacent. Bien sûr, il pourrait contourner ce problème en utilisant C++/CLR, mais actuellement tout ce que j'ai fait jusqu'à présent, fonctionne en 32 bits et 64 bits sans avoir à faire une chose. Passer en C++/CLR m'a semblé (content de me corriger si je me trompe) que je suis lié à une certaine plate-forme (32bits) et je dois compiler deux assemblées quand je laisse l'application tourner sur une autre plateforme (64bits). Est-ce vrai?

Donc demandé brièvement: Comment est-il possible d'ajouter deux tableaux de deux types différents d'une manière rapide. Est-il vrai que les développeurs de C# n'y ont pas pensé. Passer à une autre langue (C# -> C++) ne semble pas être une option.

Je me rends compte que la réalisation de cette opération simplement

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

float []C = A+B; 

est impossible et inutile, bien que ce serait bien si cela fonctionnerait. Ma solution que je tentais suivais:

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; 
    } 
} 

S'il vous plaît excuser si le code ci-dessus est pas tout à fait correct, je l'ai écrit sans utiliser un éditeur C#. Est-ce qu'une telle solution est montrée ci-dessus pensable? N'hésitez pas à demander si j'ai fait une erreur ou décrit certaines choses de manière incomplète.

Merci pour votre aide
Martin

+0

Vous devriez supprimer cette question et la redemander comme wiki non communautaire. (D'abord, cliquez sur 'edit' et copiez la source) – SLaks

+1

Il n'a pas eu à être CW mais il n'y a pas besoin de re-demander. –

Répondre

1

Cela semble une version (compliquée) du "pourquoi ne nous avons pas une interface INumerical".

La réponse courte à la dernière question est: Non, aller à des pointeurs dangereux n'est pas une solution, le compilateur ne peut toujours pas comprendre le + en ((T1)*ptrA + (T2)*ptrB)).

+0

Bonjour Henk. Merci pour votre réponse. C'est ce que j'ai compris aussi. J'espérais qu'il pourrait y avoir une solution pour cela. – msedi

1

Si vous avez seulement quelques types comme float et UInt32, toutes les fonctions de fournir conversion nécessaires, par exemple VoxelVolume<UInt32>-VoxelVolume<float> et faire le calcul sur VoxelVolume<float>. Cela devrait être assez rapide pour la plupart des cas pratiques. Vous pouvez même fournir une fonction de conversion générique de VoxelVolume<T1> à VoxelVolume<T2> (si T1 est convertible en T2). D'autre part, si vous avez vraiment besoin d'un

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

avec conversion de type T1-T2 pour chaque élément de tableau, ce qui vous empêche d'écrire ces opérateurs?

+0

Bonjour Doc Brown, il semble que votre solution va me mettre dans la bonne direction. J'y penserai. Normalement, si je veux créer de telles choses où je peux utiliser chaque type de données intégré disponible en C#, il m'a semblé raisonnable de créer les surcharges pour chaque type de données, mais je pense qu'il est juste de ne décomposer que quelques types de données. aiderait beaucoup. – msedi

1

L'importation, étant membre d'une classe générique, n'a probablement pas besoin d'être elle-même générique. Si c'est le cas, vous ne devriez certainement pas utiliser le même nom T pour le paramètre générique à la classe et le paramètre générique à la fonction.

Qu'est-ce que vous êtes probablement à la recherche est Marc Gravell de Generic Operators

Quant à vos questions sur C++/CLI, oui cela pourrait aider si vous utilisez des modèles au lieu de génériques, car toutes les valeurs possibles pour typename T sont contrôlés à la compilation temps et le compilateur recherche les opérateurs pour chacun. En outre, vous pouvez utiliser /clr:pure ou /clr:safe, auquel cas votre code sera MSIL, exécutable sur AnyCPU comme C#.

+0

Bonjour Ben, merci pour vos commentaires. Tu as tout à fait raison. Je n'avais pas l'intention d'utiliser le même T pour l'opérateur. Ce n'était qu'un exemple et devrait montrer la complexité de ce que je voulais faire. – msedi

+0

Bel article de Marc, mais si vous n'obtenez pas la sécurité du type à la compilation de toute façon, il me semble que le problème est aussi facile à résoudre en utilisant 'dynamic'. – CompuChip

+0

@CompuChip: Le mot clé 'dynamic' n'était pas disponible au moment de cette question. –

0

Certes, je ne l'ai pas lu toute la question (c'est tout à fait un peu trop long), mais:

  1. VoxelVolume<T> where T : ISummand ... T a; a.Add(b)
  2. static float Sum (this VoxelVolume<float> self, VoxelVolume<float> other) {...}
  3. Pour ajouter float à l'octet dans un sens significatif vous avoir à convertir octet pour flotter. Convertissez donc un tableau d'octets en un tableau de flottants, puis ajoutez-les, vous perdrez seulement de la mémoire.
+0

Salut Ima, tu as raison, c'était trop long. Je voulais être aussi détaillé que possible pour montrer ce que je voulais faire. Vous avez raison bien sûr que je peux jeter les valeurs. Mais pour moi, il m'a semblé que je pourrais également m'aider pour de futurs projets d'avoir une manière plus générique d'effectuer de telles opérations. – msedi