LoginSignup
9
4

More than 5 years have passed since last update.

MiniMagickを使って自動トリミングする方法

Posted at

環境

  • ruby歴 初心者
  • rails5
  • ruby 2.4.1p111
  • imagemagick 6.9.8-8 Q16 x86_64
  • minimagick 4.5.1

これはなに?

ImageMagick での自動トリミングを ruby で実現するために
ruby のインタフェースで作られた MiniMagick を利用してトリミングを実現しました。
その際に学んだことや発生した問題も踏まえて書き留めて置きたいと思います。

ちなみに筆者は画像処理についての知識は薄いのであしからず
また、コメントは細かいところでも大歓迎です。

発端

まず、やりたかったことは 透過部分のトリミング になります。
※背景は透過で、見やすいように枠線だけ加工して入れてます。

■オリジナル画像
mask.png

■ほしい画像
trimmed_mask.png

ということで、オリジナル画像にトリミングをかけていきます。
ImageMagick でトリミングしたい場合は -trim オプションをつけるだけでOKでした。(すばらしい!)

convert <path/to/オリジナル画像> -trim <path/to/ほしい画像>

さて、次に ruby の場合です。
調査したところ実現する方法としては、下記の2通りありました。

  1. MiniMagick::Image#trim を用いて処理
  2. MiniMagick::Tool::Convert を用いて CLI のように処理

もちろん 1. が前述した convert 処理してるんだろうなと思い
そちらの手法で処理をしました。

導入

基本的には下記の流れです。

  1. imagemagick のインストール
  2. minimagick の gem インストール

細かい部分は公式を見てください。
実装部分はこんな感じ

image_editor.rb
def trimming(input_path, output_path)
  image = MiniMagick::Image.open(input_path)
  image.trim
  image.write(save_path)
end

おお、簡単じゃないか..........っと!!!
ところがどっこい、世の中そんな甘くなかった

問題

鼻歌まじりに、次のサンプルをトリミングにかけた時
衝撃の結果になりました。

■問題のサンプル
mask.png

■衝撃の結果
名称未設定.png

なぜか上部の黒い部分まで切り取られてでてきやがったぜ....どういうことだってばよ
※「ちゃんと仕様みとけクソ」というツッコミありがとうございますm(_ _)m

なんでこんなことになったしまったのか、この時点でようやく trim の仕様を確認しに行く次第でした。

原因

まずは、ここをよく読んで下さい。

This option removes any edges that are exactly the same color as the corner
pixels.
「コーナー」のピクセルと同じ色のエッジにそってトリミングします」

OMG、書いてあります。
ちゃんと書いてあるんです。

んんっ??
コーナー......(いろいろ調べてみたら、左上隅の1ピクセルが対象となるらしい)

なるほどねー、左上隅のピクセル確かに黒いわー
今まで黒い部分が設置してなかったから気づかなかった....
期待してるトリミング画像ほしけりゃ左上隅のピクセル透過色にしないと無理ってことね。
いやいやいやいや、どうやるんですか
指定するピクセルで自動トリミングとかできないの??
(できる方法あったら教えてくださいm(_ _)m)

解決

ここでさらに公式をチェックしたところ.....ありました。
どうやら、トリミングしたい色のボーダーラインを追加してから -trim するようです。
なるほど、たしかにそれでできるね。
(ボダーラインみたいなゴリゴリじゃなくカラーを指定させてほしい......)

もはや、MiniMagickの範疇を超えているので、結局CLI風味で実装することになった。

image_editor.rb
def trimming(input_path, output_path)
  # trim image
  MiniMagick::Tool::Convert.new do |convert|
    convert << "#{input_path}"
    convert.merge! ["-bordercolor", "none"]
    convert.merge! ["-border", "1x1"]
    convert.merge! ["-trim"]
    convert << "#{output_path}"
  end
end

MiniMagickを使ってはいるが、結局のところCLIで下記のコマンドを実行しているだけのよう

convert <input_path> -bordercolor none -border 1x1 -trim <output_path>

-bordercolor については透過でほしかったので none にしております

結果、うまくトリミングできました。

■問題のサンプル
mask.png

■結果
correct.png

おまけ

画像以外に元画像におけるトリミング領域の座標も欲しかったので取ってみました。
公式にも実装方法は書かれております。

image_editor.rb
def trimming(input_path, output_path)
  # trim image
  MiniMagick::Tool::Convert.new do |convert|
    convert << "#{input_path}"
    convert.merge! ["-bordercolor", "none"]
    convert.merge! ["-border", "1x1"]
    convert.merge! ["-trim"]
    convert << "#{output_path}"
  end

  # get trimmed image bounding box
  info = MiniMagick::Tool::Convert.new do |convert|
    convert << "#{save_path}"
    convert.merge! ["-set", "page", "%[fx:page.width-2]x%[fx:page.height-2]+%[fx:page.x-1]+%[fx:page.y-1]"]
    convert.merge! ["-format", "{ \"x\": \"%X\", \"y\": \"%Y\", \"width\": \"%w\", \"height\": \"%h\" }"]
    convert << "info:"
  end

  # parse to json
  trimmed_bounding_box = JSON.parse(info.delete('+'))
end
image_editor.rb
convert.merge! ["-set", "page", "%[fx:page.width-2]x%[fx:page.height-2]+%[fx:page.x-1]+%[fx:page.y-1]"]

ここの指定がミソっぽいですね。
追加したボーダーライン分の座標を調整しています。

学んだこと

・自動トリミングの仕組み(左上隅ピクセル情報を用いてトリミングされる)
・指定の色でトリミングする場合はボーダーラインを追加してからトリミングする

9
4
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
9
4