2011年3月18日金曜日

Exif

Exif (Exchangeable Image File Format)

・読み方は "イグジフ" or "エグジフ"

1994年に富士フィルムが提唱したデジタルカメラ用の画像ファイルの規格
JEIDA(日本電子工業振興協会)によって標準化され、各社のデジタルカメラに採用されている
最新版はExif 2.2(Exif Print)
TIFF形式で画像についての情報や撮影日時などの付加情報を記録できるほか、縮小画像(サムネイル)を記録することができる
画像形式はRGB無圧縮方式やJPEG方式など複数の形式をサポートしている

by IT用語辞典 e-Words (http://e-words.jp/w/Exif.html)


つまり、画像情報を画像自身に埋め込むための規格
 ・カメラの機種
 ・撮影日時
 ・絞り
 ・画素数
 ・ISO感度
 ・色空間
 ・シャッタースピード
などなど


■ Exifファイルフォーマット参考サイト

Exchangeable image file format - Wikipedia -
けんしのページ - Exifファイルフォーマット -
Exif TAG -
Exif file format -

■ Android の Exif 対応

* Android 2.3

 ・ JPEG ファイルの EXIF メタデータで新しく altitude tag (高度情報)に対応した
 ・ EXIF altitude tag の値を取得するために新しく getAltitude() メソッドが追加された

 http://developer.android.com/sdk/android-2.3.html#api
 の Mixable audio effects 部分


* Android 3.0
 ・photo aperture, ISO, exposure time が新しく ExifInterface のフィールドとして追加された

 http://developer.android.com/sdk/android-3.0.html
 の Media EXIF data 部分


Camera.Parameters

 ・int getJpegThumbnailQuality()
  JPEG 画像の EXIF サムネイル用に設定された画質を返す

 ・void setJpegThumbnailQuality(int quality)
  JPEG 画像の EXIF サムネイル用の画質を設定する
  (quality は 1 - 100, 100 がベスト)

 ・Camera.Size getJpegThumbnailSize()
  JPEG 画像の EXIF サムネイル用のサイズを返す

 ・void setJpegThumbnailSize(int width, int height)
  JPEG 画像の EXIF サムネイル用サイズを設定する
  (width と height に 0 が設定された場合、EXIF はサムネイルを含まない)
  (アプリケーションは画面の向きを考慮する必要がある)

 ・void setRotation(int rotation)
  回転をセット
  引数の rotation はカメラの向きに対する回転角度、0、90、180、270 のみ

 ・void setGpsAltitude(double altitude)
  JPEG EXIF ヘッダに保存される GPS 高度(altitude [m]) を設定する

 ・void setGpsLatitude(double latitude)
  JPEG EXIF ヘッダに保存される GPS 緯度(latitude) を設定する

 ・void setGpsLongitude(double longitude)
  JPEG EXIF ヘッダに保存される GPS 経度(longitude) を設定する

 ・void setGpsProcessingMethod(String processing_method)
  GPS processing method を設定する
  JPEG EXIF ヘッダには32文字まで保存される

 ・void setGpsTimestamp(long timestamp)
  JPEG EXIF ヘッダに保存される GPS timestamp を設定する
   (timestamp は January 1, 1970 からの経過秒数[UTC]).


ExifInterface (Since : API Level 5)

EXIF 用タグフィールド

TAG_APERTURE
  "FNumber"
  F値 : 文字列

TAG_DATETIME
  "DateTime"
  撮影日時 : 文字列 (YYYY:MM:DD hh:mm:ss)

TAG_EXPOSURE_TIME
  "ExposureTime"
  露出時間 : 文字列

TAG_FLASH
  "Flash"
  フラッシュ : int (0 : なし, 1 : あり)

TAG_FOCAL_LENGTH
  "FocalLength"
  レンズ焦点距離 : 適切なタイプ

TAG_GPS_ALTITUDE
  "GPSAltitude"
  TAG_GPS_ALTITUDE_REF を基準としてメートル単位の高度

TAG_GPS_ALTITUDE_REF
  "GPSAltitudeRef"
  高度が海抜の場合 0
  
TAG_GPS_DATESTAMP
  "GPSDateStamp"
  GPS date : 文字列
  
TAG_GPS_LATITUDE
  "GPSLatitude"
  緯度 : "num1/denom1,num2/denom2,num3/denom3" 形式のフォーマット

TAG_GPS_LATITUDE_REF
  "GPSLatitudeRef"
  北緯(N) or 南緯(S)

TAG_GPS_LONGITUDE
  "GPSLongitude"
  経度 : "num1/denom1,num2/denom2,num3/denom3" 形式のフォーマット

TAG_GPS_LONGITUDE_REF
  "GPSLongitudeRef"
  東経(E) or 西経(W)

TAG_GPS_PROCESSING_METHOD
  "GPSProcessingMethod"
  位置特定に使用する GPS processing method の名前

TAG_GPS_TIMESTAMP
  "GPSTimeStamp"
  GPS 時間 (原子時計の時間)

TAG_IMAGE_LENGTH
  "ImageLength"
  画像の長さ : int

TAG_IMAGE_WIDTH
  "ImageWidth"
  画像の幅 : int

TAG_ISO
  "ISOSpeedRatings"
  ISO スピードレート

TAG_MAKE
  "Make"
  画像入力機器のメーカー名

TAG_MODEL
  "Model"
  画像入力機器のモデル名

TAG_ORIENTATION
  "Orientation"
  画像方向 : int
  0 (0x00000000) = ORIENTATION_UNDEFINED
  1 (0x00000001) = ORIENTATION_NORMAL
  2 (0x00000002) = ORIENTATION_FLIP_HORIZONTAL
  3 (0x00000003) = ORIENTATION_ROTATE_180
  4 (0x00000004) = ORIENTATION_FLIP_VERTICAL
  5 (0x00000005) = ORIENTATION_ROTATE_90
  6 (0x00000006) = ORIENTATION_TRANSVERSE
  7 (0x00000007) = ORIENTATION_TRANSPOSE
  8 (0x00000008) = ORIENTATION_ROTATE_270

TAG_WHITE_BALANCE
  "WhiteBalance"
  ホワイトバランス : int
  0 (0x00000000) = WHITEBALANCE_AUTO
  1 (0x00000001) = WHITEBALANCE_MANUAL


・ double getAltitude(double defaultValue)
  Since: API Level 9
  メートル単位での高度を返す
  EXIF タグがない場合、defaultValue を返す


・ String getAttribute (String tag)
  Since: API Level 5
  指定されたタブの値を返す
  JPEGファイルに指定されたタグがない場合は null を返す


・ double getAttributeDouble (String tag, double defaultValue)
  Since: API Level 8
  指定されたタグの double 値を返す
  JPEGファイルに指定されたタグがない場合や、double 値にパース出来ない場合は defaultValue を返す


・ int getAttributeInt (String tag, int defaultValue)
  Since: API Level 5
  指定されたタグの int 値を返す
  JPEGファイルに指定されたタグがない場合や、int 値にパース出来ない場合は defaultValue を返す

・ boolean getLatLong (float[] output)
  Since: API Level 5
  渡された float 配列に緯度経度を保存する
  最初が緯度(latitude)、2番目が軽度(longitude)
  EXIF タグが使用できない場合、false を返す

・ byte[] getThumbnail ()
  Since: API Level 5
  JPEG ファイル内のサムネイルを返す
  サムネイルがない場合は null を返す
  返ってくるデータは JPEG フォーマット
  decodeByteArray(byte[], int, int) を使ってでコード可能

・ boolean hasThumbnail ()
  Since: API Level 5
  JPEG ファイルがサムネイルを持っている場合 true を返す

・ void saveAttributes ()
  Since: API Level 5
  タグデータを JPEG ファイルに保存する
  この処理は 全てのJPEG データをあるファイルから別のファイルにコピーし、古いファイルを削除し新しいファイルをリネームするため、負荷が大きい
  そのため、 setAttribute(String, String) を使って各属性を個別に設定するほうがよい

・ void setAttribute (String tag, String value)
  Since: API Level 5
  指定されたタグの値を設定する


* Gallery アプリ
  [メニュー] - [その他] - [詳細情報]で一部の EXIF 情報が表示される


* Exif情報を見てみる


public class MainActivity extends Activity {

static final String TAG = "ExifSample";

private ExifInterface exifInterface;

@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);

// get file state
String status = Environment.getExternalStorageState();
Log.d(TAG, "status : " + status);

// get file path
//String filename = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM) + "/droids.jpg" ; // since API level 8
String filename = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM) + "/droids_flash.jpg" ; // since API level 8
Log.d(TAG, "filename : " + filename);

