Запись метаданных в изображение PNG в .NET

Я вижу много вопросов о том, как считывать метаданные с изображения, но не так много вопросов о том, как записывать метаданные. По сути, мне нужно добавить один элемент метаданных («ImageDescription») к изображению PNG, которое я создаю динамически (создавая объект Bitmap и процедурно генерируя все содержимое в нем).

Как лучше всего добавить метаданные к изображению с помощью .NET до или сразу после записи файла на диск?


person Sukasa    schedule 22.07.2010    source источник


Ответы (3)


Вы можете использовать библиотеку FreeImage.NET, которая, как мне кажется, может читать и записывать файлы PNG, а также их метаданные.

person LBushkin    schedule 22.07.2010
comment
Спасибо, похоже, это должно работать ... если бы я только мог заставить его, казалось бы, написать тег, который я хочу написать - person Sukasa; 23.07.2010

Вы можете использовать BitmapMetadata из System.Windows.Media.Imaging для записи значений iTXt. Пример ВБ:

' 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
person edhubbell    schedule 11.12.2018

Нужно было записать некоторые простые метаданные, по крайней мере, в .png и .jpg, и мне не нужно было перетаскивать в мою программу другую стороннюю библиотеку. Казалось, что через несколько часов ничего не сработало, и кажется, что даже если бы все типы файлов были разными и т. Д.

Думал просто хранить в файле рядом с изображением или держать небольшую «базу данных» файлов и данных, которые мне нужны.

Для меня мне просто снова понадобились данные, когда я перезагрузил изображения. Данные содержали коэффициент преобразования, который говорит мне, сколько пикселей = 1 мм.

Спойлер... отвернитесь, если вам не нравятся хаки.

В основном в конце файла я написал в двоичном формате "@Conv=", за которым следует 8-байтовое двойное значение.

Моя программа открывает изображение в двоичном чтении ... ищет конец минус, а затем общее количество байтов в моем «взломе» и ищет «@Conv =». Если это найдено, он считывает значение (я). Затем программа имеет возможность сохранить после определения коэффициента масштабирования, вместо того, чтобы измерять и вводить значение каждый раз при открытии.

У меня есть код для чтения/запроса, обновления и добавления данных.

Хорошо работает с .png, .jpg и, возможно, другими типами.

Файлы, которые я делаю, копируются и являются локальными. Очевидно, что если файлы перезаписываются из какого-либо другого приложения, данные будут потеряны, но это, вероятно, верно для любых метаданных для многих приложений.

В итоге я сохранил несколько элементов данных, включая исходную позицию (в пикселях) и предпочтительный поворот (0,90,180,270) изображения.

Немного vbcode просто для удовольствия.

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
person TheWhitde    schedule 16.03.2020