Como leer el EXIF de una foto desde PHP
En pocas palabras, el EXIF es un "agregado" para los archivos de fotografías (y audio) en el cual se puede incluir meta-información de las condiciones en la que fue capturada la misma. En la mayoría de los casos pasa desapercibido, y por lo cual se le critica por sus implicaciones a la privacidad del fotógrafo.
Aún así, puede ser un recurso útil si estamos interesados en mostrar la información de las fotos, tal como lo hacen sitios como Flickr, y más aún con el buzz de geolocalizar a los usuarios y sus contenidos. ¿Qué tipo de contenidos incluye el EXIF?
- Fecha y hora en que fue tomada la foto (independiente de las del archivo en sí)
- Información de la cámara con que fue tomada (modelo y fabricante)
- Condiciones en que fue capturada (Valor ISO, Orientación de la cámara, apertura, longitud focal, etc..).
- Coordenadas GPS del lugar donde fue capturada. (Solo si la cámara incluye un receptor GPS, es más común en las cámaras de los smartphones)
Hay que tener en cuenta que el EXIF no es un estándar, ni es controlado por una entidad, asi que cada fabricante puede incluir diferente información en este, incluso entre diferentes modelos de cámaras.
Desde PHP hay dos opciones para leer la información contenida en el EXIF:
Extensión EXIF
PHP ya incluye una extensión para leer el EXIF, esta quizás la forma más recomendada. Al ser una extensión, dependes de que tu servidor ya la tenga instalada/habilitada antes usarla; si tienes el acceso apropiado probablemente puedas instalarla tu mismo si es necesario.
En caso que queramos conocer la información genérica del EXIF, basta con hacer una llamada a exif_read_data()
y ver que elementos del array
están definidos:
$exif = exif_read_data( $file ); if ( !empty($exif['FNumber'] ) ) $aperture = $exif['FNumber']; if ( !empty($exif['Model'] ) ) $camera = trim( $exif['Model'] ); if ( !empty($exif['DateTimeDigitized'] ) ) $pic_date = $exif['DateTimeDigitized']; if ( !empty($exif['FocalLength'] ) ) $focal_length = $exif['FocalLength']; if ( !empty($exif['ISOSpeedRatings'] ) ) $iso = trim( $exif['ISOSpeedRatings']; if ( !empty($exif['ExposureTime'] ) ) $shutter_speed = $exif['ExposureTime'];
Es importante revisar que cada elemento esté definido, ya que no hay garantías de estarlo. Esta extensión entrega los datos tal cual vienen en el EXIF (y como strings
), necesitamos convertirlos a un formato a apropiado según sea.
// Convertir un string "1/123" a su representación float function exif_float($value) { $pos = strpos($value, '/'); if ($pos === false) return (float) $value; $a = (float) substr($value, 0, $pos); $b = (float) substr($value, $pos+1); return ($b == 0) ? ($a) : ($a / $b); } // Esta función de WordPress convierte la fecha del EXIF (YYYY:MM:DD HH:MM:SS) a Unixtime // /wp-admin/includes/image.php function wp_exif_date2ts($str) { @list( $date, $time ) = explode( ' ', trim($str) ); @list( $y, $m, $d ) = explode( ':', $date ); return strtotime( "{$y}-{$m}-{$d} {$time}" ); } $shutter_speed = exif_float( $exif['ExposureTime'] ); $pic_date = wp_exif_date2ts($exif['DateTimeDigitized'] );
Similar para leer las coordenadas GPS en el EXIF y convertirlas de formato Grados, minutos y segundos a Grados en decimal:
if ( !empty($exif['GPSLongitude']) && !empty($exif['GPSLatitude']) ) { $d = (float) $exif['GPSLongitude'][0]; $m = exif_float($exif['GPSLongitude'][1] ); $s = exif_float( $exif['GPSLongitude'][2] ); $gps_longitude = (float) $d + $m/60 + $s/3600; if ( $exif['GPSLongitudeRef'] == 'W') $gps_longitude = -$gps_longitude; $d = $exif['GPSLatitude'][0]; $m = exif_float($exif['GPSLatitude'][1] ); $s = exif_float( $exif['GPSLatitude'][2] ); $gps_latitude = (float) $d + $m/60 + $s/3600; if ( $exif['GPSLatitudeRef'] == 'S') $gps_latitude = -$gps_latitude; }
Exifixer
Otra opción es usar Exifixer de Zenphoto que en teoría debería leer datos específicos de ciertos fabricantes; y además que es independiente de la configuración de PHP, para los que requieran mayor portabilidad de su aplicación PHP, o bien no tienen opción a modificar el servidor.
La forma de leer los datos es similar, aunque la estructura del array
es diferente:
$exif = read_exif_data_raw( $file, false ); if ( !empty($exif['SubIFD']['FNumber']) ) $aperture = $exif['SubIFD']['FNumber']; if ( !empty($exif['IFD0']['Model']) ) $model = $exif['IFD0']['Model']; if ( !empty($exif['SubIFD']['DateTimeOriginal']) ) $pic_date = $exif['SubIFD']['DateTimeOriginal']; if ( !empty(['SubIFD']['FocalLength']) ) $focal_length = ['SubIFD']['FocalLength']; if ( !empty(['SubIFD']['ISOSpeedRatings']) ) $iso = ['SubIFD']['ISOSpeedRatings']; if ( !empty(['SubIFD']['ExposureTime']) ) $shutter_speed = ['SubIFD']['ExposureTime']; if ( !empty($exif['GPS'] )) { $gps_longitude = $exif['GPS']['Longitude']; if ( $exif['GPS']['Longitude Reference'] == 'W') $gps_longitude = -$gps_longitude; $gps_latitude = $exif['GPS']['Latitude']; if ( $exif['GPS']['Latitude Reference'] == 'S') $gps_latitude = -$gps_latitude; }
Un detalle con Exifixer es que las fracciones ya las convierte a su representación float
(y en muchos casos hay un equivalente string con diferente nombre).
Bonus: Eliminar el EXIF de una foto
Muchos programas de edición/administración de fotografías digitales ya incluyen una opción para eliminar el EXIF; sino existe Exifremover.com y ExifTool para dicho fin
Hola Jaroche, sólo comentar que no todas las cámaras tienen un receptor GPS para almacenar las coordenadas donde la foto fue tomada en su información EXIF. Un saludo.
De hecho, se diría que son muy pocas las cámaras que traen el GPS implementado. Mayormente vemos esta funcionalidad en los celulares que si se están usando masivamente para hacer fotografías.
Ah tienes razón, se lee como si todas lo tuvieran; y tal como dice Christian son contadas las cámaras que incluyen el receptor GPS.
Muy muy interesante artículo, me enorgullece que PHP se siga desarrollando así y felicidades al autor.