Release 1.0.0

Initial release
This commit is contained in:
Victor Westerlund 2021-04-05 07:36:26 +02:00
parent 090f68cd0e
commit 654aa1ef32
8 changed files with 190 additions and 50 deletions

3
.gitignore vendored
View file

@ -1,7 +1,8 @@
__pycache__ __pycache__
__sample__.json
input/* input/*
samples/* samples/*
mem/* memory/*
!*/.placeholder !*/.placeholder

74
classes/Collage.py Normal file
View file

@ -0,0 +1,74 @@
from PIL import Image
from bisect import bisect_left
class Schematic():
def __init__(self,template,samples):
self.template = template
self.samples = samples
self.schematic = {}
self.create_schematic()
def query_sample(self,value):
samples = [*self.samples]
pos = bisect_left(samples,value)
if(pos < 1 or pos > len(samples) - 1):
pos = 0
return samples[pos]
def create_schematic(self):
for x in range(1,self.template.size[0]):
self.schematic[x] = {}
for y in range(1,self.template.size[1]):
r,g,b = self.template.getpixel((x,y))
eyedropper = "%02x%02x%02x" % (r,g,b)
self.schematic[x][y] = self.query_sample(eyedropper)
print(f"Generated schematic for pixel at index [{x},{y}] ",end="\r",flush="True")
print("")
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()
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))
def create_collage(self):
schematic = Schematic(self.template,self.samples).schematic
offset_x = 0
offset_y = 0
for x in range(1,self.template.size[0]):
offset_x = 0
for y in range(1,self.template.size[1]):
key = schematic[x][y]
resolve_posix = self.samples[key]
sample = Image.open(self.samples_posix[resolve_posix])
sample = sample.resize(self.size)
self.collage = self.collage.copy()
self.collage.paste(sample,(offset_x,offset_y))
offset_x += self.size[0]
print(f"Pasted best matched sample for index [{x},{y}] ",end="\r",flush="True")
offset_y += self.size[1]
print("")
def put(self,dest):
self.collage.save(dest,"JPEG")

31
classes/Color.py Normal file
View file

@ -0,0 +1,31 @@
from PIL import Image
# Calculate the average color of a sample
class AverageColor():
def __init__(self,image):
self.image = Image.open(image)
self.image = self.image.resize((50,50)) # Downscale image to improve performance
def rgb(self):
width,height = self.image.size
rgb = [0,0,0]
def average(value):
return round(value / lines)
# Get RGB of each pixel with by raster scanning
lines = 0
for x in range(0,width):
for y in range(0,height):
r,g,b = self.image.getpixel((x,y))
rgb[0] += r
rgb[1] += g
rgb[2] += b
lines += 1
return tuple(map(average,rgb))
# Format RGB output as HEX (without #)
def hex(self):
return "%02x%02x%02x" % self.rgb()

View file

@ -1,24 +0,0 @@
from PIL import Image
def compute_average_image_color(img):
width, height = img.size
r_total = 0
g_total = 0
b_total = 0
count = 0
for x in range(0, width):
for y in range(0, height):
r, g, b = img.getpixel((x,y))
r_total += r
g_total += g
b_total += b
count += 1
return (r_total/count, g_total/count, b_total/count)
img = Image.open('image.png')
#img = img.resize((50,50)) # Small optimization
average_color = compute_average_image_color(img)
print(average_color)

View file

@ -1,26 +1,73 @@
import zlib
import json
from collections import OrderedDict
from .Color import AverageColor
from pathlib import Path from pathlib import Path
class Samples: # Generate a unique identifier for the current sample set
def __init__(self,samples_path): class SamplesFingerprint():
samples_files = Path(samples_path).glob("*.jpg") def __init__(self):
self.samples = [x for x in samples_files if x.is_file()]
self.hash = self.create_hash() self.hash = self.create_hash()
self.memory = f"mem/{self.hash}" self.memory = f"memory/{self.hash}.json"
self.created = False
# Create hash from sample names and path
if(not self.hash_exists()):
self.save_hash()
# Checksum by hashing next sample with sum of previous samples
def create_hash(self): def create_hash(self):
samples_hash = "" samples_hash = "I like coffee" # Initialize with padding
for i in self.samples: for i in self.samples_posix:
samples_hash = hash(i) seed = str(samples_hash) + str(i)
samples_hash = zlib.crc32(bytes(seed,encoding="utf8"))
return samples_hash return samples_hash
# Test if the current sample set has been saved # Test if sample set has been saved
def hash_exists(self): def hash_exists(self):
if(Path(self.memory).is_file()): if(self.created or not Path(self.memory).is_file()):
return True return False
return False
self.created = True
return self.created
# Save hash to memory location on disk # Save identifier of sample set to disk
def save_hash(self): def save_hash(self):
Path(self.memory,"w").touch() Path(self.memory).touch()
# Load samples from sample set memory file or from samples/
class Samples(SamplesFingerprint):
def __init__(self,samples_path,force = False):
samples_files = Path(samples_path).glob("*.jpg")
self.samples_posix = [x for x in samples_files if x.is_file()] # List of PosixPaths
self.samples = {} # HEX from color calc algorithm
super(Samples,self).__init__()
try:
self.load_sample_set()
except:
self.map_color()
self.save_sample_set()
# Get the pixel value for each sample using a desired algorithm
def map_color(self):
for i,sample in enumerate(self.samples_posix):
color = AverageColor(sample).hex() # Get the average color of a sample as HEX
self.samples[color] = i
print(f"Loaded {i + 1}/{len(self.samples_posix)} samples",end="\r",flush="True")
print("")
# Save calculated sample set data
def save_sample_set(self):
self.samples = dict(sorted(self.samples.items())) # Sort the dict by chrominance
with open(self.memory,"w") as f:
json.dump(self.samples,f)
print(f"Saved sample set with fingerprint: {self.hash}")
# Load pre-calulated sample set data
def load_sample_set(self):
with open(self.memory) as f:
self.samples = json.load(f)
print(f"Loaded {len(self.samples)} samples from set {self.hash}")

View file

@ -1,4 +0,0 @@
#! /bin/bash
export PYTHONHASHSEED=0
python3 main.py

23
main.py
View file

@ -1,7 +1,22 @@
#from classes import Eyedropper
from classes.Samples import Samples from classes.Samples import Samples
from classes.Collage import Collage
from pathlib import Path
# Load samples from folder force = False
samples = Samples("samples") input_path = "input/"
print(samples.hash) # Load all JPGs from the "samples/" folder
samples = Samples("samples",force)
input_file = str("input/coffee-resized.jpg")
collage = Collage(input_file,samples)
collage.put(input_file + "_collage.jpg")
# input_files = Path(input_path).glob("*.jpg")
# input_files_posix = [x for x in input_files if x.is_file()] # List of PosixPaths
# for input_file in input_files_posix:
# input_file = str(input_file)
# collage = Collage(input_file,samples)
# collage.put(input_file + "_collage.jpg")