image_optimメモ

画像ファイルのロスレス圧縮(というかメタファイル削り)を行う。
ImageOptim — better Save for WebRuby版。
もちろん不可逆圧縮も可能。

Optimize (lossless compress) images (jpeg, png, gif)
GitHub - toy/image_optim: Optimize images using multiple utilities

リファレンス

必要なライブラリをhomobrewで入れる

mini:~ admin$ brew install advancecomp gifsicle jpegoptim jpeg optipng pngcrush
==> Downloading http://downloads.sourceforge.net/advancemame/advancecomp-1.17.ta
######################################################################## 100.0%
==> ./configure --prefix=/usr/local/Cellar/advancecomp/1.17 --mandir=/usr/local/
==> make install
🍺  /usr/local/Cellar/advancecomp/1.17: 13 files, 976K, built in 14 seconds
==> Downloading http://www.lcdf.org/gifsicle/gifsicle-1.70.tar.gz
######################################################################## 100.0%
==> ./configure --prefix=/usr/local/Cellar/gifsicle/1.70 --enable-all
==> make install
🍺  /usr/local/Cellar/gifsicle/1.70: 10 files, 368K, built in 7 seconds
==> Downloading http://www.kokkonen.net/tjko/src/jpegoptim-1.3.0.tar.gz
######################################################################## 100.0%
==> ./configure --prefix=/usr/local/Cellar/jpegoptim/1.3.0
==> make install
🍺  /usr/local/Cellar/jpegoptim/1.3.0: 6 files, 68K, built in 4 seconds
Warning: jpeg-8d already installed
==> Downloading http://downloads.sourceforge.net/project/optipng/OptiPNG/optipng
######################################################################## 100.0%
==> ./configure --with-system-zlib --prefix=/usr/local/Cellar/optipng/0.7.4 --ma
==> make install
🍺  /usr/local/Cellar/optipng/0.7.4: 6 files, 184K, built in 4 seconds
==> Downloading http://sourceforge.net/projects/pmt/files/pngcrush/old-versions/
######################################################################## 100.0%
==> make CC=cc LD=cc CFLAGS=-DZ_SOLO -DPNGCRUSH_COUNT_COLORS LDFLAGS=
🍺  /usr/local/Cellar/pngcrush/1.7.56: 3 files, 288K, built in 4 seconds

インストールされた場所を確認。

$ which advpng
/usr/local/bin/advpng
$ which gifsicle
/usr/local/bin/gifsicle
$ which jpegoptim
/usr/local/bin/jpegoptim
$ which jpeg
$ which jpegtran
/usr/local/bin/jpegtran
$ which optipng
/usr/local/bin/optipng
$ which pngcrush
/usr/local/bin/pngcrush

rubygemsでimage_optimをインストール

$ gem search image_optim --remote

*** REMOTE GEMS ***

image_optim (0.8.1)
image_optim_bin (0.0.2)
image_optim_rake (0.0.2)
image_optimizer (1.0.0)
$ gem install image_optim
Fetching: fspath-2.0.4.gem (100%)
Fetching: image_size-1.1.2.gem (100%)
Fetching: progress-2.4.0.gem (100%)
Fetching: in_threads-1.1.1.gem (100%)
Fetching: image_optim-0.8.1.gem (100%)
Successfully installed fspath-2.0.4
Successfully installed image_size-1.1.2
Successfully installed progress-2.4.0
Successfully installed in_threads-1.1.1
Successfully installed image_optim-0.8.1
5 gems installed
Installing ri documentation for fspath-2.0.4...
Building YARD (yri) index for fspath-2.0.4...
Installing ri documentation for image_size-1.1.2...
Building YARD (yri) index for image_size-1.1.2...
Installing ri documentation for progress-2.4.0...
Building YARD (yri) index for progress-2.4.0...
Installing ri documentation for in_threads-1.1.1...
Building YARD (yri) index for in_threads-1.1.1...
Installing ri documentation for image_optim-0.8.1...
Building YARD (yri) index for image_optim-0.8.1...
Installing RDoc documentation for fspath-2.0.4...
Installing RDoc documentation for image_size-1.1.2...
Installing RDoc documentation for progress-2.4.0...
Installing RDoc documentation for in_threads-1.1.1...
Installing RDoc documentation for image_optim-0.8.1...

