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

发布于 2024-08-30 22:13:54 字数 1049 浏览 1 评论 0原文

在 Windows 7 中的某些 .JPG 文件(EPS 预览,由 Adob​​e 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;
}

图像示例

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

欢迎任何想法:)

On some .JPG files (EPS previews, generated by Adobe Illustrator) in Windows 7 InPlaceBitmapMetadataWriter.TrySave() returns true after some SetQuery() calls, but does nothing.

Code sample:

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

You can reproduce problem (if you have Windows 7) by downloading image sample and using this code sample to set title on this image.
Image has enough room for metadata - and this code sample works fine on my WinXP.
Same code works fine on Win7 with other .JPG files.

Any ideas are welcome :)

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

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

发布评论

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

评论(3

殊姿 2024-09-06 22:13:54

有两件事:

  1. 我认为您无法像那样写入元数据变量,因为它将被冻结。因此,您必须克隆它:

    BitmapMetadata元数据=frame.Metadata.Clone() as BitmapMetadata;
    
    
  2. Padding,您需要填充。我在花了大约一天的时间尝试使一些代码(与您的代码类似)工作后发现了这一点。如果图像文件中没有元数据填充,InPlaceBitmapMetadataWriter 将无法工作。因此您需要类似以下内容:

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

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

本文和附加代码应该对您有帮助出去。

Two things:

  1. I don't think you will be able to write to your metadata variable just like that, as it will be Frozen. So, you will have to clone it:

    BitmapMetadata metadata = frame.Metadata.Clone() as BitmapMetadata;
    
  2. Padding, you need padding. I found this out after about a day's worth of tinkering around trying to make some code (similar to yours) work. InPlaceBitmapMetadataWriter will not work if there is no metadata padding in your image file. So you need something like:

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

Now you can use the file located at _myoutputpath which has added metadata padding for your InPlaceBitmapMetadataWriter operations.

This article and attached code should help you out.

挽梦忆笙歌 2024-09-06 22:13:54

嗨,我发现这个 关于 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);
      }
    }
  }
}

Hi I found this article about InPlaceBitmapMetadataWriter where the guy said that TrySave() might corrupt the image and that's why he advised to do TrySave() on the copy of the original file and if this doesn't work, add padding to the copy of original file and than TrySave() again and if it works, delete the original and rename the copy.

I scratched my head and asked myself why I should bother with InPlaceBitmapMetadataWriter and writing 3x original file to the disk in case TrySave() doesn't work because there is not enough padding, if I can clone metadata, write whatever into them and assemble jpeg file right away.

Then I started to think that maybe thanks to InPlaceBitmapMetadataWriter I can edit metadata without losing quality, but it looks like it "just" helps you to write metadata more quickly if there is enough padding.

I wrote a small test where I compress one file many times to see the quality degradation and you can see it in the third-fourth compression, which is very bad.

But luckily, if you always use same QualityLevel with JpegBitmapEncoder there is no degradation.

In this example I rewrite keywords 100x in metadata and the quality seems not to change.

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);
      }
    }
  }
}
满意归宿 2024-09-06 22:13:54

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

I still didn't find the answer and has to write wrapper for exiftool instead of using WPF's way to work with metadata...
May be som1 will find it useful.

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