Как использовать большие растровые изображения в .NET? - PullRequest
12 голосов
/ 20 февраля 2009

Я пытаюсь написать облегченное приложение для просмотра изображений. Однако в .NET имеются ограничения системной памяти.

При попытке загрузить большие растровые изображения ( 9000 x 9000 px или более, 24-разрядные) я получаю исключение System.OutOfMemoryException. Это на ПК с Windows 2000 с 2 ГБ оперативной памяти (из которых 1,3 ГБ израсходовано). Загрузка файлов также занимает много времени.

Следующий код генерирует эту ошибку:

Image image = new Bitmap(filename);
using (Graphics gfx = this.CreateGraphics())
{
    gfx.DrawImage(image, new Point(0, 0));
}

Как выглядит этот код:

Stream stream = (Stream)File.OpenRead(filename);
Image image = Image.FromStream(stream, false, false);
using (Graphics gfx = this.CreateGraphics())
{
    gfx.DrawImage(image, new Rectangle(0, 0, 100, 100), 4000, 4000, 100, 100, GraphicsUnit.Pixel);
}

Также достаточно сделать следующее:

Bitmap bitmap = new Bitmap(filename);
IntPtr handle = bitmap.GetHbitmap();

Последний код был предназначен для использования с GDI. Исследуя это, я обнаружил, что это на самом деле проблема с памятью, когда .NET пытается выделить в два раза больше, чем необходимо в одном непрерывном блоке памяти.

http://bytes.com/groups/net-c/279493-drawing-large-bitmaps

Из других приложений (Internet Explorer, MS Paint и т. Д.) Я знаю, что можно открывать большие изображения довольно быстро. У меня вопрос, как использовать большие растровые изображения с .NET?

Есть ли способ их потоковой передачи или загрузка без памяти?

Ответы [ 7 ]

8 голосов
/ 20 февраля 2009

Это вопрос из двух частей. Первый вопрос заключается в том, как можно загружать большие изображения без нехватки памяти (1), а второй - об улучшении производительности загрузки (2).

(1) Создайте приложение, такое как Photoshop, где у вас есть возможность работать с огромными изображениями, потребляющими гигабайты в файловой системе. Сохранение всего изображения в памяти и все еще достаточно свободной памяти для выполнения операций (фильтры, обработка изображений и т. Д. Или даже просто добавление слоев) было бы невозможным в большинстве систем (даже в системах 8 ГБ x64).

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

(2) Performande можно улучшить (довольно много), написав пользовательские загрузчики для каждого формата файла. Это требует от вас чтения заголовков файлов и структуры форматов файлов, с которыми вы хотите работать. Как только вы получили его, это не **** что сложно, но это не так тривиально, как выполнение вызова метода.

Например, вы можете зайти в Google для FastBitmap, чтобы увидеть примеры того, как вы можете очень быстро загрузить файл растрового изображения (BMP), включая декодирование заголовка растрового изображения. Это включило pInvoke, и чтобы дать вам представление о том, с чем вы столкнулись, вам нужно будет определить растровые структуры, такие как

        [StructLayout(LayoutKind.Sequential, Pack = 1)]
        public struct BITMAPFILEHEADER
        {
            public Int16 bfType;
            public Int32 bfSize;
            public Int16 bfReserved1;
            public Int16 bfReserved2;
            public Int32 bfOffBits;
        }

        [StructLayout(LayoutKind.Sequential)]
        public struct BITMAPINFO
        {
            public BITMAPINFOHEADER bmiHeader;
            public RGBQUAD bmiColors;
        }

        [StructLayout(LayoutKind.Sequential)]
        public struct BITMAPINFOHEADER
        {
            public uint biSize;
            public int biWidth;
            public int biHeight;
            public ushort biPlanes;
            public ushort biBitCount;
            public BitmapCompression biCompression;
            public uint biSizeImage;
            public int biXPelsPerMeter;
            public int biYPelsPerMeter;
            public uint biClrUsed;
            public uint biClrImportant;
        }

