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 日分です。