Как добавить «Комментарии» в файл JPEG с помощью C # - PullRequest
17 голосов
/ 18 ноября 2009

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

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

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

Ответы [ 5 ]

15 голосов
/ 20 мая 2014

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

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];
    }
}
12 голосов
/ 19 ноября 2009

Следующий код решает мою проблему и добавляет комментарии к данному изображению 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. Прокрутите до двух ссылок, упомянутых выше, и на каждой нажмите кнопку ОК

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

1 голос
/ 26 февраля 2014

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

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

Не забудьте утилизировать изображение, возвращаемое этим методом. (Например, после сохранения изображения в файл)

1 голос
/ 13 декабря 2013

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

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();
1 голос
/ 18 ноября 2009

Легкая часть:

Добавить объект:

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

...