Full process is now producing and optimizing svg layers. It needs to be made deterministic by sorting the colors and have the ability to compare in different color spaces.
This commit is contained in:
parent
08a47465c5
commit
537ac0075b
10
py/common.py
10
py/common.py
|
@ -155,4 +155,14 @@ def rgb_to_luma (r, g, b) :
|
||||||
|
|
||||||
return (r * R) + (g * G) + (b * B)
|
return (r * R) + (g * G) + (b * B)
|
||||||
|
|
||||||
|
def convertScale(img, alpha, beta):
|
||||||
|
"""Add bias and gain to an image with saturation arithmetics. Unlike
|
||||||
|
cv2.convertScaleAbs, it does not take an absolute value, which would lead to
|
||||||
|
nonsensical results (e.g., a pixel at 44 with alpha = 3 and beta = -210
|
||||||
|
becomes 78 with OpenCV, when in fact it should become 0).
|
||||||
|
"""
|
||||||
|
|
||||||
|
new_img = img * alpha + beta
|
||||||
|
new_img[new_img < 0] = 0
|
||||||
|
new_img[new_img > 255] = 255
|
||||||
|
return new_img.astype(np.uint8)
|
||||||
|
|
|
@ -2,11 +2,12 @@ canvasWidth=1301
|
||||||
canvasHeight=914
|
canvasHeight=914
|
||||||
windowWidth=1301
|
windowWidth=1301
|
||||||
windowHeight=914
|
windowHeight=914
|
||||||
dotSize=6
|
|
||||||
maxGenerations=50
|
maxGenerations=50
|
||||||
maxParticles=2500
|
maxParticles=10000
|
||||||
minDotSize=1.8
|
minDotSize=2.3
|
||||||
line=1.8
|
maxDotSize=2.3
|
||||||
|
dotSizeFactor=0
|
||||||
|
line=2.3
|
||||||
fill=true
|
fill=true
|
||||||
mode="stipple"
|
mode="stipple"
|
||||||
display=true
|
display=false
|
|
@ -2,9 +2,19 @@ from sklearn.cluster import MiniBatchKMeans
|
||||||
import numpy as np
|
import numpy as np
|
||||||
import argparse
|
import argparse
|
||||||
import cv2
|
import cv2
|
||||||
from common import convert_color, closest_color_weighted_euclidean, closest_color_euclidean, create_colored_image, remove_from_list, list_match
|
from common import convert_color, closest_color_weighted_euclidean, closest_color_euclidean, create_colored_image, remove_from_list, list_match, to_luma, convertScale
|
||||||
import os
|
import os
|
||||||
import subprocess
|
import subprocess
|
||||||
|
from functools import cmp_to_key
|
||||||
|
|
||||||
|
def sort_pallete (a, b):
|
||||||
|
A = to_luma(a, 'BGR')
|
||||||
|
B = to_luma(b, 'BGR')
|
||||||
|
if A > B :
|
||||||
|
return 1
|
||||||
|
if B < A :
|
||||||
|
return -1
|
||||||
|
return 0
|
||||||
|
|
||||||
class Posterize:
|
class Posterize:
|
||||||
"""Posterize an image and then find nearest colors to use"""
|
"""Posterize an image and then find nearest colors to use"""
|
||||||
|
@ -13,6 +23,8 @@ class Posterize:
|
||||||
original_colors = []
|
original_colors = []
|
||||||
layers = []
|
layers = []
|
||||||
previews = []
|
previews = []
|
||||||
|
svgs = []
|
||||||
|
headless = False
|
||||||
|
|
||||||
pallete = None
|
pallete = None
|
||||||
pallete_space = 'BGR'
|
pallete_space = 'BGR'
|
||||||
|
@ -23,20 +35,21 @@ class Posterize:
|
||||||
h = 0
|
h = 0
|
||||||
w = 0
|
w = 0
|
||||||
n_colors = 3
|
n_colors = 3
|
||||||
max_particles = 3000
|
max_particles = 17000
|
||||||
conf = os.path.abspath('./conf/base.conf')
|
conf = os.path.abspath('./conf/base.conf')
|
||||||
stipple_gen = os.path.abspath('../../../src/stipple_gen')
|
stipple_gen = os.path.abspath('../../stipple_gen')
|
||||||
|
|
||||||
white = [255, 255, 255]
|
white = [255, 255, 255]
|
||||||
|
|
||||||
output = None
|
output = None
|
||||||
|
|
||||||
def __init__ (self, image, pallete, n_colors, output) :
|
def __init__ (self, image, pallete, n_colors, output, headless) :
|
||||||
self.image = cv2.imread(image)
|
self.image = cv2.imread(image)
|
||||||
(self.h, self.w) = self.image.shape[:2]
|
(self.h, self.w) = self.image.shape[:2]
|
||||||
self.pallete = pallete
|
self.pallete = pallete
|
||||||
self.n_colors = n_colors + 1
|
self.n_colors = n_colors + 1
|
||||||
self.output = output
|
self.output = output
|
||||||
|
self.headless = headless
|
||||||
|
|
||||||
if not os.path.exists(self.output) :
|
if not os.path.exists(self.output) :
|
||||||
print(f'Output directory {self.output} does not exist, creating...')
|
print(f'Output directory {self.output} does not exist, creating...')
|
||||||
|
@ -65,9 +78,7 @@ class Posterize:
|
||||||
|
|
||||||
self.image = bgrquant
|
self.image = bgrquant
|
||||||
|
|
||||||
cv2.imshow('image', bgrquant)
|
self.show(bgrquant)
|
||||||
cv2.waitKey(0)
|
|
||||||
cv2.destroyAllWindows()
|
|
||||||
|
|
||||||
def determine_colors (self):
|
def determine_colors (self):
|
||||||
reshaped = self.image.reshape(-1, self.image.shape[2])
|
reshaped = self.image.reshape(-1, self.image.shape[2])
|
||||||
|
@ -108,11 +119,14 @@ class Posterize:
|
||||||
'space' : self.pallete_space
|
'space' : self.pallete_space
|
||||||
})
|
})
|
||||||
mask = cv2.bitwise_not(mask)
|
mask = cv2.bitwise_not(mask)
|
||||||
composite[mask > 0] = np.array(closest)
|
composite[mask > 0] = np.array(convert_color(closest, self.pallete_space, 'BGR'))
|
||||||
|
|
||||||
cv2.imshow('image', composite)
|
composite_name = f'posterized.png'
|
||||||
cv2.waitKey(0)
|
composite_path = os.path.join(self.output, composite_name)
|
||||||
cv2.destroyAllWindows()
|
|
||||||
|
cv2.imwrite(composite_path, composite)
|
||||||
|
|
||||||
|
self.show(composite)
|
||||||
|
|
||||||
def extract_color_mask (self, image, color):
|
def extract_color_mask (self, image, color):
|
||||||
mask = cv2.inRange(image, color, color)
|
mask = cv2.inRange(image, color, color)
|
||||||
|
@ -121,9 +135,15 @@ class Posterize:
|
||||||
def flatten_pallete (self) :
|
def flatten_pallete (self) :
|
||||||
for color in self.pallete.colors:
|
for color in self.pallete.colors:
|
||||||
self.colors.append(color['color'])
|
self.colors.append(color['color'])
|
||||||
self.colors_dict[f'{color["color"][0]},{color["color"][1]},{color["color"][2]}'] = color['name']
|
self.colors_dict[f"{color['color'][0]},{color['color'][1]},{color['color'][2]}"] = color['name']
|
||||||
self.pallete_space = color['space']
|
self.pallete_space = color['space']
|
||||||
|
|
||||||
|
#self.colors = sorted(self.colors, key=cmp_to_key(sort_pallete))
|
||||||
|
#for color in self.colors :
|
||||||
|
# print(to_luma(color, self.pallete_space))
|
||||||
|
|
||||||
|
#quit()
|
||||||
|
|
||||||
def match_color_name (self, key) :
|
def match_color_name (self, key) :
|
||||||
return self.colors_dict[f'{key[0]},{key[1]},{key[2]}']
|
return self.colors_dict[f'{key[0]},{key[1]},{key[2]}']
|
||||||
|
|
||||||
|
@ -154,10 +174,33 @@ class Posterize:
|
||||||
]
|
]
|
||||||
print(cmd)
|
print(cmd)
|
||||||
subprocess.call(cmd, cwd = self.stipple_gen)
|
subprocess.call(cmd, cwd = self.stipple_gen)
|
||||||
self.previews.append(output_image)
|
self.svgs.append(output_svg)
|
||||||
|
self.previews.append({
|
||||||
|
'layer' : output_image,
|
||||||
|
'color' : layer['color']
|
||||||
|
})
|
||||||
|
|
||||||
def preview (self) :
|
def preview (self) :
|
||||||
#composite = create_colored_image(self.w, self.h, [255, 255, 255])
|
composite = create_colored_image(self.w, self.h, [255, 255, 255])
|
||||||
print(self.previews)
|
for layer in self.previews :
|
||||||
|
l = cv2.imread(layer['layer'], 0)
|
||||||
|
mask = cv2.bitwise_not(l)
|
||||||
|
composite[mask > 0] = np.array(convert_color(layer['color'], self.pallete_space, 'BGR'))
|
||||||
|
|
||||||
|
composite_name = f'preview.png'
|
||||||
|
composite_path = os.path.join(self.output, composite_name)
|
||||||
|
|
||||||
|
cv2.imwrite(composite_path, composite)
|
||||||
|
|
||||||
|
self.show(composite)
|
||||||
|
|
||||||
|
for svg in self.svgs :
|
||||||
|
cmd = [ 'svgopt', svg, svg]
|
||||||
|
print(cmd)
|
||||||
|
subprocess.call(cmd)
|
||||||
|
|
||||||
|
def show (self, mat) :
|
||||||
|
if not self.headless :
|
||||||
|
cv2.imshow('image', mat)
|
||||||
|
cv2.waitKey(0)
|
||||||
|
cv2.destroyAllWindows()
|
||||||
|
|
|
@ -9,6 +9,7 @@ parser.add_argument('input', type=str, help='Input image to separate')
|
||||||
parser.add_argument('colors', type=int, help='Number of colors to separate into')
|
parser.add_argument('colors', type=int, help='Number of colors to separate into')
|
||||||
parser.add_argument('pallete', type=str, help='Pallete file')
|
parser.add_argument('pallete', type=str, help='Pallete file')
|
||||||
parser.add_argument('output', type=str, help='Output dir to write to')
|
parser.add_argument('output', type=str, help='Output dir to write to')
|
||||||
|
parser.add_argument('--headless', type=bool, default=False, help='Run script headless')
|
||||||
|
|
||||||
class Separate :
|
class Separate :
|
||||||
input = ''
|
input = ''
|
||||||
|
@ -28,7 +29,7 @@ class Separate :
|
||||||
print(f'File {args.pallete} does not exist')
|
print(f'File {args.pallete} does not exist')
|
||||||
exit(2)
|
exit(2)
|
||||||
|
|
||||||
Posterize(self.input, self.pallete, args.colors, args.output)
|
Posterize(self.input, self.pallete, args.colors, args.output, args.headless)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__" :
|
if __name__ == "__main__" :
|
||||||
|
|
Loading…
Reference in New Issue