在 Windows 7 / .Net 4.0 上将多页 Tiff 拆分为单个 Tiff

发布于 2024-09-16 05:03:20 字数 4105 浏览 1 评论 0原文

我最近从 Windows XP 32 位迁移到新的开发盒到 Windows 7 64 位。两台机器都运行 .Net Framework 版本 4.0,以便使用 Visual Studio 2010 进行开发。

升级到 Windows 7 64 位后,我将多页 Tiff 图像拆分为单独图像的代码现在已损坏(之前在 XP 32 位上运行良好,除了针对 MS 填充顺序错误)。调试后,.Net Framework 似乎可以正确读取位图元数据,但是,堆栈中的某些组件错误地保留了一些 Tiff 标签(273、278 和 279)。我尝试了多种方法来完成分割,包括 GDI+ 和 FreeImage 库,但在 .Net 中都失败了。我能够使用 Image Magick 和另一个具有有效标签值的第三方工具成功分割 Tiff。

具体来说,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);
                        }
                    }
                }
            }
        }
    }
}

I recently moved to a new development box from Windows XP 32 Bit to Windows 7 64 bit. Both machines are running .Net Framework version 4.0 for development with Visual Studio 2010.

After upgrading to Windows 7 64 Bit, my code to split a multi-page Tiff image into separate images is now broken (worked fine previously on XP 32 Bit, except for the MS fill order bug). After debugging, the Bitmap Metadata appears to be read correctly by the .Net Framework, however, some component in the stack is incorrectly persisting the some Tiff Tags (273, 278, and 279). I have tried several methods to accomplish the split, including GDI+, and the FreeImage library, but all fail within .Net. I was able to successful split the Tiff using Image Magick and another 3rd party tool with valid tag values.

Specifically, Tiff Tags 273, 278 (should match 257 but does not), and 279 have incorrect values.

Is this a known Microsoft issue? Is there a workaround? Am I doing this task wrong? Very frustrated as this was working on XP 32 just fine and that OS is not a deployment option.

// 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);
                        }
                    }
                }
            }
        }
    }
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

游魂 2024-09-23 05:03:20

您可以尝试使用 LibTiff.Net 库。它是免费且开源的(BSD 许可证)。

该库附带 tiffcp 实用程序,可在您的代码中使用该实用程序来分割/合并 TIFF 图像。或者您可以使用该实用程序的源代码作为示例。

LibTiff.Net 还包含使用应用程序中的 tiffcp 合并和分割 TIFF 图像的示例。

免责声明:我是图书馆的维护者之一。

You can try LibTiff.Net library for this. It's free and open-source (BSD License).

The library comes with tiffcp utility that can be used from your code for splitting/merging TIFF images. Or you can use source code of that utility as a sample.

LibTiff.Net also contains samples for merging and splitting TIFF images using tiffcp from your application.

Disclaimer: I am one of the maintainers of the library.

給妳壹絲溫柔 2024-09-23 05:03:20

从您最初的代码来看,对于您的特定需求,我在这里的回答可能很天真,但尽管如此,它对我有用:

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;
    }
}

Judging from your initial code, my answer here may be naive with respect for your specific needs, but nonetheless, it works for me:

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;
    }
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文