Подтвердить что ты не робот

Как использовать большие растровые изображения в .NET?

Я пытаюсь написать небольшое приложение для просмотра изображений. Однако с .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?

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

4b9b3361

Ответ 1

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

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

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

(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 не так горячие. Средний ответ заключался бы в том, чтобы попытаться найти, если кто-то написал библиотеку изображений, которая могла бы вам помочь, и длинный ответ заключался бы в том, чтобы подтянуть рукава и самостоятельно написать ядро ​​вашего приложения.

Поскольку вы знаете меня в реальной жизни, вы знаете, где меня найти;)

Ответ 2

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

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

Ответ 3

Как насчет:

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

Ответ 4

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

Ответ 5

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

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

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

Ответ 6

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

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

Ответ 7

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