Obtention de données GPS à partir de L'EXIF D'une image en C#
je développe un système qui permet de charger une image sur un serveur en utilisant ASP.NET C#. Je suis le traitement de l'image et tout fonctionne très bien. J'ai réussi à trouver une méthode qui lit la Date de création des données EXIF et les analyse en tant que DateTime. Qui fonctionne très bien aussi.
j'essaie maintenant de lire les données GPS de L'EXIF. Je veux capturer les chiffres de Latitude et de Longitude.
j'utilise cette liste comme référence aux données EXIF (en utilisant les nombres pour les éléments de propriété) http://www.exiv2.org/tags.html
Voici la méthode pour capturer la date créée (qui fonctionne).
public DateTime GetDateTaken(Image targetImg)
{
DateTime dtaken;
try
{
//Property Item 306 corresponds to the Date Taken
PropertyItem propItem = targetImg.GetPropertyItem(0x0132);
//Convert date taken metadata to a DateTime object
string sdate = Encoding.UTF8.GetString(propItem.Value).Trim();
string secondhalf = sdate.Substring(sdate.IndexOf(" "), (sdate.Length - sdate.IndexOf(" ")));
string firsthalf = sdate.Substring(0, 10);
firsthalf = firsthalf.Replace(":", "-");
sdate = firsthalf + secondhalf;
dtaken = DateTime.Parse(sdate);
}
catch
{
dtaken = DateTime.Parse("1956-01-01 00:00:00.000");
}
return dtaken;
}
ci-dessous est ma tentative de faire la même chose pour le GPS..
public float GetLatitude(Image targetImg)
{
float dtaken;
try
{
//Property Item 0x0002 corresponds to the Date Taken
PropertyItem propItem = targetImg.GetPropertyItem(2);
//Convert date taken metadata to a DateTime object
string sdate = Encoding.UTF8.GetString(propItem.Value).Trim();
dtaken = float.Parse(sdate);
}
catch
{
dtaken = 0;
}
return dtaken;
}
La valeur et de sortir dans sdate est "5ltd"
et cela vient d'une image qui a été prise par un iPhone 4 qui transporte les données GPS EXIF.
je sais qu'il y a les classes qui font cela, mais préfère écrire mes propres - je suis ouvert aux suggestions :-)
Merci à l'avance.
8 réponses
Selon lien posté ci-dessus par tomfanning, propriété de l'élément de 0x0002 est la latitude exprimée en PropertyTagTypeRational
. Le type rationnel est défini comme...
spécifie que le membre de données de valeur est un tableau de paires de type unsigned long des entiers. Chaque paire représente une fraction; le premier entier est le numérateur et le deuxième entier est le dénominateur.
vous essayez de le parser comme une chaîne quand il s'agit en fait simplement d'une série d'octets. Selon ce qui précède, il devrait y avoir 3 paires d'entiers non signés de 32 bits empaquetés dans ce tableau octet, que vous pouvez récupérer en utilisant ce qui suit:
uint degreesNumerator = BitConverter.ToUInt32(propItem.Value, 0);
uint degreesDenominator = BitConverter.ToUInt32(propItem.Value, 4);
uint minutesNumerator = BitConverter.ToUInt32(propItem.Value, 8);
uint minutesDenominator = BitConverter.ToUInt32(propItem.Value, 12);
uint secondsNumerator = BitConverter.ToUInt32(propItem.Value, 16);
uint secondsDenominator = BitConverter.ToUInt32(propItem.Value, 20);
Ce que vous faites avec ces valeurs après que vous les avez est pour vous :) Voici ce que les docs disent:
la Latitude est exprimée en trois valeurs rationnelles donnant respectivement les degrés, les minutes et les secondes. Lorsque les degrés, les minutes et les secondes sont exprimée dans le format jj/1, mm/1, ss/1. Lorsque les degrés et les minutes sont utilisés et, par exemple, les fractions de minutes jusqu'à deux décimales, le format jj/1, mmmm/100, 0/1.
Un simple (et rapide! façon est d'utiliser mon open source MetadataExtractor bibliothèque:
var gps = ImageMetadataReader.ReadMetadata(path)
.OfType<GpsDirectory>()
.FirstOrDefault();
var location = gps.GetGeoLocation();
Console.WriteLine("Image at {0},{1}", location.Latitude, location.Longitude);
la Bibliothèque est écrite en C # pur et prend en charge de nombreux formats d'image et décode des données spécifiques à de nombreux modèles de caméra.
j'ai couru à travers ceci à la recherche d'un moyen d'obtenir les données GPS EXIF comme un ensemble de flotteurs. J'ai adapté le code de Jon Grant comme suit...
public static float? GetLatitude(Image targetImg)
{
try
{
//Property Item 0x0001 - PropertyTagGpsLatitudeRef
PropertyItem propItemRef = targetImg.GetPropertyItem(1);
//Property Item 0x0002 - PropertyTagGpsLatitude
PropertyItem propItemLat = targetImg.GetPropertyItem(2);
return ExifGpsToFloat(propItemRef, propItemLat);
}
catch (ArgumentException)
{
return null;
}
}
public static float? GetLongitude(Image targetImg)
{
try
{
///Property Item 0x0003 - PropertyTagGpsLongitudeRef
PropertyItem propItemRef = targetImg.GetPropertyItem(3);
//Property Item 0x0004 - PropertyTagGpsLongitude
PropertyItem propItemLong = targetImg.GetPropertyItem(4);
return ExifGpsToFloat(propItemRef, propItemLong);
}
catch (ArgumentException)
{
return null;
}
}
private static float ExifGpsToFloat(PropertyItem propItemRef, PropertyItem propItem)
{
uint degreesNumerator = BitConverter.ToUInt32(propItem.Value, 0);
uint degreesDenominator = BitConverter.ToUInt32(propItem.Value, 4);
float degrees = degreesNumerator / (float)degreesDenominator;
uint minutesNumerator = BitConverter.ToUInt32(propItem.Value, 8);
uint minutesDenominator = BitConverter.ToUInt32(propItem.Value, 12);
float minutes = minutesNumerator / (float)minutesDenominator;
uint secondsNumerator = BitConverter.ToUInt32(propItem.Value, 16);
uint secondsDenominator = BitConverter.ToUInt32(propItem.Value, 20);
float seconds = secondsNumerator / (float)secondsDenominator;
float coorditate = degrees + (minutes / 60f) + (seconds / 3600f);
string gpsRef = System.Text.Encoding.ASCII.GetString(new byte[1] { propItemRef.Value[0] } ); //N, S, E, or W
if (gpsRef == "S" || gpsRef == "W")
coorditate = 0 - coorditate;
return coorditate;
}
voici le code qui fonctionne, j'ai trouvé des erreurs qui fonctionnent avec float. J'espère que cela aide quelqu'un.
private static double ExifGpsToDouble (PropertyItem propItemRef, PropertyItem propItem)
{
double degreesNumerator = BitConverter.ToUInt32(propItem.Value, 0);
double degreesDenominator = BitConverter.ToUInt32(propItem.Value, 4);
double degrees = degreesNumerator / (double)degreesDenominator;
double minutesNumerator = BitConverter.ToUInt32(propItem.Value, 8);
double minutesDenominator = BitConverter.ToUInt32(propItem.Value, 12);
double minutes = minutesNumerator / (double)minutesDenominator;
double secondsNumerator = BitConverter.ToUInt32(propItem.Value, 16);
double secondsDenominator = BitConverter.ToUInt32(propItem.Value, 20);
double seconds = secondsNumerator / (double)secondsDenominator;
double coorditate = degrees + (minutes / 60d) + (seconds / 3600d);
string gpsRef = System.Text.Encoding.ASCII.GetString(new byte[1] { propItemRef.Value[0] }); //N, S, E, or W
if (gpsRef == "S" || gpsRef == "W")
coorditate = coorditate*-1;
return coorditate;
}
je sais que c'est un vieux post, mais je voulais vous donner une réponse qui m'a aidé.
J'ai utilisé ExifLibrary situé ici pour écrire et lire les métadonnées des fichiers image: https://code.google.com/p/exiflibrary/wiki/ExifLibrary
C'était simple et facile à utiliser. J'espère que cela aide quelqu'un d'autre.
vous devez d'abord lire les octets qui indiquent si les données EXIF sont en format big endian ou little endian afin de ne pas tout gâcher.
alors vous devez scanner chaque IFD de l'image à la recherche de la balise GPSInfo (0x25 0x88), si vous ne trouvez pas cette balise dans n'importe quel IFD signifie que l'image n'a aucune information GPS. Si vous trouvez cette balise, lisez les 4 octets de ses valeurs, ce qui vous donne un décalage vers un autre IFD, le GPS IFD, à l'intérieur de cet IFD vous n'avez besoin que de récupérer les valeurs des balises suivantes:
0x00 0x02
- Pour la latitude
0x00 0x04
- Pour la longitude
0x00 0x06
- Pour l'altitude
chacune de ces valeurs sont des rationnels non signés.
Ici vous pouvez trouver comment le faire presque tout: http://www.media.mit.edu/pia/Research/deepview/exif.html
avez-vous essayé les étiquettes 0x0013-16 par http://msdn.microsoft.com/en-us/library/ms534416 (v=vs 85).aspx qui ressemble aussi à la latitude GPS?
Je ne suis pas sûr de ce qui les distingue des étiquettes numérotées inférieures, mais cela vaut la peine d'essayer.
Voici leurs descriptions:
0x0013-chaîne de caractères à terminaison nulle indiquant si la latitude du point de destination est la latitude nord ou la latitude sud. N indique la latitude nord, et S indique la latitude sud.
0x0014 - Latitude du point de destination. La latitude est exprimée en trois valeurs rationnelles donnant respectivement les degrés, les minutes et les secondes. Lorsque les degrés, les minutes et les secondes sont exprimés, le format jj/1, mm/1, ss/1. Lorsque les degrés et les minutes sont utilisés et, par exemple, les fractions de minutes jusqu'à deux décimales, le format jj/1, mmmm/100, 0/1.
0x0015 - Nul chaîne de caractères qui indique si la longitude du point de destination est la longitude Est ou la longitude ouest. E indique la longitude Est, et W indique la longitude ouest.
0x0016 - Longitude du point de destination. La longitude est exprimée en trois valeurs rationnelles donnant respectivement les degrés, les minutes et les secondes. Lorsque les degrés, les minutes et les secondes sont exprimés, le format est ddd/1, mm/1, ss/1. Lorsque les degrés et les minutes sont utilisés et, par exemple, les fractions de minutes jusqu'à deux décimales, le format est ddd/1, mmmm / 100, 0/1.
Merci, le code est bien, mais au moins un failor,
uint minutes = minutesNumerator / minutesDenominator;
donner non un résultat exact, quand le minutesDenominator n'est pas égal 1, par exemple dans mon exif=16,