アカベコマイリ

HEAR NOTHING SEE NOTHING SAY NOTHING

PHP で EXIF を読む 2

November 17, 2009開発EXIF, GeoTag, PEL, PHP

PHP で EXIF を読むシリーズその 2。

前回は PHP の標準ライブラリ exifreaddata を使用して EXIF を読んでみた。しかし exif_read_data は引数に URL を指定できず不便。またこの関数は標準ライブラリであるにもかかわらず使用できない環境もあるとの事。

そこで今回は PEL というライブラリを検討してみる。

PEL: PHP Exif Library

PEL というのはオープンソースな PHP 用 EXIF 読み書きライブラリである。GitHub からを入手可能。

インストールに関する設定も特になく入手したファイルをそのまま require_once で取り込むだけ。以下に前回の処理を PEL 向けに修正したクラスをサンプルとして掲載する。

require_once('pel-0.9.1/PelJpeg.php');

/**
 * EXIF 情報の読み取り機能を提供します。
 */
class ExifReader
{
	/**
	 * EXIF の GPS 情報から GeoTag として値へ変換する。
	 *
	 * @param	$fraction	GPS 情報となる、分子・分母の計 2 要素の配列。
	 *
	 * @return	成功時は変換した値。失敗時は false。
	 */
	private function getGeoTagValue( $fraction )
	{
		$numerator   = ( double )$fraction[ 0 ];
		$denominator = ( double )$fraction[ 1 ];

		return ( $numerator / $denominator );
	}

	/**
	 * EXIF の GPS 情報から GeoTag を取得する。
	 *
	 * @param	$values	GPS 情報の配列 ( 度・分・秒 )。
	 *
	 * @return	成功時は GeoTag 用の値。失敗時は false。
	 */
	private function getGeoTagLocationValue( $values )
	{
		if( count( $values ) != 3 ) { return false; }

		$degrees = $this->getGeoTagValue( $values[ 0 ] );
		if( !$degrees ) { return false; }

		$minutes = $this->getGeoTagValue( $values[ 1 ] );
		if( !$minutes ) { return false; }

		$seconds = $this->getGeoTagValue( $values[ 2 ] );
		if( !$seconds ) { return false; }

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

	/**
	 * 画像から読み取った EXIF 情報を取得します。
	 *
	 * @param	$url	画像の URL。
	 *
	 * @return	成功時は EXIF 情報。失敗時は false。
	 */
	public function read( $url )
	{
		try
		{
			$jpeg = new PelJpeg($url);
			$app1 = $jpeg->getExif();
			if( !$app1 ) { return false; }

			$tiff = $app1->getTiff();
			if( !$tiff ) { return false; }

			$ifd = $tiff->getIfd();
			if( !$ifd ) { return false; }

			$gps = $ifd->getSubIfd( PelIfd::GPS );
			if( !$gps ) { return false; }

			// 緯度・経度が取得できないなら無効とする
			$v = $gps->getEntries();
			if( !isset( $v[ 1 ] ) || !isset( $v[ 2 ] ) || !isset( $v[ 3 ] ) || !isset( $v[ 4 ] ) ) { return false; }

			$data = array();

			// 緯度
			{
				$latitude = $this->getGeoTagLocationValue( $v[ 2 ]->getValue() );
				if( !$latitude ) { return false; }

				// 南半球なら負の値
				$direction = $v[ 1 ]->getText();
				if( $direction == "S" )
				{
					$latitude *= -1;
				}

				$data[ "latitude" ] = $latitude;
			}

			// 経度
			{
				$longitude = $this->getGeoTagLocationValue( $v[ 4 ]->getValue() );
				if( !$longitude ) { return false; }

				// 西半球なら負の値
				$direction = $v[ 3 ]->getText();
				if( $direction == "W" )
				{
					$longitude *= -1;
				}

				$data[ "longitude" ] = $longitude;
			}

			// 測地系
			if( isset( $v[ 18 ] ) )
			{
				$data[ "mapDatum" ] = $v[ 18 ]->getText();
			}

			// 標高
			if( isset( $v[ 6 ] ) )
			{
				$altitude = $this->getGeoTagValue( $v[ 6 ]->getValue() );
				if( $altitude )
				{
					$data[ "altitude" ] = ( string )$altitude . "m";
				}
			}

			// GPS 以外の情報
			$v = $ifd->getEntries();
			{
				// 撮影日
				if( isset( $v[ 306 ] ) )
				{
					// 撮影日の書式は "yyyy:MM:dd hh:mm:ss" となり、終端の NULL 文字と合わせて 20 バイトの文字列となる。
					// 日時の各桁は、データが存在しない場合はスペースで埋められる。
					//
					$chars = str_split( $v[ 306 ]->getText() );
					$count = count( $chars );
					if( $count == 19 )
					{
						// 年と月の区切り文字をスラッシュに変換
						$chars[ 4 ] = "/";
						$chars[ 7 ] = "/";

						$data[ "dateTime" ] = implode( $chars );
					}
				}
			}

			// 画像の URL
			$data[ "url" ] = $url;

			return $data;
		}
		catch( Exception $e )
		{
			return false;
		}
	}
}

手元の環境で試したところ PEL の場合は URL 上の画像からも EXIF を読み取れる事が確認できた。

ただしオンライン上の JPEG を読み込むと PelJpeg コンストラクタに時間がかかる。時には分単位で待たされることも。常に時間が掛かるわけではないが、この現象は結構な頻度で発生していたので気がかりである。ローカルホストでも時間が掛かっていたので、通信速度以外の要因があるのだと思う。