Created
August 16, 2023 17:15
-
-
Save ooliver1/83aa967f131609531e8c1c7a6e98e8e8 to your computer and use it in GitHub Desktop.
My Python Discord codejam qualifier solution
This file contains hidden or 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
# 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