pull/11/head
Rahul 2023-07-22 03:08:16 +05:30
rodzic 3aedc2f61d
commit af7694797c
9 zmienionych plików z 256 dodań i 251 usunięć

Wyświetl plik

@ -1,263 +1,54 @@
from random import *
import math
import argparse
from PIL import Image, ImageDraw, ImageOps
from linedraw import sketch
from linedraw.default import argument
from filters import *
from strokesort import *
import perlin
from util import *
no_cv = False
export_path = "output/out.svg"
draw_contours = True
draw_hatch = True
show_bitmap = False
resolution = 1024
hatch_size = 16
contour_simplify = 2
try:
import numpy as np
import cv2
except:
print("Cannot import numpy/openCV. Switching to NO_CV mode.")
no_cv = True
def find_edges(IM):
print("finding edges...")
if no_cv:
#appmask(IM,[F_Blur])
appmask(IM,[F_SobelX,F_SobelY])
else:
im = np.array(IM)
im = cv2.GaussianBlur(im,(3,3),0)
im = cv2.Canny(im,100,200)
IM = Image.fromarray(im)
return IM.point(lambda p: p > 128 and 255)
def getdots(IM):
print("getting contour points...")
PX = IM.load()
dots = []
w,h = IM.size
for y in range(h-1):
row = []
for x in range(1,w):
if PX[x,y] == 255:
if len(row) > 0:
if x-row[-1][0] == row[-1][-1]+1:
row[-1] = (row[-1][0],row[-1][-1]+1)
else:
row.append((x,0))
else:
row.append((x,0))
dots.append(row)
return dots
def connectdots(dots):
print("connecting contour points...")
contours = []
for y in range(len(dots)):
for x,v in dots[y]:
if v > -1:
if y == 0:
contours.append([(x,y)])
else:
closest = -1
cdist = 100
for x0,v0 in dots[y-1]:
if abs(x0-x) < cdist:
cdist = abs(x0-x)
closest = x0
if cdist > 3:
contours.append([(x,y)])
else:
found = 0
for i in range(len(contours)):
if contours[i][-1] == (closest,y-1):
contours[i].append((x,y,))
found = 1
break
if found == 0:
contours.append([(x,y)])
for c in contours:
if c[-1][1] < y-1 and len(c)<4:
contours.remove(c)
return contours
def getcontours(IM,sc=2):
print("generating contours...")
IM = find_edges(IM)
IM1 = IM.copy()
IM2 = IM.rotate(-90,expand=True).transpose(Image.FLIP_LEFT_RIGHT)
dots1 = getdots(IM1)
contours1 = connectdots(dots1)
dots2 = getdots(IM2)
contours2 = connectdots(dots2)
for i in range(len(contours2)):
contours2[i] = [(c[1],c[0]) for c in contours2[i]]
contours = contours1+contours2
for i in range(len(contours)):
for j in range(len(contours)):
if len(contours[i]) > 0 and len(contours[j])>0:
if distsum(contours[j][0],contours[i][-1]) < 8:
contours[i] = contours[i]+contours[j]
contours[j] = []
for i in range(len(contours)):
contours[i] = [contours[i][j] for j in range(0,len(contours[i]),8)]
contours = [c for c in contours if len(c) > 1]
for i in range(0,len(contours)):
contours[i] = [(v[0]*sc,v[1]*sc) for v in contours[i]]
for i in range(0,len(contours)):
for j in range(0,len(contours[i])):
contours[i][j] = int(contours[i][j][0]+10*perlin.noise(i*0.5,j*0.1,1)),int(contours[i][j][1]+10*perlin.noise(i*0.5,j*0.1,2))
return contours
def hatch(IM,sc=16):
print("hatching...")
PX = IM.load()
w,h = IM.size
lg1 = []
lg2 = []
for x0 in range(w):
for y0 in range(h):
x = x0*sc
y = y0*sc
if PX[x0,y0] > 144:
pass
elif PX[x0,y0] > 64:
lg1.append([(x,y+sc/4),(x+sc,y+sc/4)])
elif PX[x0,y0] > 16:
lg1.append([(x,y+sc/4),(x+sc,y+sc/4)])
lg2.append([(x+sc,y),(x,y+sc)])
else:
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)])
lg2.append([(x+sc,y),(x,y+sc)])
lines = [lg1,lg2]
for k in range(0,len(lines)):
for i 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][-1] == lines[k][j][0]:
lines[k][i] = lines[k][i]+lines[k][j][1:]
lines[k][j] = []
lines[k] = [l for l in lines[k] if len(l) > 0]
lines = lines[0]+lines[1]
for i in range(0,len(lines)):
for j in range(0,len(lines[i])):
lines[i][j] = int(lines[i][j][0]+sc*perlin.noise(i*0.5,j*0.1,1)),int(lines[i][j][1]+sc*perlin.noise(i*0.5,j*0.1,2))-j
return lines
def sketch(path):
IM = None
possible = [path,"images/"+path,"images/"+path+".jpg","images/"+path+".png","images/"+path+".tif"]
for p in possible:
try:
IM = Image.open(p)
break
except FileNotFoundError:
print("The Input File wasn't found. Check Path")
exit(0)
pass
w,h = IM.size
IM = IM.convert("L")
IM=ImageOps.autocontrast(IM,10)
lines = []
if draw_contours:
lines += getcontours(IM.resize((resolution//contour_simplify,resolution//contour_simplify*h//w)),contour_simplify)
if draw_hatch:
lines += hatch(IM.resize((resolution//hatch_size,resolution//hatch_size*h//w)),hatch_size)
lines = sortlines(lines)
if show_bitmap:
disp = Image.new("RGB",(resolution,resolution*h//w),(255,255,255))
draw = ImageDraw.Draw(disp)
for l in lines:
draw.line(l,(0,0,0),5)
disp.show()
f = open(export_path,'w')
f.write(makesvg(lines))
f.close()
print(len(lines),"strokes.")
print("done.")
return lines
def makesvg(lines):
print("generating svg file...")
out = '<svg xmlns="http://www.w3.org/2000/svg" version="1.1">'
for l in lines:
l = ",".join([str(p[0]*0.5)+","+str(p[1]*0.5) for p in l])
out += '<polyline points="'+l+'" stroke="black" stroke-width="2" fill="none" />\n'
out += '</svg>'
return out
if __name__ == "__main__":
if __name__ == '__main__':
parser = argparse.ArgumentParser(description='Convert image to vectorized line drawing for plotters.')
parser.add_argument('-i','--input',dest='input_path',
default='lenna',action='store',nargs='?',type=str,
help='Input path')
parser.add_argument('-i', '--input', dest='input_path',
default='lenna', action='store', nargs='?', type=str,
help='Input image path')
parser.add_argument('-o','--output',dest='output_path',
default=export_path,action='store',nargs='?',type=str,
help='Output path.')
parser.add_argument('-o', '--output', dest='output_path',
default=argument.export_path, action='store', nargs='?', type=str,
help='Output image path')
parser.add_argument('-b','--show_bitmap',dest='show_bitmap',
const = not show_bitmap,default= show_bitmap,action='store_const',
help="Display bitmap preview.")
parser.add_argument('-r', '--resolution', dest='resolution',
default=argument.show_bitmap, action='store_const',
help='Resolution of the output image')
parser.add_argument('-nc','--no_contour',dest='no_contour',
const = draw_contours,default= not draw_contours,action='store_const',
help="Don't draw contours.")
parser.add_argument('-nh','--no_hatch',dest='no_hatch',
const = draw_hatch,default= not draw_hatch,action='store_const',
help='Disable hatching.')
parser.add_argument('-b', '--show_bitmap', dest='show_bitmap',
const=not argument.show_bitmap, default=argument.show_bitmap, action='store_const',
help='Display bitmap preview.')
parser.add_argument('--no_cv',dest='no_cv',
const = not no_cv,default= no_cv,action='store_const',
help="Don't use openCV.")
parser.add_argument('-nc', '--no_contour', dest='no_contour',
const=argument.draw_contours, default=not argument.draw_contours, action='store_const',
help="Don't draw contours.")
parser.add_argument('-nh', '--no_hatch', dest='no_hatch',
const=argument.draw_hatch, default=not argument.draw_hatch, action='store_const',
help='Disable hatching.')
parser.add_argument('--hatch_size',dest='hatch_size',
default=hatch_size,action='store',nargs='?',type=int,
help='Patch size of hatches. eg. 8, 16, 32')
parser.add_argument('--contour_simplify',dest='contour_simplify',
default=contour_simplify,action='store',nargs='?',type=int,
help='Level of contour simplification. eg. 1, 2, 3')
parser.add_argument('--no_cv', dest='no_cv',
const=not argument.no_cv, default=argument.no_cv, action='store_const',
help="Don't use openCV.")
parser.add_argument('--hatch_size', dest='hatch_size',
default=argument.hatch_size, action='store', nargs='?', type=int,
help='Patch size of hatches. eg. 8, 16, 32')
parser.add_argument('--contour_simplify', dest='contour_simplify',
default=argument.contour_simplify, action='store', nargs='?', type=int,
help='Level of contour simplification. eg. 1, 2, 3')
args = parser.parse_args()
input_path = args.input_path
export_path = args.output_path
draw_hatch = not args.no_hatch
draw_contours = not args.no_contour
hatch_size = args.hatch_size
contour_simplify = args.contour_simplify
show_bitmap = args.show_bitmap
no_cv = args.no_cv
sketch(args.input_path)
argument.draw_hatch = not args.no_hatch
argument.contour_simplify = not args.no_contour
argument.hatch_size = args.hatch_size
argument.contour_simplify = args.contour_simplify
argument.show_bitmap = args.show_bitmap
argument.no_cv = args.no_cv
argument.resolution = args.resolution
sketch(input_path, export_path)

Wyświetl plik

@ -0,0 +1 @@
from linedraw.helper import sketch

Wyświetl plik

@ -0,0 +1,11 @@
class Default:
export_path = "output/out.svg"
show_bitmap = False
draw_contours = True
draw_hatch = True
no_cv = False
hatch_size = 16
contour_simplify = 2
resolution = 1024
argument = Default()

199
linedraw/helper.py 100644
Wyświetl plik

@ -0,0 +1,199 @@
from PIL import Image, ImageOps, ImageDraw
import linedraw.perlin as perlin
from linedraw.filters import appmask, F_SobelX, F_SobelY
from linedraw.default import argument
from linedraw.util import distsum
from linedraw.strokesort import sortlines
def sketch(input_path, output_path):
IMAGE = None
try:
IMAGE = Image.open(input_path)
except FileNotFoundError:
return print("The Input File wasn't found. Check Path")
width, height = IMAGE.size
IMAGE = IMAGE.convert("L")
IMAGE = ImageOps.autocontrast(IMAGE, 10)
lines = []
if argument.draw_contours:
lines += get_contours(IMAGE.resize((argument.resolution // argument.contour_simplify,
argument.resolution // argument.contour_simplify * height // width)))
if argument.draw_hatch:
lines += hatch(IMAGE.resize(
(argument.resolution // argument.hatch_size, argument.resolution // argument.hatch_size * height // width)))
lines = sortlines(lines)
if argument.show_bitmap:
disp = Image.new("RGB", (argument.resolution, argument.resolution * height // width), (255, 255, 255))
draw = ImageDraw.Draw(disp)
for l in lines:
draw.line(l, (0, 0, 0), 5)
disp.show()
file = open(output_path, 'w')
file.write(make_svg(lines))
file.close()
print(len(lines), "strokes.")
print("done.")
return lines
def get_contours(image):
print("Generating Contours....")
image = find_edges(image)
image_copy1 = image.copy()
image_copy2 = image.rotate(-90, expand=True).transpose(Image.FLIP_LEFT_RIGHT)
image_copy1_dots = get_dots(image_copy1)
image_copy1_contours = connect_dots(image_copy1_dots)
image_copy2_dots = get_dots(image_copy2)
image_copy2_contours = connect_dots(image_copy2_dots)
for i in range(len(image_copy2_contours)):
image_copy2_contours[1] = [(c[1], c[0]) for c in image_copy2_contours[i]]
contours = image_copy1_contours + image_copy2_contours
for i in range(len(contours)):
for j in range(len(contours)):
if len(contours[i]) > 0 and len(contours[j]) > 0:
if distsum(contours[j][0], contours[i][-1]) < 8:
contours[i] = contours[i] + contours[j]
contours[j] = []
for i in range(len(contours)):
contours[i] = [contours[i][j] for j in range(0, len(contours[i]), 8)]
contours = [c for c in contours if len(c) > 1]
for i in range(0, len(contours)):
contours[i] = [(v[0] * argument.contour_simplify, v[1] * argument.contour_simplify) for v in contours[i]]
for i in range(0, len(contours)):
for j in range(0, len(contours[i])):
contours[i][j] = int(contours[i][j][0] + 10 * perlin.noise(i * 0.5, j * 0.1, 1)), int(
contours[i][j][1] + 10 * perlin.noise(i * 0.5, j * 0.1, 2))
return contours
def find_edges(image):
print("Fining Edges....")
if argument.no_cv:
appmask(image, [F_SobelX, F_SobelY])
else:
import numpy as np
import cv2
image = np.array(image)
image = cv2.GaussianBlur(image, (3, 3), 0)
image = cv2.Canny(image, 100, 200)
image = Image.fromarray(image)
return image.point(lambda p: p > 128 and 255)
def get_dots(image):
print("Getting contour points...")
PX = image.load()
dots = []
width, height = image.size
for y in range(height - 1):
row = []
for x in range(1, width):
if PX[x, y] == 255:
if len(row) > 0:
if x - row[-1][0] == row[-1][-1] + 1:
row[-1] = (row[-1][0], row[-1][-1] + 1)
else:
row.append((x, 0))
else:
row.append((x, 0))
dots.append(row)
return dots
def connect_dots(dots):
print("Connecting contour points....")
contours = []
for y in range(len(dots)):
for x, v in dots[y]:
if v > -1:
if y == 0:
contours.append([(x, y)])
else:
closest = -1
cdist = 100
for x0, v0 in dots[y - 1]:
if abs(x0 - x) < cdist:
cdist = abs(x0 - x)
closest = x0
if cdist > 3:
contours.append([(x, y)])
else:
found = 0
for i in range(len(contours)):
if contours[i][-1] == (closest, y - 1):
contours[i].append((x, y,))
found = 1
break
if found == 0:
contours.append([(x, y)])
for c in contours:
if c[-1][1] < y - 1 and len(c) < 4:
contours.remove(c)
return contours
def hatch(image):
print("Hatching....")
PX = image.load()
width, height = image.size
lg1 = []
lg2 = []
for x0 in range(width):
for y0 in range(height):
x = x0 * argument.hatch_size
y = y0 * argument.hatch_size
if PX[x0, y0] > 144:
pass
elif PX[x0, y0] > 64:
lg1.append([(x, y + argument.hatch_size / 4), (x + argument.hatch_size, y + argument.hatch_size / 4)])
elif PX[x0, y0] > 16:
lg1.append([(x, y + argument.hatch_size / 4), (x + argument.hatch_size, y + argument.hatch_size / 4)])
lg2.append([(x + argument.hatch_size, y), (x, y + argument.hatch_size)])
else:
lg1.append([(x, y + argument.hatch_size / 4), (x + argument.hatch_size, y + argument.hatch_size / 4)])
lg1.append([(x, y + argument.hatch_size / 2 + argument.hatch_size / 4),
(x + argument.hatch_size, y + argument.hatch_size / 2 + argument.hatch_size / 4)])
lg2.append([(x + argument.hatch_size, y), (x, y + argument.hatch_size)])
lines = [lg1, lg2]
for k in range(0, len(lines)):
for i 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][-1] == lines[k][j][0]:
lines[k][i] = lines[k][i] + lines[k][j][1:]
lines[k][j] = []
lines[k] = [l for l in lines[k] if len(l) > 0]
lines = lines[0] + lines[1]
for i in range(0, len(lines)):
for j in range(0, len(lines[i])):
lines[i][j] = int(lines[i][j][0] + argument.hatch_size * perlin.noise(i * 0.5, j * 0.1, 1)), int(
lines[i][j][1] + argument.hatch_size * perlin.noise(i * 0.5, j * 0.1, 2)) - j
return lines
def make_svg(lines):
print("Generating SVG file....")
out = '<svg xmlns="http://www.w3.org/2000/svg" version="1.1">'
for l in lines:
l = ",".join([str(p[0] * 0.5) + "," + str(p[1] * 0.5) for p in l])
out += '<polyline points="' + l + '" stroke="black" stroke-width="2" fill="none" />\n'
out += '</svg>'
return out

Wyświetl plik

@ -1,6 +1,6 @@
from random import *
from PIL import Image, ImageDraw, ImageOps
from util import *
from linedraw.util import *
def sortlines(lines):

3
requirements.txt 100644
Wyświetl plik

@ -0,0 +1,3 @@
Pillow
Numpy
opencv-python