// below API level 8
// String filepath = Environment.getExternalStorageDirectory().toString() + "/DCIM/droids.png";

if (!status.equals(Environment.MEDIA_MOUNTED)) {
// media is not mounted
Log.d(TAG, "media is not mounted");
addTableRow("Error", "media is not mounted");
return;
} else if (!(new File(filename)).exists()) {
// file does not exists
Log.d(TAG, "file does not exists");
addTableRow("Error", "file does not exists");
return;
}

if(!filename.endsWith(".jpg") && !filename.endsWith(".jpeg") && !filename.endsWith(".JPG") && !filename.endsWith(".JPEG")) {
// file is not JPEG
Log.d(TAG, "file is not JPEG format");
addTableRow("Error", "file is not JPEG format");
return;
}

try {
exifInterface = new ExifInterface(filename);
} catch (IOException e) {
e.printStackTrace();
Log.d(TAG, e.getMessage());
addTableRow("Error", e.getMessage());
return;
}

showExifInfo(filename);
ImageView imageView = (ImageView)findViewById(R.id.thumbnail);
imageView.setImageBitmap(getExifThumbnail(filename));
}

private Bitmap getExifThumbnail(){
if(exifInterface != null) {
// get thumbnail
byte[] thumbnail = exifInterface.getThumbnail();
if(thumbnail != null) {
return BitmapFactory.decodeByteArray(thumbnail, 0, thumbnail.length);
}
}
return null;
}

private void showExifInfo(){
if(exifInterface != null) {
// get latitude and longitude
float[] latlong = new float[2];
exifInterface.getLatLong(latlong);

//String aperture = exifInterface.getAttribute (ExifInterface.TAG_APERTURE); // since API Level 11
String datetime = exifInterface.getAttribute (ExifInterface.TAG_DATETIME);
//String exposure = exifInterface.getAttribute (ExifInterface.TAG_EXPOSURE_TIME); // since API Level 11
int flash = exifInterface.getAttributeInt (ExifInterface.TAG_FLASH, 0);
double focalLength = exifInterface.getAttributeDouble (ExifInterface.TAG_FOCAL_LENGTH, 0);
double altitude = exifInterface.getAttributeDouble (ExifInterface.TAG_GPS_ALTITUDE, 0); // since API Level 9
double altitudeRef = exifInterface.getAttributeDouble (ExifInterface.TAG_GPS_ALTITUDE_REF, 0); // since API Level 9
String datestamp = exifInterface.getAttribute (ExifInterface.TAG_GPS_DATESTAMP);
String latitude = exifInterface.getAttribute (ExifInterface.TAG_GPS_LATITUDE);
String latitudeRef = exifInterface.getAttribute (ExifInterface.TAG_GPS_LATITUDE_REF);
String longitude = exifInterface.getAttribute (ExifInterface.TAG_GPS_LONGITUDE);
String longitudeRef = exifInterface.getAttribute (ExifInterface.TAG_GPS_LONGITUDE_REF);
String processing = exifInterface.getAttribute (ExifInterface.TAG_GPS_PROCESSING_METHOD);
String timestamp = exifInterface.getAttribute (ExifInterface.TAG_GPS_TIMESTAMP);
int imageLength = exifInterface.getAttributeInt (ExifInterface.TAG_IMAGE_LENGTH, 0);
int imageWidth = exifInterface.getAttributeInt (ExifInterface.TAG_IMAGE_WIDTH, 0);
//String iso = exifInterface.getAttribute (ExifInterface.TAG_ISO); // since API Level 11
String make = exifInterface.getAttribute (ExifInterface.TAG_MAKE);
String model = exifInterface.getAttribute (ExifInterface.TAG_MODEL);
int orientation = exifInterface.getAttributeInt (ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_UNDEFINED);
int whitebalance = exifInterface.getAttributeInt (ExifInterface.TAG_WHITE_BALANCE, ExifInterface.WHITEBALANCE_AUTO);

String orientationInfo = "";
switch(orientation) {
case ExifInterface.ORIENTATION_UNDEFINED :
orientationInfo = "UNDEFINED";
break;
case ExifInterface.ORIENTATION_NORMAL :
orientationInfo = "NORMAL";
break;
case ExifInterface.ORIENTATION_FLIP_HORIZONTAL :
orientationInfo = "FLIP_HORIZONTAL";
break;
case ExifInterface.ORIENTATION_ROTATE_180 :
orientationInfo = "ROTATE_180";
break;
case ExifInterface.ORIENTATION_FLIP_VERTICAL :
orientationInfo = "FLIP_VERTICAL";
break;
case ExifInterface.ORIENTATION_ROTATE_90 :
orientationInfo = "ROTATE_90";
break;
case ExifInterface.ORIENTATION_TRANSVERSE :
orientationInfo = "TRANSVERSE";
break;
case ExifInterface.ORIENTATION_TRANSPOSE :
orientationInfo = "TRANSPOSE";
break;
case ExifInterface.ORIENTATION_ROTATE_270 :
orientationInfo = "ROTATE_270";
break;
}

addTableRow("latlong", latlong[0] + ", " + latlong[1]);
addTableRow("datetime", datetime);
addTableRow("flash", flash + " (" + (flash == 1 ? "on" : "off") + ")");
addTableRow("focalLength", focalLength + "");
addTableRow("datestamp", datestamp);
addTableRow("altitude", altitude + "");
addTableRow("altitudeRef", altitudeRef + "");
addTableRow("latitude", latitude);
addTableRow("latitudeRef", latitudeRef);
addTableRow("longitude", longitude);
addTableRow("longitudeRef", longitudeRef);
addTableRow("processing", processing);
addTableRow("timestamp", timestamp);
addTableRow("imageLength", imageLength + "");
addTableRow("imageWidth", imageWidth + "");
addTableRow("make", make);
addTableRow("model", model);
addTableRow("orientation", orientation + " (" + orientationInfo + ")");
addTableRow("whitebalance", whitebalance + " (" + (whitebalance == 1 ? "manual" : "auto") + ")");

Log.d(TAG, "latlong : " + latlong[0] + ", " + latlong[1]);
Log.d(TAG, "datetime : " + datetime);
Log.d(TAG, "flash : " + flash + " (" + (flash == 1 ? "on" : "off") + ")");
Log.d(TAG, "focalLength : " + focalLength + "");
Log.d(TAG, "datestamp : " + datestamp);
Log.d(TAG, "altitude : " + altitude);
Log.d(TAG, "altitudeRef : " + altitudeRef);
Log.d(TAG, "latitude : " + latitude);
Log.d(TAG, "latitudeRef : " + latitudeRef);
Log.d(TAG, "longitude : " + longitude);
Log.d(TAG, "longitudeRef : " + longitudeRef);
Log.d(TAG, "processing : " + processing);
Log.d(TAG, "timestamp : " + timestamp);
Log.d(TAG, "imageLength : " + imageLength + "");
Log.d(TAG, "imageWidth : " + imageWidth + "");
Log.d(TAG, "make : " + make);
Log.d(TAG, "model : " + model);
Log.d(TAG, "orientation : " + orientation + " (" + orientationInfo + ")");
Log.d(TAG, "whitebalance : " + whitebalance + " " + (whitebalance == 1 ? "manual" : "auto"));
}
}

