1

我在控制台应用程序中使用 imageresizer.net 从 tiff 图像中提取页面,但是当 tiff 包含不同图像类型的页面时,imageresizer 提取的内容并不总是代表原始页面。

我正在使用托管 API,代码如下: ImageBuilder.Current.Build(sourceImagePath, destinationImagePath, new ResizeSettings("page=[whatever]"));

TIFF 图像包含...

  • 第 1 页,图像类型“Bilevel(白色为零)”,压缩“Group 3”,大小“485kb”。
  • 第 2 页,图像类型“YCbCr(2,1 二次采样)”,压缩“JPEG(旧式)”,大小“11MB”。
  • 第 3 页,图像类型“YCbCr(2,1 二次采样)”,压缩“JPEG(旧式)”,大小“11MB”。
  • 第 4 页,图像类型“灰度(256 灰度,黑色为零)”,压缩“JPEG(旧式)”,大小“4MB”。

我得到的行为是......

  • 提取第 1 页工作正常。
  • 提取第 2 页最终会提取第 1 页的图像。
  • 提取第 3 页最终提取了第 1 页的图像,但在图像左侧压缩成约 1 厘米宽的带,剩余图像为黑色。
  • 提取第 4 页工作正常。

我还使用 libtiff.net 使用这种方法提取了相同的图像,它没有表现出相同的错误。

编辑 - 使用此代码测试 Wic 解码器:

var settings = new ResizeSettings("decoder=wic&page=" + pageNumber);
Config.Current.Plugins.GetOrInstall<WicDecoderPlugin>();
ImageBuilder.Current.Build(sourceImagePath, destImagePath, settings);

这适用于第 1 页和第 4 页(那里没有变化),但我得到一个“对象引用未设置为对象的实例”。使用 Wic 解码器时,第 2 页和第 3 页出现错误。

编辑 2 - 将失败的位转换为 MVC 项目以获取 /resizer.debug:

Image resizer diagnostic sheet      29/06/2012 4:08:14 a.m.

4 Issues detected:

(Warning):  To potentially see additional errors here, perform an image resize request.


(Warning):  You are running a hotfix version of the ImageResizer.
            You should upgrade to a released version with an equal or higher version number as soon as possible. Hotfix and release DLLs with the same version number are not the same - the release DLL should be used instead.
            Assemblies marked as hotfix versions: ImageResizer, ImageResizer.Mvc

(Warning):  NoCache is only for development usage, and cannot scale to production use.
            Add DiskCache or CloudFront for production use

(Warning):  Potentially incompatible ImageResizer assemblies were detected.
            Please make sure all ImageResizer assemblies are from the same version. Compatibility issues are possible if you mix plugins from different releases.
            3.2.3 assemblies: ImageResizer, ImageResizer.Mvc
            3.2.2 assemblies: ImageResizer.Plugins.Logging, ImageResizer.Plugins.Wic


You are using paid bundles: Design Bundle

Registered plugins:

ImageResizer.Plugins.Basic.DefaultEncoder
ImageResizer.Plugins.Basic.NoCache
ImageResizer.Plugins.Basic.ClientCache
ImageResizer.Plugins.Basic.Diagnostic
ImageResizer.Plugins.Basic.SizeLimiting
ImageResizer.Plugins.MvcRoutingShim.MvcRoutingShimPlugin
ImageResizer.Plugins.WicDecoder.WicDecoderPlugin
ImageResizer.Plugins.Logging.LoggingPlugin

Configuration:

<resizer>
<plugins>
<add name="MvcRoutingShim" />
<add name="WicDecoder" />
<add name="Logging" />
</plugins>
</resizer>


Accepted querystring keys:

quality, format, thumbnail, maxwidth, maxheight, width, height, w, h, scale, stretch, crop, cropxunits, cropyunits, page, bgcolor, rotate, flip, sourceFlip, sFlip, sRotate, borderWidth, borderColor, paddingWidth, paddingColor, ignoreicc, frame, useresizingpipeline, cache, process, margin, anchor, dpi, mode, zoom, 

