この記事は Haskell でサムネイルの続きです。まだの方はそちらを先にどうぞ。
imagemagick
サムネイル生成といえば ImageMagick が思い当たります。ImageMagick は Ruby、Python、PHP、Perl、Java、Go、その他いろいろな言語のバインディングがあり、我らが Haskell にも imagemagick というパッケージが存在しました。
存在はしましたが、画像の向きをどうにかする機能は Haskell のバインディングでは提供されていませんでした(0.0.4.1 ではコメントアウトされています)。
ImageMagick
ImageMagick は画像に関する様々な処理がコマンドラインで行えます。例えば、画像ファイルの情報を取得するには以下のように行います。
$ identify original.jpg
original.jpg JPEG 3264x2448 3264x2448+0+0 8-bit sRGB 3.288MB 0.000u 0:00.000
画像の幅と高さだけが欲しい場合、以下のように行います。
$ identify -format "%w %h" original.jpg
3264 2448
画像をリサイズする場合は以下のように行います。
$ convert -resize 200x150 original.jpg thumb.jpg
今回行いたい処理は、縦横のどちらかが 200px 以内になるようにアスペクト比を維持したまま、Exif の Orientation が設定されていればその向きに補正しつつ、不要な Exif はすべて除去しつつサムネイルを生成する、というものです。
$ convert -auto-orient -strip -resize 200x200 original.jpg thumb.jpg
-resize 200x200
というのが空気を読んでくれる素晴らしいオプションでした。元画像のサイズから計算したり Exif Orientation を取得して回転計算を行ったりする必要はありません。
Haskell から実行する
import System.Process
main :: IO ()
main = do
system "convert -auto-orient -strip -resize 200x200 original.jpg thumb.jpg"
return ()
身も蓋もないんですが、システムコマンドを実行してしまいます。
thumbnail の mkThumbnail'
なら、サムネイルの画像サイズが以下のように取得できますが:
thumb <- mkThumbnail' ((50, 50), (200, 200)) contents
case thumb of
(Left _) -> return Nothing
(Right t) -> return $ Just (fst $ sz t, snd $ sz t)
convert コマンドでサムネイルの画像サイズを取得する場合、例えば以下のようにできます:
system $ "convert -auto-orient -strip -resize 200x200 " ++ org ++ " " ++ thumb
wh <- readProcess "identify" ["-format", "%w %h", thumb] []
let a = " " `splitOn` wh
return $ Just (read $ a !! 0, read $ a !! 1)
まとめ
Haskell でサムネイルで使用したライブラリは、どちらも Exif Orientation を考慮したサムネイルを生成してくれませんでした。
今回の記事では、オリジナルの JPEG から Exif Orientation を読み取り、回転しているようであれば生成したサムネイルを回転させるか Exif Orientation を付与するか、という方法を検討していました。Haskell で Exif を読み取れる hsexif というパッケージがあります。これは以下のように使用します。
import Data.Either (either)
import Data.Map.Lazy (fromList)
import Graphics.HsExif (getOrientation, parseFileExif)
file :: FilePath
file = "tools/4f517b31-caea-4ade-9cc5-069d6c9970dd.jpeg"
main :: IO ()
main = do
eexif <- parseFileExif file
let exif = either (\a -> fromList []) id eexif
mio = getOrientation exif
print mio
getOrientation
という便利な関数が提供されており、例えば左に 90 度回転した画像だと、このサンプルは Just (Rotation MinusNinety)
を出力します。
ただし、hsexif は Exif の読み取りのみをサポートし、Exif の書き込みはサポートしていないこと、ExifTool を使用すれば Exif の書き込みが可能ではあるが、システムコマンドを実行するのであれば ImageMagick でコマンド一発でサムネイルを生成する方が簡単であることから、今回のような結果となりました。
OpenCV の Haskell バインディングである CV ならできるかも、と思って API を探してみましたが、OpenCV の Resize 相当が見当たらなかったため、実装されていないのだろうという結論です。