private void addTableRow(String name, String value) {
TableLayout tl = (TableLayout)findViewById(R.id.table);

TableRow tbr = new TableRow(this);
TextView tv = new TextView(this);
tv.setText(name);
tv.setPadding(0, 0, 10, 0);
tbr.addView(tv);
TextView tv2 = new TextView(this);
tv2.setText(value);
tbr.addView(tv2);
tl.addView(tbr);
}
}



<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:padding="10dip"
android:gravity="center_horizontal"
>
<ImageView
android:id="@+id/thumbnail"
android:layout_width="fill_parent"
android:layout_height="0dip"
android:layout_weight="1"
android:cropToPadding="true"
/>
<TableLayout
android:id="@+id/table"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:paddingTop="10dip"
/>
</LinearLayout>



* その1

 Nexus S のデフォルトカメラで撮影
 フォーカス : マクロ
 露出 : +2
 シーンモード : オート
 位置情報記録 : ON
 フラッシュ : OFF

 縦で撮影





* その2

 Nexus S のデフォルトカメラで撮影
 フォーカス : 無限遠
 露出 : -1
 シーンモード : 横向き
 位置情報記録 : ON
 フラッシュ : ON

 横で撮影




* 値を設定する


public class MainActivity2 extends Activity {

static final String TAG = "ExifSample";

private ExifInterface exifInterface;

@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main2);

