PHPのお勉強!

PHP TOP

exif_read_data

(PHP 4 >= 4.2.0, PHP 5, PHP 7, PHP 8)

exif_read_data 画像ファイルから EXIF ヘッダを読み込む

説明

exif_read_data(
    resource|string $file,
    ?string $required_sections = null,
    bool $as_arrays = false,
    bool $read_thumbnail = false
): array|false

exif_read_data() は、 画像ファイルから EXIF ヘッダを読み込みます。この方法で、デジタルカメラが生成したメタデータを 読み込むことが可能です。

EXIF ヘッダは、デジタルカメラが作成した JPEG/TIFF 画像によく含まれています。しかし残念なことに、 そのタグ付けの方法はメーカーによって異なります。 したがって、特定の Exif ヘッダが常に存在すると仮定することはできません。

Height および Width は、getimagesize() と同じ方法で計算されます。 よって、これらの値は決してヘッダの一部として返してはいけません。 また、html は高さ/幅を表すテキスト文字列で、 通常の HTML の中で用いられます。

Exif ヘッダに著作権表示が含まれている場合、それ自身には 2 つの値を 含めることが可能です。Exif 2.10 のこの矛盾した規格に対応するため、 COMPUTED セクションは Copyright.Photographer および Copyright.Editor の両方を返します。 また IFD0 セクションには 2 つのエントリを NULL 文字で区切った バイト配列を含めます。データ型が間違っている場合は最初のエントリのみを かえします(Exif の通常の挙動)。COMPUTED には、元の著作権文字列あるいは カンマで区切られた写真と編集者の著作権表示のどちらかを Copyright エントリに含めることが可能です。

UserComment タグにも Copyright タグと同様の問題があります。ここにも 2 つの値を格納することが可能です。それは使用しているエンコーディングと 値自身の 2 つです。そうなると、IFD セクションにはエンコーディングのみを 含めるか、あるいはバイト配列を格納することになります。COMPUTED セクションは UserCommentEncoding および UserComment を両方格納することができます。UserComment はどちらの 場合でも有効なので、IFD0 セクションではこちらを優先すべきです。

