Как добавить комментарии к файлу JPEG с помощью С#

В окне свойств изображения JPEG есть вкладка под названием "Сводка". На этой вкладке есть поле под названием "Комментарии", я хотел бы написать некоторый код С#, который добавит заданную строку в это поле, например "Это фотография".

Знает ли какая-то душа, как это сделать?

Большое спасибо.

Ответы

Ответ 1

Следующий код решает мою проблему и добавляет комментарии к определенному изображению 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, это может быть достигнуто с помощью следующего:

  • Щелкните правой кнопкой мыши на своем проекте в обозревателе решений

  • В раскрывающемся списке выберите Добавить ссылку...

  • В открывшемся новом окне выберите вкладку ".NET"

  • Перейдите к двум ссылкам, упомянутым выше и каждому, нажмите "ОК"

Большое спасибо как данбистрому, так и Konamiman за вашу помощь в этом вопросе. Я очень ценю быстрый ответ.

Ответ 2

На основании других ответов я написал следующий класс, который позволяет выполнять различные манипуляции с метаданными. Вы используете его следующим образом:

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.

Вот полный код, который лицензируется под лицензия 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];
    }
}

Ответ 3

Благодаря предыдущим советам я смог собрать следующее. Я тестировал его и, похоже, работал. Одним из самых больших камней преткновения было определение идентификатора, необходимого для поля, которое вы хотите назначить.

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

Благодаря ответам здесь я закодировал решение для установки комментария только с использованием памяти:

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

Простая часть:

Добавьте этот элемент недвижимости:

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. Вздох