// get file state
String status = Environment.getExternalStorageState();
Log.d(TAG, "status : " + status);

// get file path
//String filename = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM) + "/droids.jpg" ; // since API level 8
//String filename = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM) + "/droids_flash.jpg" ; // since API level 8

// below API level 8
String filename = Environment.getExternalStorageDirectory().toString() + "/droids.jpg";
//String filename = Environment.getExternalStorageDirectory().toString() + "/droids_flash.jpg";
Log.d(TAG, "filename : " + filename);

if (!status.equals(Environment.MEDIA_MOUNTED)) {
// media is not mounted
Log.d(TAG, "media is not mounted");
setText("media is not mounted");
return;
} else if (!(new File(filename)).exists()) {
// file does not exists
Log.d(TAG, "file does not exists");
setText("file does not exists");
return;
}

if(!filename.endsWith(".jpg") && !filename.endsWith(".jpeg") && !filename.endsWith(".JPG") && !filename.endsWith(".JPEG")) {
// file is not JPEG
Log.d(TAG, "file is not JPEG format");
setText("file is not JPEG format");
return;
}

try {
exifInterface = new ExifInterface(filename);
} catch (IOException e) {
e.printStackTrace();
Log.d(TAG, e.getMessage());
setText(e.getMessage());
return;
}

showExifInfo();
ImageView imageView = (ImageView)findViewById(R.id.thumbnail);
imageView.setImageBitmap(getExifThumbnail());
}

private Bitmap getExifThumbnail(){
if(exifInterface != null) {
// get thumbnail
byte[] thumbnail = exifInterface.getThumbnail();
if(thumbnail != null) {
return BitmapFactory.decodeByteArray(thumbnail, 0, thumbnail.length);
}
}
return null;
}

private void showExifInfo(){
if(exifInterface != null) {
String datetime = exifInterface.getAttribute (ExifInterface.TAG_DATETIME);
setText(datetime);
}
}

private void setText(String text) {
TextView textView = (TextView)findViewById(R.id.textview);
textView.setText(text);
}

public void updateDate(View v) {
EditText editText = (EditText)findViewById(R.id.edittext);
String date = editText.getText().toString();

if(exifInterface != null) {
exifInterface.setAttribute(ExifInterface.TAG_DATETIME, date);
}
showExifInfo();
}
}



<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:padding="10dip"
android:gravity="center_horizontal"
>
<ImageView
android:id="@+id/thumbnail"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:cropToPadding="true"
/>
<TextView
android:id="@+id/textview"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:gravity="center"
/>
<EditText
android:id="@+id/edittext"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
/>
<Button
android:id="@+id/button"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:paddingTop="10dip"
android:onClick="updateDate"
android:text="Update!"
/>
</LinearLayout>



0 件のコメント:

コメントを投稿