ImageMagickで、/tmpとかにmagick-ayu89r53とかいうサイズの大きいファイルが出来るのをなんとかしたい

ImageMagickを使って日々運用していると、時々ディスクフルアラートが出たりします。。

よく調べてみると、/tmpにmagick-ayu89r53とかよく分からないファイルが数十GB位のサイズになっていてびっくりします。。

別に定期的に出る訳では無く、たまに出たりするので、なんとも分からないのですが

ImageMagick本家のドキュメントを調べてみると

http://www.imagemagick.org/script/command-line-options.php
[text]-limit <em>type value</em>[/text]
というのが設定出来るようです。

このlimitの概要にはSet the pixel cache resource limit.となっていまして
具体的には
[text]<code>area</code>, <code>disk</code>, <code>file</code>, <code>map</code>, <code>memory</code>, <code>threads</code>, <code>time</code>[/text]
が指定出来るようです。

diskは、その名の通り、disk使用量。おそらく、この設定が/tmp/magick-ナンタラ

に影響しているのでは無いかと思ったりします。

これらの現在の設定値を確認出来るコマンドがあるので、叩いてみます。
[shell]identify -list resource[/shell]
このdiskの初期値が18.446744EBとか書いてあります。

EBって何ですか!?

と一瞬びっくりしますが、エクサバイトの事ですかね。

今の運用環境からするとほぼ無限なサイズがデフォルトに指定されています。

ここを変えるだけで、バカみたいにでかい/tmp/magick-ナンタラは出来なくなるかと思います。

まだ検証はしていません。

これから実験運用してみようかと思います。

ちなみに、このオプションの設定ですが

convertコマンドを叩く時はそのまま-limit disk 1GBと書けば良いです。

別アプリからライブラリを叩く場合は

ImageMagickの設定のディレクトリに入っているpolicy.xmlに、<policymap>があるので

このなかでname=”disk”のものを探して1GBに設定すれば良いはずです。

[Scala]Ubuntu11.xx上でPlay framework 2.0 for Scalaで、ImageMagick/jMagickを使ってみる

Ubuntu 11.xx上で、Play framework 2.0 for ScalaからImageMagickを使う

ということで、今回のコンセプトですが

cookpadにはtohuがある

ぼくらにはない

でもapacheのモジュールで作るのもしんどい

という事で、Play framework for Scalaで作るという事なのです

まずはJDKのインストール

これは、

http://www.oracle.com/

からダウンロードします。

しかし、SunOracleに買収されたのは未だにショックです。。

というのはおいといて

ダウンロードしてきたJDKを解凍し、/usr/localに移動します。
[shell]tar xvfz jdk-7u4-linux-x64.tar.gz
sudo mv jdk1.7.0_04 /usr/local[/shell]
JAVA_HOMEを設定します。

/etc/environment


[shell]JAVA_HOME=”/usr/local/jdk1.7.0_04″[/shell]
を追加

その下にあるPATHにも
[shell]/usr/local/jdk1.7.0_04[/shell]
を先頭あたりに追加

続いてScalaのインストール

http://www.scala-lang.org/

からダウンロード

解凍
[shell]unzip scala-2.9.1.final.zip[/shell]
scala/usr/localに移動します。
[shell]sudo mv scala-2.9.1.final /usr/local[/shell]
JAVA_HOMEと同様にSCALA_HOMEを設定します。

/etc/environment

を開いて、JAVA_HOMEの下に
[shell]SCALA_HOME=”/usr/local/scala-2.9.1.final”[/shell]
を追加

その下のPATHにも
[shell]/usr/local/scala-2.9.1.final/bin[/shell]
を追加。

最終的に、PATHは以下のようになると思います。
[shell]PATH=”/usr/local/jdk1.7.0_04/bin:/usr/local/scala-2.9.1.final/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games”[/shell]
次にImageMagickのインストール
[shell]sudo apt-get install imagemagick[/shell]
jMagickaptでインストール
[shell]sudo apt-get install libjmagick6-java[/shell]
若干パッケージ名が違うので注意

