【问题标题】:InPlaceBitmapMetadataWriter.TrySave() returns true but does nothingInPlaceBitmapMetadataWriter.TrySave() 返回 true 但什么也不做
【发布时间】:2011-02-14 20:21:48
【问题描述】:

在 Windows 7 InPlaceBitmapMetadataWriter.TrySave() 中的某些 .JPG 文件(EPS 预览,由 Adob​​e Illustrator 生成)上,在某些 SetQuery() 调用后返回 true,但什么也不做。

代码示例:

BitmapDecoder decoder;
BitmapFrame frame;
BitmapMetadata metadata;
InPlaceBitmapMetadataWriter writer;
decoder = BitmapDecoder.Create(s, BitmapCreateOptions.PreservePixelFormat | BitmapCreateOptions.IgnoreColorProfile, BitmapCacheOption.Default);
frame = decoder.Frames[0];
metadata = frame.Metadata as BitmapMetadata;
writer = frame.CreateInPlaceBitmapMetadataWriter();
try {
    writer.SetQuery("System.Title", title);
    writer.SetQuery(@"/app1/ifd/{ushort=" + exiftagids[0] + "} ", (title + '\0').ToCharArray());
    writer.SetQuery(@"/app13/irb/8bimiptc/iptc/object name", title);
    return writer.TrySave();
}
catch {
    return false;
}

Image sample

您可以通过下载图像示例并使用此代码示例在此图像上设置标题来重现问题(如果您有 Windows 7)。 图像有足够的空间存放元数据——这个代码示例在我的 WinXP 上运行良好。 相同的代码在 Win7 上与其他 .JPG 文件一起工作正常。

欢迎任何想法:)

【问题讨论】:

    标签: c# wpf metadata


    【解决方案1】:

    两件事:

    1. 我认为你不能像那样写到你的metadata 变量,因为它会被冻结。所以,你必须克隆它:

      BitmapMetadata 元数据 = frame.Metadata.Clone() as BitmapMetadata;
    2. 填充,你需要填充。在尝试使一些代码(类似于您的代码)工作大约一天后,我发现了这一点。如果您的图像文件中没有元数据填充,InPlaceBitmapMetadataWriter 将不起作用。因此您需要类似:

      JpegBitmapEncoder 编码器 = new JpegBitmapEncoder(); 如果(帧!= null && 元数据!= null){ metadata.SetQuery("/app1/ifd/PaddingSchema:Padding", padding); encoder.Frames.Add(BitmapFrame.Create(frame, frame.Thumbnail, metadata, frame.ColorContexts)); 使用 (Stream outputFile = File.Open(_myoutputpath, FileMode.Create, FileAccess.ReadWrite)) { 编码器.保存(输出文件); } }

    现在您可以使用位于_myoutputpath 的文件,该文件为您的InPlaceBitmapMetadataWriter 操作添加了元数据填充。

    This article 和附加代码应该可以帮助您。

    【讨论】:

    • TrySave() 返回 true!但什么都不做。所以没有办法知道元数据是否被写入。
    【解决方案2】:

    您好,我发现 this 关于 InPlaceBitmapMetadataWriter 的文章,该人说 TrySave() 可能会损坏图像,这就是为什么他建议对原始文件的副本执行 TrySave() 如果这不起作用,请添加填充到原始文件的副本,然后再次 TrySave(),如果有效,请删除原始文件并重命名副本。

    我挠了挠头,问自己为什么要打扰 InPlaceBitmapMetadataWriter 并将 3x 原始文件写入磁盘,以防 TrySave() 不起作用,因为没有足够的填充,如果我可以克隆元数据,将任何内容写入其中并立即组装 jpeg 文件。

    然后我开始想,也许感谢 InPlaceBitmapMetadataWriter,我可以在不损失质量的情况下编辑元数据,但如果有足够的填充,它看起来“只是”可以帮助您更快地编写元数据。

    我写了一个小测试,我把一个文件压缩了很多次,看质量下降,在第三四次压缩的时候就可以看到,非常糟糕。

    但幸运的是,如果您始终使用与 JpegBitmapEncoder 相同的 QualityLevel,则不会出现降级。

    在此示例中,我将元数据中的关键字重写了 100 倍,质量似乎没有改变。

    private void LosslessJpegTest() {
      var original = "d:\\!test\\TestInTest\\20150205_123011.jpg";
      var copy = original;
      const BitmapCreateOptions createOptions = BitmapCreateOptions.PreservePixelFormat | BitmapCreateOptions.IgnoreColorProfile;
    
      for (int i = 0; i < 100; i++) {
        using (Stream originalFileStream = File.Open(copy, FileMode.Open, FileAccess.Read)) {
          BitmapDecoder decoder = BitmapDecoder.Create(originalFileStream, createOptions, BitmapCacheOption.None);
    
          if (decoder.CodecInfo == null || !decoder.CodecInfo.FileExtensions.Contains("jpg") || decoder.Frames[0] == null)
            continue;
    
          BitmapMetadata metadata = decoder.Frames[0].Metadata == null
            ? new BitmapMetadata("jpg")
            : decoder.Frames[0].Metadata.Clone() as BitmapMetadata;
    
          if (metadata == null) continue;
    
          var keywords = metadata.Keywords == null ? new List<string>() : new List<string>(metadata.Keywords);
          keywords.Add($"Keyword {i:000}");
          metadata.Keywords = new ReadOnlyCollection<string>(keywords);
    
          JpegBitmapEncoder encoder = new JpegBitmapEncoder {QualityLevel = 80};
          encoder.Frames.Add(BitmapFrame.Create(decoder.Frames[0], decoder.Frames[0].Thumbnail, metadata,
            decoder.Frames[0].ColorContexts));
    
          copy = original.Replace(".", $"_{i:000}.");
    
          using (Stream newFileStream = File.Open(copy, FileMode.Create, FileAccess.ReadWrite)) {
            encoder.Save(newFileStream);
          }
        }
      }
    }
    

    【讨论】:

      【解决方案3】:

      我仍然没有找到答案,不得不为 exiftool 编写包装器,而不是使用 WPF 的方式来处理元数据...... 可能 som1 会觉得它很有用。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2017-12-11
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多