Czytanie/zapisywanie metadanych EXIF

Exif jest to standard metadanych dla plików z obrazkami, wydany przez Japan Electronics and Information Technology Industries Association. Tak po prostu są to informacje dołączone do pliku graficznego, które stanowią dodatkową informację dla użytkownika. Można w nich zapisać tagi obrazka, autora, tytuł, dodać komentarz czy prawa autorskie. Takich danych, które można dołączyć do pliku, jest bardzo dużo. Podgląd niektórych metadanych można zobaczyć, klikając prawym przyciskiem myszy na plik graficzny i wybierając opcje właściwości:

exif_1

 

Dane te często przydają się do filtrowania dużej ilości zdjęć no i oczywiście do sprawdzania źródła pliku. Pełną listę dostępnych adresów metadanych i ich opis dostępny jest na stronie: http://www.exiv2.org/tags.html

U mnie w firmie te dane przydały się do stworzenia archiwum plików graficznych, ich filtrowanie i wyodrębnianie. Dzięki stworzonemu oprogramowaniu możliwe jest szybkie dodawanie i edytowanie metadanych ich segregacja i logiczny system archiwizacji.

My zrobimy sobie proty program do wyświetlania metadanych i ich zapisywania. Będziemy mieli też opcję na wyświetlenie wszystkich adresów metadanych, aby było można je dodać. Zaczynamy od zbudowania formy:

exif1

Rodzaj elementu Nazwa elementu Ustawienia
Form Form1 Name: Form1
Text: EXIF edytor
Size: 836; 492
Button Button1 Name: Button1
Size: 399; 23
Location: 13; 13
Text: Załaduj obrazek
PictureBox PictureBox1 Name: PictureBox1
Size: 400; 400
Location: 13; 42
BorderStyle: FixedSingle
Button Button2 Name: Button2
Size: 175; 23
Location: 633; 418
Text: Wyświetl wszystkie identyfikatory
Button Button3 Name: Button3
Size: 208; 23
Location: 419; 418
Text: Zapisz
DataGridView DataGridView1 Name: DataGridView1
Size: 389; 399
Location: 419; 13
AllowUserToAddRows: False
AllowUserToDeleteRows: False
RowHeadersVisible: False
OpenFileDialog OpenFileDialog1 Name: OpenFileDialog1

 

Tabela Kolumna Ustawienia
DataGridView1 Column1 Name: Column1
Width: 70
HeaderText: Exif Prop
DataGridView1 Column2 Name: Column2
Width: 150
HeaderText: Opis
DataGridView1 Column3 Name: Column3
AutoSizeMode: Fill
HeaderText: Wartość

Naszą pracę zaczniemy od zaimportowania składników wymaganych do działania naszego programu i stworzenie listy z adresami i opisem naszych metadanych.

Imports System.Drawing.Imaging
Imports System.IO
Imports System.Reflection
Imports System.Text

'słownik naszych metadanych
Public Enum EXIFProperty
    Temat = 40095
    Tytuł = 40091
    Komentarz = 40092
    Autor = 40093
    Tagi = 40094
End Enum

Public Class Form1

End Class

Musimy pobrać obrazek z dysku. W tym celu dodamy sobie dialog, który będzie filtrował nam pliki i umożliwi zapisanie jego ścieżki do zmiennej.

    Dim lokalizacjaPliku As String 'zmienna przechowująca lokalizacje pliku graficznego
    Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
        'filtr dialogu
        OpenFileDialog1.Filter = "Image Files (*.jpg, *.jpeg)|*.jpg;*.jpeg|All Files (*.*)|*.*"
        OpenFileDialog1.Title = "Wybierz format pliku graficznego"
        OpenFileDialog1.FileName = ""
        '
        Try
            With OpenFileDialog1
                'dialog z użytkownikiem
                If OpenFileDialog1.ShowDialog() = DialogResult.OK Then
                    lokalizacjaPliku = .FileName 'podpisanie zmiennej

                    Dim fi As New System.IO.FileInfo(lokalizacjaPliku)
                    If fi.Extension.ToLower = ".gif" Or fi.Extension.ToLower = ".bmp" Or fi.Extension.ToLower = ".jpg" Or fi.Extension.ToLower = ".jpeg" Or fi.Extension.ToLower = ".png" Then
                        Dim ImageFileStream As New FileStream(lokalizacjaPliku, IO.FileMode.Open)
                        Dim bm As New Bitmap(ImageFileStream)
                        PictureBox1.SizeMode = PictureBoxSizeMode.Zoom
                        PictureBox1.Image = bm
                        ImageFileStream.Close()
                        'pobiera metadane z pliku
                        pobierzDaneEXIF()
                    Else
                        'ustawia obrazek błędu jeśli plik nie jest obrazem 
                        PictureBox1.Image = PictureBox1.ErrorImage
                        'i sprawdzi czy plik zawiera metadane
                        Try
                            pobierzDaneEXIF()
                        Catch ex As Exception
                            MsgBox(ex.ToString)
                        End Try
                    End If
                End If
            End With
        Catch ex As Exception
            MsgBox(ex.ToString)
        End Try
    End Sub

