如何使用 C# 向 JPEG 文件添加“注释”
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/1755185/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to Add 'Comments' to a JPEG File Using C#
提问by John P
Within the property window of a JPEG image, there is a tab called 'Summary'. Within this tab, there is a field called 'Comments' I would like to write some c# code which will add a given string to this field e.g "This is a photo".
在 JPEG 图像的属性窗口中,有一个名为“摘要”的选项卡。在此选项卡中,有一个名为“评论”的字段,我想编写一些 c# 代码,该代码将向该字段添加给定的字符串,例如“这是一张照片”。
Does some kind soul out there know how to do this?
有没有好心人知道怎么做?
Many thanks.
非常感谢。
采纳答案by John P
The following code solves my problem and adds comments to a given JPEG image:
以下代码解决了我的问题并向给定的 JPEG 图像添加注释:
public void addImageComment(string imageFlePath, string comments)
{
string jpegDirectory = Path.GetDirectoryName(imageFlePath);
string jpegFileName = Path.GetFileNameWithoutExtension(imageFlePath);
BitmapDecoder decoder = null;
BitmapFrame bitmapFrame = null;
BitmapMetadata metadata = null;
FileInfo originalImage = new FileInfo(imageFlePath);
if (File.Exists(imageFlePath))
{
// load the jpg file with a JpegBitmapDecoder
using (Stream jpegStreamIn = File.Open(imageFlePath, FileMode.Open, FileAccess.ReadWrite, FileShare.None))
{
decoder = new JpegBitmapDecoder(jpegStreamIn, BitmapCreateOptions.PreservePixelFormat, BitmapCacheOption.OnLoad);
}
bitmapFrame = decoder.Frames[0];
metadata = (BitmapMetadata)bitmapFrame.Metadata;
if (bitmapFrame != null)
{
BitmapMetadata metaData = (BitmapMetadata)bitmapFrame.Metadata.Clone();
if (metaData != null)
{
// modify the metadata
metaData.SetQuery("/app1/ifd/exif:{uint=40092}", comments);
// get an encoder to create a new jpg file with the new metadata.
JpegBitmapEncoder encoder = new JpegBitmapEncoder();
encoder.Frames.Add(BitmapFrame.Create(bitmapFrame, bitmapFrame.Thumbnail, metaData, bitmapFrame.ColorContexts));
//string jpegNewFileName = Path.Combine(jpegDirectory, "JpegTemp.jpg");
// Delete the original
originalImage.Delete();
// Save the new image
using (Stream jpegStreamOut = File.Open(imageFlePath, FileMode.CreateNew, FileAccess.ReadWrite))
{
encoder.Save(jpegStreamOut);
}
}
}
}
}
This is essentially a lightly modified version of the code found under the link which Konamiman kindly supplied.
这基本上是在 Konamiman 善意提供的链接下找到的代码的轻微修改版本。
Please be aware that to make this work you will need to add .NET references to PresentationCoreand WindowsBase. If using Visual Studio 2008, this can be achieved via the following:
请注意,要完成这项工作,您需要添加对PresentationCore和WindowsBase 的.NET 引用。如果使用 Visual Studio 2008,可以通过以下方式实现:
Right click on your project in the Solution Explorer
From the drop down list, select Add 'Reference...'
From the new box which opens, select the '.NET' tab
Scroll to the two references mentioned above and on each, click ok
在解决方案资源管理器中右键单击您的项目
从下拉列表中,选择添加“参考...”
从打开的新框中,选择“.NET”选项卡
滚动到上面提到的两个参考文献,并在每个参考文献上单击确定
Many thanks to both danbystrom and Konamiman for your help in this matter. I really appreciate the quick response.
非常感谢 danbystrom 和 Konamiman 在这件事上的帮助。我真的很感激快速的反应。
回答by Dan Bystr?m
The easy part:
简单的部分:
Add this property item:
添加此属性项:
var data = System.Text.Encoding.UTF8.GetBytes( "Some comments" );
PropertyItem pi;
*** create an empty PropertyItem here
pi.Type = 2;
pi.Id = 37510;
pi.Len = data.Length;
pi.Value = data;
To the Image's PropertItems collection.
到图像的PropertItems 集合。
The somewhat more cumbersome part: How do you create a new PropertyItem, since it has no public constructor?
比较麻烦的部分:如何创建新的 PropertyItem,因为它没有公共构造函数?
The common "trick" is to have an empty image lying around from which you can steal a PropertyItem. sigh
常见的“技巧”是在周围放置一个空图像,您可以从中窃取一个 PropertyItem。叹
回答by Peter Kistler
Thanks to the previous tips I was able to put the following together. I've tested it and it seems to work. One of the biggest stumbling blocks was determining the Id needed for the field you want to assign.
多亏了前面的提示,我才能将以下内容放在一起。我已经测试过了,它似乎有效。最大的绊脚石之一是确定要分配的字段所需的 Id。
string fileName = "c:/SomeImage.jpg";
// Retrieve the Image
System.Drawing.Image originalImage = System.Drawing.Image.FromFile(fileName);
// Get the list of existing PropertyItems. i.e. the metadata
PropertyItem[] properties = originalImage.PropertyItems;
// Create a bitmap image to assign attributes and do whatever else..
Bitmap bmpImage = new Bitmap((Bitmap)originalImage);
// Don't need this anymore
originalImage.Dispose();
// Get / setup a PropertyItem
PropertyItem item = properties[0]; // We have to copy an existing one since no constructor exists
// This will assign "Joe Doe" to the "Authors" metadata field
string sTmp = "Joe DoeX"; // The X will be replaced with a null. String must be null terminated.
var itemData = System.Text.Encoding.UTF8.GetBytes(sTmp);
itemData[itemData.Length - 1] = 0;// Strings must be null terminated or they will run together
item.Type = 2; //String (ASCII)
item.Id = 315; // Author(s), 315 is mapped to the "Authors" field
item.Len = itemData.Length; // Number of items in the byte array
item.Value = itemData; // The byte array
bmpImage.SetPropertyItem(item); // Assign / add to the bitmap
// This will assign "MyApplication" to the "Program Name" field
sTmp = "MyApplicationX";
itemData = System.Text.Encoding.UTF8.GetBytes(sTmp);
itemData[itemData.Length - 1] = 0; // Strings must be null terminated or they will run together
item.Type = 2; //String (ASCII)
item.Id = 305; // Program Name, 305 is mapped to the "Program Name" field
item.Len = itemData.Length;
item.Value = itemData;
bmpImage.SetPropertyItem(item);
// Save the image
bmpImage.Save(fileName, System.Drawing.Imaging.ImageFormat.Jpeg);
//Clean up
bmpImage.Dispose();
回答by Bas de Raad
Thanks to the answers here, I've coded a solution to set a comment using memory only:
感谢这里的答案,我编写了一个解决方案来仅使用内存设置评论:
public static Image SetImageComment(Image image, string comment) {
using (var memStream = new MemoryStream()) {
image.Save(memStream, ImageFormat.Jpeg);
memStream.Position = 0;
var decoder = new JpegBitmapDecoder(memStream, BitmapCreateOptions.PreservePixelFormat, BitmapCacheOption.OnLoad);
BitmapMetadata metadata;
if (decoder.Metadata == null) {
metadata = new BitmapMetadata("jpg");
} else {
metadata = decoder.Metadata;
}
metadata.Comment = comment;
var bitmapFrame = decoder.Frames[0];
BitmapEncoder encoder = new JpegBitmapEncoder();
encoder.Frames.Add(BitmapFrame.Create(bitmapFrame, bitmapFrame.Thumbnail, metadata, bitmapFrame.ColorContexts));
var imageStream = new MemoryStream();
encoder.Save(imageStream);
imageStream.Position = 0;
image.Dispose();
image = null;
return Image.FromStream(imageStream);
}
}
Don't forget to dispose the image that is returned by this method. (For example after saving the image to a file)
不要忘记处理此方法返回的图像。(例如将图像保存到文件后)
回答by Martin Eden
Based on other answers I wrote the following class which allows various metadata manipulations. You use it like this:
基于其他答案,我编写了以下允许各种元数据操作的类。你像这样使用它:
var jpeg = new JpegMetadataAdapter(pathToJpeg);
jpeg.Metadata.Comment = "Some comments";
jpeg.Metadata.Title = "A title";
jpeg.Save(); // Saves the jpeg in-place
jpeg.SaveAs(someNewPath); // Saves with a new path
The differences between my solution and the others are not large. Principally I have refactored this to be cleaner. I also use the higher level properties of BitmapMetadata
, rather than the SetQuery
method.
我的解决方案与其他解决方案之间的差异并不大。主要是我重构了它以使其更干净。我还使用BitmapMetadata
, 而不是SetQuery
方法的更高级别的属性。
Here is the full code, which is licensed under the MIT licence. You will need to add references to PresentationCore
, WindowsBase
, and System.Xaml
.
这是完整的代码,它在 MIT许可下获得许可。您需要将引用添加到PresentationCore
,WindowsBase
和System.Xaml
。
public class JpegMetadataAdapter
{
private readonly string path;
private BitmapFrame frame;
public readonly BitmapMetadata Metadata;
public JpegMetadataAdapter(string path)
{
this.path = path;
frame = getBitmapFrame(path);
Metadata = (BitmapMetadata)frame.Metadata.Clone();
}
public void Save()
{
SaveAs(path);
}
public void SaveAs(string path)
{
JpegBitmapEncoder encoder = new JpegBitmapEncoder();
encoder.Frames.Add(BitmapFrame.Create(frame, frame.Thumbnail, Metadata, frame.ColorContexts));
using (Stream stream = File.Open(path, FileMode.Create, FileAccess.ReadWrite))
{
encoder.Save(stream);
}
}
private BitmapFrame getBitmapFrame(string path)
{
BitmapDecoder decoder = null;
using (Stream stream = File.Open(path, FileMode.Open, FileAccess.ReadWrite, FileShare.None))
{
decoder = new JpegBitmapDecoder(stream, BitmapCreateOptions.PreservePixelFormat, BitmapCacheOption.OnLoad);
}
return decoder.Frames[0];
}
}
回答by SimonKravis
A variant on Peter Kistler's solution to set Title, Subject and Comment. I found I had to create items as Unicode byte array (type 1) and the IDs for Title, Subject and Comment are the same as for EXIF XPTitle, XPSubject and XP Comment. sFileOut can be the same as sFile.
Peter Kistler 设置标题、主题和评论的解决方案的变体。我发现我必须将项目创建为 Unicode 字节数组(类型 1),并且标题、主题和评论的 ID 与 EXIF XPTitle、XPSubject 和 XP 评论的 ID 相同。sFileOut 可以与 sFile 相同。
public static void SetWindowsTags2(string sFile, string sFileOut, string Title = "", string Subject = "", string Comment = "", bool bShowError = false)
{
try
{
// Retrieve the Image
System.Drawing.Image originalImage = System.Drawing.Image.FromFile(sFile);
// Get the list of existing PropertyItems. i.e. the metadata
PropertyItem[] properties = originalImage.PropertyItems;
/*foreach (PropertyItem propItem in properties)
{
string sTag = System.Text.Encoding.UTF8.GetString(propItem.Value);
string sItem = sTag.Replace("##代码##", string.Empty);
Debug.Print(propItem.Id.ToString() + ", " + propItem.Type + ", " + sItem);
}*/
// Create a bitmap image to assign attributes and do whatever else..
Bitmap bmpImage = new Bitmap((Bitmap)originalImage);
// Don't need this anymore
originalImage.Dispose();
// Get / setup a PropertyItem
PropertyItem item = properties[0]; // We have to copy an existing one since no constructor exists
var itemData = System.Text.Encoding.Unicode.GetBytes(Title);
itemData[itemData.Length - 1] = 0;// Strings must be null terminated or they will run together
item.Type = 1; //Unicode Byte Array
item.Id = 40091; // Title ID
item.Len = itemData.Length; // Number of items in the byte array
item.Value = itemData; // The byte array
bmpImage.SetPropertyItem(item); // Assign / add to the bitmap
itemData = System.Text.Encoding.Unicode.GetBytes(Subject);
itemData[itemData.Length - 1] = 0; // Strings must be null terminated or they will run together
item.Type = 1; //Unicode Byte Array
item.Id = 40095; // subject
item.Len = itemData.Length;
item.Value = itemData;
bmpImage.SetPropertyItem(item); // Assign / add to the bitmap
itemData = System.Text.Encoding.Unicode.GetBytes(Comment);
itemData[itemData.Length - 1] = 0; // Strings must be null terminated or they will run together
item.Type = 1; ////Unicode Byte Array
item.Id = 40092; // comment
item.Len = itemData.Length;
item.Value = itemData;
bmpImage.SetPropertyItem(item); // Assign / add to the bitmap
// Save the image
bmpImage.Save(sFileOut, System.Drawing.Imaging.ImageFormat.Jpeg);
//Clean up
bmpImage.Dispose();
}
catch (Exception Ex)
{
}
}