完成品のパフォーマンスがよくなかったので修正しました
require 'RMagick' require 'fileutils' labels = %w(airplane automobile bird cat deer dog frog horse ship truck) FileUtils.rm_rf("output") labels.each { |l| FileUtils.mkdir_p("output/#{l}") } open("./data_batch_1.bin", "rb") do |f| while b = f.read(3073) do datasets = b.unpack("C*") # labelと赤緑青のチャネルにわける label_idx = datasets.shift rgb_list = datasets.each_slice(1024).to_a.transpose # レンダリング img = Magick::Image.new(32, 32) d = Magick::Draw.new 32.times do |y| 32.times do |x| rgb = rgb_list.shift d.fill("rgb(#{rgb.join(',')})") d.point(x, y) end end d.draw(img) # ファイル出力 name = labels[label_idx] filepath = "output/#{name}/#{Dir.glob("output/#{name}/*").count + 1}.jpg" puts "output: #{filepath}...." img.write(filepath) end end
CIFARという画像セットがある。
10クラスのCIFAR-10、100クラスのCIFAR-100があり次のサイトで提供されてます。
CIFAR-10 and CIFAR-100 datasets
画像サイズは32x32で提供されておりRGBの3チャンネルカラー画像として提供されます。
データセットはPython, Matlab向けとbinaryの三種類が提供されています。
今回はbinaryをRubyで扱います。
Binaryでのデータセットレイアウト
圧縮されたファイルを解凍するとdata_batch_1.bin, data_batch_2.bin, data_batch_3.bin, data_batch_4.bin, data_batch_5.binというトレーニングデータとtest_batch.binというテストデータが入っています。
これらは次のようなデータフォーマットをしています。
1byteのラベルと3072bytesのピクセル値をひとつのイメージの組として1つのファイルに10000組入っています。
1byte目のラベルは0-9の値をとってその値はbatches.meta.txtに記載されている順番のindexと対応しています。
3072bytesは1024bytesづつ分けて赤、緑、黄の順番で並んでいます。
また隣り合うイメージの境目はないのできっちり3073bytesを取得する必要があります。
まとめると次のようなイメージになります。
ここで1つ問題があります。
ピクセルデータだと目で見た時に何の画像が認識できない!!
そこで今回はこの画像セットをファイルに出力していこうと思います。
完成品
実行が終わると output
配下にそれぞれのラベルのディレクトリが出来て、その中に該当するラベルの画像ファイルが生成されていく。
require 'RMagick' require 'fileutils' labels = %w(airplane automobile bird cat deer dog frog horse ship truck) FileUtils.rm_rf("output") labels.each { |l| FileUtils.mkdir_p("output/#{l}") } binary = File.binread("./data_batch_1.bin") datasets = binary.unpack("C*") while true do label_idx = datasets.shift break if label_idx.nil? rgb_list = datasets.slice!(0, 3072).each_slice(1024).to_a.transpose img = Magick::Image.new(32, 32) d = Magick::Draw.new 32.times do |y| 32.times do |x| rgb = rgb_list.shift d.fill("rgb(#{rgb.join(',')})") d.point(x, y) end end d.draw(img) name = labels[label_idx] filepath = "output/#{name}/#{Dir.glob("output/#{name}/*").count + 1}.jpg" puts "output: #{filepath}...." img.write(filepath) end
ポイントとなるところをいくつか紹介します。
binary = File.binread("./data_batch_1.bin") datasets = binary.unpack("C*")
この部分でバイナリのファイルを読み込んでそれを8bit 符号なし整数でアンパックしたものをarrayとして返します。
label_idx = datasets.shift break if label_idx.nil? rgb_list = datasets.slice!(0, 3072).each_slice(1024).to_a.transpose
datasets.shiftでラベルのindexを取り出します。
次に赤緑青のそれぞのチャネルの取り出しを行なっているのですが、slice!
で赤緑青をあわせて3072をまず切り出して、each_slice(1024).to_a
で1024づつにわけて最後のtransposeを使って赤青黄の組みにしています。
図にすると次のようになります。
画像出力にはimagemagickを使いgemとしてrmagickを使います。
ベースとなる画像があって切り取りとかで使われたりするけど0から画像を生成することも出来ます。
RMagick 2.12.0: How to use RMagick
このgemを使って左上から順番にRGBで色をつけていって画像を生成してファイルに出力します。
最後に
32x32なのでかなり小さいですが、無事画像ファイルを出力することが出来ました。