-
Notifications
You must be signed in to change notification settings - Fork 1
/
preview.py
148 lines (112 loc) · 4.58 KB
/
preview.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
import argparse
import cv2
import glob
import numpy
import os
import pathlib
IMAGE_EXTENSIONS = ("jpg", "jpeg", "png")
SUFFIX = "_preview"
# For the Betsy laser cutter at the Cambridge Makespace, and with laser plywood
# from Slec, use either spillovers of 3 and 3 and a burn of 0.38, or spillovers
# of 5 and 5 and a burn of 0.13
X_SPILLOVER = 5
Y_SPILLOVER = 5
BURN = 0.13
def get_args():
"""This function parses and return arguments passed in"""
# Assign description to the help doc
parser = argparse.ArgumentParser(
description='Preview result of bitmaps engraved in laser-cutter')
# Add arguments
parser.add_argument("-f", "--filepath", required=True)
parser.add_argument("-x", "--x_spillover", default=X_SPILLOVER)
parser.add_argument("-y", "--y_spillover", default=Y_SPILLOVER)
parser.add_argument("-b", "--burn", default=0.38)
args = parser.parse_args()
return args
def generate_influence(x_pad, y_pad, burn):
"""Creates an image with the burn of the black pixels."""
# Initialize output
rows = 1 + 2 * y_pad
cols = 1 + 2 * x_pad
influence = numpy.zeros((rows, cols), dtype = numpy.float32)
# The semi-axes of the ellipsis equal an increment of x_pad and y_pad
# which lie beyond the span of the filter and equal 0
x_axis = x_pad + 1
y_axis = y_pad + 1
# Assign to each pixel the gradient of its distance to center
for col in range(cols):
for row in range(rows):
distance = ((row - y_pad)**2 / y_axis**2 +
(col - x_pad)**2 / x_axis**2)
#value = max(0, 1 - distance)**3 / 9
if distance == 0:
influence[row, col] = 1
elif distance < 1:
influence[row, col] = burn * (1 - distance)
return influence
def convolution(img, influence):
influence_rows, influence_cols = influence.shape
if 0 == influence_rows % 2:
raise ValueError("The influence should have an odd number of rows")
if 0 == influence_cols % 2:
raise ValueError("The influence should have an odd number of columns")
# Pad image with white
pad_x = influence_cols // 2
pad_y = influence_rows // 2
padded = cv2.copyMakeBorder(img, pad_y, pad_y, pad_x, pad_x,
cv2.BORDER_CONSTANT, value = 255)
# The ouptut contains the amount of char, or black
img_rows, img_cols = img.shape
output = numpy.zeros((img_rows, img_cols), dtype = numpy.uint8)
for i in range(img_rows):
for j in range(img_cols):
# Extract region of interest
roi = padded[i:i + 2 * pad_y + 1, j:j + 2 * pad_x + 1]
# Convert to burn on percent scale
area = 1 - roi / 255
# Convolve, with maximum at 100% black
burn_spread = min(1, (influence * area).sum())
# Store output back to white
output[i, j] = int(255 * (1 - burn_spread))
return output
def process_image(filepath, x = X_SPILLOVER, y = Y_SPILLOVER, burn = BURN):
new_filepath = os.path.splitext(filepath)[0] + SUFFIX + ".png"
if os.path.exists(new_filepath):
#raise ValueError("The destination file %s already exists" % new_filepath)
# TODO(mmorin): remove this
pass
# X and Y should be odd, otherwise increment
x_odd = x if 0 != x % 2 else x + 1
y_odd = y if 0 != y % 2 else y + 1
x_pad = x_odd // 2
y_pad = y_odd // 2
influence = generate_influence(x_pad = x_pad, y_pad = y_pad, burn = burn)
# Save filter to disk
cv2.imwrite("images/filter.png", (255 * (1 - influence)).astype(numpy.uint8))
# Read image as grayscale
img = cv2.imread(filepath, cv2.IMREAD_GRAYSCALE)
# Convolution
output = convolution(img, influence)
# Save
cv2.imwrite(new_filepath, output)
return new_filepath
def main():
args = get_args()
filepath = args.filepath
x_spillover = args.x_spillover
y_spillover = args.y_spillover
burn = float(args.burn)
p = pathlib.Path(filepath)
if not p.exists():
raise ValueError("The source file or directory %s does not exist" %filepath)
if not p.is_dir():
process_image(filepath, x = x_spillover, y = y_spillover, burn = burn)
else:
for f in glob.glob(os.path.join(filepath, "*.*")):
if (f.lower().endswith(IMAGE_EXTENSIONS) and
not os.path.splitext(f)[0].endswith(SUFFIX)):
print("Processing %s" % f)
process_image(f, x = x_spillover, y = y_spillover, burn = burn)
if "__main__" == __name__:
main()