exif_read_data() は、EXIF 仕様(» http://exif.org/Exif2-2.PDF, 20 ページ)に基づいて EXIF データタグの検証も行います。

パラメータ

file

画像ファイルの位置。 ファイルのパス (ストリームラッパーもいつもどおりサポートされています) またはストリームresource

required_sections

結果の配列を作成するために存在する必要のあるセクションのカンマ区切り リスト。要求されたセクションがひとつも見つからなかった場合の戻り値は false となります。

FILE FileName, FileSize, FileDateTime, SectionsFound
COMPUTED html, Width, Height, IsColor, および他の取得可能なもの。Height および Width は getimagesize() と同じ方法で取得した もので、その値はヘッダの一部ではありません。また、html は 通常の HTML 内で使用される height/width の 文字列です。
ANY_TAG タグを有するすべての情報。例えば IFD0, EXIF, ...
IFD0 IFD0 のすべてのタグつきデータ。通常の画像ファイルでは、 ここに画像のサイズが含まれます。
THUMBNAIL 2 番目の IFD がある場合、ファイルにサムネイルが含まれている 可能性があります。埋め込まれたサムネイルに関するすべての タグつき情報はこのセクションに格納されます。
COMMENT JPEG 画像のコメントヘッダ。
EXIF EXIF セクションは IFD0 のサブセクションです。ここには 画像に関する詳細な情報が含まれています。これらのエントリの ほとんどはデジタルカメラに関連するものです。

as_arrays

各セクションを配列とするかどうかを指定します。 required_sectionsCOMPUTEDTHUMBNAIL および COMMENT は常に配列となります。これは、これらのセクションに 含まれる値の名前が他のセクションと衝突する可能性があるからです。

read_thumbnail

true を指定すると、サムネイル本体を読み込みます。それ以外の場合は タグつきデータのみを読み込みます。

戻り値

ヘッダ名がキー・ヘッダの内容が値となる連想配列を返します。 返されるデータがない場合は exif_read_data()false を返します。

エラー / 例外

サポートされていないタグや、エラーが起きうる条件が指定された場合は、 E_WARNING かつ/または E_NOTICE が発生します。 警告が発生しても、この関数は対応する情報を全て読み取ろうとします。

変更履歴

バージョン 説明
8.0.0 required_sections は、nullable になりました。
7.2.0 file パラメータは ローカルファイルとストリームリソースを両方サポートするようになりました。
7.2.0

以下の EXIF フォーマットのサポートが追加されました:

  • Samsung
  • DJI
  • Panasonic
  • Sony
  • Pentax
  • Minolta
  • Sigma/Foveon
  • AGFA
  • Kyocera
  • Ricoh
  • Epson

例1 exif_read_data() の例

<?php
echo "test1.jpg:<br />\n";
$exif = exif_read_data('tests/test1.jpg', 'IFD0');
echo
$exif===false ? "No header data found.<br />\n" : "Image contains headers<br />\n";

$exif = exif_read_data('tests/test2.jpg', 0, true);
echo
"test2.jpg:<br />\n";
foreach (
$exif as $key => $section) {
foreach (
$section as $name => $val) {
echo
"$key.$name: $val<br />\n";
}
}
?>

最初のコールは失敗します。画像がヘッダ情報を有していないためです。

上の例の出力は、 たとえば以下のようになります。

test1.jpg:
No header data found.
test2.jpg:
FILE.FileName: test2.jpg
FILE.FileDateTime: 1017666176
FILE.FileSize: 1240
FILE.FileType: 2
FILE.SectionsFound: ANY_TAG, IFD0, THUMBNAIL, COMMENT
COMPUTED.html: width="1" height="1"
COMPUTED.Height: 1
COMPUTED.Width: 1
COMPUTED.IsColor: 1
COMPUTED.ByteOrderMotorola: 1
COMPUTED.UserComment: Exif test image.
COMPUTED.UserCommentEncoding: ASCII
COMPUTED.Copyright: Photo (c) M.Boerger, Edited by M.Boerger.
COMPUTED.Copyright.Photographer: Photo (c) M.Boerger
COMPUTED.Copyright.Editor: Edited by M.Boerger.
IFD0.Copyright: Photo (c) M.Boerger
IFD0.UserComment: ASCII
THUMBNAIL.JPEGInterchangeFormat: 134
THUMBNAIL.JPEGInterchangeFormatLength: 523
COMMENT.0: Comment #1.
COMMENT.1: Comment #2.
COMMENT.2: Comment #3end
THUMBNAIL.JPEGInterchangeFormat: 134
THUMBNAIL.Thumbnail.Height: 1
THUMBNAIL.Thumbnail.Height: 1

例2 PHP 7.2.0 で利用できるストリームを exif_read_data() で使う例

<?php
// Open a the file, this should be in binary mode
$fp = fopen('/path/to/image.jpg', 'rb');

if (!
$fp) {
echo
'Error: Unable to open image for reading';
exit;
}

// Attempt to read the exif headers
$headers = exif_read_data($fp);

if (!
$headers) {
echo
'Error: Unable to read exif headers';
exit;
}

// Print the 'COMPUTED' headers
echo 'EXIF Headers:' . PHP_EOL;

foreach (
$headers['COMPUTED'] as $header => $value) {
printf(' %s => %s%s', $header, $value, PHP_EOL);
}
?>

上の例の出力は、 たとえば以下のようになります。

EXIF Headers:
 Height => 576
 Width => 1024
 IsColor => 1
 ByteOrderMotorola => 0
 ApertureFNumber => f/5.6
 UserComment =>
 UserCommentEncoding => UNDEFINED
 Copyright => Denis
 Thumbnail.FileType => 2
 Thumbnail.MimeType => image/jpeg

注意

注意:

mbstring が有効な場合、 exif はUnicodeを処理し、 exif.decode_unicode_motorola および exif.decode_unicode_intel で指定された文字コードとして取り出そうとします。 exif 拡張モジュールはエンコーディングを自分自身で探そうとはしないので、 exif_read_data() を呼ぶ前に、 これらの ini ディレクティブのうちのひとつに どのエンコーディングをデコードに使うかを適切に設定するのはユーザーの責任です。

注意:

この関数にストリームを渡すために file を使った場合、 ストリームはシーク可能でなければなりません。 ファイルポインタの位置は、関数から戻った後でも変化しないことに注意してください。

参考

add a note

User Contributed Notes 19 notes

up
83
chadsmith729 at gmail dot com
12 years ago
When the new update came out from Apple for iOS6 it provided the ability for iPad, iPod, and iPhones to be able to upload files from the device through Safari. Obviously this will open up an array of implementations where at one point it was just not possible.

The issue comes when a photo is uploaded it will be dependent on the location of the "button" when the photo was taken. Imagine if you will that you have your iPhone turned with the button at the top and you take a photo. The photo when uploaded to your server might be "upside down".

The following code will ensure that all uploaded photos will be oriented correctly upon upload:
<?php
$image
= imagecreatefromstring(file_get_contents($_FILES['image_upload']['tmp_name']));
$exif = exif_read_data($_FILES['image_upload']['tmp_name']);
if(!empty(
$exif['Orientation'])) {
switch(
$exif['Orientation']) {
case
8:
$image = imagerotate($image,90,0);
break;
case
3:
$image = imagerotate($image,180,0);
break;
case
6:
$image = imagerotate($image,-90,0);
break;
}
}
// $image now contains a resource with the image oriented correctly
?>

What you do with the image resource from there is entirely up to you.

I hope that this helps you identify and orient any image that's uploaded from an iPad, iPhone, or iPod. Orientation for the photo is the key to knowing how to rotate it correctly.
up
3
Tim
5 years ago
The following code:
<?php
$data
= exif_read_data('foo.jpg');
var_dump($data['Keywords']);
?>
produces string(15) "???????????????"

Adding
<?php
ini_set
('exif.decode_unicode_motorola', 'UCS-2LE');
?>
before the call to exif_red_data produces
string(15) "landscape;;field"
up
6
darkain at darkain dot com
16 years ago
I wanted some quick and easy functions for computing the shutter speed and f-stop. I couldn't find any anywhere, so I made some. It took some research :

<?php
function exif_get_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);
}