Accepted file extensions:

bmp, gif, exif, png, tif, tiff, tff, jpg, jpeg, jpe, jif, jfif, jfi, 

Environment information:

Running Microsoft Windows NT 6.1.7601 Service Pack 1 and CLR 4.0.30319.269
Trust level: Unrestricted
OS bitness: x86 !! Warning, running as 32-bit on a 64-bit OS(AMD64). This will limit ram usage !!
Executing assembly: C:\Program Files (x86)\Common Files\Microsoft Shared\DevServer\10.0\WebDev.WebServer40.exe
IntegratedPipeline: False

Loaded assemblies:

mscorlib                                 Assembly: 4.0.0.0         File: 4.0.30319.269   Info: 4.0.30319.269
System.Web                               Assembly: 4.0.0.0         File: 4.0.30319.272   Info: 4.0.30319.272
System                                   Assembly: 4.0.0.0         File: 4.0.30319.269   Info: 4.0.30319.269
System.Core                              Assembly: 4.0.0.0         File: 4.0.30319.233   Info: 4.0.30319.233
System.Configuration                     Assembly: 4.0.0.0         File: 4.0.30319.1     Info: 4.0.30319.1
System.Xml                               Assembly: 4.0.0.0         File: 4.0.30319.233   Info: 4.0.30319.233
System.Runtime.Caching                   Assembly: 4.0.0.0         File: 4.0.30319.237   Info: 4.0.30319.237
Microsoft.Build.Utilities.v4.0           Assembly: 4.0.0.0         File: 4.0.30319.1     Info: 4.0.30319.1
System.Web.RegularExpressions            Assembly: 4.0.0.0         File: 4.0.30319.1     Info: 4.0.30319.1
Microsoft.Build.Framework                Assembly: 4.0.0.0         File: 4.0.30319.1     Info: 4.0.30319.1
System.Data                              Assembly: 4.0.0.0         File: 4.0.30319.237   Info: 4.0.30319.237
Microsoft.JScript                        Assembly: 10.0.0.0        File: 10.0.30319.1    Info: 10.0.30319.1
CppCodeProvider                          Assembly: 10.0.0.0        File: 10.0.30319.1    Info: 10.0.30319.1
System.Web.WebPages.Deployment           Assembly: 1.0.0.0         File: 1.0.20105.407  
System.Web.WebPages                      Assembly: 1.0.0.0         File: 1.0.20105.407  
System.Web.Mvc                           Assembly: 3.0.0.0         File: 3.0.20105.0    
Microsoft.Web.Infrastructure             Assembly: 1.0.0.0         File: 1.0.20105.407  
System.Web.WebPages.Razor                Assembly: 1.0.0.0         File: 1.0.20105.407  
System.Web.Razor                         Assembly: 1.0.0.0         File: 1.0.20105.407  
App_global.asax.ijrgfwnu                 Assembly: 0.0.0.0        
PNCC.LimDocumentBuilder.ImageResizerTest Assembly: 1.0.0.0         File: 1.0.0.0        
WebDev.WebHost40                         Assembly: 10.0.0.0        File: 10.0.40219.1    Info: 10.0.40219.1
System.Web.Mobile                        Assembly: 4.0.0.0         File: 4.0.30319.1     Info: 4.0.30319.1
System.ServiceModel.Activation           Assembly: 4.0.0.0         File: 4.0.30319.233   Info: 4.0.30319.233
System.ServiceModel                      Assembly: 4.0.0.0         File: 4.0.30319.233   Info: 4.0.30319.233
System.Runtime.DurableInstancing         Assembly: 4.0.0.0         File: 4.0.30319.1     Info: 4.0.30319.1
SMDiagnostics                            Assembly: 4.0.0.0         File: 4.0.30319.1     Info: 4.0.30319.1
System.Xaml.Hosting                      Assembly: 4.0.0.0         File: 4.0.30319.1     Info: 4.0.30319.1
System.Web.Extensions                    Assembly: 4.0.0.0         File: 4.0.30319.272   Info: 4.0.30319.272
Microsoft.CSharp                         Assembly: 4.0.0.0         File: 4.0.30319.1     Info: 4.0.30319.1
System.Web.Services                      Assembly: 4.0.0.0         File: 4.0.30319.1     Info: 4.0.30319.1
System.Drawing                           Assembly: 4.0.0.0         File: 4.0.30319.282   Info: 4.0.30319.282
System.EnterpriseServices                Assembly: 4.0.0.0         File: 4.0.30319.1     Info: 4.0.30319.1
System.IdentityModel                     Assembly: 4.0.0.0         File: 4.0.30319.1     Info: 4.0.30319.1
System.Runtime.Serialization             Assembly: 4.0.0.0         File: 4.0.30319.1     Info: 4.0.30319.1
System.ServiceModel.Web                  Assembly: 4.0.0.0         File: 4.0.30319.233   Info: 4.0.30319.233
System.Activities                        Assembly: 4.0.0.0         File: 4.0.30319.1     Info: 4.0.30319.1
System.ServiceModel.Activities           Assembly: 4.0.0.0         File: 4.0.30319.1     Info: 4.0.30319.1
System.WorkflowServices                  Assembly: 4.0.0.0         File: 4.0.30319.1     Info: 4.0.30319.1
System.Data.DataSetExtensions            Assembly: 4.0.0.0         File: 4.0.30319.1     Info: 4.0.30319.1
System.Xml.Linq                          Assembly: 4.0.0.0         File: 4.0.30319.1     Info: 4.0.30319.1
System.ComponentModel.DataAnnotations    Assembly: 4.0.0.0         File: 4.0.30319.1     Info: 4.0.30319.1
System.Web.DynamicData                   Assembly: 4.0.0.0         File: 4.0.30319.1     Info: 4.0.30319.1
System.Web.ApplicationServices           Assembly: 4.0.0.0         File: 4.0.30319.1     Info: 4.0.30319.1
EntityFramework                          Assembly: 4.1.0.0         File: 4.1.10331.0     Info: 4.1.10331.0
ImageResizer                             Assembly: 3.2.3.766       File: 3.2.3.766       Info: 3-2-beta-3-temp-hotfix-jun-27-2012-9am  Commit: a0f4f1e
ImageResizer.Mvc                         Assembly: 3.2.3.766       File: 3.2.3.766       Info: 3-2-beta-3-temp-hotfix-jun-27-2012-9am  Commit: a0f4f1e
ImageResizer.Plugins.Logging             Assembly: 3.2.2.0         File: 3.2.2.0         Info: 3-2-beta-2  Commit: 3704dd6
ImageResizer.Plugins.Wic                 Assembly: 3.2.2.0         File: 3.2.2.0         Info: 3-2-beta-2  Commit: 3704dd6
NLog                                     Assembly: 2.0.0.0        
System.Web.Abstractions                  Assembly: 4.0.0.0         File: 4.0.30319.1     Info: 4.0.30319.1
System.Web.Helpers                       Assembly: 1.0.0.0         File: 1.0.20105.407  
System.Web.Routing                       Assembly: 4.0.0.0         File: 4.0.30319.1     Info: 4.0.30319.1
4

