2017/04/08(土) [n年前の日記]
#1 [dxruby][ruby] Scale2x や Scale3x をRubyで書いてみたり
Ruby + DXRuby を使って、ドット絵専用の整数倍拡大アルゴリズム、Scale2x や Scale3x の処理を書いて試してみたり。
以下のページに書かれてる内容をそのままコピペ。
_Scale2x Algorithm
画像端の部分を読み取る際に、元画像をはみ出しながら読み取ろうとしてしまうので、ちょっと一手間入れる必要が。本来、画像端とソレ以外でループを分けて速度面の最適化をすべきだろうけど、動くかどうかをひとまず試したいだけなので遅くなるのは分かってるけど安易な書き方を。
ソースはこんな感じに。
_scalenx.rb
テスト用画像は、 _Hq3x - Oss4art で紹介されてる画像を使わせてもらったり。消えるとアレなので転載させてください…。
_hq3x_original.png
ruby scalenx.rb で実行すると、こんな結果に。
一応実装できた、ような気がする。
コレ、ライセンスはどうなるのかな…。 _scale2x-4.0.tar.gz をDLして中身を見てみたら、COPYING に GPL と書いてあったので、コレも GPL になるのだろうか。たぶん。自分が書いて公開・配布するアレコレは、できれば CC0 / Public Domain にしたいけど、コレを使ってしまうとソレができないなと…。これもいわゆるGPL汚染、だろうか…。
書いた後で気が付いたけど、 _Scale2x のページで、「Scale2x の結果と EPX の結果は全く同じになるんやで」と説明が。EPX を書いてみるべきだったか…。8点ではなくて4点を見るだけで済むみたいだし…。
以下のページに書かれてる内容をそのままコピペ。
_Scale2x Algorithm
画像端の部分を読み取る際に、元画像をはみ出しながら読み取ろうとしてしまうので、ちょっと一手間入れる必要が。本来、画像端とソレ以外でループを分けて速度面の最適化をすべきだろうけど、動くかどうかをひとまず試したいだけなので遅くなるのは分かってるけど安易な書き方を。
ソースはこんな感じに。
_scalenx.rb
# pixel scalking , Scale2x, Scale3x module ScaleNx # pixel scaling, Scale2x # @param src [Array<Array>] pixel data array # @return [Array<Array>] 2x pixel data array def scale2x(src) width, height = src[0].size, src.size nwidth, nheight = width * 2, height * 2 dst = Array.new(nheight).map { Array.new(nwidth).map { Array.new(4, 0) } } height.times do |y| width.times do |x| e = src[y][x] d = (x - 1 >= 0)? src[y][x - 1] : e f = (x + 1 < width)? src[y][x + 1] : e if y - 1 >= 0 b = src[y - 1][x] a = (x - 1 >= 0)? src[y - 1][x - 1] : b c = (x + 1 < width)? src[y - 1][x + 1] : b else a = d b = e c = f end if y + 1 < height h = src[y + 1][x] g = (x - 1 >= 0)? src[y + 1][x - 1] : h i = (x + 1 < width)? src[y + 1][x + 1] : h else g = d h = e i = f end if b != h && d != f e0 = (d == b)? d : e e1 = (b == f)? f : e e2 = (d == h)? d : e e3 = (h == f)? f : e else e0 = e e1 = e e2 = e e3 = e end dx = x * 2 dy = y * 2 dst[dy][dx] = e0 dst[dy][dx + 1] = e1 dst[dy + 1][dx] = e2 dst[dy + 1][dx + 1] = e3 end end return dst end # pixel scaling, Scale3x # @param src [Array<Array>] pixel data array # @return [Array<Array>] 3x pixel data array def scale3x(src) width, height = src[0].size, src.size nwidth, nheight = width * 3, height * 3 dst = Array.new(nheight).map { Array.new(nwidth).map { Array.new(4, 0) } } height.times do |y| width.times do |x| e = src[y][x] d = (x - 1 >= 0)? src[y][x - 1] : e f = (x + 1 < width)? src[y][x + 1] : e if y - 1 >= 0 b = src[y - 1][x] a = (x - 1 >= 0)? src[y - 1][x - 1] : b c = (x + 1 < width)? src[y - 1][x + 1] : b else a = d b = e c = f end if y + 1 < height h = src[y + 1][x] g = (x - 1 >= 0)? src[y + 1][x - 1] : h i = (x + 1 < width)? src[y + 1][x + 1] : h else g = d h = e i = f end if b != h and d != f e0 = (d == b)? d : e e1 = ((d == b and e != c) or (b == f and e != a))? b : e e2 = (b == f)? f : e e3 = ((d == b and e != g) or (d == h and e != a))? d : e e4 = e e5 = ((b == f and e != i) or (h == f and e != c))? f : e e6 = (d == h)? d : e e7 = ((d == h and e != i) or (h == f and e != g))? h : e e8 = (h == f)? f : e else e0 = e e1 = e e2 = e e3 = e e4 = e e5 = e e6 = e e7 = e e8 = e end dx = x * 3 dy = y * 3 dst[dy][dx] = e0 dst[dy][dx + 1] = e1 dst[dy][dx + 2] = e2 dst[dy + 1][dx] = e3 dst[dy + 1][dx + 1] = e4 dst[dy + 1][dx + 2] = e5 dst[dy + 2][dx] = e6 dst[dy + 2][dx + 1] = e7 dst[dy + 2][dx + 2] = e8 end end return dst end end if $0 == __FILE__ # ---------------------------------------- require 'dxruby' include ScaleNx # convert DXRuby Image to array def conv_image2array(img) w, h = img.width, img.height data = [] h.times do |y| dt = [] w.times do |x| a, r, g, b = img[x, y] dt.push([r, g, b, a]) end data.push(dt) end return data end # convert array to DXRuby Image def conv_array2image(src) w, h = src[0].size, src.size img = Image.new(w, h, [0, 0, 0, 0]) h.times do |y| src[y].each_with_index do |v, x| r, g, b, a = v img[x, y] = [a, r, g, b] end end return img end srcimg = Image.load("hq3x_original.png") src = conv_image2array(srcimg) dst = scale2x(src) dstimg2x = conv_array2image(dst) dst = scale3x(src) dstimg3x = conv_array2image(dst) Window.resize(540, 360) Window.minFilter = TEXF_POINT Window.magFilter = TEXF_POINT Window.scale = 2 Window.loop do break if Input.keyPush?(K_ESCAPE) x, y = 0, 0 Window.draw(0, 0, srcimg) x += srcimg.width + 8 Window.draw(x, y, dstimg2x) y += dstimg2x.height + 8 Window.draw(x, y, dstimg3x) end end
テスト用画像は、 _Hq3x - Oss4art で紹介されてる画像を使わせてもらったり。消えるとアレなので転載させてください…。
_hq3x_original.png
ruby scalenx.rb で実行すると、こんな結果に。
一応実装できた、ような気がする。
コレ、ライセンスはどうなるのかな…。 _scale2x-4.0.tar.gz をDLして中身を見てみたら、COPYING に GPL と書いてあったので、コレも GPL になるのだろうか。たぶん。自分が書いて公開・配布するアレコレは、できれば CC0 / Public Domain にしたいけど、コレを使ってしまうとソレができないなと…。これもいわゆるGPL汚染、だろうか…。
書いた後で気が付いたけど、 _Scale2x のページで、「Scale2x の結果と EPX の結果は全く同じになるんやで」と説明が。EPX を書いてみるべきだったか…。8点ではなくて4点を見るだけで済むみたいだし…。
◎ hqxも試してみたいけど。 :
hq2x、hq3x、hq4x も試してみたいけど、ソースをググってみたらなかなか複雑な感じで…。
一応、関連ページをメモ。
_Pixel art scaling algorithms - Wikipedia
_hqx - Wikipedia
_hq2x - HiEnd3D (Internet Archive)
_hq3x - HiEnd3D (Internet Archive)
_hq4x - HiEnd3D (Internet Archive)
_Usage - Arcnor/hqx-java Wiki
_Arcnor/hqx-java
ちなみに、hqx も GPL らしい。
一応、関連ページをメモ。
_Pixel art scaling algorithms - Wikipedia
_hqx - Wikipedia
_hq2x - HiEnd3D (Internet Archive)
_hq3x - HiEnd3D (Internet Archive)
_hq4x - HiEnd3D (Internet Archive)
_Usage - Arcnor/hqx-java Wiki
_Arcnor/hqx-java
ちなみに、hqx も GPL らしい。
◎ 処理をまとめたツールがあるらしい。 :
_2dimagefilter - Google Code Archive
_2dimagefilter by Hawkynt
_2dimagefilter - ImageResizer.wiki - Google Code Archive
GUI もしくは CUI で各アルゴリズムを試せる模様。ImageResizer-r133.exe をDLして実行してみたけど、Scale3x や hq3x が動くことを確認できた。
_2dimagefilter by Hawkynt
_2dimagefilter - ImageResizer.wiki - Google Code Archive
GUI もしくは CUI で各アルゴリズムを試せる模様。ImageResizer-r133.exe をDLして実行してみたけど、Scale3x や hq3x が動くことを確認できた。
[ ツッコむ ]
以上です。