making it pretty

This commit is contained in:
iou1name 2018-03-21 12:00:46 -04:00
parent 64b1b91f21
commit 0d0fd3e638
2 changed files with 106 additions and 100 deletions

View File

@ -5,8 +5,8 @@ machine chooch.
""" """
import os import os
import linedraw
import stream import stream
import linedraw
def draw(rec_filename): def draw(rec_filename):
""" """

View File

@ -1,11 +1,15 @@
#!/usr/bin/env python3
"""
Takes a raster image file and vectorizes it to create line art usable by
a pen plotter.
"""
import os import os
from random import *
import math import math
from random import *
from PIL import Image, ImageDraw, ImageOps from PIL import Image, ImageDraw, ImageOps
no_cv = True no_cv = True
export_path = "output/out.svg"
draw_contours = True draw_contours = True
draw_hatch = False draw_hatch = False
@ -24,6 +28,7 @@ F_Blur = {
(-2,1):4,(-1,1):9,(0,1):12,(1,1):9,(2,1):4, (-2,1):4,(-1,1):9,(0,1):12,(1,1):9,(2,1):4,
(-2,2):2,(-1,2):4,(0,2):5,(1,2):4,(2,2):2, (-2,2):2,(-1,2):4,(0,2):5,(1,2):4,(2,2):2,
} }
F_SobelX = { F_SobelX = {
(-1,-1): 1, (-1,-1): 1,
(0,-1): 0, (0,-1): 0,
@ -33,7 +38,8 @@ F_SobelX = {
(1,0): -2, (1,0): -2,
(-1,1): 1, (-1,1): 1,
(0,1): 0, (0,1): 0,
(1,1): -1} (1,1) -1}
F_SobelY = { F_SobelY = {
(-1,-1): 1, (-1,-1): 1,
(0,-1): 2, (0,-1): 2,
@ -45,23 +51,23 @@ F_SobelY = {
(0,1): -2, (0,1): -2,
(1,1): -1} (1,1): -1}
def appmask(IM,masks): def appmask(image, masks):
PX = IM.load() PX = image.load()
w,h = IM.size w, h = image.size
NPX = {} NPX = {}
for x in range(0,w): for x in range(0, w):
for y in range(0,h): for y in range(0, h):
a = [0]*len(masks) a = [0]*len(masks)
for i in range(len(masks)): for i in range(len(masks)):
for p in masks[i].keys(): for p in masks[i].keys():
if 0<x+p[0]<w and 0<y+p[1]<h: if 0 < x + p[0] < w and 0 < y + p[1] < h:
a[i] += PX[x+p[0],y+p[1]] * masks[i][p] a[i] += PX[x + p[0], y + p[1]] * masks[i][p]
if sum(masks[i].values())!=0: if sum(masks[i].values()) != 0:
a[i] = a[i] / sum(masks[i].values()) a[i] = a[i] / sum(masks[i].values())
NPX[x,y]=int(sum([v**2 for v in a])**0.5) NPX[x,y] = int(sum([v**2 for v in a])**0.5)
for x in range(0,w): for x in range(0, w):
for y in range(0,h): for y in range(0, h):
PX[x,y] = NPX[x,y] PX[x, y] = NPX[x, y]
def distsum(*args): def distsum(*args):
@ -71,8 +77,8 @@ def distsum(*args):
""" """
dists = [] dists = []
for i in range(1, len(args): for i in range(1, len(args):
a = args[i][0]-args[i-1][0] a = args[i][0] - args[i-1][0]
b = args[i][1]-args[i-1][1] b = args[i][1] - args[i-1][1]
dist = (a**2 + b**2)**0.5 dist = (a**2 + b**2)**0.5
dists.append(dist) dists.append(dist)
return sum(dists) return sum(dists)
@ -83,14 +89,14 @@ def sortlines(lines):
clines = lines[:] clines = lines[:]
slines = [clines.pop(0)] slines = [clines.pop(0)]
while clines != []: while clines != []:
x,s,r = None,1000000,False x, s, r = None, 1000000, False
for l in clines: for l in clines:
d = distsum(l[0],slines[-1][-1]) d = distsum(l[0], slines[-1][-1])
dr = distsum(l[-1],slines[-1][-1]) dr = distsum(l[-1], slines[-1][-1])
if d < s: if d < s:
x,s,r = l[:],d,False x, s, r = l[:], d, False
if dr < s: if dr < s:
x,s,r = l[:],s,True x, s, r = l[:], s, True
clines.remove(x) clines.remove(x)
if r == True: if r == True:
@ -99,49 +105,48 @@ def sortlines(lines):
return slines return slines
def auto_canny(image, sigma=0.33):
def auto_canny(img, sigma=0.33):
""" """
Automatically determines appropriate upper and lower boundries for the Canny function. Automatically determines appropriate upper and lower boundries for the Canny function.
""" """
med = np.median(img) med = np.median(image)
lower = int(max(0, (1.0 - sigma) * med)) lower = int(max(0, (1.0 - sigma) * med))
upper = int(min(255, (1.0 + sigma) * med)) upper = int(min(255, (1.0 + sigma) * med))
edges = cv2.Canny(img, lower, upper) edges = cv2.Canny(image, lower, upper)
return edges return edges
def find_edges(IM): def find_edges(image):
print("finding edges...") print("finding edges...")
no_cv = True no_cv = True
if no_cv: if no_cv:
#appmask(IM,[F_Blur]) #appmask(image, [F_Blur])
appmask(IM,[F_SobelX,F_SobelY]) appmask(image, [F_SobelX, F_SobelY])
else: else:
im = np.array(IM) image = np.array(image)
im = cv2.GaussianBlur(im,(3,3),0) image = cv2.GaussianBlur(image, (3, 3), 0)
#im = cv2.Canny(im,100,200) #image = cv2.Canny(image,100,200)
im = auto_canny(im) image = auto_canny(image)
IM = Image.fromarray(im) image = Image.fromarray(image)
return IM.point(lambda p: p > 128 and 255) return image.point(lambda p: p > 128 and 255)
def getdots(IM): def getdots(image):
print("getting contour points...") print("getting contour points...")
PX = IM.load() PX = image.load()
dots = [] dots = []
w,h = IM.size w, h = image.size
for y in range(h-1): for y in range(h-1):
row = [] row = []
for x in range(1,w): for x in range(1, w):
if PX[x,y] == 255: if PX[x, y] == 255:
if len(row) > 0: if len(row) > 0:
if x-row[-1][0] == row[-1][-1]+1: if x-row[-1][0] == row[-1][-1] + 1:
row[-1] = (row[-1][0],row[-1][-1]+1) row[-1] = (row[-1][0], row[-1][-1] + 1)
else: else:
row.append((x,0)) row.append((x, 0))
else: else:
row.append((x,0)) row.append((x, 0))
dots.append(row) dots.append(row)
return dots return dots
@ -150,110 +155,110 @@ def connectdots(dots):
print("connecting contour points...") print("connecting contour points...")
contours = [] contours = []
for y in range(len(dots)): for y in range(len(dots)):
for x,v in dots[y]: for x, v in dots[y]:
if v > -1: if v > -1:
if y == 0: if y == 0:
contours.append([(x,y)]) contours.append([(x, y)])
else: else:
closest = -1 closest = -1
cdist = 100 cdist = 100
for x0,v0 in dots[y-1]: for x0, v0 in dots[y-1]:
if abs(x0-x) < cdist: if abs(x0 - x) < cdist:
cdist = abs(x0-x) cdist = abs(x0 - x)
closest = x0 closest = x0
if cdist > 3: if cdist > 3:
contours.append([(x,y)]) contours.append([(x, y)])
else: else:
found = 0 found = 0
for i in range(len(contours)): for i in range(len(contours)):
if contours[i][-1] == (closest,y-1): if contours[i][-1] == (closest, y-1):
contours[i].append((x,y,)) contours[i].append((x, y,))
found = 1 found = 1
break break
if found == 0: if found == 0:
contours.append([(x,y)]) contours.append([(x, y)])
for c in contours: for c in contours:
if c[-1][1] < y-1 and len(c)<4: if c[-1][1] < y-1 and len(c) < 4:
contours.remove(c) contours.remove(c)
return contours return contours
def getcontours(IM,sc=2): def getcontours(image, sc=2):
print("generating contours...") print("generating contours...")
IM = find_edges(IM) image = find_edges(image)
IM1 = IM.copy() image1 = IM.copy()
IM2 = IM.rotate(-90,expand=True).transpose(Image.FLIP_LEFT_RIGHT) image2 = image.rotate(-90, expand=True).transpose(Image.FLIP_LEFT_RIGHT)
dots1 = getdots(IM1) dots1 = getdots(image1)
contours1 = connectdots(dots1) contours1 = connectdots(dots1)
dots2 = getdots(IM2) dots2 = getdots(image2)
contours2 = connectdots(dots2) contours2 = connectdots(dots2)
for i in range(len(contours2)): for i in range(len(contours2)):
contours2[i] = [(c[1],c[0]) for c in contours2[i]] contours2[i] = [(c[1], c[0]) for c in contours2[i]]
contours = contours1+contours2 contours = contours1 + contours2
for i in range(len(contours)): for i in range(len(contours)):
for j in range(len(contours)): for j in range(len(contours)):
if len(contours[i]) > 0 and len(contours[j])>0: if len(contours[i]) > 0 and len(contours[j]) > 0:
if distsum(contours[j][0],contours[i][-1]) < 8: if distsum(contours[j][0], contours[i][-1]) < 8:
contours[i] = contours[i]+contours[j] contours[i] = contours[i] + contours[j]
contours[j] = [] contours[j] = []
for i in range(len(contours)): for i in range(len(contours)):
contours[i] = [contours[i][j] for j in range(0,len(contours[i]),8)] contours[i] = [contours[i][j] for j in range(0, len(contours[i]), 8)]
contours = [c for c in contours if len(c) > 1] contours = [c for c in contours if len(c) > 1]
for i in range(0,len(contours)): for i in range(0, len(contours)):
contours[i] = [(v[0]*sc,v[1]*sc) for v in contours[i]] contours[i] = [(v[0] * sc, v[1] * sc) for v in contours[i]]
for i in range(0,len(contours)): for i in range(0, len(contours)):
for j in range(0,len(contours[i])): for j in range(0, len(contours[i])):
contours[i][j] = int(contours[i][j][0]+10),int(contours[i][j][1]+10) contours[i][j] = int(contours[i][j][0] + 10), int(contours[i][j][1] + 10)
return contours return contours
def hatch(IM,sc=16): def hatch(image, sc=16):
print("hatching...") print("hatching...")
PX = IM.load() PX = image.load()
w,h = IM.size w,h = image.size
lg1 = [] lg1 = []
lg2 = [] lg2 = []
for x0 in range(w): for x0 in range(w):
for y0 in range(h): for y0 in range(h):
x = x0*sc x = x0 * sc
y = y0*sc y = y0 * sc
if PX[x0,y0] > 144: if PX[x0, y0] > 144:
pass pass
elif PX[x0,y0] > 64: elif PX[x0, y0] > 64:
lg1.append([(x,y+sc/4),(x+sc,y+sc/4)]) lg1.append([(x, y+sc/4), (x+sc, y+sc/4)])
elif PX[x0,y0] > 16: elif PX[x0, y0] > 16:
lg1.append([(x,y+sc/4),(x+sc,y+sc/4)]) lg1.append([(x, y+sc/4), (x+sc, y+sc/4)])
lg2.append([(x+sc,y),(x,y+sc)]) lg2.append([(x+sc, y), (x, y+sc)])
else: else:
lg1.append([(x,y+sc/4),(x+sc,y+sc/4)]) lg1.append([(x, y+sc/4), (x+sc, y+sc/4)])
lg1.append([(x,y+sc/2+sc/4),(x+sc,y+sc/2+sc/4)]) lg1.append([(x, y+sc/2 + sc/4), (x+sc, y+sc/2 + sc/4)])
lg2.append([(x+sc,y),(x,y+sc)]) lg2.append([(x+sc, y), (x, y+sc)])
lines = [lg1,lg2] lines = [lg1, lg2]
for k in range(0,len(lines)): for k in range(0, len(lines)):
for i in range(0,len(lines[k])): for i in range(0, len(lines[k])):
for j in range(0,len(lines[k])): for j in range(0, len(lines[k])):
if lines[k][i] != [] and lines[k][j] != []: if lines[k][i] != [] and lines[k][j] != []:
if lines[k][i][-1] == lines[k][j][0]: if lines[k][i][-1] == lines[k][j][0]:
lines[k][i] = lines[k][i]+lines[k][j][1:] lines[k][i] = lines[k][i] + lines[k][j][1:]
lines[k][j] = [] lines[k][j] = []
lines[k] = [l for l in lines[k] if len(l) > 0] lines[k] = [l for l in lines[k] if len(l) > 0]
lines = lines[0]+lines[1] lines = lines[0] + lines[1]
for i in range(0,len(lines)): for i in range(0, len(lines)):
for j in range(0,len(lines[i])): for j in range(0, len(lines[i])):
lines[i][j] = int(lines[i][j][0]+sc),int(lines[i][j][1]+sc)-j lines[i][j] = int(lines[i][j][0] + sc), int(lines[i][j][1] + sc) - j
return lines return lines
@ -268,6 +273,7 @@ def sketch(path, export_path=None, resolution=1024, hatch_size=16, contour_simpl
width = int(resolution/contour_simplify) width = int(resolution/contour_simplify)
height = int(resolution/contour_simplify*image.size[0]/image.size[1]) height = int(resolution/contour_simplify*image.size[0]/image.size[1])
lines += getcontours(image.resize((width, height)), contour_simplify) lines += getcontours(image.resize((width, height)), contour_simplify)
if draw_hatch: if draw_hatch:
width = int(resolution/hatch_size) width = int(resolution/hatch_size)
height = int(resolution/hatch_size*image.size[0]/image.size[1]) height = int(resolution/hatch_size*image.size[0]/image.size[1])