C# 如何使用C向JPEG文件添加“注释”

C# 如何使用C向JPEG文件添加“注释”,c#,metadata,jpeg,C#,Metadata,Jpeg,在JPEG图像的属性窗口中,有一个名为“摘要”的选项卡。在这个选项卡中,有一个名为“Comments”的字段,我想写一些c代码,它将向这个字段添加一个给定的字符串,例如这是一张照片 有什么善良的灵魂知道怎么做吗 非常感谢。简单的部分: 添加此属性项: var data = System.Text.Encoding.UTF8.GetBytes( "Some comments" ); PropertyItem pi; *** create an empty PropertyItem here pi.

在JPEG图像的属性窗口中,有一个名为“摘要”的选项卡。在这个选项卡中,有一个名为“Comments”的字段,我想写一些c代码,它将向这个字段添加一个给定的字符串,例如这是一张照片

有什么善良的灵魂知道怎么做吗

非常感谢。

简单的部分:

添加此属性项:

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;
到图像的PropertItems集合

更麻烦的部分是: 既然没有公共构造函数,如何创建新的PropertyItem


常见的技巧是有一个空的图像,你可以从中窃取一个PropertyItem。叹息

以下代码解决了我的问题,并向给定的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);
                    }
                }
            }
        }
    }
这基本上是科纳米曼善意提供的链接下的代码的一个稍加修改的版本

请注意,要使此功能正常工作,您需要添加对PresentationCore和WindowsBase的.NET引用。如果使用Visual Studio 2008,可以通过以下方式实现:

在解决方案资源管理器中右键单击项目

从下拉列表中,选择添加“参考…”

从打开的新框中,选择“.NET”选项卡

滚动到上面提到的两个参考,并在每个参考上单击“确定”


非常感谢danbystrom和Konamiman在这件事上的帮助。我真的很感谢您的快速回复。

多亏了前面的提示,我才能够将以下内容组合在一起。我已经测试过了,它似乎有效。最大的障碍之一是确定要分配的字段所需的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();

多亏了这里的答案,我编写了一个解决方案,只使用内存设置注释:

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

不要忘记处理此方法返回的映像。例如,在根据其他答案将图像保存到文件后,我编写了以下类,该类允许各种元数据操作。您可以这样使用它:

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
我的解决方案与其他解决方案之间的差异不大。基本上,我已经将其重构为更干净。我还使用BitmapMetadata的更高级别属性,而不是SetQuery方法

这是完整的代码,它是根据。您需要添加对PresentationCore、WindowsBase和System.Xaml的引用


Peter Kistler解决方案的变体,用于设置标题、主题和评论。我发现我必须将项目创建为Unicode字节数组类型1,标题、主题和注释的ID与EXIF XPTitle、XPSubject和XP Comment的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("\0", 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)
        {

        }

    }

你如何将被盗财产项目与真实图像联系起来?是否为读写属性/变量?realImage.PropertyItems.Add emptyImage.PropertyItems[0];令人沮丧的。来自msdn:PropertyItem不打算用作独立对象。PropertyItem对象用于从Image派生的类。PropertyItem对象用于检索和更改现有图像文件的元数据,而不是创建元数据。因此,PropertyItem类没有定义的公共构造函数,并且您无法创建PropertyItem对象的实例。此答案为如何创建PropertyItem的新实例提供了解决方案:如果您觉得他们的答案有用,您应该投票赞成和/或接受他们的答案。干杯哎呀,我在上面的解决方案中犯了一个小错误。我应该说你需要添加PresentationCore参考,而不是系统。核心Ps。谢谢你的提醒!我会马上这么做。鉴于jpeg是一种有损格式,您是否希望通过使用此方法添加元数据来降低质量,或者这只是在不影响图片的情况下修改元数据?这太棒了,正是我所需要的干净有序的方式。非常感谢你!使用System.Windows.Media.ImagingI也需要对System.Xaml进行引用。我知道这很旧,但对其他类型的文件(如Office文档、文本文件、PDF等)也可以这样做吗?
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("\0", 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)
        {

        }

    }