［Android］Exifを理解して、取得して、表示する

# はじめに

今回は Exif を理解して、取得して、表示して見ようと思います。
最終的には次のような Android アプリを作成して動作確認します。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/79387/e3f3f48e-4493-085d-b76d-3c7913173c4a.gif" Height=750px/>

# Exif を理解する

私は写真の位置情報を取得するなら Exif で理解が止まっているので、
はじめに Exif について調べて理解を深めていこうかなと思います。

## Exif ってなんなのか？
次のようなことが Wikipedia の Exif のページに書いていました。

```:Wikipediaから引用
Exchangeable image file format（エクスチェンジャブル・イメージ・ファイル・フォーマット）は、
富士フイルムが開発し、当時の日本電子工業振興協会 (JEIDA)で規格化された、写真用のメタデータを含む画像ファイルフォーマット。
デジタルカメラの画像の保存に使われる。略称はExifで「エグジフ」（もしくは「イグジフ」）。
```

また Exif の規格書には次のようなことが書かれています。

```:デジタルスチルカメラ用画像　ファイルフォーマット規格　Exif2.3　より引用
この規格は、ディジタルスチルカメラ、及びデジタルスキルカメラで記録される画像ファイル又は
音声ファイルを取り扱うシステムにおいて、画像、音声及びタグのフォーマットを規定する。
```

Exif は ”だだのメタデータ群” だと勘違いをしていましたが ”画像、音声及びタグのファイルフォーマット” なんですね。

## Exif はJPEGじゃないのか？

Exif はファイルフォーマットだと理解できましたが、 Exif という拡張子は見たことないし結局どんな画像なのかって思いますよね。
もう少し Exif の規格書を読み進めていくと次のようにその疑問の答えが書いていました。

```:デジタルスチルカメラ用画像　ファイルフォーマット規格　Exif2.3　より引用
ファイルの記録形式は、既存のフォーマットをベースとする。圧縮ファイルはJPEG(ISO/IEC 10918-1)に
アプリケーション・マーカーセグメント（APP1, APP2）を挿入記録し、非圧縮ファイルはTIFF Rev.6.0で記録する。
```

つまりは Exif 画像と言えば JPEG 画像 + メタデータ情報 のことらしいです。
なので JPEG 画像であるのは間違いありませんがそこにメタデータが含まれるかどうかで Exif 画像であるか決まるという感じですね。

## Exif にはどんな情報が含められている？

Exif ですがここに書ききれない種類の情報（タグ）が含められています。
例えば情報（タグ）の一覧は例えば[ここ](http://www.vieas.com/exif23.html)とかでまとめてくれています。
なので Exif から情報を取得するときにはタグ一覧から欲しい情報があるか確認するのがよいと思います。

# Exif を取得して表示する

ある程度ですが Exif への理解も深められたところで、
実際に Android で Exif を取得して表示してみます。

## 1. Exif 情報を読み取る画像を格納する。

今回は Assets フォルダにある画像の Exif 情報を読み取りたいと考えています。
ですので次の手順で Assets フォルダを作成して画像を格納しておきます。

(1) 「appを右クリック」→「New」→「Folder」→「Assets Folder」で Assets フォルダを作成する
(2) 作成した Assets フォルダを開き、Exif 情報を読み取る画像をコピーする

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/79387/05fd1ab1-4927-63a2-c545-510e2d9e20ef.png)

## 2. Exif 情報を表示する View を作成する。

Exif情報を読み取った画像を表示するための ImageView と
読み取った Exif 情報を表示するための ListView を MainActivity に定義します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/79387/79686878-9608-5499-b38f-207e78fb2391.png)

```xml:main_activity.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context=".MainActivity">

    <ImageView
        android:id="@+id/image_view"
        android:layout_width="match_parent"
        android:layout_height="250dp"
        android:background="@android:color/black"
        android:scaleType="centerCrop"/>

    <ListView
        android:id="@+id/exif_data_list"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>
</LinearLayout>
```

## 3. Exif 情報を読み取る。

画像から Exif 情報を読取るには [ExifInterface](https://developer.android.com/reference/android/media/ExifInterface) を利用します。
次のように ExifInterface を作成し getAttribute でタグを指定することで Exif のタグ情報を取得できます。

```kotlin:Exifタグを取得する
{
        // getAttribute に指定するタグの定義は Exit Interface ページに記載されてるのでそこを参照する。
        val inputStream = assets.open("sample.jpg")
        val exifInterface = ExifInterface(inputStream)
        exifInterface.getAttribute(TAG_NAME)
}
```


今回の ListView に複数の Exif タグ情報を表示したいので、
次のように Map にセットしたタグの情報を取得して表示するように処理を記述します。

```kotlin:MainActivity.kt
    private val exifMap = mapOf(
        "Datetime" to ExifInterface.TAG_DATETIME,
        "Latitude" to ExifInterface.TAG_GPS_DEST_LATITUDE,
        "Latitude Ref" to ExifInterface.TAG_GPS_DEST_LATITUDE_REF,
        "Longitude" to ExifInterface.TAG_GPS_DEST_LONGITUDE,
        "Longitude Ref" to ExifInterface.TAG_GPS_DEST_LONGITUDE_REF
        )

    private fun createDrawableFromAssets(assetsName: String) : Drawable {
        val inputStream = assets.open(assetsName)
        return Drawable.createFromStream(inputStream, null)
    }

    private fun createExifArrayFromAssetsFile(assetsName : String) : ArrayList<String> {
        val inputStream = assets.open(assetsName)
        val exifInterface = ExifInterface(inputStream)
        return arrayListOf<String>().also { array ->
            exifMap.forEach { item ->
                array.add("${item.key} : ${exifInterface.getAttribute(item.value)}")
            }
        }
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        val image = findViewById<ImageView>(R.id.image_view)
        val drawable =  createDrawableFromAssets("sample.jpg")
        image.setImageDrawable(drawable)

        val list = findViewById<ListView>(R.id.exif_data_list)
        val array = createExifArrayFromAssetsFile("sample.jpg")
        list.adapter = ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, array)
    }
```

# 4. 動作を確認する

アプリを起動して動作を確認すると次のような感じで Exif 情報が表示されます。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/79387/e3f3f48e-4493-085d-b76d-3c7913173c4a.gif" Height=750px/>

# おわりに

- Exif とは画像・音声又はタグのファイルフォーマット
- Exif 画像は JPEG に複数のメタデータ（タグ）を含めたもの
- Android で Exif 情報は ExifInterface を利用すれば取得できる


# 参考文献

- [デジタルスチルカメラ用 画像ファイルフォーマット規格 Exif 2.3](http://www.cipa.jp/std/documents/j/DC-008-2012_J.pdf)
- [Excangeable image file format](https://ja.wikipedia.org/wiki/Exchangeable_image_file_format)
- [Exif 2.3 タグ - Vieas Web](http://www.vieas.com/exif23.html)
- [ExifInterface | Android Developers](https://developer.android.com/reference/android/media/ExifInterface)