function
exif_get_shutter(&$exif) {
if (!isset(
$exif['ShutterSpeedValue'])) return false;
$apex = exif_get_float($exif['ShutterSpeedValue']);
$shutter = pow(2, -$apex);
if (
$shutter == 0) return false;
if (
$shutter >= 1) return round($shutter) . 's';
return
'1/' . round(1 / $shutter) . 's';
}

function
exif_get_fstop(&$exif) {
if (!isset(
$exif['ApertureValue'])) return false;
$apex = exif_get_float($exif['ApertureValue']);
$fstop = pow(2, $apex/2);
if (
$fstop == 0) return false;
return
'f/' . round($fstop,1);
}
?>
up
7
drpain at webster dot org dot za
12 years ago
Please note that when resizing images with GD and most image processing scripts or applications you will loose the EXIF information. What I did as a workaround is book this information into MySQL before I re-size images.

<?php

// This function is used to determine the camera details for a specific image. It returns an array with the parameters.
function cameraUsed($imagePath) {

// Check if the variable is set and if the file itself exists before continuing
if ((isset($imagePath)) and (file_exists($imagePath))) {

// There are 2 arrays which contains the information we are after, so it's easier to state them both
$exif_ifd0 = read_exif_data($imagePath ,'IFD0' ,0);
$exif_exif = read_exif_data($imagePath ,'EXIF' ,0);

//error control
$notFound = "Unavailable";

// Make
if (@array_key_exists('Make', $exif_ifd0)) {
$camMake = $exif_ifd0['Make'];
} else {
$camMake = $notFound; }

// Model
if (@array_key_exists('Model', $exif_ifd0)) {
$camModel = $exif_ifd0['Model'];
} else {
$camModel = $notFound; }

// Exposure
if (@array_key_exists('ExposureTime', $exif_ifd0)) {
$camExposure = $exif_ifd0['ExposureTime'];
} else {
$camExposure = $notFound; }

// Aperture
if (@array_key_exists('ApertureFNumber', $exif_ifd0['COMPUTED'])) {
$camAperture = $exif_ifd0['COMPUTED']['ApertureFNumber'];
} else {
$camAperture = $notFound; }

// Date
if (@array_key_exists('DateTime', $exif_ifd0)) {
$camDate = $exif_ifd0['DateTime'];
} else {
$camDate = $notFound; }

// ISO
if (@array_key_exists('ISOSpeedRatings',$exif_exif)) {
$camIso = $exif_exif['ISOSpeedRatings'];
} else {
$camIso = $notFound; }

$return = array();
$return['make'] = $camMake;
$return['model'] = $camModel;
$return['exposure'] = $camExposure;
$return['aperture'] = $camAperture;
$return['date'] = $camDate;
$return['iso'] = $camIso;
return
$return;

} else {
return
false;
}
}

?>

An example of it's use follows:

<?php

$camera
= cameraUsed("/img/myphoto.jpg");
echo
"Camera Used: " . $camera['make'] . " " . $camera['model'] . "<br />";
echo
"Exposure Time: " . $camera['exposure'] . "<br />";
echo
"Aperture: " . $camera['aperture'] . "<br />";
echo
"ISO: " . $camera['iso'] . "<br />";
echo
"Date Taken: " . $camera['date'] . "<br />";

?>

Will display the following, depending on the data:

Camera Used: SONY DSC-S930
Exposure Time: 1/400
Aperture: f/4.3
ISO: 100
Date Taken: 2010:12:10 18:18:45

If the image has been re-sized and the information is no longer available then you should receive the following when echoing the same:

Camera Used: Unavailable
Exposure Time: Unavailable
Aperture: Unavailable
ISO: Unavailable
Date Taken: Unavailable

Some cameras do not capture all the information, for instance Blackberry phones do not record an aperture, or iso and you will get Unavailable for those fields.

I hope you find this helpful.