我看到有不少问题询问如何从图像中读取元数据,但是关于如何写入元数据的问题却没有那么多。基本上,我需要向我动态生成的PNG图像添加一个元数据项("ImageDescription")(创建一个位图对象并在其中过程性地生成所有内容)。
.NET中在将文件写入磁盘之前或之后添加图像元数据的最佳方法是什么?
我看到有不少问题询问如何从图像中读取元数据,但是关于如何写入元数据的问题却没有那么多。基本上,我需要向我动态生成的PNG图像添加一个元数据项("ImageDescription")(创建一个位图对象并在其中过程性地生成所有内容)。
.NET中在将文件写入磁盘之前或之后添加图像元数据的最佳方法是什么?
您可以使用System.Windows.Media.Imaging中的BitmapMetadata来编写iTXt值。 VB示例:
' 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
需要将一些简单的元数据写入至少 .png 和 .jpg 文件中,而不必将另一个第三方库拖入我的程序。几个小时后似乎什么都没用,因为所有文件类型都不同。
考虑过只是将其存储在图像旁边的文件中或保留一个小型的“数据库”来存储文件和所需数据。
对我来说,当我重新加载图像时,我只需要再次获取数据。该数据包含一个转换因子,告诉我多少像素等于1毫米。
剧透... 如果您不喜欢黑客,请立即离开。
基本上,在文件末尾以二进制形式写入“@Conv=”,然后是8字节的double值。
我的程序以二进制读取方式打开图像...定位到“hack”中总字节数减去结尾处,然后查找“@Conv=”。如果找到,则读取值。然后程序就可以在确定比例因子后选择保存,而不必每次打开时测量并输入值。
我有代码来读取/查询、更新和添加数据。
适用于 .png、.jpg 和可能其他类型。
我要处理的文件是复制的,而且是本地的。显然,如果文件被其他应用程序重新写入,数据就会丢失,但对于许多应用程序的任何元数据来说,这可能都是正确的。
最终,我存储了一些数据项,包括“原始”位置(以像素为单位)和图像的首选旋转(0、90、180、270)。
一些VB代码只是为了好玩。
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