コマンドラインでのimage_optim --help

$ image_optim --help
image_optim v0.8.1

Usege:
  image_optim [options] image_path …

    -r, -R, --recursive              Recurively scan directories for images

        --[no-]threads N             Number of threads or disable (defaults to number of processors)
        --[no-]nice N                Nice level (defaults to 10)

  Disabling workers:
        --no-pngcrush                disable pngcrush worker
        --no-pngout                  disable pngout worker
        --no-optipng                 disable optipng worker
        --no-advpng                  disable advpng worker
        --no-jpegoptim               disable jpegoptim worker
        --no-jpegtran                disable jpegtran worker
        --no-gifsicle                disable gifsicle worker

  Worker options:
        --pngcrush-chunks a,b,c      List of chunks to remove or 'alla' - all except
                                       tRNS/transparency or 'allb' - all except tRNS and gAMA/gamma
                                       (defaults to alla)
        --pngcrush-fix B             Fix otherwise fatal conditions such as bad CRCs
                                       (defaults to false)
        --pngcrush-brute B           Brute force try all methods, very time-consuming and
                                       generally not worthwhile (defaults to false)

        --pngout-copy-chunks B       Copy optional chunks (defaults to false)
        --pngout-strategy N          Strategy: 0 - xtreme, 1 - intense, 2 - longest Match,
                                       3 - huffman Only, 4 - uncompressed (defaults to 0)

        --optipng-level N            Optimization level preset 0 is least, 7 is best
                                       (defaults to 6)
        --optipng-interlace B        Interlace, true - interlace on, false - interlace off,
                                       nil - as is in original image (defaults to false)

        --advpng-level N             Compression level: 0 - don't compress, 1 - fast, 2 - normal,
                                       3 - extra, 4 - extreme (defaults to 4)

        --jpegoptim-strip a,b,c      List of extra markers to strip: comments, exif, iptc, icc or
                                       all (defaults to all)
        --jpegoptim-max-quality N    Maximum image quality factor 0..100 (defaults to 100)

        --jpegtran-copy-chunks B     Copy all chunks (defaults to false)
        --jpegtran-progressive B     Create progressive JPEG file (defaults to true)
        --jpegtran-jpegrescan B      Use jpegtran through jpegrescan, ignore progressive option
                                       (defaults to false)

        --gifsicle-interlace B       Turn interlacing on (defaults to false)

  Common options:
    -v, --verbose                    Verbose output
    -h, --help                       Show full help
        --version                    Show version

image_optimの中身

pry(main)> require 'image_optim'
=> true
pry(main)> image_optim = ImageOptim.new
=> #,
 @threads=4,
 @verbose=false,
 @workers_by_format=
  {:png=>
    [#>,
     #,
      @strategy=0>,
     #,
      @interlace=false,
      @level=6>,
     #,
      @level=4>],
   :jpeg=>
    [#,
      @max_quality=100,
      @strip=["all"]>,
     #,
      @jpegrescan=false,
      @progressive=true>],
   :gif=>
    [#,
      @interlace=false>]}>

image_optimのメソッド

pry(main)> image_optim.methods.sort=> [:!, :!=, :!~, :<=>, :==, :===, :=~, 
:FSPath, 
:__binding__, :__id__, :__send__, 
:assert_options_empty!, 
:class, :clone, 
:define_singleton_method, :display, :dup, 
:enum_for, :env_path, :eql?, :equal?, :extend, 
:freeze, :frozen?, 
:get_option!, 
:hash, 
:initialize_clone, :initialize_dup, :inspect, :instance_eval, :instance_exec, :instance_of?, :instance_variable_defined?, :instance_variable_get, :instance_variable_set, :instance_variables, :is_a?, 
:kind_of?, 
:method, :methods, 
:nice, :nil?, 
:object_id, :optimizable?, :optimize_image, :optimize_image!, :optimize_images, :optimize_images!, 
:pretty_inspect, :pretty_print, :pretty_print_cycle, :pretty_print_inspect, :pretty_print_instance_variables, :private_methods, :protected_methods, :pry, :public_method, :public_methods, :public_send, 
:resolve_bin!, :resolve_dir, :respond_to?, :respond_to_missing?, 
:send, :singleton_class, :singleton_methods, 
:taint, :tainted?, :tap, :threads, :to_enum, :to_s, :trust, 
:untaint, :untrust, :untrusted?, 
:verbose?, 
:workers_for_image]

