読者です 読者をやめる 読者になる 読者になる

takafumi blog

日々の勉強メモ

【Haskell】 HsExifを使い、Exif情報で画像を整理するスクリプトを書いてみた

環境   ghc 7.8.3 CentOS7.0

HaskellExif情報を使って、写真を整理するスクリプトを書いてみた。

github.com

・・・まだExif撮影日時ごとにディレクトリ作成して、画像をコピーするだけ。

Exif取得自体はHsExifといモジュールを使ったが、そもそもHaskell自体がまだまだなので、このスクリプトを書くだけで約5時間かかった。

とりあえず、まだディレクトリを撮影日時から作成して、コピーしているだけなので、時間あるときに少しずつ拡張していく予定。


以下HsExif使い方
▽インストール

cabal installで可能。

# cabal install hsexif


ByteString.Lazyは一緒に使うのでこれもimport。
ApplicativeMonadは必須ではないが、多分使うのでimport。

import qualified Data.ByteString.Lazy as B 
import Graphics.HsExif

import Control.Applicative
import Control.Monad


▽画像からExif情報取り出し

実際にExifを取り出すには、以下のparseFileExifparseExifを使う。

-- 【定義】
-- | Read EXIF data from the file you give. It's a key-value map.
parseFileExif :: FilePath -> IO (Either String (Map ExifTag ExifValue))
parseFileExif filename = parseExif <$> B.readFile filename

-- | Read EXIF data from a lazy bytestring.
parseExif :: B.ByteString -> Either String (Map ExifTag ExifValue)
parseExif = runEitherGet getExif


Exif情報だけ必要ならparseFileExifでファイルを一発読み込みするのが楽。
ただしファイルの中身も必要なら、一度とりだしてやる必要がある。

-- parseFileExif
ghci> rightExif <- parseFileExif "Foo.JPG"

-- parseExif
ghci> file <- return "Foo.JPG" >>= B.readFile
ghci> rightExif <- parseExif <$> return file


一度ファイルを取り出すとき、parseExifが一見、以下でも通りそうに感じる(私だけかな?)が、IO束縛なのでこれはエラーになる。

ghci> rightExif <- return file >>= parseExif
<interactive>:28:30:
    Couldn't match type ‘Either String’ with ‘IO’


Exif情報の各情報を抽出

exif情報のかたまりはRight (Map ExifTag ExifValue)というデータ型になっている。
Data.Mapなので、lookup等を駆使して力技で取得する事もできるが、各情報の専用関数が用意されている。
例えば撮影日時だと

-- 撮影日時取得
getDateTimeOriginal :: Map ExifTag ExifValue -> Maybe LocalTime
getDateTimeOriginal exifData = Map.lookup dateTimeOriginal exifData >>= readExifDateTime . show

があるのでコレを使う。
ただし、parseExifEither型、各種情報取得系(getDateTimeOriginal等)はMaybe型になるので、一度取り出してやる。

ghci> let exif = (\(Right x) -> x) rightExif
ghci> return exif >>= getDateTimeOriginal
Just 2012-01-24 06:45:41

EitherMaybeの交換はもっといい手がありそうな気がするが、わからなかったので強引にいった。

日付系はData.Time.LocalTime.LocalTime.LocalTimeが使われているので、これを更に弄るときはimport Data.Timeも必要になる。


githubのTests.hsがとても参考になる。

【参考】HsExif

▽Hacakge
hsexif: EXIF handling library in pure Haskell | Hackage

GitHub
emmanueltouzery/hsexif · GitHub


takafumi-s.hatenablog.com