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

Как избежать растрового изображения из памяти при работе на очень большом изображении, например: 10.000.000 пикселей и выше

В настоящее время я работаю над системой, загружающей очень большое изображение с минимальной шириной x heigh >= 10.000.000 пикселей.

Но отношение загружаемого изображения пользователя обычно не соответствует нашему соотношению требований, поэтому я должен обрезать его до соответствующего соотношения, но при использовании растрового изображения System.Drawing для его обрезки я всегда получал исключение SytemOutOfMemory.

Я попробовал Bitmap.Clone и Graphic.DrawImage с правильным RectangleF, но не повезло.

Есть ли вообще делать это без получения исключения outofmemory или есть ли альтернативы библиотеке System.Drawing для облегчения этой задачи?

Мой код для загрузки изображения из файла загрузки пользователя:

    var fileBinary = new byte[stream.Length];
    stream.Read(fileBinary, 0, fileBinary.Length);
    stream.Position = 0;
    var fileExtension = Path.GetExtension(fileName);
    using (Image image = Image.FromStream(stream, false, false))
    {
        //validation and check ratio
        CropImage(image, PORTRAIT_RATIO, fileExtension);
     }

И функция CropImage:

//Crop Image from center with predefine ratio
    private byte[] CropImage(Image sourceImg, float ratio, string fileExtension)
        var height = sourceImg.Height;
        var width = sourceImg.Width;

        var isPortrait = width < height;
        RectangleF croppingRec = new RectangleF();

        float positionX = 0;
        float positionY = 0;
        float cropHeight = (float)height;
        float cropWidth = cropHeight * PORTRAIT_RATIO;
        positionY = 0;
        positionX = (width - cropWidth) / 2;

        if (cropWidth > width)
        {
            cropWidth = width;
            cropHeight = cropWidth * (1 / PORTRAIT_RATIO);
            positionX = 0;
            positionY = ((height - cropHeight) / 2);

        }

        croppingRec.Width = cropWidth;
        croppingRec.Height = cropHeight;
        croppingRec.X = positionX;
        croppingRec.Y = positionY;

        Bitmap bmpImage = sourceImg as Bitmap;
        Bitmap bmpCrop = bmpImage.Clone(croppingRec, bmpImage.PixelFormat);
        bmpCrop.Save("D:/test" + fileExtension, ImageFormat.Jpeg);

        ImageConverter converter = new ImageConverter();

        return (byte[])converter.ConvertTo(bmpCrop, typeof(byte[]));
    }

}
4b9b3361

Ответ 1

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

int pixelSize = 3;
int bytesCount = imgHeight * imgWidth * pixelSize;
byte[] byteArray= new byte[bytesCount];

BitmapData bitmapData = bitmap.LockBits(new System.Drawing.Rectangle(0, 0, imgWidth, imgHeight), ImageLockMode.ReadOnly, bitmap.PixelFormat);
Marshal.Copy(bitmapData.Scan0, byteArray, 0, bytesCount);

Каждый пиксель в этом массиве представлен 3 байтами (это зависит от типа растрового изображения). Итак, вы знаете, что длина растровой линии равна 3 * imgWidth. Используя это, вы можете просто перемещаться в массиве байтов и копировать только то, что вам нужно, в новый массив.

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

Bitmap newBitmap = new Bitmap(Width, Height);
BitmapData newBitmapData = b.LockBits(BoundsRect,
                                ImageLockMode.WriteOnly,
                                newBitmap.PixelFormat);
Marshal.Copy(newByteArray, 0, newBitmapData.Scan0, newBytesCount);

Разблокируйте растровые изображения в конце:

newBitmap.UnlockBits(newBitmapData );
bitmap.UnlockBits(bitmapData);

Надеюсь, это поможет. Приветствия.

Ответ 2

Попробуйте использовать graphicsmagick.net библиотеку, а затем используйте метод Crop для MagickImage. Он должен хорошо работать под asp.net и обрабатывать огромные файлы изображений, используя диск для царапин.