Skip to content

Instantly share code, notes, and snippets.

@ooliver1
Created August 16, 2023 17:15
Show Gist options
  • Save ooliver1/83aa967f131609531e8c1c7a6e98e8e8 to your computer and use it in GitHub Desktop.
Save ooliver1/83aa967f131609531e8c1c7a6e98e8e8 to your computer and use it in GitHub Desktop.
My Python Discord codejam qualifier solution
# I haven't used numpy at all outside of tutorials years ago. They may have better perf
# instead of cropping but I don't really care to try that out. My funky part is the `map`
# which lazily gets each item (the thing which doesn't even need to be lazy, the cropping
# really should.
import functools
import operator
from PIL import Image
def valid_input(
image_size: tuple[int, int], tile_size: tuple[int, int], ordering: list[int]
) -> bool:
"""
Return True if the given input allows the rearrangement of the image, False otherwise.
The tile size must divide each image dimension without remainders, and `ordering` must use each input tile exactly
once.
"""
return (
# Check `ix` is divisble by `tx`.
image_size[0] % tile_size[0] == 0
# Check `iy` is divisble by `ty`.`
and image_size[1] % tile_size[1] == 0
# Check `ordering` uses each input tile exactly once.
and len(ordering) == len(set(ordering))
# Check `ordering` actually uses real tile indexes.
and max(ordering)
< image_size[0] // tile_size[0] * image_size[1] // tile_size[1]
)
def rearrange_tiles(
image_path: str, tile_size: tuple[int, int], ordering: list[int], out_path: str
) -> None:
"""
Rearrange the image.
The image is given in `image_path`. Split it into tiles of size `tile_size`, and rearrange them by `ordering`.
The new image needs to be saved under `out_path`.
The tile size must divide each image dimension without remainders, and `ordering` must use each input tile exactly
once. If these conditions do not hold, raise a ValueError with the message:
"The tile size of ordering are not valid for the given image".
"""
with Image.open(image_path) as image:
if not valid_input(image.size, tile_size, ordering):
raise ValueError(
"The tile size or ordering are not valid for the given image"
)
x_multiplier = image.width // tile_size[0]
y_multiplier = image.height // tile_size[1]
smaller_images = [
image.crop(
(
x * tile_size[0],
y * tile_size[1],
(x + 1) * tile_size[0],
(y + 1) * tile_size[1],
)
)
for y in range(y_multiplier)
for x in range(x_multiplier)
]
ordered_images = map(
functools.partial(operator.getitem, smaller_images), ordering
)
with Image.new(mode=image.mode, size=(image.width, image.height)) as new_image:
for y in range(y_multiplier):
for x in range(x_multiplier):
new_image.paste(
next(ordered_images),
(
x * tile_size[0],
y * tile_size[1],
(x + 1) * tile_size[0],
(y + 1) * tile_size[1],
),
)
new_image.save(out_path)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment