mirror of
https://codeberg.org/vlw/collage.git
synced 2025-09-13 23:23:41 +02:00
96 lines
2.7 KiB
Python
96 lines
2.7 KiB
Python
from PIL import Image, ImageOps
|
|
from bisect import bisect_left
|
|
|
|
# Create instructions for the Collage constructor
|
|
class Schematic():
|
|
def __init__(self,template,samples):
|
|
self.template = template
|
|
self.samples = samples
|
|
self.length = 0
|
|
|
|
self.schematic = {}
|
|
self.create_schematic()
|
|
|
|
# Use array bisection to find the closest matching sample by HEX color
|
|
def query_sample(self,value):
|
|
samples = [*self.samples]
|
|
pos = bisect_left(samples,value)
|
|
|
|
# Reset on under- and overflows
|
|
if(pos < 1 or pos > len(samples) - 1):
|
|
pos = 0
|
|
|
|
return samples[pos]
|
|
|
|
# Build a 2-Dimensional list of matches
|
|
def create_schematic(self):
|
|
for y in range(1,self.template.size[0]):
|
|
self.schematic[y] = {}
|
|
for x in range(1,self.template.size[1]):
|
|
r,g,b = self.template.getpixel((x,y)) # Extract RGB from current pixel
|
|
eyedropper = "%02x%02x%02x" % (r,g,b) # Convert RGB to HEX
|
|
|
|
self.schematic[y][x] = self.query_sample(eyedropper)
|
|
self.length += 1
|
|
print(f"Found best match for index [{x},{y}] ",end="\r",flush="True")
|
|
print("")
|
|
|
|
# Construct collage by loading images defined in schematic
|
|
class Collage():
|
|
def __init__(self,input_file,samples):
|
|
self.template = Image.open(input_file)
|
|
self.samples = samples.samples
|
|
self.samples_posix = samples.samples_posix
|
|
|
|
self.size = (20,20)
|
|
|
|
self.collage = self.create_canvas()
|
|
self.create_collage()
|
|
|
|
# Create the upscaled output image
|
|
def create_canvas(self):
|
|
canvas_width = self.size[0] * self.template.size[0]
|
|
canvas_height = self.size[1] * self.template.size[1]
|
|
|
|
return Image.new("RGB",(canvas_width,canvas_height))
|
|
|
|
# Assemble the collage
|
|
def create_collage(self):
|
|
build = Schematic(self.template,self.samples)
|
|
|
|
offset_x = 0
|
|
offset_y = 0
|
|
|
|
i = 0
|
|
print("Pasting samples..")
|
|
# Apply each sample by raster scanning
|
|
for y in range(1,self.template.size[0]):
|
|
offset_x = 0
|
|
for x in range(1,self.template.size[1]):
|
|
key = build.schematic[y][x] # Get sample index for current pixel
|
|
resolve_posix = self.samples[key] # Convert sample index to sample set index
|
|
|
|
# Load and resize the requested sample from disk
|
|
sample = Image.open(self.samples_posix[resolve_posix])
|
|
sample = sample.resize(self.size)
|
|
|
|
# Add the loaded sample to the collage
|
|
self.collage = self.collage.copy()
|
|
self.collage.paste(sample,(offset_y,offset_x))
|
|
|
|
offset_x += self.size[0]
|
|
|
|
progress = round(i / build.length * 100,2)
|
|
print(f"Progress: (%) {progress} ",end="\r",flush="True")
|
|
i += 1
|
|
offset_y += self.size[1]
|
|
print("")
|
|
print("Collage created")
|
|
|
|
# Correct rotation and reflection
|
|
self.collage = self.collage.rotate(-90)
|
|
self.collage = ImageOps.mirror(self.collage)
|
|
|
|
# Save collage to disk
|
|
def put(self,dest):
|
|
self.collage.save(dest,"JPEG")
|