パッケージで入れた場合、jmagick6.jar/usr/share/javaに置かれます。

libJMagick.soですが、

/usr/lib/jniに入ります。

このjni、パスが通っていませんので、後々エラーが出ると思うので

ここでパスを通しちゃいます。

/etc/bash.bashrcあたりに
[shell]export LD_LIBRARY_PATH=/usr/lib/jni:$LD_LIBRARY_PATH[/shell]
を追加しておきます。

次にPlay Frameworkを入れます。

http://www.playframework.org/

からダウンロード

今回は2.0を使っています。

1.x.xではないです。

ダウンロードした物を解凍
[shell]unzip play-2.0.zip[/shell]
play自体は、個人用ホームに置きます。
[shell]mv play-2.0 ~[/shell]
playコマンドにエイリアスを設定

~/.bashrc


[shell]alias play=~/play-2.0/play[/shell]
を追加

これで、playコマンドがたたけるようになりました。

早速新しいプロジェクトを作成します。
[shell]play new ImageResizer[/shell]
ImageResizerディレクトリに入ります。
[shell]cd ImageResizer[/shell]
libディレクトリを作成します。
[shell]mkdir lib[/shell]
とりあえず、/usr/share/javaからjmagick6.jarをこのlibにコピーします。

ちょっとめんどくさくなってきたので、ソースを貼り付けます。

#/app/controller/ImagesController.scala
[scala]package controllers

import play.api._
import play.api.mvc._
import java.io.File
import scalax.file.Path
import java.io.FileInputStream
import play.api.libs.iteratee.Enumerator
import java.security.MessageDigest

import magick.Magick
import magick.MagickImage
import magick.MagickException
import magick.ImageInfo

object ImagesController extends Controller {

trait createpathtrait[T];
implicit def createpathimp(a: Array[String]) = new createpathtrait[Array[String]]{def createpath = {(“” /: a)(_ + “/” + _)}}

// 画像サイズ指定部分の文字列解析用正規表現
val regWithCut = “””(.+)x(.+)c”””.r
val regXY = “””(.+)x(.+)”””.r
val regY = “””^x(.+)”””.r
val regX = “””^(.+)x”””.r
val reg = “””^(.+)”””.r

/*
* イメージを返す
* imageType :: imagesディレクトリの第一階層のディレクトリ名を指定する
* imageId :: イメージのID
* size :: イメージのサイズ
* hash :: imageType、imageId、sizeから算出したハッシュ値
* ext :: 拡張子
*/

def index(iamgeType:String, imageId:String, size:String, hash:String, ext:String) = Action {

val filePath = Play.current.path.getPath() + “/images/” + Array(iamgeType, imageId + “.” + ext).mkString(“/”)

// ハッシュ用文字列生成
val hashSeed = iamgeType + imageId + size + ext

val digestedBytes = MessageDigest.getInstance(“MD5”).digest(hashSeed.getBytes)
val hash = digestedBytes.map(“%02x”.format(_)).mkString

// TODO
// ハッシュ値がURLの物と一致しない場合、エラー

// TODO
// imageType、imageIdから、イメージを探す
// 拡張子はいずれかなので、正規表現で検索か?
val info = new ImageInfo(filePath)

val image = new MagickImage(info)
var image2:MagickImage = null

var width:Float = 0
var height:Float = 0

// TODO
// sizeによって画像のサイズを変換する
// 変換エンジンはImageMagickを使いたい
val changedSize:(Double, Double) = size match {
// case regWithCut(x, y) => {
// // TODO x,yのサイズで画像をカット
// // とりあえず、まだ実装しない
// (x.toFloat, y.toFloat)
// }

case regXY(x, y) = {
// TODO 縦横比を固定で、x,yの範囲に収まるように拡大縮小
val scaleX = x.toFloat / image.getDimension().getWidth()
val scaleY = y.toFloat / image.getDimension().getHeight()

if(scaleX > scaleY) {
(image.getDimension().getWidth() * scaleY, image.getDimension().getHeight() * scaleY)
} else {
(image.getDimension().getWidth() * scaleX, image.getDimension().getHeight() * scaleX)
}
}

// case regY(y) => {
// // 縦横比は固定で縦のサイズをyに設定
// (y)
// }

// case regX(x) => {
// // 縦横比は固定で横のサイズをxに設定
// (x)
// }

// case reg(x) => {
// // 縦横比は固定で、縦横の長い方の辺をxに設定
// (x)
// }
}

image2 = image.scaleImage(changedSize._1.toInt, changedSize._2.toInt)

val file = new java.io.File(filePath)
val fileContent: Enumerator[Array[Byte]] = Enumerator.fromFile(file)

SimpleResult(
header = ResponseHeader(200),
body = Enumerator.apply(image2.imageToBlob(info))
)
}
}[/scala]
#/conf/routes
[text]# Routes
# This file defines all application routes (Higher priority routes first)
# ~~~~

