2010-04-11 1 views
3

В настоящее время я участвую в проекте, где у меня очень большие объемы изображений. Эти объемы должны обрабатываться очень быстро (добавление, вычитание, пороговое значение и т. Д.). Кроме того, большая часть тома настолько велика, что они не помещаются в память системы. По этой причине я создал абстрактный класс томов (VoxelVolume), который размещает данные тома и изображения и перегружает операторов, чтобы можно было выполнять обычные математические операции над томами. Тем самым открылось еще два вопроса, которые я буду помещать в stackoverflow в два дополнительных потока.C# Общие массивы и математические операции на нем

Вот мой первый вопрос. Мой том реализован таким образом, что он может содержать только данные массива float, но большинство содержащихся данных - из источника изображения 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 (мне понравилось, если я ошибаюсь), что я привязан к определенной платформе (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# редактора. Является ли такое решение показанным выше понятным? Пожалуйста, не стесняйтесь спрашивать, допустил ли я ошибку или описал некоторые вещи не полностью.

Спасибо за вашу помощь
Martin

+0

Вы должны удалить этот вопрос и запросить его как не-Community Wiki. (Сначала нажмите «править» и скопируйте источник) – SLaks

+1

Это не должно быть CW, но нет необходимости повторно его просить. –

ответ

1

Это кажется (осложненная) версия «почему мы не имеем INumerical интерфейса».

Короткий ответ на последний вопрос: Нет, переход к небезопасным указателям не является решением, компилятор все еще не может определить + в ((T1)*ptrA + (T2)*ptrB)).

+0

Привет, Хенк. Спасибо за ваш ответ. Это то, что я тоже выяснил. Я надеялся, что для этого может быть решение. – msedi

1

Если у вас есть только несколько типов, как float и UInt32, обеспечивает все необходимые функции преобразования, например, от VoxelVolume<UInt32> до VoxelVolume<float> и делать математику на VoxelVolume<float>. Это должно быть достаточно быстро для большинства практических случаев. Вы даже можете предоставить общую функцию преобразования от VoxelVolume<T1> до VoxelVolume<T2> (если T1 конвертируется в T2). С другой стороны, если вам действительно нужен

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

с преобразованием типа из T1 в T2 для каждого элемента массива, что мешает вам писать такие операторы?

+0

Здравствуйте, Док Браун, похоже, ваше решение поможет мне в правильном направлении. Я подумаю об этом. Обычно, если я хочу создать такие вещи, где я могу использовать каждый встроенный тип данных, доступный на C#, мне показалось разумным, что я создаю перегрузки для каждого возможного типа данных, но я думаю, что это правдивые вещи вплоть до нескольких типов данных поможет много. – msedi

1

Импорт, являющийся членом универсального класса, вероятно, также не обязательно должен быть общим. Если это так, вы определенно не должны использовать одно и то же имя T как для общего параметра для класса, так и для общего параметра для функции.

То, что вы, вероятно, глядя в Марк Gravell-х Generic Operators

Что касается ваших вопросов о C++/CLI, да это может помочь, если вы используете шаблоны вместо дженериков, потому что тогда все возможные значения для typename T управляются при компиляции время, и компилятор ищет операторов для каждого. Кроме того, вы можете использовать /clr:pure или /clr:safe, и в этом случае ваш код будет MSIL, runnable на AnyCPU, как и C#.

+0

Привет, Бен, спасибо за ваши комментарии. Вы абсолютно правы. Я не собирался использовать тот же Т для оператора. Это был только пример и должен показать сложность того, что я хотел сделать. – msedi

+0

Хорошая статья Марка, но если вы не получите безопасность типа компиляции, мне кажется, что проблему так же легко решить с помощью 'dynamic'. – CompuChip

+0

@CompuChip: ключевое слово 'dynamic' было недоступно во время этого вопроса. –

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. Поэтому преобразуйте массив байтов в массив поплавков, а затем добавьте их, вы потеряете только некоторую память.
+0

Привет Има, вы правы, это был путь к долго. Я хотел быть настолько подробным, насколько это возможно, чтобы показать, что я хочу. Вы, конечно, правы, что я могу использовать ценности. Но для меня казалось, что я также помогу мне в будущих проектах иметь более общий способ выполнения таких операций. – msedi