16番会議室「玉石混淆みんなで作るSample蔵」に寄せられたサンプル
"JPEGファイルからExifのデータを取得・表示"
この発言に対し以下のコメントが寄せられています
#01348 Done さん RE:JPEGファイルからExifのデータを取得・
#01349 Done さん RE:JPEGファイルからExifのデータを取得・
#01350 Done さん RE:JPEGファイルからExifのデータを取得・
#01351 河邦 正 さん RE:JPEGファイルからExifのデータを取得・
【タイトル】JPEGファイルからExifのデータを取得&表示する
【概略】
最近のほとんどのデジタルカメラが記録するJPEGファイルはExif形式と
呼ばれるフォーマットで様々な情報が記録されているそうです。
Exifの情報は APP1 セグメントに入っている(拡張情報は APP2 ?)の
で、このセグメントを JPEG ファイルから抽出して、フォーマットの検査
と情報の取得を行うように、このサンプルコードを書いてみました。
付属の使用例で、OpenDialog で選択した JPEG ファイルの Exif 形式
の情報の一部を取得&表示します。
【サンプルコード】
// JPEGファイルのセグメント(SegTypeで種類を指定)を取得する
procedure GetSegFromJPEG(Dst, Src: TStream; SegType: BYTE);
var
bCode: BYTE;
wData: WORD;
begin
if (Src.Read(wData, 2) = 2)and(wData = $d8ff) then
begin
while Src.Read(bCode, 1) = 1 do
begin
if bCode <> $ff then
break; // フォーマットがおかしい
// セグメント間の $ff は無視する
while Src.Read(bCode, 1) = 1 do
if bCode <> $ff then break;
// 次のセグメントが見つからなかった
if bCode = $ff then
break;
if bCode = SegType then
begin
// 取得するセグメントが見つかった
// 長さを取得する
if Src.Read(wData, 2) <> 2 then
break;
wData := (wData shl 8 and $ff00)or(wData shr 8 and $ff);
if wData < 2 then
break; // サイズ情報が異常
// セグメントのデータをコピーする
Dst.CopyFrom(Src, wData - 2);
end
else if bCode in [$01, $b0..$b7] then
// これらのセグメントは長さ情報も何もない
else if bCode = $da then
// SOS(Start Of Scan) セグメント
// このセグメント以降がイメージデータ
break
else // その他のセグメントはスキップする
begin
if Src.Read(wData, 2) <> 2 then
break;
wData := (wData shl 8 and $ff00)or(wData shr 8 and $ff);
if wData < 2 then
break; // サイズ情報が異常
Src.Seek(wData - 2, soFromCurrent);
end;
end;
end; // Code = $d8ff
end;
// JPEGファイル中の Exif 情報(の一部)を取得する
function GetExifFromJPEG(Dst: TStrings; Src: TStream): Boolean;
type
TTiffHeader = packed record
ID: WORD;
Code: WORD;
Offset: DWORD;
end;
TFIDHeader = packed record
ID: WORD;
DataType: WORD;
Count: DWORD;
Offset: DWORD;
end;
const
OfsToHeader = 6;
var
Data: TMemoryStream;
Header: TTiffHeader;
BigEndian: Boolean;
wValue, wNumEntry: WORD;
hFID: TFIDHeader;
I, J: Integer;
function GetWORD(Value: WORD): WORD;
begin
if BigEndian then
Result := (Value shl 8 and $ff00)or(Value shr 8 and $ff)
else
Result := Value;
end;
function GetDWORD(Value: DWORD): DWORD;
var
I, IMax: Integer;
pDst, pSrc: PChar;
begin
if BigEndian then
begin
pDst := @Result;
pSrc := @Value;
IMax := sizeof(DWORD) - 1;
for I := 0 to IMax do
pDst[I] := pSrc[iMax - I];
end
else
Result := Value;
end;
function GetFIDStr: string;
var
P1, P2: Pointer;
begin
Result := '';
case GetWORD(hFID.DataType) of
1: // BYTE
Result := IntToStr(GetDWORD(hFID.Offset));
2: // ASCII
Result := PChar(
DWORD(Data.Memory) + OfsToHeader +
+ GetDWORD(hFID.Offset)
);
3: // SHORT
Result := IntToStr(GetDWORD(hFID.Offset));
4: // LONG
Result := IntToStr(GetDWORD(hFID.Offset));
5: // RATIONAL
begin
P1 := Pointer(
DWORD(Data.Memory) + OfsToHeader +
+ GetDWORD(hFID.Offset)
);
P2 := Pointer(DWORD(P1) + 4);
Result := FloatToStr(PDWORD(P1)^ / PDWORD(P2)^);
end;
7:; // UNDEFINED
9: // SLONG
Result := IntToStr(Integer(GetDWORD(hFID.Offset)));
10:// SRATIONAL
begin
P1 := Pointer(
DWORD(Data.Memory) + OfsToHeader +
+ GetDWORD(hFID.Offset)
);
P2 := Pointer(DWORD(P1) + 4);
Result := FloatToStr(PInteger(P1)^ / PInteger(P2)^);
end;
end;
end;
function GetFIDExpTime: string;
var
P1, P2: Pointer;
exValue: Extended;
begin
Result := '';
case GetWORD(hFID.DataType) of
1: // BYTE
Result := IntToStr(GetDWORD(hFID.Offset));
2: // ASCII
Result := PChar(
DWORD(Data.Memory) + OfsToHeader +
+ GetDWORD(hFID.Offset)
);
3: // SHORT
Result := IntToStr(GetDWORD(hFID.Offset));
4: // LONG
Result := IntToStr(GetDWORD(hFID.Offset));
5: // RATIONAL
begin
P1 := Pointer(
DWORD(Data.Memory) + OfsToHeader +
+ GetDWORD(hFID.Offset)
);
P2 := Pointer(DWORD(P1) + 4);
exValue := PDWORD(P1)^ / PDWORD(P2)^;
if (exValue > 0)and(exValue < 1.0) then
Result := '1/' + IntToStr(Round(1/exValue))
else
Result := FloatToStr(exValue);
end;
7:; // UNDEFINED
9: // SLONG
Result := IntToStr(Integer(GetDWORD(hFID.Offset)));
10:// SRATIONAL
begin
P1 := Pointer(
DWORD(Data.Memory) + OfsToHeader +
+ GetDWORD(hFID.Offset)
);
P2 := Pointer(DWORD(P1) + 4);
exValue := PInteger(P1)^ / PInteger(P2)^;
if (exValue > 0)and(exValue < 1.0) then
Result := '1/' + IntToStr(Round(1/exValue))
else
Result := FloatToStr(exValue);
end;
end;
end;
begin
Result := FALSE;
Data := TMemoryStream.Create;
try
// APP1 セグメントを取得する
GetSegFromJPEG(Data, Src, $e1);
if(Data.Size > 0)and
(StrLIComp(PChar(Data.Memory), PChar('Exif'+#0), 5) = 0) then
begin
Data.Position := OfsToHeader;
if (Data.Read(Header, sizeof(Header)) = sizeof(Header)) and
((Header.ID = $4949)or(Header.ID = $4d4d)) and
(GetWORD(Header.Code) = 42) then
begin
BigEndian := Header.ID = $4d4d;
Data.Position := OfsToHeader + GetDWORD(Header.Offset);
Data.ReadBuffer(wValue, sizeof(wValue));
wValue := GetWORD(wValue);
// 最初のFIDのスキャン
for I := 0 to wValue - 1 do
begin
Data.ReadBuffer(hFID, sizeof(hFID));
if GetWORD(hFID.ID) = $8769 then
begin
// Exif が見つかった
Data.Position :=
OfsToHeader + GetDWORD(hFID.Offset);
// エントリの数を得る
Data.ReadBuffer(wNumEntry, sizeof(wNumEntry));
wNumEntry := GetWORD(wNumEntry);
for J := 0 to wNumEntry - 1 do
begin
Data.ReadBuffer(hFID, sizeof(hFID));
case GetWORD(hFID.ID) of
$829a: Dst.Values['シャッター速度'] := GetFIDExpTime;
$829d: Dst.Values['F値'] := GetFIDStr;
$8827: Dst.Values['ISO感度'] := GetFIDStr;
$9003: Dst.Values['撮影日時'] := GetFIDStr;
$920a: Dst.Values['レンズの焦点距離'] := GetFIDStr;
end;
end;
Result := TRUE;
break;
end;
end;
end; // Header のチェック
end; // 'Exif' のチェック'
finally
Data.Free;
end;
end;
以下は、上記の関数の使用例です。
procedure Form1.Button1Click(Sender: TObject);
var
Src: TFileStream;
Dst: TStrings;
begin
if OpenDialog1.Execute then
begin
Src := TFileStream.Create(
FileName, fmOpenRead or fmShareDenyWrite);
try
Dst := TStringList.Create;
try
GetExifFromJPEG(Dst, Src);
if Dst.Count > 0 then
ShowMessage(Dst.Text)
else
ShowMessage('Exif 情報が見つかりませんでした');
finally
Dst.Free;
end;
finally
Src.Free;
end;
end;
end;
【あとがき】
私が持っている Exif 準拠のファイルを書き出すデジタルカメラは
OLYMPUS C-990 Zoom だけです。そのため、上記のコードの検証は1機種でし
か行っていません。ですから、このサンプルコードを使用される場合には自
己責任でデバッグ等を行い、必要に応じて修正を行う必要があります。
(参考:http://www.pima.net/standards/it10/PIMA15740/exif.htm )
2001/06/14、河邦 正(GCC02240@nifty.com)
(http://homepage2.nifty.com/kht0000/、NIFTY外へ私作のComponentの
公開用)
Original document by 河邦 正 氏 ID:(GCC02240)
ここにあるドキュメントは NIFTY SERVEの Delphi Users' Forum の16番会議室「玉石混淆みんなで作るSample蔵」に投稿されたサンプルです。これらのサンプルはボーランド株式会社がサポートする公式のものではありません。また、必ずしも動作が検証されているものではありません。これらのサンプルを使用したことに起因するいかなる損害も投稿者、およびフォーラムスタッフはその責めを負いません。使用者のリスクの範疇でご使用下さい。
Copyright 1996-2002 Delphi Users' Forum
|