Разделение многостраничного Tiff на отдельные Tiff в Windows 7/.Net 4.0

Недавно я перешел на новую среду разработки с 32-разрядной версии Windows XP на 64-разрядную версию Windows 7. Обе машины работают под управлением .Net Framework версии 4.0 для разработки с помощью Visual Studio 2010.

После обновления до 64-разрядной версии Windows 7 мой код для разделения многостраничного изображения Tiff на отдельные изображения теперь не работает (ранее работал нормально на 32-разрядной версии XP, за исключением ошибки порядка заполнения MS). После отладки метаданные растрового изображения правильно считываются .Net Framework, однако некоторые компоненты в стеке неправильно сохраняют некоторые теги Tiff (273, 278 и 279). Я пробовал несколько способов выполнить разделение, включая GDI+ и библиотеку FreeImage, но все они терпят неудачу в .Net. Мне удалось успешно разделить Tiff с помощью Image Magick и другого стороннего инструмента с допустимыми значениями тегов.

В частности, теги Tiff 273, 278 (должны совпадать с 257, но не совпадают) и 279 имеют неверные значения.

Это известная проблема Microsoft? Есть ли обходной путь? Я неправильно выполняю это задание? Очень разочарован, так как это прекрасно работало на XP 32, и эта ОС не является вариантом развертывания.

// Copy bytes into our memory
using (MemoryStream ms = new MemoryStream())
{
    using (BinaryWriter bw = new BinaryWriter(ms))
    {
        // Get the multi page tiff image's size, and allocate a place to put it.
        int size = RdmInstance.ImageSize;
        byte[] imgContents = new byte[size + 1];

        // Create the pointer and assign the Rdm image address to it
        IntPtr pointerToImage = new IntPtr(RdmInstance.ImageData);

        // Copy the bytes from unmanaged memory to managed memory
        Marshal.Copy(pointerToImage, imgContents, 0, size);

        // Now, write that contents into our memory stream
        bw.Write(imgContents);


        // Check for multiple tiff pages, split them out and write them out correctly for the Fed
        TiffBitmapDecoder decoder = new TiffBitmapDecoder(ms, BitmapCreateOptions.PreservePixelFormat, BitmapCacheOption.Default);

        if (decoder.Frames.Count > 0)
        {

            // check for multi page tiff
            for (int i = 0; i < decoder.Frames.Count; i++)
            {
                log.InfoFormat("Loading Multi Page Tiff Frame [{0}]... to bitmap", i);

                // First is front, second is back 
                // TODO - it would be better to get this out of tiff tag RDM sets with the page info
                string fileName = (i == 0) ? frontFileName : backFileName;
                BitmapSource bmSrc = decoder.Frames[i];
                TiffBitmapEncoder encoder = new TiffBitmapEncoder();

                encoder.Compression = TiffCompressOption.Ccitt4;
                encoder.Frames.Add(BitmapFrame.Create(bmSrc));

                log.InfoFormat("Saving Multi Page Tiff Frame [{0}]... to file {1}.", i, fileName);
                using (var fs = new FileStream(fileName, FileMode.Create))
                {
                    encoder.Save(fs);
                }

                /*
                 * jknipp - 6/4/2010 
                 * Microsoft has a bug in their TiffBitmapEncoder where
                 * they incorrectly set tag 266 (Fill Order) to 0, where the TIFF
                 * spec says it should be 1 or 2. We fix this here.
                 * Reopen the stupid file and fix the fill order
                 */
                using (var file = new FileStream(fileName, FileMode.Open))
                {
                    TiffBitmapDecoder output = new TiffBitmapDecoder(file, BitmapCreateOptions.PreservePixelFormat, BitmapCacheOption.Default);
                    InPlaceBitmapMetadataWriter metadata = output.Frames[0].CreateInPlaceBitmapMetadataWriter();

                    var fillOrder = metadata.GetQuery("/ifd/{ushort=266}");
                    log.DebugFormat("Read Fill Order Metadata tag as {0}", fillOrder);

                    // If .Net added a bogus fill order, correct it
                    if (fillOrder != null && (ushort)fillOrder == 0)
                    {
                        log.InfoFormat("Correcting FILL ORDER in file {0}", fileName);
                        metadata.SetQuery("/ifd/{ushort=266}", (ushort)1);

                        // Try to save new metadata
                        if (metadata.TrySave())
                        {
                            fillOrder = metadata.GetQuery("/ifd/{ushort=266}");
                            log.Info("Fill order correction successful!");
                            log.DebugFormat("Read New Fill Order Metadata tag as {0}", fillOrder);
                        }
                    }
                }
            }
        }
    }
}

person Jared Knipp    schedule 26.08.2010    source источник


Ответы (2)


Для этого вы можете попробовать библиотеку LibTiff.Net. Это бесплатно и с открытым исходным кодом (лицензия BSD).

Библиотека поставляется с утилитой tiffcp, которую можно использовать из вашего кода для разделения/объединения изображений TIFF. Или вы можете использовать исходный код этой утилиты в качестве образца.

LibTiff.Net также содержит примеры объединения и разделения изображений TIFF с помощью tiffcp из вашего приложения.

Отказ от ответственности: я являюсь одним из сопровождающих библиотеки.

person Bobrovsky    schedule 26.08.2010
comment
Вчера я рассматривал использование LibTiff.Net, но решил полениться. После взлома кода TiffCP в моем приложении оно снова работает! - person Jared Knipp; 27.08.2010
comment
Здорово, что LibTiff.Net вам помог :-) - person Bobrovsky; 27.08.2010

Судя по вашему исходному коду, мой ответ здесь может быть наивным с учетом ваших конкретных потребностей, но, тем не менее, он работает для меня:

public static MyImage New(string filePath, ImageFormat imageFormat, 
    int imageSequence = 0)
{
    byte[] imageBytes;

    using (FileStream imageStream = File.OpenRead(filePath))
    {
        Image tmpImg = Image.FromStream(imageStream);
        if (imageSequence > 0)
        {
            tmpImg.SelectActiveFrame(FrameDimension.Page, imageSequence);
        }
        imageBytes = ToByteArray(tmpImg, imageFormat);
        //FileStream must be open for entire lifetime of the Image
        imageStream.Close();
    }
    return new MyImage(filePath, imageBytes);
}

public static byte[] ToByteArray(Image image, ImageFormat imageFormat)
{
    using (MemoryStream ms = new MemoryStream())
    {
        image.Save(ms, imageFormat);
        byte[] bytes = ms.ToArray();
        ms.Close();
        return bytes;
    }
}
person Facio Ratio    schedule 10.04.2013