开发者

Write metadata to PNG image in .NET

开发者 https://www.devze.com 2023-01-08 06:07 出处:网络
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\")

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
0

精彩评论

暂无评论...
验证码 换一张
取 消