お知らせ

電子会議

ライブラリ

パレット

Delphi FAQ検索

Delphi FAQ一覧

サンプル蔵





FDelphi FAQ
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