プログラミングスクールでおしえていることもあって、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シリーズ
- pura-jpeg — JPEG
- pura-png — PNG
- pura-bmp — BMP
- pura-gif — GIF
- pura-tiff — TIFF
- pura-ico — ICO
- pura-webp — WebP
- pura-image — 統合
RubyGemsにも公開済み。gem install pura-image で全部入る。
今後
各種高速化、WebPエンコーダのnormal huffman対応、Rails本体への提案(variant_processor = :puraのビルトインサポート)とかやっていきたい。