2009-11-18 10 views
15

Dentro de la ventana de propiedades de una imagen JPEG, hay una pestaña llamada 'Resumen'. Dentro de esta pestaña, hay un campo llamado 'Comentarios' Me gustaría escribir un código C# que agregará una cadena dada a este campo, por ejemplo, "Esta es una foto".Cómo agregar 'Comentarios' a un archivo JPEG usando C#

¿Algún tipo de alma sabe cómo hacer esto?

Muchas gracias.

Respuesta

11

El siguiente código resuelve mi problema y añade los comentarios de una imagen JPEG dada a:

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

Ésta es esencialmente una versión ligeramente modificada del código que se encuentra bajo el enlace que gentilmente suministrada Konamiman.

Tenga en cuenta que para hacer este trabajo que tendrá que añadir referencias a .NET PresentationCore y WindowsBase. Si el uso de Visual Studio 2008, esto se puede lograr a través de lo siguiente:

  1. clic derecho en su proyecto en el Explorador de soluciones

  2. De la lista desplegable, seleccione Agregar 'Referencia ...'

  3. Desde la nueva caja que se abre, seleccione la pestaña 'NET'

  4. Vaya a las dos referencias mencionadas anteriormente y en cada uno, haga clic en OK

Muchas gracias a danbystrom y Konamiman por su ayuda en este asunto. Realmente aprecio la respuesta rápida.

+0

Debe votar y/o aceptar sus respuestas si las encontró útiles. ¡Aclamaciones! – RickL

+0

Vaya, cometí un pequeño error con mi solución anterior. Debería haber dicho que necesita agregar la referencia de PresentationCore, no el System.Core Ps. Gracias por el cara a cara Rick! Lo haré enseguida. –

+3

Dado que jpeg es un formato con pérdida, ¿esperaría perder calidad al agregar metadatos con este método, o solo modifica los metadatos sin afectar la imagen? –

0

La parte fácil:

este objeto propiedad:

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; 

a la colección del PropertItems de la Imagen.

La parte algo más engorrosa: ¿Cómo se crea un nuevo PropertyItem, ya que no tiene un constructor público?

El "truco" común es tener una imagen vacía de la que se puede robar un PropertyItem. suspiro

+1

y cómo asocias el PropertyItem robado a la imagen real? ¿Es una propiedad/variable de lectura/escritura? –

+0

realImage.PropertyItems.Add (emptyImage.PropertyItems [0]); –

+1

Frustrante. Desde msdn: "Un PropertyItem no está destinado a ser utilizado como un objeto independiente. Un objeto PropertyItem está destinado a ser utilizado por las clases que se derivan de Image. Un objeto PropertyItem se utiliza para recuperar y cambiar los metadatos de la imagen existente. archivos, no para crear los metadatos. Por lo tanto, la clase PropertyItem no tiene un constructor público definido, y no puede crear una instancia de un objeto PropertyItem ". – JYelton

1

Gracias a los consejos anteriores, pude juntar lo siguiente. Lo probé y parece funcionar. Uno de los mayores obstáculos fue determinar la Id necesaria para el campo que desea asignar.

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

Gracias a las respuestas aquí, he codifican una solución para establecer un comentario usando sólo la memoria:

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

No se olvide de disponer la imagen devuelta por este método.(Por ejemplo, después de guardar la imagen en un archivo)

14

En base a otras respuestas, escribí la siguiente clase que permite varias manipulaciones de metadatos. Lo usa así:

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 

Las diferencias entre mi solución y las demás no son grandes. Principalmente he refactorizado esto para ser más limpio. También uso las propiedades de nivel superior de BitmapMetadata, en lugar del método SetQuery.

Aquí está el código completo, que se licencia bajo the MIT licence. Deberá agregar referencias a PresentationCore, WindowsBase y 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]; 
    } 
} 
+1

Esto es fantástico y es exactamente lo que necesitaba de una manera limpia y ordenada. ¡Muchas gracias! –

+3

También es necesaria una referencia a System.Xaml – Ben

+1

usando System.Windows.Media.Imaging –

Cuestiones relacionadas