Add project files

This commit is contained in:
Julian Müller (ChaoticByte) 2024-01-04 23:27:49 +01:00
parent cfed3330cf
commit d55a4aaf08
4 changed files with 126 additions and 0 deletions

2
.gitignore vendored
View file

@ -158,3 +158,5 @@ cython_debug/
# and can be added to the global gitignore or merged into this file. For a more nuclear # and can be added to the global gitignore or merged into this file. For a more nuclear
# option (not recommended) you can uncomment the following to ignore the entire idea folder. # option (not recommended) you can uncomment the following to ignore the entire idea folder.
#.idea/ #.idea/
playground.py

View file

@ -1,2 +1,8 @@
# glitch # glitch
Experimental implementations of some destructive image effects in Python Experimental implementations of some destructive image effects in Python
## Requirements
Those alGoRitHms are designed to work with Pillow.
Have a look at [requirements.txt](./requirements.txt)

117
glitch/__init__.py Normal file
View file

@ -0,0 +1,117 @@
# Copyright (c) 2024 Julian Müller (ChaoticByte)
from colorsys import rgb_to_hsv as _rgb_to_hsv
from typing import Any
from typing import Callable
from PIL import Image
from PIL import ImageChops
def led_screen(img: Image.Image):
assert isinstance(img, Image.Image)
assert img.mode == "RGB"
data = list(img.getdata())
width = img.width
height = img.height
i = 0
while i < len(data):
y = i // width
row_limit = ((y + 1) * width)
while i < row_limit:
p = data[i]
x = i % width
for j in range(4):
x_curr = x + j
if x_curr < width:
for k in range(4):
if j > 2 or k > 2:
p_curr = (0, 0, 0)
else:
p_curr = [0, 0, 0]
p_curr[j] = p[j]
p_curr = tuple(p_curr)
y_curr = y + k
if y_curr < height:
img.putpixel((x_curr, y_curr), p_curr)
i += 1
i += width * 3
def pixel_sort(img: Image.Image, limiter: Callable[[tuple], bool] = lambda p: hsv(p)[2] > 0.5, sortkey: Callable[[tuple], Any] = lambda p: hsv(p)[2], wrap: bool = False):
assert isinstance(img, Image.Image)
assert img.mode == "RGB"
assert callable(limiter)
assert callable(sortkey)
assert type(wrap) == bool
data = list(img.getdata())
width = img.width
i = 0
while i < len(data):
y = i // width
x = i % width
if limiter(data[i]): # sort valid sub-rows
start = i
if wrap:
i_limit = len(data)
else:
i_limit = (y + 1) * width
while i < i_limit:
if not limiter(data[i]):
break
i += 1
sorted_sub = sorted(data[start:i], key=sortkey)
if wrap:
for j, p in enumerate(sorted_sub):
img.putpixel(
((x + j) % width,
y + ((x + j) // width)),
p)
else:
for j, p in enumerate(sorted_sub):
img.putpixel((x + j, y), p)
else: i += 1
def pixel_smear(img: Image.Image, limiter: Callable[[tuple], bool] = lambda p: hsv(p)[2] > 0.5, wrap: bool = False):
assert isinstance(img, Image.Image)
assert img.mode == "RGB"
assert callable(limiter)
assert type(wrap) == bool
data = list(img.getdata())
width = img.width
i = 0
while i < len(data):
p = data[i]
if limiter(p): # repeat valid sub-rows
if wrap:
i_limit = len(data)
else:
i_limit = ((i // width) + 1) * width
while i < i_limit:
if not limiter(data[i]):
break
img.putpixel((i % width, (i // width)), p)
i += 1
else: i += 1
def channel_offset(img: Image.Image, offset_red: tuple, offset_green: tuple, offset_blue: tuple):
assert isinstance(img, Image.Image)
assert img.mode == "RGB"
assert all([type(t) == tuple for t in (offset_red, offset_green, offset_blue)])
assert all([len(t) == 2 and all([type(v) == int for v in t]) for t in (offset_red, offset_green, offset_blue)])
width = img.width
height = img.height
img_copy = img.copy()
img.paste((0, 0, 0), (0, 0, width, height)) # /?
for i, o in enumerate([offset_red, offset_green, offset_blue]):
chan_shifted = ImageChops.offset(img_copy.getchannel(i), o[0], o[1])
for j, p in enumerate(chan_shifted.getdata()):
x = j % width
y = j // width
p_dest = list(img.getpixel((x, y)))
p_dest[i] = p
img.putpixel((x, y), tuple(p_dest))
# Helpers
def hsv(pixel: tuple) -> float:
r, g, b = pixel[0], pixel[1], pixel[2]
return _rgb_to_hsv(r / 255.0, g / 255.0, b / 255.0)

1
requirements.txt Normal file
View file

@ -0,0 +1 @@
Pillow~=10.1.0