12 messaggi dal 05 settembre 2010
Anche se specifico Imageformat.jpeg non mi funziona!
3.939 messaggi dal 28 gennaio 2003
In effetti non funziona neanche a me.
Ho provato a copiare i metadati da una immagine ad un'altra ridimensionata. E questo funziona.
Ho provato pure a modificare un metadato. E questo funziona.
Non funziona se tento di ricopiare UN SOLO metadato.
Quello di sotto è il codice di prova.

Using bmp As Bitmap = l.ImgLoad("C:\dati\ProveNikon\DSC_0117.jpg")
  Using bmp1 As Bitmap = l.GetImgResize(bmp, 500)
    For Each p As PropertyItem In bmp.PropertyItems
      If p.Id = &H131 Then 'Software
        Dim v0 As Byte() = p.Value
        Dim v1 As Byte() = l.StringToBytes(" (Libreria)" & vbNullChar, Encoding.ASCII)
        Dim v2(v0.Length + v1.Length) As Byte
        Array.Copy(v0, v2, v0.Length - 1)
        Array.Copy(v1, 0, v2, v0.Length - 1, v1.Length)
        p.Value = v2
        p.Len = p.Value.Length
        bmp1.SetPropertyItem(p)

      ElseIf p.Id = &H112 Then 'Orientation
        bmp1.SetPropertyItem(p)

      Else
        'funziona solo se abilito l'istruzione di sotto
        'bmp1.SetPropertyItem(p)
      End If

      'PrintLn(String.Format("{0}   {1}   {2}", p.Id.ToString("X"), p.Value, p.Type))
    Next
    l.ImgSaveToFile(bmp1, "c:\tmp\prova1.jpg")
  End Using
End Using

Me.Literal1.Text = GetMetadata("C:\dati\ProveNikon\DSC_0117.jpg")
Me.Literal2.Text = GetMetadata("c:\tmp\prova1.jpg")



ripeto: funziona solo se tolgo il commento all'ultimo Else
La cosa è interessante perché è giusto copiare solo i metadati che interessano e non tutti.

Ciao
Modificato da pietro09 il 27 novembre 2013 13.09 -
11.886 messaggi dal 09 febbraio 2002
Contributi
ciao,

pietro09 ha scritto:

ripeto: funziona solo se tolgo il commento all'ultimo Else

ok, può darsi che alcuni visualizzatori richiedano la presenza di altre proprietà, tipo la versione Exif. Infatti nella documentazione si legge.

Nonexistence of this field is taken to mean nonconformance to the standard
...e quindi sarebbe comprensibile se un visualizzatore decidesse di ignorare tutte le altre proprietà esistenti nel file.
Onestamente non ne sono sicuro, io su Windows 8 riesco a vedere l'immagine correttamente copiando giusto la proprietà dell'orientamento. Provate a copiare anche la versione Exif, codice 0x9000, magari così si risolve.

pietro09 ha scritto:

La cosa è interessante perché è giusto copiare solo i metadati che interessano e non tutti.

Già... se le foto sono caricate dagli utenti forse è il caso di tralasciare la maggior parte delle informazioni, tipo l'esatta posizione geografica, così come rilevata dal GPS. O quantomeno sarebbe il caso di farsi dare l'esplicito consenso dall'utente, che potrebbe non essere consapevole delle informazioni che sta inviando insieme alla foto.

ciao,
Moreno
Modificato da BrightSoul il 27 novembre 2013 13.32 -

Enjoy learning and just keep making
3.939 messaggi dal 28 gennaio 2003
 Hai ragione, così funziona.

Il codice di sotto, con la tua aggiunta, permette di copiare il metadato Orientation e di modificare il metadato Software

Using bmp As Bitmap = l.ImgLoad("C:\dati\ProveNikon\DSC_0117.jpg")
  Using bmp1 As Bitmap = l.GetImgResize(bmp, 500)
    For Each p As PropertyItem In bmp.PropertyItems
      If p.Id = &H131 Then 'Software
        Dim v0 As Byte() = p.Value
        Dim v1 As Byte() = l.StringToBytes(" (Libreria)" & vbNullChar, Encoding.ASCII)
        Dim v2(v0.Length + v1.Length) As Byte
        Array.Copy(v0, v2, v0.Length - 1)
        Array.Copy(v1, 0, v2, v0.Length - 1, v1.Length)
        p.Value = v2
        p.Len = p.Value.Length
        bmp1.SetPropertyItem(p)

      ElseIf p.Id = &H112 Then 'Orientation
        bmp1.SetPropertyItem(p)

      ElseIf p.Id = &H9000 Then 'ExifVersion
        'è necessario copiarla altrimenti non funziona
        bmp1.SetPropertyItem(p)

      Else
        'se commento la riga di sotto, non funziona!
        'copiando ExifVersion funziona!
        'bmp1.SetPropertyItem(p)
      End If

      'PrintLn(String.Format("{0}   {1}   {2}", p.Id.ToString("X"), p.Value, p.Type))
    Next
    l.ImgSaveToFile(bmp1, "c:\tmp\prova1.jpg")
  End Using
