Rails MiniMagickの使い方

dscf5059_tp_v

Railsで画像処理したいときはMiniMagickが使えます。MiniMagickはImageMagickのラッパーです。

リファレンスはhttps://github.com/minimagick/minimagickです。

準備 – ImageMagick

ImageMagickのインストールが必要です。

OS Xなら

$ brew install imagemagick

Ubuntuなら

$ sudo apt-get install imagemagick

終わったらバージョンを確認してみます。

$ convert --version
Version: ImageMagick 6.7.7-10 2016-06-01 Q16 http://www.imagemagick.org
Copyright: Copyright (C) 1999-2012 ImageMagick Studio LLC
Features: OpenMP

準備 – MiniMagick

MiniMagickのインストールはGemfileにgemを追加してインストールします。

gem 'mini_magick'
$ bundle install

使うときはrequireします。

require 'mini_magick'

画像を開く

MiniMagickでファイルを開くには、MiniMagick::Image.openを使います。こんな感じです。

image = MiniMagick::Image.open('画像ファイルへのパス')

リモートサーバー上の画像をURL経由で開く場合もopenを使います。

image = MiniMagick::Image.open('画像へのURL')

画像データを直接流し込むにはMiniMagick::Image.readを使います。

image = MiniMagick::Image.read(画像データ)

画像がないと始まらないので、Railsアプリケーションのルートに画像を置き、読み込んでみます。

image = MiniMagick::Image.open('sample.jpg')
p image

読み込んだMiniMagickオブジェクトをpで表示するとこんな感じです。@tempfileとあるのが見えるかと思いますが、openすると画像をTempfileとしてコピーして、それが編集されます。オリジナル画像には編集を加えないのが特長です。

#<MiniMagick::Image:0x007fb9c4289f80 @path="/tmp/mini_magick20160926-30947-1nwlz88.jpg", @tempfile=#<Tempfile:/tmp/mini_magick20160926-30947-1nwlz88.jpg (closed)>, @info=#<MiniMagick::Image::Info:0x007fb9c4289f58 @path="/tmp/mini_magick20160926-30947-1nwlz88.jpg", @info={}>>

画像が見つからないと次のようなエラーが発生します。

#<Errno::ENOENT: No such file or directory @ rb_sysopen - sample.jpg>

画像以外を指定すると次のようなエラーが発生します。

#<MiniMagick::Invalid: `identify /tmp/mini_magick20160926-31462-5q8fym` failed with error:

画像の情報を取得する

パスを取得するにはpathを使います。

p image.path
#=> "/tmp/mini_magick20160926-6262-1x2fdps.jpg"

いろいろな情報を取得するにはdetailsを使います。

p image.details
#=> {"Base filename"=>"mini_magick20160926-6388-1nx74jz.jpg", "Format"=>"JPEG (Joint Photographic Experts Group JFIF format)", "Class"=>"DirectClass", "Geometry"=>"1600x1062+0+0", "Resolution"=>"400x400", "Print size"=>"4x2.655", "Units"=>"PixelsPerInch", "Type"=>"TrueColor", "Endianess"=>"Undefined", "Colorspace"=>"sRGB", "Depth"=>"8-bit", "Channel depth"=>{"red"=>"8-bit", "green"=>"8-bit", "blue"=>"8-bit"}, "Channel statistics"=>{"Red"=>{"min"=>"0 (0)", "max"=>"255 (1)", "mean"=>"126.904 (0.497664)", "standard deviation"=>"46.5197 (0.18243)", "kurtosis"=>"2.84988", "skewness"=>"-0.36877"}, "Green"=>{"min"=>"3 (0.0117647)", "max"=>"255 (1)", "mean"=>"142.097 (0.557243)", "standard deviation"=>"33.7838 (0.132485)", "kurtosis"=>"2.71775", "skewness"=>"1.84254"}, "Blue"=>{"min"=>"0 (0)", "max"=>"255 (1)", "mean"=>"152.065 (0.596332)", "standard deviation"=>"43.5204 (0.170668)", "kurtosis"=>"-0.0318651", "skewness"=>"1.17011"}}, "Image statistics"=>{"Overall"=>{"min"=>"0 (0)", "max"=>"255 (1)", "mean"=>"140.355 (0.550413)", "standard deviation"=>"41.6311 (0.163259)", "kurtosis"=>"3.26784", "skewness"=>"0.529974"}}, "Rendering intent"=>"Perceptual", "Gamma"=>"0.454545", "Chromaticity"=>{"red primary"=>"(0.64,0.33)", "green primary"=>"(0.3,0.6)", "blue primary"=>"(0.15,0.06)", "white point"=>"(0.3127,0.329)"}, "Interlace"=>"None", "Background color"=>"white", "Border color"=>"srgb(223,223,223)", "Matte color"=>"grey74", "Transparent color"=>"black", "Compose"=>"Over", "Page geometry"=>"1600x1062+0+0", "Dispose"=>"Undefined", "Iterations"=>"0", "Compression"=>"JPEG", "Quality"=>"90", "Orientation"=>"Undefined", "Properties"=>{"comment"=>"Optimized by JPEGmini 3.13.0.4 0x01ee8039", "date:create"=>"2016-09-26T11:54:16+00:00", "date:modify"=>"2016-09-26T11:54:16+00:00", "jpeg:colorspace"=>"2", "jpeg:sampling-factor"=>"2x2,1x1,1x1", "signature"=>"96d0bd9e2b65b05cfc7cf78d156a5074dbfa0afd8a38d800f7518e053d714dd8"}, "Artifacts"=>{"filename"=>"/tmp/mini_magick20160926-6388-1nx74jz.jpg[0]", "verbose"=>"true"}, "Tainted"=>"False", "Filesize"=>"87.5KB", "Number pixels"=>"1.699M", "Pixels per second"=>"169.9MB", "User time"=>"0.010u", "Elapsed time"=>"0:01.010", "Version"=>"ImageMagick 6.7.7-10 2016-06-01 Q16 http://www.imagemagick.org"}

