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.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
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.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 |
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():
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
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:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
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:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
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