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:
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:
| 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:
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:
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:
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.
Pełen kod programu dostępny tutaj: EXIF_edytor
Projek do pobrania tutaj: ZmianaDanychExif







