Skip to content

Instantly share code, notes, and snippets.

@wareya
Created June 14, 2023 12:32
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save wareya/3e3da5f61dfb4dbd5933f0bd80854983 to your computer and use it in GitHub Desktop.
Save wareya/3e3da5f61dfb4dbd5933f0bd80854983 to your computer and use it in GitHub Desktop.
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