Сбои, OutOfMemoryException, общая ошибка при использовании System.Drawing

Я выполняю задание cron на C#, которое принимает 200 000 изображений и конвертирует их в 1-битное изображение. Во время выполнения этого задания иногда process crashes (хотя у меня есть глобальная попытка catch), иногда для некоторых изображений (не для всех) он выдает OutOfMemoryException, а иногда для некоторых изображений он выдает A generic error occurred in GDI.

        int pageSize = 1000;
        for (int pageNumber = 0; pageNumber < 200; pageNumber++)
        {
            var imageUrls = allIMageUrls.Skip(pageSize * pageNumber).Take(pageSize).ToList();
            var counter = 0;
            var total = imageUrls.Count;
            Logger.Log($"Page Number : {pageNumber}");
            var failedImageUrls = new System.Collections.Concurrent.ConcurrentBag<string>();
            Parallel.ForEach(imageUrls, imageUrl =>
            {
                try
                {
                    Interlocked.Increment(ref counter);
                    var image = _httpService.DownloadImage(imageUrl);
                    if (image != null && image.Length > 0)
                    {
                        var oneBitImage = ConvertToOnebitFaxGroup4(contract);
                        _httpService.UploadImage(image, oneBitImage);
                        oneBitImage = null;
                        image  = null;
                    }
                }
                catch (Exception ex)
                {
                    failedImageUrls.Add(imageUrl);                        
                    Logger.Log(ex);
                }
            });

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

 public static class ImageProcessor
 {

    static ImageCodecInfo _codecInfo;
    static EncoderParameters _encoderParameters;

    static ImageProcessor()
    {
        foreach (var codec in ImageCodecInfo.GetImageEncoders())
        {
            if (codec.MimeType == "image/tiff")
            {
                _codecInfo = codec;
                break;
            }
        }
        _encoderParameters = new EncoderParameters(2);
        _encoderParameters.Param[0] = new EncoderParameter(Encoder.Compression, (long)EncoderValue.CompressionCCITT4);
        _encoderParameters.Param[1] = new EncoderParameter(Encoder.ColorDepth, (long)1);
    }

    public static byte[] ConvertToOnebitFaxGroup4(byte[] bytes)
    {
        using (var memoryStream = new MemoryStream(bytes))
        {
            var image = Image.FromStream(memoryStream);
            var pData = Marshal.AllocHGlobal(bytes.Length);
            Marshal.Copy(bytes, 0, pData, bytes.Length);
            var bytesPerLine = (image.Width + 31) / 32 * 4; 
            var img = new Bitmap(image.Width, image.Height, bytesPerLine, PixelFormat.Format1bppIndexed, pData);
            using (var ms = new MemoryStream())
            {
                image.Save(ms, _codecInfo, _encoderParameters);
                img.Dispose();
                Marshal.FreeHGlobal(pData);
                return ms.ToArray();
            }
        }
    }

Обновлено:

    public static byte[] ConvertToOnebitFaxGroup4(byte[] bytes)
    {
        using (var memoryStream = new MemoryStream(bytes))
        {
            using (var image = Image.FromStream(memoryStream))
            {
                var pData = Marshal.AllocHGlobal(bytes.Length);
                Marshal.Copy(bytes, 0, pData, bytes.Length);
                var bytesPerLine = (image.Width + 31) / 32 * 4;
                using (var img = new Bitmap(image.Width, image.Height, bytesPerLine, PixelFormat.Format1bppIndexed, pData))
                {
                    using (var ms = new MemoryStream())
                    {
                        img.Save(ms, _codecInfo, _encoderParameters);
                        Marshal.FreeHGlobal(pData);
                        return ms.ToArray();
                    }
                }
            }
        }
    }

Обновление2

    public static byte[] ConvertToOnebitFaxGroup4(byte[] bytes)
    {
        using (var memoryStream = new MemoryStream(bytes))
        {
            using (var image = Image.FromStream(memoryStream))
            {
                using (var ms = new MemoryStream())
                {
                    image.Save(ms, _codecInfo, _encoderParameters);
                    return ms.ToArray();
                }
            }
        }
    }

Вы забыли удалить объекты Image и Bitmap. Объекты GDI дороги и ограничены. Еще одна странная вещь - это копирование байтов вроде этого, Marshal.AllocHGlobal, нескольких потоков памяти. Преобразование формата в GDI + выполняется с помощью объектов ImageEncoder и ImageDecorer.

Panagiotis Kanavos 13.09.2018 15:04

Вы имеете в виду, что я должен поместить изображение и img (Bitmap) в блоки using или вызвать явное удаление? Этот код был скопирован с использованием stackoverflow.com/questions/26930656/convert-tiff-to-1bit. У вас есть предложения по улучшению приведенного выше кода.

Imran Qadir Baksh - Baloch 13.09.2018 15:07

Или вы можете просто вызвать Изображение.Save с целевым потоком и информацией о формате. Как: преобразовать изображение BMP в изображение PNG показывает, что Save может принимать определенный формат или более детально закодированные параметры кодера.

Panagiotis Kanavos 13.09.2018 15:07

Изображение, объекты Bitmap имеют, подлежащие удалению. Да, вам нужны блоки using для всех. Однако большую часть кода можно удалить. Image.Save может сохранять в целевой поток и использовать другой формат. Вы можете написать image.Save(ms,codecInfo,encoderParameters) для создания нового образа. Не надо повторно использует переменную image как для исходного, так и для целевого изображения. Оба они должны быть утилизированы в конце

Panagiotis Kanavos 13.09.2018 15:08

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

Imran Qadir Baksh - Baloch 13.09.2018 15:13

Я обнаружил ошибку в коде. Думаю стоит img.Save вместо image.Save(ms?

Imran Qadir Baksh - Baloch 13.09.2018 15:33

Вы можете увидеть обновленный код?

Imran Qadir Baksh - Baloch 13.09.2018 15:40

Вам даже не нужен img, он никогда не использовался. После того, как вы удалите его, ни pdata, ни какой-либо из Marhal не нужен. код. image должен быть объявлен в операторе using, чтобы гарантировать его удаление.

Panagiotis Kanavos 13.09.2018 15:41

img - это Bitmap, мне нужно было указать 1-битное изображение?

Imran Qadir Baksh - Baloch 13.09.2018 15:43

Вы указали, что в параметрах кодировщика

Panagiotis Kanavos 13.09.2018 15:43

Снова обновлен код. Пожалуйста, проверьте. Спасибо за вашу помощь

Imran Qadir Baksh - Baloch 13.09.2018 15:47

Не могли бы вы ответить на это? Чтобы другим было полезно

Imran Qadir Baksh - Baloch 13.09.2018 16:05

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

Imran Qadir Baksh - Baloch 13.09.2018 16:21
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
13
82
0

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