I see more than a few questions asking how to read metadata from an image, but I haven't seen as many asking how to write metadata. Basically, I need to add one item of metadata ("ImageDescription") to a PNG image I'm generating dynamically (creating a Bitmap object and procedurally generating all the content in it).
What would be the best way to add metadata to an image with .NET either before or just af开发者_C百科ter writing the file to disk?
You could use the FreeImage.NET library, which I believe can both read and write PNG files, as well as their metadata.
You can use BitmapMetadata from System.Windows.Media.Imaging to write iTXt values. VB Example:
' Imports System.Windows.Media.Imaging
' Imports System.Windows.Media
Dim width = 256
Dim height = 256
Dim pngMetadata = New BitmapMetadata("png")
' PNG spec: http://www.libpng.org/pub/png/book/chapter11.html - Info on the iTXt chunk (and other custom metadata tags).
pngMetadata.SetQuery("/iTXt/Keyword", "SomeKeyword".ToCharArray())
pngMetadata.SetQuery("/iTXt/TextEntry", "SomeValue")
Dim bitmap = New WriteableBitmap(width, height, 96, 96, PixelFormats.Gray8, Nothing)
Dim pixels = New Byte(width * height - 1) {}
For y = 0 To height - 1
For x = 0 To width - 1
pixels(y * width + x) = CByte(x)
Next
Next
bitmap.WritePixels(New Int32Rect(0, 0, width, height), pixels, width, 0)
Dim encoder = New PngBitmapEncoder()
encoder.Frames.Add(BitmapFrame.Create(bitmap, Nothing, pngMetadata, Nothing))
Using stream = File.Create("c:\pngWithMetaData.png")
encoder.Save(stream)
End Using
Needed to write some simple metadata into at least .png and .jpg and not have to drag another 3rd party library into my program. Nothing seemed to work after a few hours and it seems even if it was all the file type are different etc.
Thought about just storing in a file next to the image or keeping a small "database" of files and the data I needed.
For me, I just needed the data again when I reloaded the images. The data held a conversion factor that tells me how many pixels = 1mm.
Spoiler... turn away now if you don't like hacks.
Basically at the end of the file I wrote in binary "@Conv=" followed by the 8 byte double of the value.
My program opens the image in a binary read... seeks to the end minus then number of bytes total in my "hack" and looks for "@Conv=". If this is found it reads the value(s). The program then has the options to save once the scale factor is determined rather than having to measure and enter a value every time when opening.
I have code to read/query, update and add the data.
Works well with .png, .jpg and probably other types.
The files I do it to are copied and are local. Obviously if the files are re-written from some other application the data would be lost but that's probably true of any metadata for a lot of applications.
I ended up storing a few data items including an "origin" position (in pixels) and the preferred rotation (0,90,180,270) of the image.
Some vbcode just for kicks.
Shared arr_magic As Byte() = {CByte(AscW("@"c)), CByte(AscW("C"c)), CByte(AscW("o"c)), CByte(AscW("n"c)), CByte(AscW("v"c)), CByte(AscW("2"c)), CByte(AscW("="c))}
Const metaSize As Integer = 8 + 4 + 4 + 4
Function FileCheckScale(fn As String, ByRef origin As Point, ByRef angle As Integer, update As Boolean) As Double
Dim fr = New IO.StreamReader(fn)
Dim br As New IO.BinaryReader(fr.BaseStream)
fr.BaseStream.Seek(-(metaSize + arr_magic.Length), IO.SeekOrigin.End)
For i = 0 To arr_magic.Length - 1
If br.ReadByte() <> arr_magic(i) Then
fr.Close()
Return 0.0 ' <= 0 means not found
End If
Next
Dim v As Double = br.ReadDouble
If update Then
origin.X = br.ReadInt32
origin.Y = br.ReadInt32
angle = br.ReadInt32
End If
fr.Close()
Return v
End Function
Sub FileUpdateScale(fn As String, v As Double, origin As Point, angle As Integer)
Dim fr = New IO.FileStream(fn, IO.FileMode.Open, IO.FileAccess.Write)
Dim br As New IO.BinaryWriter(fr)
fr.Seek(-(metaSize + arr_magic.Length), IO.SeekOrigin.End)
br.Write(arr_magic)
br.Write(v)
br.Write(origin.X)
br.Write(origin.Y)
br.Write(angle)
fr.Close()
End Sub
Sub FileAddScale(fn As String, v As Double, origin As Point, angle As Integer)
Dim fr = New IO.StreamWriter(fn, True)
Dim br As New IO.BinaryWriter(fr.BaseStream)
fr.BaseStream.Seek(0, IO.SeekOrigin.End)
br.Write(arr_magic)
br.Write(v)
br.Write(origin.X)
br.Write(origin.Y)
br.Write(angle)
fr.Close()
End Sub
精彩评论