Mangiare Senza Glutine disponibile su App Store

Per altre informazioni scrivi a fabriziocaldarelli@negusweb.it

Estrarre metadati Exif GPS da immagini

Da Programmazione Software.

Descrizione

Estrarre i metadati Exif da una foto è abbastanza semplice in C#.

I dati Exif, come da specifica (http://www.exif.org), sono strutturati in questo modo:

   * Tag name: una breve descrizione del campo
   * Field name: nome del campo
   * Tag ID (può essere decimale o esadecimale): id del campo
   * Type: tipo di valore del campo
   * Count: numero di ripetizioni del tipo di valore del campo

"Tag Name" e "Field Name" non richiedono particolare attenzione; "Tag ID" è un indice univoco che serve a distinguere un campo dall’altro; "Type" fornisce il tipo del campo, che può essere uno tra questi, come da specifica:

   * BYTE: 8-bit intero senza segno
   * ASCII: 8-bit byte contenente un 7-bit ASCII code. Il byte finale è il terminatore NULL
   * SHORT: 16-bit (2-byte) intero senza segno
   * LONG: 32-bit (4-byte) intero senza segno
   * RATIONAL: 2 LONG. Il primo LONG è il numeratore ed il secondo LONG è il denominatore; in pratica serve ad esprimere numeri razionali (float e double);
   * UNDEFINED: 8-bit byte che può assumere qualsiasi valore
   * SLONG: 32-bit (4-byte) intero con segno
   * SRATIONAL: 2 SLONG. Il primo SLONG è il numeratore ed il secondo SLONG è il denominatore

"Count" è il numero di ripetizioni del tipo di valore del campo, da non confondere con il numero di bytes. Infatti, se il campo è di tipo “ASCII” e count vale 2, allora il numero di bytes è 2. Ma se il campo è di tipo “RATIONAL” e count vale 2, il numero di numero di bytes è 16, perchè il RATIONAL è già di per sè da 8 bytes.

Il framework .Net mette a disposizione la classe System.Drawing.Imaging.PropertyItem che wrappa correttamente tutti i campi della struttura dei metadati exif, ovvero:

   * Id: Ottiene o imposta l’ID della proprietà
   * Len: Ottiene o imposta la lunghezza in byte della proprietà Value
   * Type: Ottiene o imposta un valore integer che definisce il tipo di dati contenuti nella proprietà Value
   * Value: Ottiene o imposta il valore dell’elemento di proprietà

Da un punto di vista strettamente pratico, accedere ai metadati presenti in un’immagine è semplice:: infatti basta caricare un’immagine (tramite la funzione System.Drawing.Image.FromFile) e da qui estrarre le proprietà presenti nella lista PropertyItems.

Banalmente un codice di questo tipo:

using System.Drawing;
Image image = Image.FromFile();
foreach(PropertyItem pi in image.PropertyItems)
{
 
}
 
 
 
Il tipo di dato ritornato dalla proprietà .Value è un array di Byte.
Vi scrivo qui delle rapide conversioni:
 
// Da pi.Value ad Ascii:
Encoding.ASCII.GetString(pi.Value);
 
// Da pi.Value ad Integer 16 bit:
BitConverter.ToInt16(pi.Value, 0);
 
// Da pi.Value ad Integer 32 bit:
BitConverter.ToInt32(pi.Value, 0);
 
Qui di seguito vi propongo il codice per leggere ed incapsulare comodamente tutti i dati GPS:
 
 
using System;
using System.Collections.Generic;
using System.Text;
using System.Drawing;
using System.Drawing.Imaging;
 
namespace Negusweb.Classes
{
    public class CExif
    {
        class CGpsCoordinate
        {
            double gradi;
            double minuti;
            double secondi;
 
            public CGpsCoordinate(byte[] arrBt)
            {
                gradi = (double) BitConverter.ToInt32(arrBt, 0) / BitConverter.ToInt32(arrBt, 4);
                minuti = (double) BitConverter.ToInt32(arrBt, 8) / BitConverter.ToInt32(arrBt, 12);
                secondi = (double) BitConverter.ToInt32(arrBt, 16) / BitConverter.ToInt32(arrBt, 20);
            }
 
            public CGpsCoordinate(double iGradi, double iMinuti, double iSecondi)
            {
                this.gradi = iGradi;
                this.minuti = iMinuti;
                this.secondi = iSecondi;
            }
 
            public string ToString()
            {
                string s = gradi.ToString() + ":" + minuti.ToString() + ":" + secondi.ToString();
                return s;
            }
        }
        class CGps
        {
            string GPSVersionID;
            string GPSLatitudeRef;
            CGpsCoordinate GPSLatitude;
            string GPSLongitudeRef;
            CGpsCoordinate GPSLongitude;
            byte GPSAltitudeRef;
            double GPSAltitude;
            TimeSpan GPSTimeStamp;
            string GPSSatellites;
            string GPSStatus;
            string GPSMeasureMode;
            double GPSDOP;
            string GPSSpeedRef;
            double GPSSpeed;
            string GPSTrackRef;
            double GPSTrack;
            string GPSImgDirectionRef;
            double GPSImgDirection;
            string GPSMapDatum;
            string GPSDestLatitudeRef;
            CGpsCoordinate GPSDestLatitude;
            string GPSDestLongitudeRef;
            CGpsCoordinate GPSDestLongitude;
            string GPSDestBearingRef;
            string GPSDestBearing;
            string GPSDestDistanceRef;
            string GPSDestDistance;
            // Object GPSProcessingMethod;
            // Object GPSAreaInformation;
            DateTime GPSDateStamp;
            int GPSDifferential;
 
            Image imageObj;
 
            private double GPSRationalToDouble(byte[] arrBt, int startIndex)
            {
                return (((double)BitConverter.ToInt32(arrBt, startIndex)) / ((double)BitConverter.ToInt32(arrBt, startIndex + 4)));
            }
            private string GPSAsciiToString(byte[] arrBt)
            {
                byte[] arrBt2 = new byte[arrBt.Length - 1];
                Array.Copy(arrBt, arrBt2, arrBt2.Length);
                return Encoding.ASCII.GetString(arrBt2);
            }
            private TimeSpan GPSTimeStampToTimeSpan(byte[] arrBt)
            {
                int h = (int) GPSRationalToDouble(arrBt, 0);
                int m = (int) GPSRationalToDouble(arrBt, 8);
                int s = (int) GPSRationalToDouble(arrBt, 16);
 
                return new TimeSpan(h, m, s);
            }
            private DateTime GPSDateStampToDateTime(byte[] arrBt)
            {
                String s = GPSAsciiToString(arrBt);
                string[] arrStr = s.Split(':');
 
                return new DateTime(Int32.Parse(arrStr[0]), Int32.Parse(arrStr[1]), Int32.Parse(arrStr[2]));
            }
 
            public CGps(Image iImageObj)
            {
                imageObj = iImageObj;
 
                foreach (PropertyItem pi in imageObj.PropertyItems)
                {
                    switch(pi.Id)
                    {
                        // VersionID;
                        case 0:
                            GPSVersionID = pi.Value[0].ToString() + "." + pi.Value[1].ToString() + "." + pi.Value[2].ToString() + "." + pi.Value[3].ToString();
                            break;
                        // LatitudeRef
                        case 1:
                            GPSLatitudeRef = GPSAsciiToString(pi.Value);
                            break;
                        // Latitude
                        case 2:
                            GPSLatitude = new CGpsCoordinate(pi.Value);
                            break;
                        // LongitudeRef
                        case 3:
                            GPSLongitudeRef = GPSAsciiToString(pi.Value);
                            break;
                        // Longitude
                        case 4:
                            GPSLongitude = new CGpsCoordinate(pi.Value);
                            break;
                        // AltitudeRef
                        case 5:
                            GPSAltitudeRef = pi.Value[0];
                            break;
                        // Altitude
                        case 6:
                            GPSAltitude = GPSRationalToDouble(pi.Value,0);
                            break;
                        // TimeStamp
                        case 7:
                            GPSTimeStamp = GPSTimeStampToTimeSpan(pi.Value);
                            break;
                        // Satellites
                        case 8:
                            GPSSatellites = GPSAsciiToString(pi.Value);
                            break;
                        // GPSStatus
                        case 9:
                            GPSStatus = GPSAsciiToString(pi.Value);
                            break;
                        // MeasureMode
                        case 10:
                            GPSMeasureMode = GPSAsciiToString(pi.Value);
                            break;
                        // DOP
                        case 11:
                            GPSDOP = GPSRationalToDouble(pi.Value,0);
                            break;
                        // SpeedRef
                        case 12:
                            GPSSpeedRef = GPSAsciiToString(pi.Value);
                            break;
                        // Speed
                        case 13:
                            GPSSpeed = GPSRationalToDouble(pi.Value,0);
                            break;
                        // TrackRef
                        case 14:
                            GPSTrackRef = GPSAsciiToString(pi.Value);
                            break;
                        // Track
                        case 15:
                            GPSTrack = GPSRationalToDouble(pi.Value,0);
                            break;
                        // ImgDirectionRef
                        case 16:
                            GPSImgDirectionRef = GPSAsciiToString(pi.Value);
                            break;
                        // ImgDirection
                        case 17:
                            GPSImgDirection = GPSRationalToDouble(pi.Value,0);
                            break;
                        // MapDatum
                        case 18:
                            GPSMapDatum = GPSAsciiToString(pi.Value);
                            break;
                        // DestLatitudeRef
                        case 19:
                            GPSDestLatitudeRef = GPSAsciiToString(pi.Value);
                            break;
                        // DestLatitude
                        case 20:
                            GPSDestLatitude = new CGpsCoordinate(pi.Value);
                            break;
                        // DestLongitudeRef
                        case 21:
                            GPSDestLongitudeRef = GPSAsciiToString(pi.Value);
                            break;
                        // DestLongitude
                        case 22:
                            GPSDestLongitude = new CGpsCoordinate(pi.Value);
                            break;
                        // DestBearingRef
                        case 23:
                            GPSDestBearingRef = GPSAsciiToString(pi.Value);
                            break;
                        // DestBearing
                        case 24:
                            GPSDestBearing = BitConverter.ToInt32(pi.Value, 0) + "," + BitConverter.ToInt32(pi.Value, 4);
                            break;
                        // DestDistanceRef
                        case 25:
                            GPSDestDistanceRef = GPSAsciiToString(pi.Value);
                            break;
                        // DestDistance
                        case 26:
                            GPSDestDistance = BitConverter.ToInt32(pi.Value, 0) + "," + BitConverter.ToInt32(pi.Value, 4);
                            break;
                        // DateStamp
                        case 29:
                            GPSDateStamp = GPSDateStampToDateTime(pi.Value);
                            break;
                        // Differential
                        case 30:
                            GPSDifferential = BitConverter.ToInt16(pi.Value,0);
                            break;
                    }
                }
            }
 
            public string ToXml()
            {
                string xml = "";
                xml += "" + GPSVersionID + "";
                xml += "\n";
                xml += "" + GPSLatitudeRef + "";
                xml += "\n";
                xml += "" + GPSLatitude.ToString() + "";
                xml += "\n";
                xml += "" + GPSLongitudeRef + "";
                xml += "\n";
                xml += "" + GPSLongitude.ToString() + "";
                xml += "\n";
                xml += "" + GPSAltitudeRef + "";
                xml += "\n";
                xml += "" + GPSAltitude + "";
                xml += "\n";
                xml += "" + GPSTimeStamp + "";
                xml += "\n";
                xml += "" + GPSSatellites.Trim() + "";
                xml += "\n";
                xml += "" + GPSStatus + "";
                xml += "\n";
                xml += "" + GPSMeasureMode + "";
                xml += "\n";
                xml += "" + GPSDOP + "";
                xml += "\n";
                xml += "" + GPSSpeedRef + "";
                xml += "\n";
                xml += "" + GPSSpeed + "";
                xml += "\n";
                xml += "" + GPSTrackRef + "";
                xml += "\n";
                xml += "" + GPSTrack + "";
                xml += "\n";
                xml += "" + GPSImgDirectionRef + "";
                xml += "\n";
                xml += "" + GPSImgDirection + "";
                xml += "\n";
                xml += "" + GPSMapDatum.Trim() + "";
                xml += "\n";
                xml += "" + GPSDestLatitudeRef + "";
                xml += "\n";
                xml += "" + GPSDestLatitude + "";
                xml += "\n";
                xml += "" + GPSDestLongitudeRef + "";
                xml += "\n";
                xml += "" + GPSDestLongitude + "";
                xml += "\n";
                xml += "" + GPSDestBearingRef + "";
                xml += "\n";
                xml += "" + GPSDestBearing + "";
                xml += "\n";
                xml += "" + GPSDestDistanceRef + "";
                xml += "\n";
                xml += "" + GPSDestDistance + "";
                xml += "\n";
                xml += "" + GPSDateStamp + "";
                xml += "\n";
                xml += "" + GPSDifferential + "";
                xml += "\n";
                xml += "";
                return xml;
            }
        }
 
        string pathImage;
        CGps gps;
 
 
        public CExif(String iPathImage)
        {
            this.pathImage = iPathImage;
            Image image = Image.FromFile(iPathImage);
 
            gps = new CGps(image);
 
            image = null;
 
        }
 
        public string ToXml()
        {
            string xml = "";
            xml += gps.ToXml();
            xml += "";
            return xml;
        }
    }
}