From 79a7de05945f076893af75bef8e4635ab4add12b Mon Sep 17 00:00:00 2001 From: jaseg Date: Sat, 25 Nov 2017 20:46:52 +0100 Subject: Add better status tracking --- gerbimg.py | 98 ++++++++++++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 73 insertions(+), 25 deletions(-) (limited to 'gerbimg.py') diff --git a/gerbimg.py b/gerbimg.py index 2d3cb72..51d2367 100755 --- a/gerbimg.py +++ b/gerbimg.py @@ -6,9 +6,11 @@ import tempfile import os.path as path import os import sys +import time import shutil import math +import tqdm import gerber from gerber.render import GerberCairoContext import numpy as np @@ -21,6 +23,7 @@ def paste_image( subtract_gerber:list=[], extend_overlay_r_mil:float=12, extend_picture_r_mil:float=2, + status_print=lambda *args:None, debugdir:str=None): debugctr = 0 def debugimg(img, name): @@ -29,66 +32,99 @@ def paste_image( cv2.imwrite(path.join(debugdir, '{:02d}{}.png'.format(debugctr, name)), img) debugctr += 1 + status_print('Parsing outline gerber') outline = gerber.loads(outline_gerber) (minx, maxx), (miny, maxy) = outline.bounds grbw, grbh = maxx - minx, maxy - miny + status_print(' * outline has offset {}, size {}'.format((minx, miny), (grbw, grbh))) imgh, imgw = source_img.shape scale = math.ceil(max(imgw/grbw, imgh/grbh)) # scale is in dpi + status_print(' * source image has size {}, going for scale {}dpi'.format((imgw, imgh), scale)) + status_print('Parsing target gerber') target = gerber.loads(target_gerber) (tminx, tmaxx), (tminy, tmaxy) = target.bounds + status_print(' * target layer has offset {}, size {}'.format((tminx, tminy), (tmaxx-tminx, tmaxy-tminy))) with tempfile.TemporaryDirectory() as tmpdir: img_file = path.join(tmpdir, 'target.png') + status_print('Combining keepout composite') fg, bg = gerber.render.RenderSettings((1, 1, 1)), gerber.render.RenderSettings((0, 0, 0)) ctx = GerberCairoContext(scale=scale) + status_print(' * target layer') ctx.render_layer(target, settings=fg, bgsettings=bg) + status_print(' * outline') ctx.render_layer(outline, settings=fg, bgsettings=bg) - for sub in subtract_gerber: + for i, sub in enumerate(subtract_gerber): + status_print(' * extra layer', i) layer = gerber.loads(sub) ctx.render_layer(layer, settings=fg, bgsettings=bg) + status_print('Rendering keepout composite') ctx.dump(img_file) original_img = cv2.imread(img_file, cv2.IMREAD_GRAYSCALE) + status_print('Expanding keepout composite') r = 1+2*max(1, int(extend_overlay_r_mil/1000 * scale)) target_img = cv2.blur(original_img, (r, r)) _, target_img = cv2.threshold(target_img, 255//(1+r), 255, cv2.THRESH_BINARY) + status_print('Thresholding source image') qr = 1+2*max(1, int(extend_picture_r_mil/1000 * scale)) source_img = source_img[::-1] _, source_img = cv2.threshold(source_img, 127, 255, cv2.THRESH_BINARY) debugimg(source_img, 'thresh') + + status_print('Padding source image') tgth, tgtw = target_img.shape padded_img = np.zeros(shape=(max(imgh, tgth), max(imgw, tgtw)), dtype=source_img.dtype) - offx = int((minx-tminx if tminx < minx else 0)*scale) offy = int((miny-tminy if tminy < miny else 0)*scale) offx += int(grbw*scale - imgw) // 2 offy += int(grbh*scale - imgh) // 2 padded_img[offy:offy+imgh, offx:offx+imgw] = source_img - debugimg(padded_img, 'padded') + + status_print('Padding target image') + padded_target = np.zeros(shape=padded_img.shape, dtype=source_img.dtype) + offx = int(max(tminx, 0)*scale) + offy = int(max(tminy, 0)*scale) + padded_target[offy:offy+tgth, offx:offx+tgtw] = target_img debugimg(target_img, 'target') - out_img = (np.multiply((padded_img/255.0), (target_img/255.0) * -1 + 1) * 255).astype(np.uint8) + debugimg(padded_target, 'target_padded') + + status_print('Masking source image') + out_img = (np.multiply((padded_img/255.0), (padded_target/255.0) * -1 + 1) * 255).astype(np.uint8) debugimg(out_img, 'multiplied') - debugimg(out_img + original_img, 'vis') - plot_contours(out_img, target, offx=(min(tminx, minx), min(tminy, miny)), scale=scale) + status_print('Calculating contour lines') + plot_contours(out_img, + target, + offx=(min(tminx, minx), min(tminy, miny)), + scale=scale, + status_print=lambda *args: status_print(' ', *args)) + status_print('Generating output gerber') from gerber.render import rs274x_backend ctx = rs274x_backend.Rs274xContext(target.settings) target.render(ctx) return ctx.dump().getvalue() -def plot_contours(img:np.ndarray, layer:gerber.rs274x.GerberFile, offx:tuple, scale:float, debug=lambda *args:None): +def plot_contours( + img:np.ndarray, + layer:gerber.rs274x.GerberFile, + offx:tuple, + scale:float, + debug=lambda *args:None, + status_print=lambda *args:None): imgh, imgw = img.shape # Extract contours + status_print('Extracting contours') img_cont_out, contours, hierarchy = cv2.findContours(img, cv2.RETR_TREE, cv2.CHAIN_APPROX_TC89_KCOS) aperture = list(layer.apertures)[0] @@ -108,21 +144,26 @@ def plot_contours(img:np.ndarray, layer:gerber.rs274x.GerberFile, offx:tuple, sc done = [] process_stack = [-1] next_process_stack = [] + parents = [ (i, parent) for i, (_1, _2, _3, parent) in enumerate(hierarchy[0]) ] is_dark = True - while len(done) != len(contours): - for i, (_1, _2, _3, parent) in enumerate(hierarchy[0]): - if parent in process_stack: - contour = contours[i] - polarity = 'dark' if is_dark else 'clear' - debug('rendering {} with parent {} as {} with {} vertices'.format(i, parent, polarity, len(contour))) - debug('process_stack is', process_stack) - debug() - layer.primitives.append(Region(contour_lines(contour[:,0]), level_polarity=polarity, units=layer.settings.units)) - next_process_stack.append(i) - done.append(i) - debug('skipping to next level') - process_stack, next_process_stack = next_process_stack, [] - is_dark = not is_dark + status_print('Converting contours to gerber primitives') + with tqdm.tqdm(total=len(contours)) as progress: + while len(done) != len(contours): + for i, parent in parents[:]: + if parent in process_stack: + contour = contours[i] + polarity = 'dark' if is_dark else 'clear' + debug('rendering {} with parent {} as {} with {} vertices'.format(i, parent, polarity, len(contour))) + debug('process_stack is', process_stack) + debug() + layer.primitives.append(Region(contour_lines(contour[:,0]), level_polarity=polarity, units=layer.settings.units)) + next_process_stack.append(i) + done.append(i) + parents.remove((i, parent)) + progress.update(1) + debug('skipping to next level') + process_stack, next_process_stack = next_process_stack, [] + is_dark = not is_dark debug('done', done) # Utility foo @@ -167,13 +208,13 @@ def replace_file_in_zip(zip_path, filename, contents): zipout.writestr(filename, contents) shutil.move(tempname, zip_path) -def paste_image_file(zip_or_dir, target, outline, source_img, subtract=[], debugdir=None): +def paste_image_file(zip_or_dir, target, outline, source_img, subtract=[], status_print=lambda *args:None, debugdir=None): if path.isdir(zip_or_dir): tname, target = find_gerber_in_dir(zip_or_dir, target) _, outline = find_gerber_in_dir(zip_or_dir, outline) subtract = [ layer for _fn, layer in (find_gerber_in_dir(zip_or_dir, elem) for elem in subtract) ] - out = paste_image(target, outline, source_img, subtract, debugdir=debugdir) + out = paste_image(target, outline, source_img, subtract, debugdir=debugdir, status_print=status_print) # XXX with open('/tmp/out.GTO', 'w') as f: @@ -183,7 +224,7 @@ def paste_image_file(zip_or_dir, target, outline, source_img, subtract=[], debug tname, target = find_gerber_in_zip(zip_or_dir, target) _, outline = find_gerber_in_zip(zip_or_dir, outline) - out = paste_image(target, outline, source_img, debugdir=debugdir) + out = paste_image(target, outline, source_img, subtract, debugdir=debugdir, status_print=status_print) replace_file_in_zip(zip_or_dir, tname, out) else: raise ValueError('{} does not look like either a folder or a zip file') @@ -203,5 +244,12 @@ if __name__ == '__main__': args = parser.parse_args() source_img = cv2.imread(args.source, cv2.IMREAD_GRAYSCALE) - paste_image_file(args.zip_or_dir, args.target, args.outline, source_img, args.subtract, args.debug) + paste_image_file( + args.zip_or_dir, + args.target, + args.outline, + source_img, + args.subtract, + status_print=lambda *args: print(*args, flush=True), + debugdir=args.debug) -- cgit