2 回答 2

1

这是我在 libtiff.net 中对 BuilderExtension 进行子类化、覆盖 DecodeStream 和粘合的尝试。

这是我的两个测试 tiff不同大小的页面具有不同颜色空间的页面。我已经针对 ImageBuilder v3.2.2 进行了编译。

当我使用 ImageResizer v3.2.2 源代码中的 ConsoleApplication 项目和以下代码时出现错误。目前我已经没有时间解决这个问题了,所以我想我会把我的进展放在这里,以防其他人想更进一步。

我的测试代码

ImageResizer.ImageBuilder.PrepareDestinationBitmap()我认为在线失败,s.destBitmap = ...参数无效。错误。尺寸为 w=9994,h=7102,我怀疑这可能是内存问题 - 但是,如果我DecodeTiffTo32BitBitmap()首先使用解码 tiff 并将其作为内存流传递给 ImageResizer,则错误不存在。此外,v3.2.2 和 v3.2.3 都会出现此故障。

var Path = @"[whatever]\TiffDecoderTests\";
var c = new Config();      
new TiffDecoder().Install(c);      
c.BuildImage(Path + "different_page_sizes.TIF", Path + "different_page_sizes-page2-TiffReader.png", "format=png&page=2&decoder=tiffdecoder");   

我的 TiffDecoder 类:

