2009-11-18 5 views
15

Im Eigenschaftenfenster eines JPEG-Bilds befindet sich eine Registerkarte mit der Bezeichnung "Zusammenfassung". Innerhalb dieser Registerkarte gibt es ein Feld namens "Kommentare". Ich möchte einen C# -Code schreiben, der eine bestimmte Zeichenfolge zu diesem Feld hinzufügt, z. B. "Dies ist ein Foto".Hinzufügen von Kommentaren zu einer JPEG-Datei mit C#

Kennt eine Art Seele da draußen das?

Vielen Dank.

Antwort

11

Der folgende Code löst mein Problem und fügt Kommentare zu einem bestimmten JPEG-Bild:

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

Dies ist im Wesentlichen eine leicht modifizierte Version des Codes unter dem Link gefunden, die Konamiman freundlich versorgt.

Bitte beachten Sie, dass diese Arbeit zu machen, müssen Sie .NET Verweise auf PresentationCore und Windows hinzuzufügen. Bei der Verwendung von Visual Studio 2008, kann dies über folgenden erreicht werden:

  1. Rechtsklick auf das Projekt im Solution Explorer

  2. aus der Dropdown-Liste wählen Sie Add 'Reference ...'

  3. Von der neuen Box, die, wählen Sie das ‚.NET‘ Tab

  4. Blättern Sie zu den beiden Referenzen oben erwähnt, und auf jedem öffnet, klicken Sie auf oK

Vielen Dank an danbystrom und Konamiman für Ihre Hilfe in dieser Angelegenheit. Ich schätze die schnelle Antwort sehr.

+0

Sie sollten abstimmen und/oder ihre Antworten akzeptieren, wenn Sie sie hilfreich fanden. Prost! – RickL

+0

Hoppla, mit meiner obigen Lösung habe ich einen kleinen Fehler gemacht. Ich hätte sagen sollen, dass Sie die PresentationCore Referenz hinzufügen müssen, nicht die System.Core Ps. Danke für das Heads-up Rick! Ich werde das sofort machen. –

+3

Angesichts der Tatsache, dass JPEG ein verlustbehaftetes Format ist, würden Sie erwarten, die Qualität zu verlieren, indem Sie Metadaten mit dieser Methode hinzufügen, oder ändert dies nur die Metadaten, ohne das Bild zu beeinflussen? –

0

Der einfache Teil:

diese Eigenschaft Artikel hinzufügen:

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; 

Um die PropertItems Kollektion Bild.

Der etwas schwerere Teil: Wie erstellen Sie ein neues PropertyItem, da es keinen öffentlichen Konstruktor hat?

Der übliche "Trick" ist, ein leeres Bild herumliegen zu haben, von dem Sie ein PropertyItem stehlen können. Seufzer

+1

und wie verbinden Sie das gestohlene PropertyItem mit dem realen Bild? Ist es eine Lese-Schreib-Eigenschaft/Variable? –

+0

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

+1

Frustrierend. Von msdn: "Ein PropertyItem soll nicht als eigenständiges Objekt verwendet werden. Ein PropertyItem-Objekt soll von Klassen verwendet werden, die von Image abgeleitet sind. Ein PropertyItem-Objekt wird zum Abrufen und Ändern der Metadaten vorhandener Bilder verwendet Dateien, um die Metadaten nicht zu erstellen. Daher hat die PropertyItem-Klasse keinen definierten öffentlichen Konstruktor und Sie können keine Instanz eines PropertyItem-Objekts erstellen. " – JYelton

1

Dank der vorherigen Tipps konnte ich folgendes zusammenstellen. Ich habe es getestet und es scheint zu funktionieren. Einer der größten Stolpersteine ​​war das Ermitteln der Id für das Feld, das Sie zuweisen möchten.

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

Dank der Antworten hier, ich habe eine Lösung codiert nur einen Kommentar mit Speicher zu setzen:

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

Vergessen Sie nicht, das Bild zu entsorgen, die von dieser Methode zurückgegeben wird.(Zum Beispiel nach dem Speichern des Bildes in eine Datei)

14

Basierend auf anderen Antworten schrieb ich die folgende Klasse, die verschiedene Metadaten-Manipulationen ermöglicht. Sie verwenden es wie folgt:

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 

Die Unterschiede zwischen meiner Lösung und den anderen sind nicht groß. Hauptsächlich habe ich das umgestaltet, um sauberer zu sein. Ich verwende auch die höheren Eigenschaften von BitmapMetadata anstelle der SetQuery Methode.

Hier ist der vollständige Code, der unter the MIT licence lizenziert ist. Sie müssen Referenzen zu PresentationCore, WindowsBase und System.Xaml hinzufügen.

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

Das ist fantastisch und genau das, was ich in einer sauberen ordentlichen Weise brauchte. Vielen Dank! –

+3

Ein Verweis auf System.Xaml ist ebenfalls erforderlich – Ben

+1

mit System.Windows.Media.Imaging –