# Home page
GET / controllers.Application.index
GET /images/:imageType/:imageId/:size/:hash.:ext controllers.ImagesController.index(imageType, imageId, size, hash, ext)

# Map static resources from the /public folder to the /assets URL path
GET /assets/*file controllers.Assets.at(path=”/public”, file)[/text]
テスト用の画像をどこからか持ってきます。

/images/sample

に入れます。

このsampleというディレクトリは

イメージタイプとしてURLから指定可能なように作ってみました。

このプロジェクトを実行します。
[shell]play
[ImageResizer] $ run[/shell]
URLにアクセスしてみます。

http://localhost:9000/images/sample/Chrysanthemum/200×200/b0f87c3f6aa9818bfa2942a6224a9648.jpg

どうでしょうか、縮小されたイメージが表示されましたでしょうか。

このImageResizerの今後の展開ですが

まだ未実装な部分を実装していきます。

また、

/imagesに置いた画像から、別の拡張子の画像を生成できるように

というのも実装したいと思います。

ImageMagickでテスト用画像を大量に作成する

ImageMagickはコマンドラインから画像を編集したり出来るライブラリですが

これを使って、テスト用の画像を大量に作成する方法

作るに当たって

  • 画像を見ただけでどの画像なのかを識別できる
  • 簡単に画像を生成できる

という事に重点をおいてみます。

ImageMagickは、convertというコマンドで実行する事が出来ます。

画像のサイズを指定するときは

-size 128×128

というオプションを付けます。

画像の背景色を指定するときは

xc:yellow

というオプションを付けます。

yellowの部分は、RGB指定でも出来ます。

ここまでのオプションで、画像を書き出す場合

で画像が作成されます。

これだけだと、画像の識別が出来ないので、文字を書き込んでみます。

フォントサイズを指定するオプション

-pointsize 20

続いて、書き出す文字を指定

-draw “text 0,20 Sample”

これは、テキストで、書き出し開始位置がx=0、y=20の場所からSampleという文字を出力するというオプションです。

文字は左下に起点があるので

y軸のサイズはフォントサイズを考慮した位置を指定しないといけません。

ここまでで、コマンドライン上だけで画像にテキストを描画する事が出来ました。

あとは、Windows上であれば、バッチファイルで連番などでこのコマンドを回すか

Linux系であればシェルで回せば、大量の画像を一度に作成する事が出来ます。

シェルでのバッチファイルの例

Passengerで動かすRailsプロジェクトで、rMagickを使う時の設定

ImageMagickのをソースからインストール
※/usr/localに入るので、rootで実行

/usr/local/libをライブラリ登録

/etc/ld.so.conf.dに
ImageMagick.confファイルを作成
ファイルの中に

/usr/local/lib

を記入
ldconfigを実行

rmagickのインストール