mieki256's diary



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 の並びじゃないといけない。つまり、配列の並びを変更しないといけないわけだけど、どういう書き方をしたら多少でも処理が速くなるのかなと。

ということで。

問題:

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の達人ならもっと速い書き方を知ってそう。

以上です。

過去ログ表示

Prev - 2017/04 - Next
1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30

カテゴリで表示

検索機能は Namazu for hns で提供されています。(詳細指定/ヘルプ


注意: 現在使用の日記自動生成システムは Version 2.19.6 です。
公開されている日記自動生成システムは Version 2.19.5 です。

Powered by hns-2.19.6, HyperNikkiSystem Project