Created
June 14, 2023 12:32
-
-
Save wareya/3e3da5f61dfb4dbd5933f0bd80854983 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
extends Sprite2D | |
func convolve(base : Image, kernel : Image, offset : Vector2i, stride : Vector2i = Vector2i(1, 1), factor : float = 1.0, bias : float = 0.0): | |
#var new = Image.create(base.get_size().x/stride.x, base.get_size().y/stride.y, false, Image.FORMAT_RGBA8) | |
var new = Image.create(base.get_size().x, base.get_size().y, false, Image.FORMAT_RGBAF) | |
for y in range(0, base.get_size().y):#, stride.y): | |
for x in range(0, base.get_size().x):#, stride.x): | |
var sum = Color(0, 0, 0, 0) | |
var coord = Vector2i(x, y) | |
var center = base.get_pixelv(coord) | |
for y2 in range(0, kernel.get_size().y): | |
for x2 in range(0, kernel.get_size().x): | |
var kcoord = Vector2i(x2, y2) | |
var coord2 = coord + kcoord*stride - offset*stride | |
coord2.x = posmod(coord2.x, base.get_size().x) | |
coord2.y = posmod(coord2.y, base.get_size().y) | |
var mask : Color = kernel.get_pixelv(kcoord) | |
var read : Color = base.get_pixelv(coord2) | |
mask = mask - Color(0.5, 0.5, 0.5, 0.0) | |
mask = mask / Color(0.5, 0.5, 0.5, 1.0) | |
mask.r *= factor | |
mask.g *= factor | |
mask.b *= factor | |
sum += mask * read | |
sum += Color(bias, bias, bias, 0.0) | |
sum.a = center.a | |
var write_coord = coord#/stride | |
new.set_pixelv(write_coord, sum) | |
return new | |
func overlay_single(a : float, b : float) -> float: | |
if a <= 0.5: | |
return a*b*2.0 | |
else: | |
return 1.0 - overlay_single(1.0 - a, 1.0 - b) | |
func overlay(a : Color, b : Color) -> Color: | |
a.r = overlay_single(a.r, b.r) | |
a.g = overlay_single(a.g, b.g) | |
a.b = overlay_single(a.b, b.b) | |
return a | |
func random_image(w : int, h : int, mode : int): | |
var new = Image.create(w, h, false, Image.FORMAT_RGBAF) | |
for y in range(0, new.get_size().y): | |
for x in range(0, new.get_size().x): | |
var c : Color = Color.WHITE | |
var f = randf() | |
c = Color(f, f, f, 1.0) | |
if mode == 1: | |
var rg = randf()*2.0-1.0 | |
var by = randf()*2.0-1.0 | |
var amp = Vector2(rg, by).length() | |
if amp > 1.0: | |
rg /= amp | |
by /= amp | |
var r = clamp(-rg, 0.0, 1.0) + by | |
var g = clamp(rg, 0.0, 1.0) + by | |
var b = clamp(-by, 0.0, 1.0) * 0.5 | |
c = lerp(c, overlay(c, Color(r, g, b)), 0.2) | |
if mode == 2: | |
c.a = randf() | |
new.set_pixel(x, y, c) | |
return new | |
func random_kernel(w : int, h : int, mode : int, normalize = true): | |
var new = random_image(w, h, mode) | |
var c_x = w/2 | |
var c_y = h/2 | |
var energy = 0.0 | |
for y in range(0, new.get_size().y): | |
for x in range(0, new.get_size().x): | |
var c = new.get_pixel(x, y) | |
energy += (c.r/3.0 + c.g/3.0 + c.b/3.0)-0.5 | |
var energy_adjust = clamp(energy, -0.0, 0.0) - energy | |
print(energy_adjust) | |
if !normalize: | |
energy_adjust += 0.5 | |
energy_adjust /= float(w*h) | |
#energy_adjust *= 0.0 | |
for y in range(0, new.get_size().y): | |
for x in range(0, new.get_size().x): | |
var c = new.get_pixel(x, y) | |
c.r += energy_adjust | |
c.g += energy_adjust | |
c.b += energy_adjust | |
new.set_pixel(x, y, c) | |
return new | |
func relu(x : float) -> float: | |
return lerp(max(0.0, x), x, 0.1) | |
func felu(x : float) -> float: | |
x = relu(x) | |
#var x2 = x*x | |
#x = 2.0*x/(1.0+x2) | |
x = sig(x) | |
return x | |
func sig(x : float) -> float: | |
return 1.0/(1.0+exp(-(x)*3.0)) | |
func main(x : float) -> float: | |
return (x+0.5)/(1.0+pow(2.0, -x)) | |
func apply_fn_to_image(p_image : Image, fn : Callable): | |
for y in range(0, p_image.get_size().y): | |
for x in range(0, p_image.get_size().x): | |
var c = p_image.get_pixel(x, y) | |
c.r = fn.call(c.r) | |
c.g = fn.call(c.g) | |
c.b = fn.call(c.b) | |
p_image.set_pixel(x, y, c) | |
func median(base : Image, percentile : float): | |
#var new = Image.create(base.get_size().x/stride.x, base.get_size().y/stride.y, false, Image.FORMAT_RGBA8) | |
var new = Image.create(base.get_size().x, base.get_size().y, false, Image.FORMAT_RGBAF) | |
for y in range(0, base.get_size().y):#, stride.y): | |
for x in range(0, base.get_size().x):#, stride.x): | |
var coord = Vector2i(x, y) | |
var list = [] | |
for y2 in range(-1, 2): | |
for x2 in range(-1, 2): | |
var kcoord = Vector2i(x2, y2) | |
var coord2 = coord + kcoord | |
coord2.x = posmod(coord2.x, base.get_size().x) | |
coord2.y = posmod(coord2.y, base.get_size().y) | |
var read : Color = base.get_pixelv(coord2) | |
list.push_back(read) | |
list.sort_custom(func (a : Color, b : Color): return a.get_luminance() <= b.get_luminance()) | |
var i = round(lerp(0, list.size()-1, percentile)) | |
new.set_pixel(x, y, list[i]) | |
return new | |
func boxify(base : Image, percentile : float): | |
var h = randi_range(1, 3) | |
var w = randi_range(1, 3) | |
#var new = Image.create(base.get_size().x/stride.x, base.get_size().y/stride.y, false, Image.FORMAT_RGBA8) | |
var new = Image.create(base.get_size().x, base.get_size().y, false, Image.FORMAT_RGBAF) | |
for y in range(0, base.get_size().y):#, stride.y): | |
for x in range(0, base.get_size().x):#, stride.x): | |
var coord = Vector2i(floor(x/w)*w, floor(y/h)*h) | |
var read : Color = base.get_pixelv(coord) | |
new.set_pixel(x, y, read) | |
return new | |
func structured_palette(p_image : Image): | |
var palette : Array[Color] = [] | |
for y in range(0, p_image.get_size().y): | |
for x in range(0, p_image.get_size().x): | |
var c = p_image.get_pixel(x, y) | |
palette.push_back(c) | |
palette.sort_custom(func (a, b): return color_distance(a, Color.BLACK) < color_distance(b, Color.BLACK)) | |
var real_palette : Array[Color] = [] | |
for f in [0.05, 0.25, 0.35, 0.7, 1.0]: | |
var i = floor(lerp(0, palette.size()-1, f)) | |
real_palette.push_back(palette[i]) | |
return real_palette | |
func random_palette(p_image : Image): | |
var palette : Array[Color] = [] | |
for i in 4: | |
var x = randi_range(0, p_image.get_size().x) | |
var y = randi_range(0, p_image.get_size().y) | |
palette.push_back(p_image.get_pixel(x, y)) | |
return palette | |
func color_distance(a : Color, b : Color) -> float: | |
var r = a-b | |
return Vector3(r.r, r.g, r.b).length() | |
func palettize_image(p_image : Image, palette : Array[Color]): | |
for y in range(0, p_image.get_size().y): | |
for x in range(0, p_image.get_size().x): | |
var c = p_image.get_pixel(x, y) | |
palette.sort_custom(func (a, b): return color_distance(a, c) < color_distance(b, c)) | |
c = palette[0] | |
p_image.set_pixel(x, y, c) | |
pass | |
func normalize_kernel(p_image : Image): | |
var w = p_image.get_size().x | |
var h = p_image.get_size().y | |
var energy = 0.0 | |
for y in range(0, p_image.get_size().y): | |
for x in range(0, p_image.get_size().x): | |
var c = p_image.get_pixel(x, y) | |
energy += (c.r/3.0 + c.g/3.0 + c.b/3.0)-0.5 | |
var energy_adjust = clamp(energy, -0.0, 0.0) - energy | |
energy_adjust /= float(w*h) | |
for y in range(0, p_image.get_size().y): | |
for x in range(0, p_image.get_size().x): | |
var c = p_image.get_pixel(x, y) | |
c.r += energy_adjust | |
c.g += energy_adjust | |
c.b += energy_adjust | |
p_image.set_pixel(x, y, c) | |
var image : Image = null | |
var tex : ImageTexture = null | |
# Called when the node enters the scene tree for the first time. | |
func _ready() -> void: | |
regen() | |
func regen(): | |
image = Image.create(16, 16, false, Image.FORMAT_RGBAF) | |
var size = image.get_size() | |
randomize() | |
var s = randi() | |
print("seed: ", s) | |
seed(s) | |
image = random_image(16, 16, 0) | |
if randf() < 0.5: | |
image = boxify(image, randf()) | |
if randf() < 0.5: | |
image = median(image, randf()) | |
$Sprite2D3.texture = ImageTexture.create_from_image(image) | |
var kernel = random_kernel(5, 5, 1, true) | |
var kernel2 = random_kernel(3, 3, 0, false) | |
var kernel3 = random_kernel(2, 2, 1, false) | |
var stride_5x5 = Vector2i(randi_range(1, 2), randi_range(1, 2)) | |
var stride_3x3 = Vector2i(randi_range(1, 2), randi_range(1, 2)) | |
kernel = boxify(kernel, 0.5) | |
normalize_kernel(kernel) | |
image = convolve(image, kernel2, Vector2i(1, 1), stride_3x3, randf()*100.0+0.2) | |
apply_fn_to_image(image, relu) | |
image = convolve(image, kernel, Vector2i(2, 2), stride_5x5, randf()*10.0+0.2) | |
apply_fn_to_image(image, felu) | |
image = convolve(image, kernel3, Vector2i(1, 1)) | |
apply_fn_to_image(image, main) | |
if randf() < 0.5: | |
var palette = random_palette(image) | |
palettize_image(image, palette) | |
else: | |
var palette = structured_palette(image) | |
palettize_image(image, palette) | |
texture = ImageTexture.create_from_image(image) | |
$Sprite2D5.texture = texture | |
$Sprite2D6.texture = texture | |
$Sprite2D7.texture = texture | |
$Sprite2D.texture = ImageTexture.create_from_image(kernel) | |
$Sprite2D.scale = stride_5x5 | |
$Sprite2D2.texture = ImageTexture.create_from_image(kernel2) | |
$Sprite2D2.scale = stride_3x3 | |
$Sprite2D4.texture = ImageTexture.create_from_image(kernel3) | |
pass # Replace with function body. | |
# Called every frame. 'delta' is the elapsed time since the previous frame. | |
func _process(delta: float) -> void: | |
pass |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment