みんな大好き ImageMagick ですが、手軽にサクッとヒートマップを作る記事が意外と出てこなかったのでメモ。 ImageMagick のバージョンは以下のとおり。
$ convert --version
Version: ImageMagick 6.8.6-3 2013-10-02 Q16 http://www.imagemagick.org
Copyright: Copyright (C) 1999-2013 ImageMagick Studio LLC
Features: DPC Modules
Delegates: bzlib freetype jng jpeg png xml zlib
ヒートマップとは
ヒートマップ (heat map, temperature map) とは何かというと、こんなやつです。
データが二次元上のどんな位置に分布しているのかを可視化する場合によく使うと思います。
使い方
適当な画像とヒートマップ作成の元画像、あと座標データ (x, y) を用意します。画像とデータには特に意味はありません。
$ cat heatmap.csv
415,130
415,140
415,135
420,140
430,140
425,135
450,120
395,140
400,135
430,135
420,145
470,130
460,130
470,170
470,160
470,130
475,110
470,120
データを元に次のようにコマンドを叩きます。各座標データが point.png
(50px x 50px) の中心に来るように x, y 座標から 25 を引いています。
size=`identify -format '%wx%h' original.png`
convert -size $size pattern:gray100 heatmap-tmp.png
convert heatmap-tmp.png -page +390+105 point.png -flatten heatmap-tmp.png
convert heatmap-tmp.png -page +390+115 point.png -flatten heatmap-tmp.png
convert heatmap-tmp.png -page +390+110 point.png -flatten heatmap-tmp.png
convert heatmap-tmp.png -page +395+115 point.png -flatten heatmap-tmp.png
convert heatmap-tmp.png -page +405+115 point.png -flatten heatmap-tmp.png
convert heatmap-tmp.png -page +400+110 point.png -flatten heatmap-tmp.png
convert heatmap-tmp.png -page +425+95 point.png -flatten heatmap-tmp.png
convert heatmap-tmp.png -page +370+115 point.png -flatten heatmap-tmp.png
convert heatmap-tmp.png -page +375+110 point.png -flatten heatmap-tmp.png
convert heatmap-tmp.png -page +405+110 point.png -flatten heatmap-tmp.png
convert heatmap-tmp.png -page +395+120 point.png -flatten heatmap-tmp.png
convert heatmap-tmp.png -page +445+105 point.png -flatten heatmap-tmp.png
convert heatmap-tmp.png -page +435+105 point.png -flatten heatmap-tmp.png
convert heatmap-tmp.png -page +445+145 point.png -flatten heatmap-tmp.png
convert heatmap-tmp.png -page +445+135 point.png -flatten heatmap-tmp.png
convert heatmap-tmp.png -page +445+105 point.png -flatten heatmap-tmp.png
convert heatmap-tmp.png -page +450+85 point.png -flatten heatmap-tmp.png
convert heatmap-tmp.png -page +445+95 point.png -flatten heatmap-tmp.png
convert -channel ALL -clut heatmap-tmp.png gradation.png heatmap-tmp.png
composite -dissolve 50%x100% original.png heatmap-tmp.png result.png
なお座標データ CSV ファイル heatmap.csv
を元に座標入力コマンドを作る Ruby スクリプト heatmap.rb
は次のような感じです。
require 'csv'
ORGFILE = 'original.png'
TMPFILE = 'heatmap-tmp.png'
RESFILE = 'result.png'
POINT_PNG = 'point.png'
GRADATION_PNG = 'gradation.png'
CSVFILE = 'heatmap.csv'
puts "size=`identify -format '%wx%h' #{ORGFILE}`"
puts "convert -size $size pattern:gray100 #{TMPFILE}"
CSV.foreach(CSVFILE) do |row|
puts "convert #{TMPFILE} -page +#{row[0].to_i - 25}+#{row[1].to_i - 25} #{POINT_PNG} -flatten #{TMPFILE}"
end
puts "convert -channel ALL -clut #{TMPFILE} #{GRADATION_PNG} #{TMPFILE}"
puts "composite -dissolve 50%x100% #{ORGFILE} #{TMPFILE} #{RESFILE}"
これを次のように一旦コマンドを確認してパイプで sh に食わせると簡単でしょう。
$ ruby heatmap.rb
size=`identify -format '%wx%h' original.png`
convert -size $size pattern:gray100 heatmap-tmp.png
convert heatmap-tmp.png -page +390+105 point.png -flatten heatmap-tmp.png
convert heatmap-tmp.png -page +390+115 point.png -flatten heatmap-tmp.png
convert heatmap-tmp.png -page +390+110 point.png -flatten heatmap-tmp.png
convert heatmap-tmp.png -page +395+115 point.png -flatten heatmap-tmp.png
convert heatmap-tmp.png -page +405+115 point.png -flatten heatmap-tmp.png
convert heatmap-tmp.png -page +400+110 point.png -flatten heatmap-tmp.png
convert heatmap-tmp.png -page +425+95 point.png -flatten heatmap-tmp.png
convert heatmap-tmp.png -page +370+115 point.png -flatten heatmap-tmp.png
convert heatmap-tmp.png -page +375+110 point.png -flatten heatmap-tmp.png
convert heatmap-tmp.png -page +405+110 point.png -flatten heatmap-tmp.png
convert heatmap-tmp.png -page +395+120 point.png -flatten heatmap-tmp.png
convert heatmap-tmp.png -page +445+105 point.png -flatten heatmap-tmp.png
convert heatmap-tmp.png -page +435+105 point.png -flatten heatmap-tmp.png
convert heatmap-tmp.png -page +445+145 point.png -flatten heatmap-tmp.png
convert heatmap-tmp.png -page +445+135 point.png -flatten heatmap-tmp.png
convert heatmap-tmp.png -page +445+105 point.png -flatten heatmap-tmp.png
convert heatmap-tmp.png -page +450+85 point.png -flatten heatmap-tmp.png
convert heatmap-tmp.png -page +445+95 point.png -flatten heatmap-tmp.png
convert -channel ALL -clut heatmap-tmp.png gradation.png heatmap-tmp.png
composite -dissolve 50%x100% original.png heatmap-tmp.png result.png
$ ruby heatmap.rb | sh
結果は次のようになります。
各コマンドの説明
一応各コマンドの意味も調べたのでメモ。
size=`identify -format '%wx%h' original.png`
identify
コマンドは画像ファイルの各パラメータを取得するコマンドです。ここでは後のコマンドで使いやすいように幅と高さを 600x310
のような形式で取得します。
convert -size $size pattern:gray100 heatmap-tmp.png
ヒートマップデータを作るための一時ファイルを作成しています。
pattern:gray100
では ImageMagick 組み込みの画像パターンを指定しています。この組み込みパターンはあたかもそのパターンの画像があるかのように扱います。またサイズはヒートマップの元画像と同じサイズです。出力先ファイルとして heatmap-tmp.png
という PNG ファイルを指定します。
convert heatmap-tmp.png -page +390+105 point.png -flatten heatmap-tmp.png
- http://www.imagemagick.org/script/command-line-options.php#page
- http://www.imagemagick.org/script/command-line-options.php#flatten
- http://www.imagemagick.org/script/command-line-options.php#layers
「どの座標がどのくらい濃いのか」というヒートマップデータを作っていきます。それまでできた heatmap-tmp.png
を背景に、新たに (x, y) = (390, 105) の位置に point.png
を重ねていく感じです。
convert -channel ALL -clut heatmap-tmp.png gradation.png heatmap-tmp.png
- http://www.imagemagick.org/script/command-line-options.php#channel
- http://www.imagemagick.org/script/command-line-options.php#clut
heatmap-tmp.png
の濃い部分と薄い部分を gradation.png
からよしなに選んでマップする感じだと思います。あまりわかっていない。
composite -dissolve 50%x100% original.png heatmap-tmp.png result.png
- http://www.imagemagick.org/script/composite.php
- http://www.imagemagick.org/script/command-line-options.php#dissolve
オリジナル画像と作成したヒートマップ画像を重ねあわせています。
ここでは dissolve
オプションで original.png
を 50% の「濃さ」で、 heatmap-tmp.png
を 100% の「濃さ」で重ねることを指定しています。