JPEGファイルを最適化してみる

--verboseで確認。

  • コマンド
$ image_optim -v img.jpg
Options:
  verbose: true
&#10003; 0.272353s jpegoptim --strip-all -q -- /var/folders/ll/zptzvr8j3w75svm2_2pj6hmw0000gp/T/00920130611-28525-s5thku.jpg
&#10003; 0.628848s jpegtran -progressive -copy none -optimize -outfile /var/folders/ll/zptzvr8j3w75svm2_2pj6hmw0000gp/T/00920130611-28525-1j8w7jl.jpg /var/folders/ll/zptzvr8j3w75svm2_2pj6hmw0000gp/T/00920130611-28525-s5thku.jpg
 7.40% 156.3K  009.jpg
Total:  7.40% 156.3K
pry(main)> image_optim.optimize_image("img.jpg")
&#10003; 0.295203s jpegoptim --strip-all -q -- /var/folders/ll/zptzvr8j3w75svm2_2pj6hmw0000gp/T/00220130611-26325-zrv6b3.jpg
&#10003; 0.636089s jpegtran -progressive -copy none -optimize -outfile /var/folders/ll/zptzvr8j3w75svm2_2pj6hmw0000gp/T/00220130611-26325-j58t8a.jpg /var/folders/ll/zptzvr8j3w75svm2_2pj6hmw0000gp/T/00220130611-26325-zrv6b3.jpg
=> #<ImageOptim::ImagePath:/var/folders/ll/zptzvr8j3w75svm2_2pj6hmw0000gp/T/00220130611-26325-j58t8a.jpg>

最適化した画像を目的の場所に保存する方法

image_optim.optimize_image(filename) はテンポラリに画像を保存する(例:/var/folders/…)。
image_optim.optimize_image(filename).to_sが画像のpath。
クオリティXXでJPEGrescanオンにした場合に目的の場所へ保存するには以下のようにする。

def optimize(source_path, out_path)
  source_path = File.expand_path(source_path)
  param = { :jpegoptim => { :max_quality => XX }, :jpegtran => { :jpegrescan => true }}
  optimized_temp_img = ImageOptim.new(param).optimize_image(source_path)
  temp_path = optimized_temp_img ? optimized_temp_img.to_s : filename
  FileUtils.cp temp_path, out_path
end

参考:nanoc-image-compressor/image_compressor.rb at master · jingoro/nanoc-image-compressor · GitHub

各種オプションを変更する方法

ライブラリの名前をシンボルにしたハッシュで指定する。

例:jpegoptimライブラリにてjpegのクオリティを80(max: 100)にする

pry(main)> require 'image_optim'
=> true
pry(main)> image_optim = ImageOptim.new(:jpegoptim => { :max_quality => 80 })
=> #<ImageOptim:0x007ff0a28597c0
 @nice=10,
 @processor_count=4,
 @resolved_bins={},
 @resolver_lock=#<Mutex:0x007ff0a2859770>,
 @threads=4,
 @verbose=false,
 @workers_by_format=
  {:png=>
    [#<ImageOptim::Worker::Pngcrush:0x007ff0a2859400
      @brute=false,
      @chunks=["alla"],
      @fix=false,
      @image_optim=#<ImageOptim:0x007ff0a28597c0 ...>>,
     #<ImageOptim::Worker::Pngout:0x007ff0a2859068
      @copy_chunks=false,
      @image_optim=#<ImageOptim:0x007ff0a28597c0 ...>,
      @strategy=0>,
     #<ImageOptim::Worker::Optipng:0x007ff0a2858e60
      @image_optim=#<ImageOptim:0x007ff0a28597c0 ...>,
      @interlace=false,
      @level=6>,
     #<ImageOptim::Worker::Advpng:0x007ff0a2858bb8
      @image_optim=#<ImageOptim:0x007ff0a28597c0 ...>,
      @level=4>],
   :jpeg=>
    [#<ImageOptim::Worker::Jpegoptim:0x007ff0a2858a28
      @image_optim=#<ImageOptim:0x007ff0a28597c0 ...>,
      @max_quality=80,
      @strip=["all"]>,
     #<ImageOptim::Worker::Jpegtran:0x007ff0a28583c0
      @copy_chunks=false,
      @image_optim=#<ImageOptim:0x007ff0a28597c0 ...>,
      @jpegrescan=false,
      @progressive=true>],
   :gif=>
    [#<ImageOptim::Worker::Gifsicle:0x007ff0a2858168
      @image_optim=#<ImageOptim:0x007ff0a28597c0 ...>,
      @interlace=false>]}>

