Как сохранить многостраничное TIFF изображение в поток памяти?

Вопрос или проблема

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

Когда я сохраняю в файл, все работает, как и ожидалось (я получаю три страницы в изображении TIFF на диске). Однако когда я пытаюсь сохранить в MemoryStream, а затем взять этот MemoryStream и сохранить в файл, я получаю только первую страницу.

Вот что у меня сейчас, и в итоге я получаю только первую страницу.

public void Test()
{
    // Добавить 3 файла изображений в кеш
    List<Image> imageCache = new List<Image>();
    imageCache.Add(Image.FromFile(@"c:\test\testInput1.tif"));
    imageCache.Add(Image.FromFile(@"c:\test\testInput2.tif"));
    imageCache.Add(Image.FromFile(@"c:\test\testInput3.tif"));

    // Получить кодек изображения TIFF
    ImageCodecInfo codec = ImageCodecInfo.GetImageEncoders().Where(c => c.MimeType == "image/tiff").First();
    if (codec == null)
        throw new Exception("Кодек TIFF не найден.");

    // Создать параметр кодировщика сжатия
    EncoderParameter compressionParam = new EncoderParameter(Encoder.Compression, (long)EncoderValue.CompressionCCITT4);

    // Создать параметр для первого кадра страницы
    EncoderParameter firstFrameParam = new EncoderParameter(Encoder.SaveFlag, (long)EncoderValue.MultiFrame);

    // Создать параметры для дополнительных страниц
    EncoderParameter additionalFramesParam = new EncoderParameter(Encoder.SaveFlag, (long)EncoderValue.FrameDimensionPage);

    // Создать параметр цветовой глубины
    EncoderParameter colorDepthParam = new EncoderParameter(Encoder.ColorDepth, (long)1);

    // Создать параметр сброса
    EncoderParameter flushParam = new EncoderParameter(Encoder.SaveFlag, (long)EncoderValue.Flush);

    // Создать параметр последнего кадра
    EncoderParameter lastPageParam = new EncoderParameter(Encoder.SaveFlag, (long)EncoderValue.LastFrame);

    // Создать параметры кодировщика для первой страницы
    EncoderParameters firstFrameParams = new EncoderParameters(2)
    {
        Param = new EncoderParameter[]
        {
            compressionParam,
            firstFrameParam,
        }
    };

    // Создать параметры кодировщика для дополнительных страниц
    EncoderParameters additionalFrameParams = new EncoderParameters(2)
    {
        Param = new EncoderParameter[]
        {
            compressionParam,
            additionalFramesParam,
        }
    };

    // Создать параметры кодировщика для сохранения в файл
    EncoderParameters saveToFileParams = new EncoderParameters(2)
    {
        Param = new EncoderParameter[]
        {
            compressionParam,
            colorDepthParam,
        }
    };

    // Создать параметры сброса
    EncoderParameters flushParams = new EncoderParameters(1)
    {
        Param = new EncoderParameter[]
        {
                flushParam,
                lastPageParam,
        }
    };

    // Сохранить битмап в память как TIFF
    using (MemoryStream byteStream = new MemoryStream())
    {
        _imageCache[0].Save(byteStream, ImageFormat.Tiff);

        // Поместить TIFF в другой объект Image
        using (Image tiff = Image.FromStream(byteStream))
        {
            // Сохранить в файл - раскомментируйте следующую строку для работающего решения
            //tiff.Save(@"c:\test\testOutput.tif", codec, firstFrameParams);

            // Сохранить в память - закомментируйте следующие три строки для работающего решения
            using (MemoryStream tiffStream = new MemoryStream())
            {
                tiff.Save(tiffStream, codec, firstFrameParams);

                // Для последующих страниц подготовить кодировщики
                for (int page = 1; page < _imageCache.Count; page++)
                    tiff.SaveAdd(_imageCache[page], additionalFrameParams);

                // Наконец, сбросить файл
                tiff.SaveAdd(flushParams);

                // Сохранить изображение в памяти в файл - закомментируйте следующие три строки для работающего решения
                using (Image tiffOutput = Image.FromStream(tiffStream))
                    tiffOutput.Save(@"c:\test\testOutput.tif", codec, saveToFileParams);
            }
        }
    }
}

Стоит отметить, что объект изображения tiffOutput, созданный из потока, НЕ содержит ожидаемых 3 рамок, а только 1 (первую) страницу. Похоже, что метод Image.SaveAdd не работает с потоками памяти, хотя и указывается, что он работает.

