2017/04/15(土) [n年前の日記]
#1 [ruby] RGBAで並んでる配列をARGBに並び替えたい
先日書いたスクリプトでは、Ruby の配列から DXRuby の Image にする際に Image#[x, y] = [a, r, g, b] を使って1ドットずつチマチマと描き込んで変換していたのだけど。ふと、
_Image.create_from_array
を使えばよかったんじゃないかと思い出したわけで。コレを使えば配列を渡して Image を作成できる。使わない手は無いよなと。
しかし、元画像データに相当する配列は、1ドット分が RGBA で並んでいて。対して、Image.create_from_array に渡す配列は ARGB の並びじゃないといけない。つまり、配列の並びを変更しないといけないわけだけど、どういう書き方をしたら多少でも処理が速くなるのかなと。
ということで。
てなわけで、いくつか思いついた書き方を並べて、ベンチマークを取ってみたり。
_array_bench2.rb
結果はこんな感じに。Ruby 2.2.6 p396 を使用。
どうやら、素直にループを回して、新しい並びの配列を新規作成して渡すやり方が比較的マシっぽい。
ただ、自分はRubyに関して初心者なので、Rubyの達人ならもっと速い書き方を知ってそう。
しかし、元画像データに相当する配列は、1ドット分が RGBA で並んでいて。対して、Image.create_from_array に渡す配列は ARGB の並びじゃないといけない。つまり、配列の並びを変更しないといけないわけだけど、どういう書き方をしたら多少でも処理が速くなるのかなと。
ということで。
問題: Ruby を使って、 [y][x][r, g, b, a] の多次元配列を、 [a, r, g, b, a, r, g, b ...] の一次元配列に変換せよ。 ただし個数が多いので、処理時間はできるだけ短いほうが望ましい。
てなわけで、いくつか思いついた書き方を並べて、ベンチマークを取ってみたり。
_array_bench2.rb
# [y][x][r, g, b, a] の配列からDXRubyのImageに変換する処理をいくつか試す require 'dxruby' require 'benchmark' # 元画像データの生成 def make_src_array(w, h) src = Array.new(h) { Array.new(w) } h.times do |y| w.times do |x| r = 255 * x / (w - 1) g = 255 * y / (h - 1) b = 255 * (w - 1 - x) / (w - 1) a = 255 src[y][x] = [r, g, b, a] end end return src end # Imageに1ドットずつRGBを書き込んでいくやり方 def convert_to_image(ary) w, h = ary[0].size, ary.size img = Image.new(w, h, [0, 0, 0, 0]) h.times do |y| w.times do |x| r, g, b, a = ary[y][x] img[x, y] = a, r, g, b end end return img end # Imageに1ドットずつRGBを書き込んでいくやり方その2 def convert_to_image2(ary) w, h = ary[0].size, ary.size img = Image.new(w, h, [0, 0, 0, 0]) ary.each_with_index do |line, y| line.each_with_index do |rgba, x| # img[x, y] = rgba[3], rgba[0], rgba[1], rgba[2] r, g, b, a = rgba img[x, y] = a, r, g, b end end return img end # 配列を平坦化してRGBAをARGBに並び替え、Image.createFromArrayを使う def convert_to_image3(ary) w, h = ary[0].size, ary.size d = ary.flatten i = 0 while i < d.size # d[i], d[i+1], d[i+2], d[i+3] = d[i+3], d[i], d[i+1], d[i+2] # d[i, 4] = d.values_at(i + 3, i, i + 1, i + 2) d[i, 4] = d[i+3], d[i], d[i+1], d[i+2] i += 4 end return Image.createFromArray(w, h, d) end # ループを回してARGB並びの配列を作成後、Image.createFromArrayを使う def convert_to_image4(ary) w, h = ary[0].size, ary.size d = [] ary.each do |line| line.each do |r, g, b, a| # d.push(a) ; d.push(r) ; d.push(g) ; d.push(b) d << a << r << g << b end end return Image.createFromArray(w, h, d) end # ---------------------------------------- src = make_src_array(1600, 140) # 元画像データ作成 imgs = [] # 配列をDXRubyのImageに変換 Benchmark.bm(12) do |x| x.report("Image[] ") { imgs.push(convert_to_image(src)) } x.report("Image[]2 ") { imgs.push(convert_to_image2(src)) } x.report("flatten ") { imgs.push(convert_to_image3(src)) } x.report("loop ") { imgs.push(convert_to_image4(src)) } end Window.resize(800, 600) Window.loop do break if Input.keyPush?(K_ESCAPE) x, y = 0, 0 imgs.each do |img| Window.draw(x, y, img) y += img.height + 4 end end
結果はこんな感じに。Ruby 2.2.6 p396 を使用。
> ruby array_bench2.rb user system total real Image[] 0.125000 0.000000 0.125000 ( 0.120086) Image[]2 0.125000 0.000000 0.125000 ( 0.127031) flatten 0.156000 0.000000 0.156000 ( 0.160704) loop 0.047000 0.000000 0.047000 ( 0.044219)
どうやら、素直にループを回して、新しい並びの配列を新規作成して渡すやり方が比較的マシっぽい。
ただ、自分はRubyに関して初心者なので、Rubyの達人ならもっと速い書き方を知ってそう。
[ ツッコむ ]
以上、1 日分です。