Возможно, работа с созданием DIB (http://www.herdsoft.com/ti/davincie/imex3j8i.htm) и странностей, таких как данные, которые хранятся "вверх ногами" в растровом изображении, которые вы должны принять во внимание, или вы увидите зеркальное отображение при открытии: - )

Теперь это только для растровых изображений. Скажем, вы хотите сделать PNG, тогда вам нужно будет делать аналогичные вещи, но декодировать заголовок PNG, что в простейшей форме не так сложно, но если вы хотите получить полную поддержку спецификации PNG, то вас ждет увлекательная поездка: - )

PNG отличается от растрового изображения тем, что использует формат на основе чанка, где у него есть «заголовки», которые можно найти, чтобы найти разные данные. Пример некоторых фрагментов, которые я использовал во время игры с форматом, был

    string[] chunks =  
new string[] {"?PNG", "IHDR","PLTE","IDAT","IEND","tRNS",
"cHRM","gAMA","iCCP","sBIT","sRGB","tEXt","zTXt","iTXt",
"bKGD","hIST","pHYs","sPLT","tIME"};

Вам также нужно будет узнать о контрольных суммах Adler32 для файлов PNG. Таким образом, каждый формат файла, который вы захотите сделать, будет добавлять свой набор проблем.

Я действительно хотел бы привести более полные примеры исходного кода в своем ответе, но это сложная тема, и, честно говоря, я сам не реализовал обмен, поэтому я не смог бы дать слишком много твердых советов по этому вопросу. ,

Короткий ответ: возможности обработки изображений в BCL не так уж и высоки. Средний ответ - попытаться выяснить, не написал ли кто-нибудь библиотеку изображений, которая могла бы вам помочь, а длинный ответ - поднять рукава и самостоятельно написать суть вашего приложения.

Раз ты знаешь меня в реальной жизни, то знаешь, где меня найти;)

3 голосов
/ 20 февраля 2009

Чтобы получить действительно исчерпывающий ответ, я бы использовал Reflector, чтобы взглянуть на исходный код Paint.NET (http://www.getpaint.net/); продвинутая программа для редактирования графики, написанная на C #.

(как указано в комментарии, Paint.NET раньше был открытым исходным кодом, но теперь закрытым).

0 голосов
/ 23 февраля 2009

Одна вещь просто поразила меня. Вы рисуете все изображение, а не только видимую часть? Вы не должны рисовать большую часть изображения, чем показываете в своем приложении, используйте параметры x, y, width и heigth, чтобы ограничить нарисованную область.

0 голосов
/ 20 февраля 2009

То есть вы говорите, что не загрузка растрового изображения, а рендеринг вызывает нехватку памяти?

Если это так, можете ли вы использовать Bitmap.LockBits, чтобы получить доступ к пикселям и самостоятельно написать базовый преобразователь изображения?

0 голосов
/ 20 февраля 2009

Можете ли вы создать новое, пустое растровое изображение с такими же размерами и глубиной цвета? Если это так, вы, по крайней мере, знаете, что ваша среда может обрабатывать изображение. Тогда проблема заключается в подсистеме загрузки изображений, как, конечно, может указывать ваша ссылка.

Полагаю, вы могли бы написать свои собственные загрузчики растровых изображений, но для нетривиальных форматов это большая работа, поэтому я бы не советовал.

Возможно, есть доступные библиотеки замены, которые решают эти проблемы со стандартными загрузчиками?

0 голосов
/ 20 февраля 2009

просто быстрое решение, у меня была та же проблема, я создал второй экземпляр растрового изображения и передал растровое изображение в конструктор.

0 голосов
/ 20 февраля 2009

А как же:

Image image = new Bitmap(filename);
using (Graphics gfx = Graphics.FromImage(image))
{    
// Stuff
}
...