InPlaceBitmapMetadataWriter.TrySave()返回true但不执行任何操作

时间:2010-05-02 21:10:38

标签: c# wpf metadata

在Windows 7中的一些.JPG文件(EPS预览,由Adobe Illustrator生成)InPlaceBitmapMetadataWriter.TrySave()在一些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文件一起正常工作。

欢迎任何想法:)

3 个答案:

答案 0 :(得分:4)

两件事:

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

    BitmapMetadata metadata = frame.Metadata.Clone() as BitmapMetadata;
    
  2. 填充,你需要填充。经过大约一天的修补工作,试图制作一些代码(类似于你的代码)后,我发现了这一点。如果您的图片文件中没有元数据填充, InPlaceBitmapMetadataWriter将无效。所以您需要以下内容:

    JpegBitmapEncoder encoder = new JpegBitmapEncoder();
    if(frame != null && metadata != null) {
        metadata.SetQuery("/app1/ifd/PaddingSchema:Padding", padding);
        encoder.Frames.Add(BitmapFrame.Create(frame, frame.Thumbnail, metadata, frame.ColorContexts));
        using (Stream outputFile = File.Open(_myoutputpath, FileMode.Create, FileAccess.ReadWrite)) {
            encoder.Save(outputFile);
        }
    }
    
  3. 现在,您可以使用位于_myoutputpath的文件,该文件为InPlaceBitmapMetadataWriter操作添加了元数据填充。

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

答案 1 :(得分:3)

您好我发现了this有关InPlaceBitmapMetadataWriter的文章,其中有人说TrySave()可能会损坏图像,这就是他建议在原始文件的副本上执行TrySave()的原因,如果是这样的话没有工作,添加填充到原始文件的副本,再次添加TrySave(),如果它工作,删除原始文件并重命名副本。

我挠了挠头问自己,为什么我要打扰InPlaceBitmapMetadataWriter并将3x原始文件写入磁盘以防TrySave()因为没有足够的填充而无法工作,如果我可以克隆元数据,写任何东西进入它们并立即组装jpeg文件。

然后我开始认为,也许多亏了InPlaceBitmapMetadataWriter,我可以编辑元数据而不会降低质量,但它看起来像是"只是"如果有足够的填充,可以帮助您更快地编写元数据。

我写了一个小测试,我多次压缩一个文件以查看质量下降,你可以在第三次压缩中看到它,这非常糟糕。

但幸运的是,如果你总是和JpegBitmapEncoder一起使用相同的QualityLevel,那么就没有降级。

在这个例子中,我在元数据中重写关键字100x,质量似乎没有改变。

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

答案 2 :(得分:0)

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