LoginSignup
3
2

More than 5 years have passed since last update.

Haskell でサムネイル #2

Last updated at Posted at 2016-02-17

この記事は 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 から実行する

Sample.hs
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 というパッケージがあります。これは以下のように使用します。

Sample.hs
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 相当が見当たらなかったため、実装されていないのだろうという結論です。

3
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
2