アカベコマイリ

HEAR NOTHING SEE NOTHING SAY NOTHING

PHP で EXIF を読む

November 14, 2009開発EXIF, GeoTag, PHP

PHP で EXIF を読む方法 & GPS 情報を正規化する方法の覚書。exif_read_data 関数で読んだ EXIF は連想配列として返される。例えば取得した EXIF 情報をテーブルとして出力する場合の処理は以下のようになる。

<?php
/**
 * 指定された画像の EXIF 情報をテーブルとして出力します。
 *
 * @param $path 画像のパス。
 */
function printExif( $path )
{
    $exif = exif_read_data( $path );
    if( !$exif ) { return; }

    $text = "";
    foreach( $exif as $key => $value )
    {
        $text .= "<tr><td>" . $key . "</td><td>" . $value . "</td></tr>";
    }

    echo( "<table>" . $text . "</table>" );
}
?>

配列の要素はデータごとに文字列であったり配列だったりする。標高は "50/1" のような文字列、緯度と経度は [ "34/1", "48/1", "59749145/1000000" ] のような文字列に配列になる。分数になっているものを数値化する処理は以下。

<?php
/**
 * EXIF の GPS 情報を正規化します。
 *
 * @param $values GPS 情報。
 *
 * @return 成功時は正規化した値。失敗時は false。
 */
function normalizeGpsValue( $value )
{
    // GPS 情報は "35/1" のような書式となるので、スラッシュで分割する
    $fraction = explode( "/", $value );
    if( count( $fraction ) != 2 ) { return false; }

    $numerator   = ( double )$fraction[ 0 ];
    $denominator = ( double )$fraction[ 1 ];

    return ( $numerator / $denominator );
}
?>

標高はこの関数だけで数値化できる。緯度と経度は「度・分・秒」の配列となっている。そのためこれらをひとつの数値に直すなら各要素をを数値化した上で「分・秒」部分を時間の桁に合わせ、それらを足した値を求めることになる。

実装は以下のようになるだろう。配列の要素の正規化には既に紹介した normalizeGpsValue 関数を流用している。

<?php
/**
 * EXIF の GPS の位置情報 ( 緯度・経度 ) を正規化します。
 *
 * @param $values GPS の位置情報を示す配列 ( 度・分・秒 )。
 *
 * @return 成功時は正規化された値。失敗時は false。
 */
function normalizeGpsLocationValue( $values )
{
    if( count( $values ) != 3 ) { return false; }

    $degrees = normalizeGpsValue( $values[ 0 ] );
    if( !$degrees ) { return false; }

    $minutes = normalizeGpsValue( $values[ 1 ] );
    if( !$minutes ) { return false; }

    $seconds = normalizeGpsValue( $values[ 2 ] );
    if( !$seconds ) { return false; }

    return $degrees + ( $minutes / 60.0 ) + ( $seconds / 3600 );
}
?>

これを利用して緯度を算出。

<?php
$exif = exif_read_data( $path );
if( $exif && isset( $exif[ 'GPSLatitude' ] ) && isset( $exif[ 'GPSLatitudeRef' ] ) )
{
    $latitude = normalizeGpsLocationValue( $exif[ 'GPSLatitude' ] );
    if( $latitude )
    {
        // 南半球なら負の値
        if( $exif[ 'GPSLatitudeRef' ] == "S" )
        {
            $latitude *= -1;
        }
    }
}
?>

こんな感じの処理になるはず。

2009/11/15

exif_read_data のリファレンスを読むと URL 形式のパスには対応していないとの事。この記事を書いた時は "./test.jpg" のような形式のパス指定で動作していたが、後からオンライン上の画像の URL を指定したら false が返されたので Web サービスなどには利用できなさそうである。

Copyright © 2009 - 2023 akabeko.me All Rights Reserved.