C# 修改元数据后保存图像

C# 修改元数据后保存图像,c#,image,C#,Image,我在修改图像元数据后保存图像时遇到问题。 基本上,我想打开我的图像,显示它,修改它的EXIF元数据,然后保存它 首先,我在保存图像时遇到问题,因为原始文件被锁定(我有一个通用的GTI+错误)。在阅读了这里的一些帖子并找到了问题所在之后,我用using关键字解决了锁的问题 但问题是,如果我使用“using”,我的图像的propertyItems列表是空的,我无法修改元数据 下面是我如何加载图像(通过不同的测试) 使用streamReader或MemoryStream没有帮助 下面是我如何修改和保存

我在修改图像元数据后保存图像时遇到问题。 基本上,我想打开我的图像,显示它,修改它的EXIF元数据,然后保存它

首先,我在保存图像时遇到问题,因为原始文件被锁定(我有一个通用的GTI+错误)。在阅读了这里的一些帖子并找到了问题所在之后,我用using关键字解决了锁的问题

但问题是,如果我使用“using”,我的图像的propertyItems列表是空的,我无法修改元数据

下面是我如何加载图像(通过不同的测试)

使用streamReader或MemoryStream没有帮助

下面是我如何修改和保存图像:

 PropertyItem p = this.photo.Image.GetPropertyItem(0x5090);

 p.Id = 0x320;
 p.Type = 2; // Type ASCII
 p.Len = boxTitle.Text.Length;
 p.Value = Encoding.ASCII.GetBytes(boxTitle.Text);

 this.photo.Image.SetPropertyItem(p);
 this.photo.Image.Save(this.photo.Path);

你能解释一下为什么我使用“using”时没有属性项,以及我如何修改图像元数据并保存它吗?

你必须在阅读原始图像后保留它,修改属性值,然后将其保存为新图像。显然,微软并没有在内存中复制元数据——只复制更改。因此,原始图像将保持锁定状态,以保留原始元数据。要修改图像的元数据,请保存一个新图像,然后用它替换旧图像(或删除并重命名)。

似乎
位图
构造函数没有复制
属性项。您可以在图像之间复制
PropertyItems
,然后释放文件:

using (var bmpTemp = new Bitmap(this.path))
{
    this.image = new Bitmap(bmpTemp);
    foreach (var pi in bmpTemp.PropertyItems)
    {
        this.image.SetPropertyItem(pi);
    }
}

这似乎仍然比复制文件好。

我同意——这相当愚蠢。问题在于,每当exif数据的大小发生变化时,几乎都必须重新写入图像,因为它位于文件中的较早位置,并且具有绝对和相对偏移量。另一个需要考虑的是,如果使用.NET重写图像,则必须小心,否则最终会导致质量降低。如果你想使用第三方库,我相信imageMagick会更好。它似乎可以工作,但读/写每张图片的元数据会导致很多图片的性能问题正如我所说的,这是一种更干净的解决问题的方法,但在我的情况下效果不佳。当我要加载数百个图像时,它会导致性能的真正问题。此外,还有一些副作用需要使用此解决方案来管理,而不是使用以下方法之一xpda@thibon我理解。不幸的是,我没有想出更好的办法:)但我很好奇,在内存中复制属性怎么会比复制文件更痛苦呢?它实际上并没有在内存中复制属性。即使bmpTemp在内存中,元数据也不在内存中。这就是文件被锁定的原因。每个propertyitem至少需要一次磁盘访问,并且它们以相当混乱的格式保存。@BartoszKP在我的例子中,我的所有映像都是在程序启动时加载的。使用您的解决方案,我会将每个文件的propertyItem复制到解锁图像中。使用另一种解决方案,我只创建/删除/重命名要更改属性项的图像,一次一个,对用户来说是透明的。我希望我的头脑足够清楚explanations@thibon是的,这是有道理的。
using (var bmpTemp = new Bitmap(this.path))
{
    this.image = new Bitmap(bmpTemp);
    foreach (var pi in bmpTemp.PropertyItems)
    {
        this.image.SetPropertyItem(pi);
    }
}