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

StackOverflow https://stackoverflow.com/questions/569889

  •  05-09-2019
  •  | 
  •  

Вопрос

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

При попытке загрузить большие растровые изображения (9000 x 9000 пикселей или больше, 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?

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

Это было полезно?

Решение

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

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

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

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

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

        [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.Таким образом, каждый формат файла, который вы хотели бы использовать, добавлял бы другой набор проблем.

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

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

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

Другие советы

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

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

О чем:

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

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

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

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

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

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

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

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top