End Using




ps. se non volessi copiare un metadato ma crearlo ex novo, si potrebbe fare? in rete non ho visto nulla
11.886 messaggi dal 09 febbraio 2002
Contributi
ciao,

pietro09 ha scritto:

ps. se non volessi copiare un metadato ma crearlo ex novo, si potrebbe fare?

Sì, è possibile, anche se la API pone degli ostacoli.
Il problema è che la classe PropertyItem non ha un costruttore pubblico, quindi non potresti crearne una nuova istanza. La documentazione su MSDN lo giustifica così:

A PropertyItem is not intended to be used as a stand-alone object.
[...]
the PropertyItem class does not have a defined Public constructor, and you cannot create an instance of a PropertyItem object.

Non riesco a comprenderne il motivo dato che ci viene messo a disposizione un metodo SetPropertyItem. Oltretutto, la classe PropertyItem non ha alcun comportamento interno: il suo costruttore è vuoto e nel complesso ha l'aspetto di una semplice struttura dati.

Può darsi che questa API non sia ancora abbastanza matura e user-friendly per un uso pubblico. Comunque noi non ci badiamo, penso che in questo specifico caso sia legittimo "barare" e usare la reflection per arrivare al costruttore interno. Tanto potremmo comunque giungere ad una soluzione prendendo in prestito l'istanza di un PropertyItem da un altro oggetto Bitmap.
Detto questo, mi creerei un'istanza così:
var nuovoPropItem = (PropertyItem)typeof(PropertyItem).GetConstructor(BindingFlags.NonPublic | BindingFlags.Instance, null, Type.EmptyTypes, null).Invoke(null);


Adesso dobbiamo capire come valorizzare le quattro proprietà (documentate qui) del PropertyItem che abbiamo appena creato.

Ipotizziamo di voler aggiungere una descrizione alla foto. Nella documentazione si scopre che questa proprietà Exif è rappresentata dal codice 0x010E. Impostiamo il suo Id di conseguenza.
nuovoPropItem.Id = 0x010E;

Ora, la descrizione è una proprietà di tipo PropertyTagTypeASCII. MSDN non ci dice il codice corrispondente ma lo troviamo in questa pagina.
nuovoPropItem.Type = 2; //PropertyTagTypeASCII

Adesso andiamo ad impostare il contenuto vero e proprio della descrizione. Qui per non fare errori bisogna leggere attentamente la documentazione, eventualmente integrandola con la specifica Exif. Ogni proprietà Exif ha le sue peculiarità. Per la descrizione si legge:
Null-terminated character string that specifies the title of the image.

Dunque prepariamo una stringa terminata da null e la convertiamo in array di bytes, perché questo è il tipo di dato che il PropertyItem si aspetta di ricevere.
//il \0 finale è il carattere di escaping per null
var descrizione = System.Text.Encoding.UTF8.GetBytes("Visita alla città d'arte" + "\0");
nuovoPropItem.Value = descrizione;

Infine dobbiamo indicare il numero di bytes occupato dal nostro testo. La documentazione riferisce:

Length of the string including the NULL terminator

Benissimo, allora ci basta prendere in considerazione la lunghezza dell'array di bytes, perché quello già includeva il terminatore null.
nuovoPropItem.Len = descrizione.Length;

Adesso non resta che impostare il PropertyItem sull'immagine.
immagine.SetPropertyItem(nuovoPropItem);


Ricapitolando, il codice che abbiamo scritto per impostare un nuovo PropertyItem su una Bitmap è il seguente.
var nuovoPropItem = (PropertyItem)typeof(PropertyItem).GetConstructor(BindingFlags.NonPublic | BindingFlags.Instance, null, Type.EmptyTypes, null).Invoke(null);
nuovoPropItem.Id = 0x010E;
nuovoPropItem.Type = 2; //PropertyTagTypeASCII
var descrizione = System.Text.Encoding.UTF8.GetBytes("Visita alla città d'arte" + "\0");
nuovoPropItem.Value = descrizione;
nuovoPropItem.Len = descrizione.Length;
immagine.SetPropertyItem(nuovoPropItem);


ciao,
Moreno
Modificato da BrightSoul il 27 novembre 2013 15.14 -

Enjoy learning and just keep making
3.939 messaggi dal 28 gennaio 2003
Davvero interessante. Me lo studierò con attenzione.
Grazie davvero. Ciao.
3.939 messaggi dal 28 gennaio 2003
Forse sto invadendo un po' troppo un post non mio, ma è solo per dire che funziona perfettamente.
Per creare questo metadato ImageDescription ex novo, bisogna aggiungere il metadato ExifVersion, magari col valore predefinito:

id = &h9000
type = 7
len = 4
value = New Byte() {0, 2, 1, 0}


Ringrazio ancora. Ciao.

Torna al forum | Feed RSS

ASPItalia.com non è responsabile per il contenuto dei messaggi presenti su questo servizio, non avendo nessun controllo sui messaggi postati nei propri forum, che rappresentano l'espressione del pensiero degli autori.