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 が動くことを確認できた。
[ ツッコむ ]
以上です。
