def trace_it(img, geoimg, turdsize=10.0): """ Trace raster image using potrace """ potrace.Bitmap(img) bmp = potrace.Bitmap(img) path = bmp.trace(turdsize=turdsize, turnpolicy=potrace.TURNPOLICY_WHITE) geojson = to_geojson(path, geoimg) return geojson
def gen_gcode(self): print('generate gcode...') # bmp=potrace.Bitmap(self.pre[:,:]) # binary fill bmp = potrace.Bitmap(self.pre[:, :, 0]) path = bmp.trace() flag = 0 for curve in path: ratio = self.x_max / max(self.w, self.h) #normalize for drawing machine self.gcode.append('M280 P0 S60') #抬筆 self.gcode.append('G0 X%.4f Y%.4f' % (curve.start_point[0] * ratio, curve.start_point[1] * ratio)) #移動到起始點 self.gcode.append('M280 P0 S0') #下筆 for segment in curve: # print(segment) if segment.is_corner: self.gcode.append('G1 X%.4f Y%.4f' % (segment.c[0] * ratio, segment.c[1] * ratio)) #畫至corner的轉角點 self.gcode.append( 'G1 X%.4f Y%.4f' % (segment.end_point[0] * ratio, segment.end_point[1] * ratio)) #畫至corner的終點 else: self.gcode.append( 'G1 X%.4f Y%.4f' % (segment.end_point[0] * ratio, segment.end_point[1] * ratio)) #畫至Bezier segment的終點 self.gcode.append('M280 P0 S60') #抬筆 return self.gcode
def trace_image(data, transform): im1 = Image.open(BytesIO(base64.b64decode(data))) # convert to b&w im2 = im1.convert("L").point(lambda x: 255 if x > 254 else 0, mode="1") bmp = potrace.Bitmap(np.array(im2)) path = bmp.trace() dwg = svgwrite.Drawing(viewBox=(f"0 0 {im1.width} {im1.height}")) # TODO: as multiple svg paths? along these lines.. # for curve in path: # elements = ["M", *curve.start_point] # for p in curve.tesselate(): # elements.extend(["L", *p]) # dwg.add(dwg.path(d=elements, stroke='black', stroke_width='1', fill='white')) # as single path elements = [] for curve in path: elements.extend([ "M", *apply_transform(transform, np.array([curve.start_point]))[0] ]) for p in apply_transform(transform, curve.tesselate()): elements.extend(["L", *p]) if elements: return dwg.path(d=elements, stroke="black", stroke_width="1", fill="white").tostring()
def traceBin(self, imgBin, color): #tiny erosion reduces color overlap and #gets rid of tiny points that can occur on middle layers imgBin = cv2.erode(imgBin,kernel5) size = np.shape(imgBin) #pad a border around the binary image. This will allow the erosions to #erode away from the edge of the canvas imgBinPadded = np.zeros((size[0]+2, size[1]+2), dtype=np.uint8) imgBinPadded[1:-1,1:-1] = imgBin while True: bmp = potrace.Bitmap(imgBinPadded) #bitmap in preparation for potrace path = bmp.trace() #trace it if (path.curves == []): #check for blank break for curve in path: #tessellate aka break into line segments #yes, their function is mispelled tessellation = curve.tesselate() #uses the default 'adaptive' interpolation #and now put coords into the array if (color == 0): self.addArray0(tessellation) elif (color == 1): self.addArray1(tessellation) else: self.addArray2(tessellation) imgBinPadded = cv2.erode(imgBinPadded, kernel9) #go one layer deeper
def convert_to_3_stroke(im, eps=None): """ params: im image method: 1) dilate and erode the image to group line segments together 2) convert to bitmap 3) trace bitmap to SVG 4) convert SVG to 3-stroke format returns: strokes 3-stroke format """ # black background, white sketch _, thresh = cv2.threshold(im, 200, 255, cv2.THRESH_BINARY_INV) # dilate -> erode kernel = np.ones((3, 3), np.uint8) im = cv2.morphologyEx(thresh, cv2.MORPH_CLOSE, kernel) # Get inverted boolean matrix: # potrace only works with boolean images data = im == 0 # Create a bitmap from the array bmp = potrace.Bitmap(data) path = bmp.trace() # plt.imshow(data, cmap=plt.cm.gray) # get the xy coordinates for each curve lines = [] for i, curve in enumerate(path): # for some reason, the first curve # is always weird if i == 0: continue line = curve.tesselate() # perform Ramer-Douglas-Peuker algorithm if eps: line = rdp(line, epsilon=eps) x, y = line.T plt.plot(x, y, c='red') lines.append(line) plt.show() # get the 3 stroke format strokes = lines_to_strokes(lines) return strokes
def trace_image(filecontents): output = StringIO() output.write(filecontents) _image = Image.open(output) pixels = posturize(_image) output_paths = [] attributes = [] for color in pixels: data = zeros(_image.size, uint32) for pixel in pixels[color]: data[pixel[0], pixel[1]] = 1 # Create a bitmap from the array bmp = potrace.Bitmap(data) # Trace the bitmap to a path path = bmp.trace() # Iterate over path curves for curve in path: svg_paths = [] start_point = curve.start_point true_start = curve.start_point for segment in curve: if true_start is None: true_start = segment.start_point if start_point is None: start_point = segment.start_point if isinstance(segment, BezierSegment): svg_paths.append( CubicBezier( start=start_point[1] + 1j * start_point[0], control1=segment.c1[1] + segment.c1[0] * 1j, control2=segment.c2[1] + segment.c2[0] * 1j, end=segment.end_point[1] + 1j * segment.end_point[0])) elif isinstance(segment, CornerSegment): svg_paths.append( Line(start=start_point[1] + 1j * start_point[0], end=segment.c[1] + segment.c[0] * 1j)) svg_paths.append( Line(start=segment.c[1] + segment.c[0] * 1j, end=segment.end_point[1] + 1j * segment.end_point[0])) else: print("not sure what to do with: ", segment) start_point = segment.end_point # is the path closed? if true_start == start_point: output_paths.append(Path(*svg_paths)) color = pixel[2] rgb = "#%02x%02x%02x" % (color[0], color[1], color[2]) fill = rgb attributes.append({"fill": fill, "stroke": rgb}) true_start = None start_point = None svg_paths = [] return output_paths, attributes
def get_potrace_path(data): """get path from raster image data Args: data (array): M*N array, white and black image """ # Create a bitmap from the array bmp = potrace.Bitmap(data) # Trace the bitmap to a path path = bmp.trace() return path
def addImageData(self, data, lineColor): """ Convert data into potrace Bitmap and store internally """ if self.xPx is not None and self.yPx is not None: if data.shape[0] != self.xPx or data.shape[1] != self.yPx: print 'New image dimensions do not match previous image dimensions!' return # TODO: Perhaps raise an exception of some sort here. else: self.xPx = data.shape[0] self.yPx = data.shape[1] self.imData.append(potrace.Bitmap(data)) self.lineColorData.append(lineColor)
def potrace_array(arr, turdsize=10.0, tolerance=0.2): """ Trace numpy array using potrace """ bmp = _potrace.Bitmap(arr) polines = bmp.trace(turdsize=turdsize, turnpolicy=_potrace.TURNPOLICY_WHITE, alphamax=0.0, opticurve=1.0, opttolerance=tolerance) lines = [] for line in polines: lines.append(line.tesselate().tolist()) return lines
def potrace_array(arr, minsize=10.0, tolerance=0.2, alphamax=0.0, opticurve=1): """ Trace numpy array using potrace """ # tolerance, alphamax, and opticurve are best kept at their defaults bmp = _potrace.Bitmap(arr) logger.debug('potrace: minsize=%s, tolerance=%s, alphamax=%s, opticurve=%s' % (minsize, tolerance, alphamax, opticurve)) polines = bmp.trace(turdsize=minsize, turnpolicy=_potrace.TURNPOLICY_WHITE, alphamax=alphamax, opticurve=opticurve, opttolerance=tolerance) lines = [] for line in polines: lines.append(line.tesselate().tolist()) return lines
def svg2img(): svg_code = """ <svg xmlns="http://www.w3.org/2000/svg" width="1625" height="814"> <g transform="translate(19.48441247002388,178.6258652637545) scale(1586.0311750599524) rotate(0)"> <path stroke="#37946e" stroke-alignment="inner" fill="none" id="MAIN_0" stroke-width="0.03" stroke-linecap="round" opacity="1" mask="url(#eraser_2)" d="M0.5852174165288777,0.07800307487018826C0.5942457382105804,0.07630222686353572,0.604320289308541,0.07315967085351822,0.6291822585791779,0.06936443013372785C0.6540442278498148,0.06556918941393748,0.7006294781423144,0.05889536738897108,0.7343892321526992,0.05523163055144606C0.7681489861630841,0.05156789371392104,0.8040002320656746,0.049213394057184855,0.8317407826414875,0.04738200910857773C0.8594813332173005,0.04555062415997061,0.888008973206084,0.0450284763921523,0.9008325356075769,0.044243320859803315"></path> </g> </svg> """ svg2png(bytestring=svg_code, write_to='output.png') im = imageio.imread('output.png') bmp = potrace.Bitmap(im) path = bmp.trace() return path
def binary_image_to_svg2(mask): """ same as binary_image_to_svg, but use `pypotrace` instead of calling `potrace` from shell it is more convenient and faster, this way """ import potrace bmp = potrace.Bitmap(mask) bmp.trace() xml = bmp.to_xml() fo = StringIO() fo.write(xml) fo.seek(0) paths = svg2paths2(fo) return paths
def FromMask(maskImage): outputSplines = [] bmp = potrace.Bitmap(maskImage.astype(np.float)) # Trace the bitmap to a path path = bmp.trace(alphamax=1.33, opticurve=1, opttolerance=1) # Iterate over path curves for curve in path: contour = curve.tesselate(potrace.Curve.regular, 4) sp = SplineInterpROIClass() sp.addKnots(np.round(contour), False) outputSplines.append(sp) return outputSplines
def main(): #cats() myTracer = Tracer() # Make a numpy array with a rectangle in the middle layer = cv2.imread("../imageFiltering/binIm/layer1.png",cv2.CV_LOAD_IMAGE_GRAYSCALE) # Create a bitmap from the array bmp = potrace.Bitmap(layer) # Trace the bitmap to a path path = bmp.trace() for curve in path: #tessellate aka break into line segments #yes, their function is mispelled tessellation = curve.tesselate() #uses the default 'adaptive' interpolation myTracer.addArray(tessellation) outputFileName = '../MATLAB/pythonOutput2.txt' if os.path.isfile(outputFileName): os.remove(outputFileName) file = open(outputFileName, 'w') file.write("commands0 = ") myTracer.writeToFile(file, myTracer.commands) file.write("xcoords0 = ") myTracer.writeToFile(file, myTracer.xcoords) file.write("ycoords0 = ") myTracer.writeToFile(file, myTracer.ycoords) file.write("commands1 = [C]") #mySVG.writeToFile(file, mySVG.commands1) file.write("xCoords1 = [1]") #mySVG.writeToFile(file, mySVG.xCoords1) file.write("yCoords1 = [0]") #mySVG.writeToFile(file, mySVG.yCoords1) file.write("commands2 = [C]") #mySVG.writeToFile(file, mySVG.commands2) file.write("xCoords2 = [2]") #mySVG.writeToFile(file, mySVG.xCoords2) file.write("yCoords2 =[0]") #mySVG.writeToFile(file, mySVG.yCoords2) file.close()
def raster_to_bezier(self, mono_data): bmp = potrace.Bitmap(mono_data) path = bmp.trace() shapes = [] for curve in path: bezier = objects.Bezier() shapes.append(bezier) bezier.add_point(NVector(*curve.start_point)) for segment in curve: if segment.is_corner: bezier.add_point(NVector(*segment.c)) bezier.add_point(NVector(*segment.end_point)) else: sp = NVector(*bezier.vertices[-1]) ep = NVector(*segment.end_point) c1 = NVector(*segment.c1) - sp c2 = NVector(*segment.c2) - ep bezier.out_tangents[-1] = c1 bezier.add_point(ep, c2) return shapes
def test_tesselate(): # Make a circle data = np.zeros((32, 32), np.uint32) radius2 = 8 * 8 for j in range(32): y = j - 16 for i in range(32): x = i - 16 if x * x + y * y > radius2: data[j, i] = 0 else: data[j, i] = 1 # Trace it bmp = potrace.Bitmap(data) path = bmp.trace() out = Image.new("RGB", (32, 32), (0, 0, 0)) ImageDraw.Draw(out) curve = path.curves[0] points = curve.tesselate(curve.adaptive) assert np.abs(points - adaptive).sum() < 1e-6 points = curve.tesselate(curve.regular, res=10) assert np.abs(points - regular).sum() < 1e-6
def get_trace(data): for i in range(len(data)): data[i][data[i] > 1] = 1 bmp = potrace.Bitmap(data) path = bmp.trace(2, potrace.TURNPOLICY_MINORITY, 1.0, 1, .5) return path
import potrace import numpy as np data = np.zeros((32, 32), np.uint32) data[8:32-8, 8:32-8] = 1 # Create a bitmap from the array bmp = potrace.Bitmap(data) # Trace the bitmap to a path path = bmp.trace() # Iterate over path curves for curve in path: print ("start_point =", curve.start_point) for segment in curve: print (segment) end_point_x, end_point_y = segment.end_point if segment.is_corner: c_x, c_y = segment.c else: c1_x, c1_y = segment.c1 c2_x, c2_y = segment.c2
def get_trace(data): for i in range(len(data)): data[i][data[i] > 1] = 1 bmp = potrace.Bitmap(data) path = bmp.trace() return path
def convert(self, page_number): """Returns SVG string of the given page. Parameters ---------- page_number : int page number to convert Returns ------- string an SVG string """ dwg = svgwrite.Drawing('dummy.svg', profile='full', size=(fileformat.PAGE_WIDTH, fileformat.PAGE_HEIGHT)) vo_only_bg = ImageConverter.build_visibility_overlay( background=VisibilityOverlay.VISIBLE, main=VisibilityOverlay.INVISIBLE, layer1=VisibilityOverlay.INVISIBLE, layer2=VisibilityOverlay.INVISIBLE, layer3=VisibilityOverlay.INVISIBLE) bg_img = self.image_converter.convert(page_number, visibility_overlay=vo_only_bg) buffer = BytesIO() bg_img.save(buffer, format='png') bg_b64str = base64.b64encode(buffer.getvalue()).decode('ascii') dwg.add(dwg.image('data:image/png;base64,' + bg_b64str, insert=(0, 0), size=(fileformat.PAGE_WIDTH, fileformat.PAGE_HEIGHT))) vo_except_bg = ImageConverter.build_visibility_overlay(background=VisibilityOverlay.INVISIBLE) img = self.image_converter.convert(page_number, visibility_overlay=vo_except_bg) def generate_color_mask(img, c): mask = img.copy().convert('L') return mask.point(lambda x: 0 if x == c else 1, mode='1') default_palette = color.DEFAULT_COLORPALETTE default_color_list = [default_palette.black, default_palette.darkgray, default_palette.gray, default_palette.white] user_color_list = [self.palette.black, self.palette.darkgray, self.palette.gray, self.palette.white] for i, c in enumerate(default_color_list): user_color = user_color_list[i] mask = generate_color_mask(img, c) # create a bitmap from the array bmp = potrace.Bitmap(mask) # trace the bitmap to a path path = bmp.trace() # iterate over path curves if len(path) > 0: svgpath = dwg.path(fill=color.web_string(user_color, mode=self.palette.mode)) for curve in path: start = curve.start_point svgpath.push("M", start.x, start.y) for segment in curve: end = segment.end_point if segment.is_corner: c = segment.c svgpath.push("L", c.x, c.y) svgpath.push("L", end.x, end.y) else: c1 = segment.c1 c2 = segment.c2 svgpath.push("C", c1.x, c1.y, c2.x, c2.y, end.x, end.y) svgpath.push("Z") dwg.add(svgpath) return dwg.tostring()
#Single threshold return (gradSup > Threshold) maxX = 9000 maxY = 7000 # Make a grayscale numpy array gray = misc.imread("download.png",False,'L') gray = CannyEdgeDetector(gray) height = gray.shape[0] width = gray.shape[1] scaleFactor = min(float(maxX)/width,float(maxY)/height) misc.imsave('sobel.png',gray/np.amax(gray)) # quit() # Create a bitmap from the array bmp = potrace.Bitmap(gray/np.amax(gray)) # Trace the bitmap to a path path = bmp.trace(alphamax=0) msg = "D 0\n" ser.write(msg) while (ser.readline().strip("\n\r\x00") != msg.strip("\n\r")): ser.write(msg) # Iterate over path curves first = 0 for curve in path: # skip first path, which is the border if first == 0: first = 1 continue
def get_image_cuve_paths(image_path = None): im = get_edge_detected_image(image_path) bmp = potrace.Bitmap(im) return bmp.trace(alphamax=0)
x, y = np.where(erode > 100) pointcoords = [] for x, y in zip(x.tolist(), y.tolist()): if x * density < 400: continue p0 = round(y * density - fram.shape[1] / 2, 2) p1 = round(x * density - fram.shape[0] / 2, 2) pointcoords.append([p0, p1]) print(len(pointcoords), 'pointcoords') cv2.imshow('e', erode) # cv2.waitKey(0) # Create bitmap from the array fram = fram > 100 bmp = potrace.Bitmap(fram) path = bmp.trace(alphamax=0, opttolerance=1e10) # Find the start and end position of each segment in the octopus # Box2D wants a center, width, height, and rotation to draw boxes # So for each segment, this computes the center and rotation offset = (0, 0) ptanglesssss = [] for i, curve in enumerate(path): # x, y = curve.tesselate().T # plt.plot(x, y) # plt.show() points = [] for segment in curve: points.append(segment.c) points.append(segment.end_point)
def png_to_svg(filename): data = png_to_np_array(filename) bmp = potrace.Bitmap(data) path = bmp.trace() return path
def __generateShapeFill(self, bounds, toolConfig): xFillMin = bounds[0] xFillMax = bounds[1] yFillMin = bounds[2] yFillMax = bounds[3] pattern = Image.open(toolConfig.infillPattern) # Filter on small images for better traced outlines if max(pattern.size) < 800: # TODO: Make it possible to force extra filtering pattern = filterLoRez(pattern, 3) # pattern = pattern.convert('P', palette = Image.ADAPTIVE, colors = int(2)) # name = srcImagePath.split('.') # newname = name[0] + "WITHFILTER.png" # pattern.convert('RGB').save(newname) # # return pattern = ImageOps.flip(pattern) # make pattern image into 2 color image for tracing mask, color = extractColors(pattern, 2)[0] bmp = potrace.Bitmap(mask) poopoo = int(math.pi * (((toolConfig.lineWidth * (pattern.size[0]/self.plotWidth)) / 2.0) ** 2)) # trace the image path = bmp.trace(turdsize = poopoo, opttolerance = 0.2) # patternCurves = path.curves_tree[0].tesselate() # extract all outermost outlines patternCurves = [] for c in path.curves_tree: c = c.tesselate() c = np.array(c) c = np.flip(c, 1) patternCurves.append(c) # thumbnailCurve = np.array(thumbnailCurve) # thumbnailCurve = np.flip(thumbnailCurve, 1) # # print thumbnailCurve.shape # Now that the shape is traced, center it about (0, 0) and # scale it to measure 1.0 across the larger axis patternMinX = float('inf') patternMaxX = - float('inf') patternMinY = float('inf') patternMaxY = - float('inf') # find the bounding box for the outlines for c in patternCurves: for p in c: if p[0] < patternMinX: patternMinX = deepcopy(p[0]) elif p[0] > patternMaxX: patternMaxX = deepcopy(p[0]) if p[1] < patternMinY: patternMinY = deepcopy(p[1]) elif p[1] > patternMaxY: patternMaxY = deepcopy(p[1]) patternTransX = (patternMinX + patternMaxX) / 2 patternTransY = (patternMinY + patternMaxY) / 2 patternTrans = np.array([patternTransX, patternTransY]) patternScaleNormalize = max(patternMaxX - patternMinX, patternMaxY - patternMinY) # center the outlines about (0,0) for c in patternCurves: for p in c: p -= patternTrans # p /= patternScaleNormalize patternDimX = patternMaxX - patternMinX patternDimY = patternMaxY - patternMinY fillShapeMax = mapToRange(toolConfig.infillDensity, 100.0, (self.plotWidth, 3.0*toolConfig.lineWidth)) if patternDimX > patternDimY: fillShapeWidth = fillShapeMax fillShapeHeight = (fillShapeMax * patternDimY) / patternDimX else: fillShapeHeight = fillShapeMax fillShapeWidth = (fillShapeMax * patternDimX) / patternDimY # scale the outlines for c in patternCurves: c /= patternScaleNormalize c *= (4 * fillShapeMax) / 5 patternCurvesReduced = [] for c in patternCurves: pattern = c.tolist() patternReduced = [pattern.pop(0)] # reduce the number of segments. Many segments are likely too # small to be resolved and are unnecessary lastPoint = patternReduced[-1] for p in pattern: if distance(p, lastPoint) >= toolConfig.lineWidth / 4.0: lastPoint = p patternReduced.append(p) if not compPt(patternReduced[0], patternReduced[-1]): patternReduced.append(patternReduced[0]) patternCurvesReduced.append(np.array(patternReduced)) shapeFill = [] everyOtherShift = False for i in floatRange(yFillMin, yFillMax, fillShapeHeight): for j in floatRange(xFillMin, xFillMax, fillShapeWidth): if everyOtherShift: j += fillShapeWidth / 2.0 patternTrans = np.array([j, i]) for pattern in deepcopy(patternCurvesReduced): # translate the outline to its proper position for p in pattern: p += patternTrans shapeFill.append(pattern) everyOtherShift = not everyOtherShift # print shapeFill return shapeFill
def conv_image_to_module(name, scale_factor): module = header % {"name": name.upper()} front_image = Image.open("%s_front.png" % name).transpose(Image.FLIP_TOP_BOTTOM) print("Reading image from \"%s_front.png\"" % name) front_image_red, front_image_green, front_image_blue, front_image_alpha = front_image.split() # Soldermask needs to be inverted front_image_red = ImageOps.invert(front_image_red) front_image_red = Image.composite(front_image_red, front_image_alpha, front_image_alpha) front_image_red = front_image_red.point(lambda i: 0 if i < 127 else 1) red_array = np.array(front_image_red) bmp_red = potrace.Bitmap(red_array) path_red = bmp_red.trace(alphamax = 0.0, opttolerance = 50) # Soldermask needs to be inverted front_image_green = ImageOps.invert(front_image_green) front_image_green = Image.composite(front_image_green, front_image_alpha, front_image_alpha) front_image_green = front_image_green.point(lambda i: 0 if i < 127 else 1) green_array = np.array(front_image_green) bmp_green = potrace.Bitmap(green_array) path_green = bmp_green.trace(alphamax = 0.0, opttolerance = 50) front_image_blue = front_image_blue.point(lambda i: 0 if i < 127 else 1) blue_array = np.array(front_image_blue) bmp_blue = potrace.Bitmap(blue_array) path_blue = bmp_blue.trace(alphamax = 0.0, opttolerance = 50) front_image_alpha = front_image_alpha.point(lambda i: 0 if i < 127 else 1) front_image_alpha_array = np.array(front_image_alpha) bmp_alpha = potrace.Bitmap(front_image_alpha_array) path_alpha = bmp_alpha.trace(alphamax = 0.0, opttolerance = 50) w, h = front_image.size # print("Generating Outline layer from front alpha channel") # module += render_path_to_layer(path_alpha, "line", "20", scale_factor) print("Generating tKeepout layer from front red channel") module += render_path_to_layer(path_red, "poly", "39", scale_factor) print("Generating tStop layer from front green channel") module += render_path_to_layer(path_green, "poly", "29", scale_factor) print("Generating tPlace layer from front blue channel") module += render_path_to_layer(path_blue, "poly", "21", scale_factor) try: back_image = Image.open("%s_back.png" % name).transpose(Image.FLIP_TOP_BOTTOM) back_image = ImageOps.mirror(back_image) print("Reading image from \"%s_back.png\"" % name) back_image_red, back_image_green, back_image_blue, back_image_alpha = back_image.split() back_image_red = back_image_red.point(lambda i: 0 if i < 127 else 1) red_array = np.array(back_image_red) bmp_red = potrace.Bitmap(red_array) path_red = bmp_red.trace(alphamax = 0.0, opttolerance = 50) # Soldermask needs to be inverted back_image_green = ImageOps.invert(back_image_green) back_image_green = back_image_green.point(lambda i: 0 if i < 127 else 1) green_array = np.array(back_image_green) bmp_green = potrace.Bitmap(green_array) path_green = bmp_green.trace(alphamax = 0.0, opttolerance = 50) back_image_blue = back_image_blue.point(lambda i: 0 if i < 127 else 1) blue_array = np.array(back_image_blue) bmp_blue = potrace.Bitmap(blue_array) path_blue = bmp_blue.trace(alphamax = 0.0, opttolerance = 50) print("Generating bKeepout layer from back red channel") module += render_path_to_layer(path_red, "poly", "40", scale_factor) print("Generating bStop layer from back green channel") module += render_path_to_layer(path_green, "poly", "30", scale_factor) print("Generating bPlace layer from back blue channel") module += render_path_to_layer(path_blue, "poly", "22", scale_factor) except IOError: pass module += footer % {"name": name.upper()} return module, (w * 25.4 / scale_factor, h * 25.4 / scale_factor)