書き出す

何もしていませんが、読み込んだ画像を書き出してみます。writeで書き出せます。

image.write "resize.jpg"

次のように元の名前と同じにすると、上書きになるので注意します。

image.write "sample.jpg"

リサイズする

リサイズするには、resizeを使います。乗算記号はエックス(x)です。

image.resize "横幅x高さ"

横幅か高さのいずれかの長辺が300pxに収まるように、比率を保ったまま縮小するには次のようにします。

image.resize "300x300"
image.write "wh300.jpg"

横幅が300pxに収まるように、比率を保ったまま縮小するには次のようにします。

image.resize "300"
image.write "w300.jpg"

高さが300pxに収まるように、比率を保ったまま縮小するには次のようにします。

image.resize "x300"
image.write "h300.jpg"

もっといろいろなリサイズ方法については公式サイトに詳しく乗っています。

http://www.imagemagick.org/script/command-line-processing.php#geometry

リサイズで画像が破損する!?

リサイズした画像を再度リサイズすると次のように画像が破損することがありました。

本来の画像。

w400

横幅400pxへのリサイズに失敗した画像。

w400_broken

どうやら、次のようにリサイズ後に元のファイル名と同じ名前で保存したところ、画像情報がうまく更新されなかったようです。

image = MiniMagick::Image.open('sample.jpg')
p image.dimensions
#=> [1600, 1062]

image.resize "600"
image.write "sample.jpg"

image = MiniMagick::Image.open('sample.jpg')
p image.dimensions
#=> [1600, 1062]

2番目のdimensions600x398のはずなのに1600x1062と認識されていました。2番目の画像ファイル名を変更したところ、うまくいきました。

例)ダウンロードした画像をリサイズする

MiniMagick::Image.openでURLから直接画像を読み込み、横幅300pxにリサイズします。URLは架空です。

require 'mini_magick'
begin
 uri = 'http://example.com/sample.jpg'
 image = MiniMagick::Image.open(uri)
 image.resize "300"
 image.write "image300.jpg"
rescue => e
 p e
end

OpenURIで画像をダウンロードしたあとに、画像データを読み込む場合は次のようにします。

require 'open-uri'
require 'mini_magick'
begin
 uri = 'http://example.com/sample.jpg'
 open(uri) { |io|
  image = MiniMagick::Image.read(io.read)
  image.resize "300"
  image.write "image300.jpg"
 }
rescue => e
 p e
end

OpenURIで画像をダウンロードしたあとにTempfileに書き出し、Tempfileへのパス経由で画像を読み込むには次のようにします。

require 'open-uri'
require 'tempfile'
require 'mini_magick'
begin
 uri = 'http://example.com/sample.jpg'
 open(uri) { |io|
  Tempfile.open { |t|
   t.binmode
   t.write io.read
   t.close

   image = MiniMagick::Image.open(t.path)
   image.resize "300"
   image.write "image300.jpg"
  }
 }
rescue => e
 p e
end