Teraz pobierzemy nasze metadane. Służy do tego metoda pobierzDaneEXIF():

    Private Sub pobierzDaneEXIF()
        DataGridView1.Rows.Clear() 'czyści tabele
        'otwiera nasz obrazek
        Dim imgFile As FileStream = New FileStream(lokalizacjaPliku, FileMode.Open, FileAccess.Read)
        'ładuje go do zmiennej TheImage
        Dim TheImage As Image = Image.FromStream(imgFile)
        'czyta wszystkie metadane i tworzy tablice AllProperties
        Dim AllProperties As PropertyItem() = TheImage.PropertyItems
        'ładujemy identyfikatory naszych metadanych do listy
        Dim listaWszystkichPropItem As New List(Of Integer)
        For i As Integer = 0 To AllProperties.Length - 1
            listaWszystkichPropItem.Add(AllProperties(i).Id)
        Next
        Dim PropItem As PropertyItem = AllProperties(0)
        'uruchamiamy pętle wszystkich znanych przez nas metadanych (tych z Enum)
        For Each tstEnum As EXIFProperty In System.Enum.GetValues(GetType(EXIFProperty))
            DataGridView1.Rows.Add() 'dodaje wiersz
            DataGridView1.Rows(DataGridView1.Rows.Count - 1).Cells(0).Value = CInt(tstEnum).ToString 'dodaje identyfikator EXIF z enum
            DataGridView1.Rows(DataGridView1.Rows.Count - 1).Cells(1).Value = tstEnum.ToString 'dodaje tytuł numeru EXIF z enum
            ' jeśli plik zawiera nasz identyfikator enum wtedy doda go do tabeli
            If listaWszystkichPropItem.Contains(CInt(tstEnum)) Then
                DataGridView1.Rows(DataGridView1.Rows.Count - 1).Cells(2).Value = System.Text.Encoding.Unicode.GetString(TheImage.GetPropertyItem(CInt(tstEnum)).Value).ToString
            End If
        Next
        'zwalnia pamięć
        imgFile.Close()
        TheImage.Dispose()
    End Sub

Efekt powinien być taki jak na zdjęciu poniżej:

exif2

Ręczna zmiana danych EXIF będzie widoczna w programie. Teraz dodamy sobie aktywność przycisku wyświetlającego wszystkie identyfikatory metadanych, które zawiera nasz plik. Pętla, która nam to wykona, nie różni się za bardzo od pętli pobierającej metadane:

    Private Sub Button2_Click(sender As Object, e As EventArgs) Handles Button2.Click
        Dim wszystkieMetadane As String = "" 'zmienna przechowująca nasze identyfikatory
        Dim imgFile As FileStream = New FileStream(lokalizacjaPliku, FileMode.Open, FileAccess.Read)
        Dim TheImage As Image = Image.FromStream(imgFile)
        Dim AllProperties As PropertyItem() = TheImage.PropertyItems
        Dim listaWszystkichPropItem As New List(Of Integer)
        'pętla wypełniająca zmienną wszystkieMetadane
        For i As Integer = 0 To AllProperties.Length - 1
            wszystkieMetadane += AllProperties(i).Id.ToString + " "
        Next
        imgFile.Close()
        TheImage.Dispose()
        'komunikat dla użytkownika
        MsgBox(wszystkieMetadane)
    End Sub

Efekt jest taki, że mój plik graficzny zawiera identyfikatory Exif:

exif4

Nie warto wyświetlać nieznanych nam metadanych ponieważ mogą posiadać inny niż standardowy sposób kodowania (my używamy System.Text.Encoding.Unicode) lub nieznany typ zmiennej co przy próbie jego otworzenia może spowodować błąd. Sprawdźmy co oznacza identyfikator 50706:

exif5

Dla 20625 i 20624 nie ma opisu, nie wiem, co one oznaczają jak ktoś, będzie dociekliwy to na pewno znajdzie. Trzeba jeszcze zaznaczyć, że program będzie czytał i zapisywał tylko te zmienne, których typ jest Byte, ale przerobienie go tak, aby zapisywał inny typ zmiennych, nie będzie stanowił problemu. Musimy tylko wiedzieć jaki typ zmiennej kryje się pod jej identyfikatorem. Przejdźmy teraz do zapisywania:

    Private Sub Button3_Click(sender As Object, e As EventArgs) Handles Button3.Click
        'otwiera nasz plik w tle
        Dim imgFile As FileStream = New FileStream(lokalizacjaPliku, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)
        'czyta i ładuje wszystkie Byt'y programu do zmiennej
        Dim fileBytes(imgFile.Length) As Byte
        imgFile.Read(fileBytes, 0, fileBytes.Length)
        imgFile.Close()
        'ładujemy fileBytes do pamięci komputera
        Dim MS As MemoryStream = New MemoryStream(fileBytes)
        'tworzymy zmienną TheImage
        Dim TheImage As Image = Image.FromStream(MS)
        'ładujemy wszystkie klucze metadanych do listy
        Dim AllProperties() As PropertyItem = TheImage.PropertyItems
        Dim PropItem As PropertyItem = AllProperties(0)
        Dim strNewValue() As Byte
        'dodajemu lub aktualizujemy metadane
        For i As Integer = 0 To DataGridView1.Rows.Count - 1
            If Not DataGridView1.Rows(i).Cells(2).Value = "" Then
                strNewValue = System.Text.ASCIIEncoding.Unicode.GetBytes(DataGridView1.Rows(i).Cells(2).Value)
                PropItem.Id = DataGridView1.Rows(i).Cells(0).Value
                PropItem.Len = strNewValue.Length
                PropItem.Value = strNewValue
                PropItem.Type = 1
                TheImage.SetPropertyItem(PropItem)
            End If
        Next
        'zapisujemy obrazek z pamięci komputera
        TheImage.Save(lokalizacjaPliku)
        MS.Close()
        TheImage.Dispose()
    End Sub

To by było na tyle. Program zapisuje i czyta metadane.

exif6

Pełen kod programu dostępny tutaj: EXIF_edytor

Projek do pobrania tutaj: ZmianaDanychExif

 

Permalink do tego artykułu: https://visualmonsters.cba.pl/czytaniezapisywanie-metadanych-exif/

Dodaj komentarz

Twój adres email nie będzie publikowany.