Ответ или решение

Для сохранения многостраничного TIFF-изображения в объект MemoryStream, важно правильно управлять последовательностью сохранения страниц и параметрами кодирования. Из вашего примера видно, что существует ошибка при работе с потоками и сохранении страниц. Давайте детально разберёмся, как это можно сделать правильно.

Шаги для сохранения многостраничного TIFF в MemoryStream

  1. Загрузка изображений: Вы уже загружаете изображения в список, и это правильно. Обратите внимание на то, что изображения должны быть совместимы по размеру и глубине цвета, если они будут объединены в одну TIFF.

  2. Настройка кодека: Правильный выбор кодека для TIFF является критически важным. Вы делаете это корректно, получая кодек по MIME-типу.

  3. Создание параметров кодирования: Вам необходимо создать параметры кодирования для первого кадра, добавляемых кадров и для завершения. Это уже сделано, однако стоит уделить внимание правильному их использованию.

  4. Сохранение в MemoryStream: Главное здесь – корректно использовать метод SaveAdd. Запишите многостраничное изображение в поток MemoryStream, следя за тем, чтобы каждый кадр был добавлен, а затем завершите сохранение.

Пример кода

Ниже приведён обновлённый код для сохранения многостраничного TIFF в объект MemoryStream и последующего сохранения в файл:

public void Test()
{
    // Создаём список изображений
    List<Image> imageCache = new List<Image>
    {
        Image.FromFile(@"c:\test\testInput1.tif"),
        Image.FromFile(@"c:\test\testInput2.tif"),
        Image.FromFile(@"c:\test\testInput3.tif")
    };

    // Получаем кодек для TIFF
    ImageCodecInfo codec = ImageCodecInfo.GetImageEncoders().FirstOrDefault(c => c.MimeType == "image/tiff");
    if (codec == null)
        throw new Exception("Tiff codec not found.");

    // Создаём параметры кодирования
    EncoderParameters encoderParamsFirst = new EncoderParameters(2);
    encoderParamsFirst.Param[0] = new EncoderParameter(Encoder.Compression, (long)EncoderValue.CompressionCCITT4);
    encoderParamsFirst.Param[1] = new EncoderParameter(Encoder.SaveFlag, (long)EncoderValue.MultiFrame);

    EncoderParameters encoderParamsAdd = new EncoderParameters(2);
    encoderParamsAdd.Param[0] = new EncoderParameter(Encoder.Compression, (long)EncoderValue.CompressionCCITT4);
    encoderParamsAdd.Param[1] = new EncoderParameter(Encoder.SaveFlag, (long)EncoderValue.FrameDimensionPage);

    EncoderParameters encoderParamsLast = new EncoderParameters(1);
    encoderParamsLast.Param[0] = new EncoderParameter(Encoder.SaveFlag, (long)EncoderValue.LastFrame);

    // Сохраняем изображения в MemoryStream
    using (MemoryStream byteStream = new MemoryStream())
    {
        // Начинаем сохранять первое изображение
        imageCache[0].Save(byteStream, ImageFormat.Tiff, encoderParamsFirst);

        // Добавляем остальные изображения
        for (int i = 1; i < imageCache.Count; i++)
        {
            imageCache[i].SaveAdd(byteStream, encoderParamsAdd);
        }

        // Завершаем процесс сохранения
        imageCache[0].SaveAdd(encoderParamsLast);

        // Теперь сохраняем MemoryStream в файл
        using (FileStream fileStream = new FileStream(@"c:\test\testOutput.tif", FileMode.Create, FileAccess.Write))
        {
            byteStream.Position = 0; // Сбрасываем позицию потока
            byteStream.CopyTo(fileStream);
        }
    }
}

Объяснение

  1. Сохранение в MemoryStream: Теперь мы сохраняем все страницы в MemoryStream, корректно добавляя каждую из них, начиная с первого. После добавления всех страниц мы завершаем процесс вызовом SaveAdd с параметром LastFrame.

  2. Сохранение в файл: После завершения сохранения мы переносим данные из MemoryStream в файл, сбрасывая позицию потока для правильного чтения.

Данный метод обеспечивает корректное сохранение всех страниц изображения в MemoryStream, что позволяет избежать проблем, которые вы встречали ранее. Убедитесь, что все изображения имеют одинаковые параметры, чтобы избежать ошибок.

Оцените материал
Добавить комментарий

Капча загружается...