(余談:クオリティ40ぐらいまでは大丈夫かと。30になると偽色が目立ち始める。白黒だと25でも大丈夫。20以下にすると暗所のブロックが荒くなって画質が徐々に落ちる。)

Options

Worker can be disabled by passing false instead of options hash.

  • jpegoptim
    • :strip ― List of extra markers to strip: comments, exif, iptc, icc or all (defaults to all)
    • :max_quality ― Maximum image quality factor 0..100 (defaults to 100)
  • jpegtran
    • :copy_chunks ― Copy all chunks (defaults to false)
    • :progressive ― Create progressive JPEG file (defaults to true)
    • :jpegrescan ― Use jpegtran through jpegrescan, ignore progressive option (defaults to true)
  • pngcrush
    • :chunks ― List of chunks to remove or 'alla' - all except tRNS/transparency or 'allb' - all except tRNS and gAMA/gamma (defaults to alla)
    • :fix ― Fix otherwise fatal conditions such as bad CRCs (defaults to false)
    • :brute ― Brute force try all methods, very time-consuming and generally not worthwhile (defaults to false)
  • pngout
    • :copy_chunks ― Copy optional chunks (defaults to false)
    • :strategy ― Strategy: 0 - xtreme, 1 - intense, 2 - longest Match, 3 - huffman Only, 4 - uncompressed (defaults to 0)
  • optipng
    • :level ― Optimization level preset 0 is least, 7 is best (defaults to 6)
    • :interlace ― Interlace, true - interlace on, false - interlace off, nil - as is in original image (defaults to false)
  • advpng
    • :level ― Compression level: 0 - don't compress, 1 - fast, 2 - normal, 3 - extra, 4 - extreme (defaults to 4)
  • gifsicle
    • :interlace ― Turn interlacing on (defaults to false)

GitHub - toy/image_optim: Optimize images using multiple utilities

JPEG向けのおすすめ設定は

Optionの説明では":jpegrescan defaults to true"って書いてるのに"@jpegrescan=false"なのよね。
JPEGrescanって良さそうなので、利用するようにおいたほうがよさそう。なお、クオリティ変更した場合には5%ぐらい*1圧縮してくれます。
クオリティXX、JPEGrescanオン、PNGとGIFは使わないのでオフにすると、こうなります。

require 'image_optim'
image_optim = ImageOptim.new(:jpegoptim => { :max_quality => XX }, :jpegtran => { :jpegrescan => true}, :pngcrush => false, :pngout => false, :optipng => false, :advpng => false, :gifsicle => false)

クオリティの値ですが、劣化が目立たないのはカラーだと40ぐらい、白黒だと25ぐらいまでです。

ちなみにJPEGrescanの解説はこちら。

DarkShikari
Basically. There are three ways to losslessly compress a JPEG:
1) Huffman table optimization
2) Ordinary progressive scan
3) Trying all sorts of split orders for progressive scan
Most image apps worth their salt hopefully do 1) and 2). This script does 1) if it wasn't done already, and 3), which is its main purpose.
JPEGrescan: unique way to losslessly shrink any JPEG file | Hacker News

*1:クオリティ変えない場合には1%ぐらい…つまりjpegoptimが生成するjpegファイルが乱れてるんですね。