namespace ImageResizer.Plugins.TiffDecoder
{
  using System;
  using System.Collections.Generic;
  using System.Drawing;
  using System.Drawing.Imaging;
  using System.Globalization;
  using System.IO;
  using System.Linq;

  using BitMiracle.LibTiff.Classic;

  using ImageResizer;
  using ImageResizer.Configuration;
  using ImageResizer.Resizing;

  public class TiffDecoder : BuilderExtension, IPlugin, IFileExtensionPlugin
  {
    private static readonly string[] supportedExtensions = new[] { ".tiff", ".tif", ".tff" };

    public IPlugin Install(Config c)
    {
      c.Plugins.add_plugin(this);
      return this;
    }

    public bool Uninstall(Config c)
    {
      c.Plugins.remove_plugin(this);
      return true;
    }

    public IEnumerable<string> GetSupportedFileExtensions()
    {
      return supportedExtensions;
    }

    public override Bitmap DecodeStream(Stream s, ResizeSettings settings, string optionalPath)
    {
      var requested = "tiffdecoder".Equals(settings["decoder"], StringComparison.OrdinalIgnoreCase);
      if (!string.IsNullOrEmpty(settings["decoder"]) && !requested)
      {
        return null; // Don't take it away from the requested decoder
      }

      // If a tiff is coming in, try first, before Bitmap tries to parse it.      
      if (requested || (optionalPath != null && supportedExtensions.Any(ext => optionalPath.EndsWith(ext, StringComparison.OrdinalIgnoreCase))))
      {
        return this.Decode(s, settings);
      }

      return null;
    }

    public override Bitmap DecodeStreamFailed(Stream s, ResizeSettings settings, string optionalPath)
    {
      // Catch tiff files not ending in an expected extension
      try
      {
        return this.Decode(s, settings);
      }
      catch
      {
        return null;
      }
    }

    public Bitmap Decode(Stream s, ResizeSettings settings)
    {
      return DecodeTiffTo32BitBitmap(s, settings["page"]);
    }

    private static Bitmap DecodeTiffTo32BitBitmap(Stream s, string page)
    {
      s.Position = 0;
      using (var image = Tiff.ClientOpen(string.Empty, "r", s, new TiffStream()))
      {
        SetDirectory(image, page);

        // Find the width and height of the image
        var value = image.GetField(TiffTag.IMAGEWIDTH);
        var width = value[0].ToInt();

        value = image.GetField(TiffTag.IMAGELENGTH);
        var height = value[0].ToInt();

        // Read the image into the memory buffer
        var raster = new int[height * width];
        if (!image.ReadRGBAImage(width, height, raster))
        {
          return null;
        }

        // Caller needs to handle disposing the bitmap.
        var bmp = new Bitmap(width, height, PixelFormat.Format32bppRgb);
        var rect = new Rectangle(0, 0, bmp.Width, bmp.Height);

        var bmpdata = bmp.LockBits(rect, ImageLockMode.ReadWrite, PixelFormat.Format32bppRgb);
        var bits = new byte[bmpdata.Stride * bmpdata.Height];

        for (var y = 0; y < bmp.Height; y++)
        {
          var rasterOffset = y * bmp.Width;
          var bitsOffset = (bmp.Height - y - 1) * bmpdata.Stride;

          for (var x = 0; x < bmp.Width; x++)
          {
            var rgba = raster[rasterOffset++];
            bits[bitsOffset++] = (byte)((rgba >> 16) & 0xff);
            bits[bitsOffset++] = (byte)((rgba >> 8) & 0xff);
            bits[bitsOffset++] = (byte)(rgba & 0xff);
            bits[bitsOffset++] = (byte)((rgba >> 24) & 0xff);
          }
        }

        System.Runtime.InteropServices.Marshal.Copy(bits, 0, bmpdata.Scan0, bits.Length);
        bmp.UnlockBits(bmpdata);

        return bmp;
      }
    }

