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

时间:2009-11-18 10:51:44

标签: c# metadata jpeg

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

有些灵魂知道怎么做吗?

非常感谢。

6 个答案:

答案 0 :(得分:15)

根据其他答案,我编写了以下类,允许进行各种元数据操作。你这样使用它:

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方法。

以下是完整代码,已在the MIT licence下获得许可。您需要添加对PresentationCoreWindowsBaseSystem.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];
    }
}

答案 1 :(得分:12)

以下代码解决了我的问题并为给定的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);
                    }
                }
            }
        }
    }

这实际上是Konamiman友情提供的链接下的代码的轻微修改版本。

请注意,为了完成这项工作,您需要将.NET引用添加到 PresentationCore WindowsBase 。如果使用Visual Studio 2008,可以通过以下方式实现:

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

  2. 从下拉列表中,选择添加'参考...'

  3. 在打开的新框中,选择“.NET”标签

  4. 滚动到上面提到的两个参考文献,然后单击“确定”

  5. 非常感谢danbystrom和Konamiman对此事的帮助。我非常感谢快速反应。

答案 2 :(得分:1)

简单的部分:

添加此属性项:

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;

到Image的PropertItems集合。

稍微麻烦的部分: 如何创建一个新的PropertyItem,因为它没有公共构造函数?

常见的“技巧”是在周围放置一个空图像,您可以从中偷取PropertyItem。 叹息

答案 3 :(得分:1)

感谢之前的提示,我能够将以下内容放在一起。我测试过它似乎有效。最大的绊脚石之一是确定您想要分配的字段所需的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();

答案 4 :(得分:1)

感谢此处的答案,我编写了一个解决方案,仅使用内存设置注释:

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

不要忘记处理此方法返回的图像。 (例如,将图像保存到文件后)

答案 5 :(得分:0)

Peter Kistler解决方案的一种变体,用于设置标题,主题和评论。我发现必须将项目创建为Unicode字节数组(类型1),并且Title,Subject和Comment的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)
        {

        }

    }