komagataのブログ

プログラミングスクールでおしえていることもあって、railsを初めて使う時の敷居を下げたかった。

そこでpure Rubyで画像処理ができるgemシリーズ pura-* を作りました。

railsで使う

# Gemfile
gem "image_processing"
gem "pura-image"

user.avatar.variant(resize_to_limit: [200, 200]) とかがlibvipsなしで動く。

コマンドとして使う

各gemにCLIがついてるので、コマンドラインから使える。

# 画像の情報を見る
pura-jpeg decode photo.jpg --info

# リサイズ
pura-jpeg resize photo.jpg --width 200 --height 200 --out thumb.jpg

# アスペクト比を維持してリサイズ
pura-png resize screenshot.png --fit 800x600 --out fitted.png

# フォーマット変換(JPEGで読んでPNGで書く)
pura-jpeg decode photo.jpg --out pixels.dat
pura-png encode pixels.dat --width 400 --height 400 --out photo.png

Rubyから使う

require "pura-image"

# 読み込み(フォーマット自動判別)
image = Pura::Image.load("photo.jpg")

# リサイズして別フォーマットで保存
thumb = image.resize_to_limit(200, 200)
Pura::Image.save(thumb, "thumb.png")

# フォーマット変換
Pura::Image.convert("input.bmp", "output.jpg", quality: 85)

ベンチマーク

コマンドとして実行。400×400の画像デコード(Ruby 4.0.2 + YJIT)。

  • TIFF: 14ms(ffmpegコマンド 59ms → 4倍速い)
  • BMP: 39ms(ffmpegコマンド 59ms → 速い)
  • GIF: 77ms(ffmpegコマンド 65ms → ほぼ互角)
  • PNG: 111ms(ffmpegコマンド 60ms)
  • WebP: 207ms(ffmpegコマンド 66ms)
  • JPEG: 304ms(ffmpegコマンド 55ms)

コマンドの起動コストがあるので軽いフォーマット(TIFF, BMP)だとffmpegコマンドより速くなる。逆にJPEGみたいに計算量が多いフォーマットだとCの圧倒的な速さに勝てない。

ただ、開発環境やCIでサムネイル生成する程度なら数百msは許容範囲だと思う。

スクリプト言語でJPEGをfull decodeしてるpure実装はJavaScript(jpeg-js)とTcl(ptjd)と、このpura-jpegしかない。PythonにもPerlにもPHPにもないので良い比較相手がいない。

メリット

セットアップが簡単な点以外にも、ruby.wasmやエッジコンピューティングの文脈でC拡張が使えない環境は増えてる。pure Rubyで完結するメリットは今後大きくなると思う。

gemシリーズ

RubyGemsにも公開済み。gem install pura-image で全部入る。

今後

各種高速化、WebPエンコーダのnormal huffman対応、Rails本体への提案(variant_processor = :puraのビルトインサポート)とかやっていきたい。

Comments


Option