    /// <summary>
    /// Sets the TIFF directory to the first that matches the requested page number, otherwise:
    /// - If page not set OR less than 0, use the first page in the TIFF.
    /// - If tiff has no directories with the "PAGE" tag, use the first frame in the TIFF.
    /// </summary>
    /// <param name="image">The image.</param>
    /// <param name="page">The page.</param>
    /// <remarks>
    /// From what I can tell a TIFF image can store any pages in any directory, so the 1st dir 
    /// might not hold the 1st page - this is why we first iterate all frames looking for a PAGE tag.
    /// </remarks>    
    private static void SetDirectory(Tiff image, string page)
    {
      var pageIndex = GetAdjustedPage(page);
      var numberOfDirectories = image.NumberOfDirectories();

      for (short dirNumber = 0; dirNumber < numberOfDirectories; ++dirNumber)
      {
        image.SetDirectory(dirNumber);

        // Ignore pages that might be thumbnails
        var subFileType = image.GetField(TiffTag.SUBFILETYPE);
        if (subFileType == null || subFileType[0].ToString().ToUpper() != "PAGE")
        {
          continue;
        }

        // Stop if page found
        if (pageIndex == int.Parse(image.GetField(TiffTag.PAGENUMBER)[0].ToString()))
        {
          return;
        }
      }

      // Default to the first frame if the page wasn't found.
      image.SetDirectory(0);
    }

    private static int GetAdjustedPage(string page)
    {
      var pageIndex = 1;
      if (!string.IsNullOrEmpty(page))
      {
        int.TryParse(page, NumberStyles.Number, NumberFormatInfo.InvariantInfo, out pageIndex);
      }

      // So users can use 1-based numbers
      return Math.Max(1, pageIndex) - 1;
    }
  }
}
于 2012-07-03T23:20:48.870 回答
0

2012 年 12 月 12 日更新:

FreeImageDecoder 插件现在支持多页 TIFF 文件,并在内部使用 libtiff。这是处理非常复杂/罕见的 .TIFF 文件的建议方法。


TIFF 格式极其复杂且标准化程度很低。大多数库(如 GDI+ (System.Drawing) 和 WIC (WPF))只实现了标准的一小部分,更不用说事实上的特性和附加功能了。

要获得这种级别的 .TIFF 支持,您需要使用专用于 .TIFF 支持的库,例如libtifflibtiff.net

ImageResizer确实通过 FreeImage 和 FreeImageDecoder 插件与 LibTiff 集成,但尚未实现多页面支持

没有桨,你就不会上岸——有很多方法可以解决你的困境:

  1. Fork FreeImageDecoder并添加多页支持。难度:7
  2. 子类BuilderExtension,覆盖DecodeStream,并粘在 libtiff.net 中。难度:5
  3. 子类 BuilderExtension,覆盖 DecodeStream,并在 libtiff 中粘合。难度:10
  4. 在 UserVoice 网站上发布您的想法并获得 30 票。困难:?
  5. 付钱给我。难度:3(不过,我的下一个可用位置是几个月后)。

解码器的示例实现已经有 4 个:ImageBuilder.DecodeStream、PsdReader、WicDecoder、FreeImageDeocoder 和 PdfRenderer。它们的源代码包含在下载中。平均解码器需要少于 80 行代码。

于 2012-06-30T15:35:52.497 回答