Skip to content
Snippets Groups Projects
img2nc.py 6.3 KiB
Newer Older
  • Learn to ignore specific revisions
  • Quentin Bolsee's avatar
    Quentin Bolsee committed
    import math
    import numpy as np
    import matplotlib.pyplot as plt
    import cv2
    
    
    def imshow(img, w_max=1200, h_max=700, name="show"):
        h = img.shape[0]
        w = img.shape[1]
        ratio = w / h
        ratio_max = w_max / h_max
    
        if ratio >= ratio_max:
            w_new = w_max
            h_new = int(h * w_new / w)
        else:
            h_new = h_max
            w_new = int(w * h_new / h)
        img_show = cv2.resize(img, (w_new, h_new))
        cv2.imshow(name, img_show)
    
    
    def mill(img_mill, img_keep, dpi, dia_mm, stepover=0.8, once=False):
        global count
        img_mill_bin = np.zeros_like(img_mill)
        img_mill_bin[img_mill >= 127] = 255
    
        img_keep_bin = np.zeros_like(img_keep)
        img_keep_bin[img_keep >= 127] = 255
    
        dpmm = dpi / 25.4
        dia_px = int(dia_mm * dpmm)
    
        print(f"Tool: {dia_mm} mm = {dia_px} px")
        if dia_px < 10:
            print(f"Warning: tool is only {dia_px} pixels wide")
    
        k_step = int(dia_mm * 2 * stepover * dpmm)
        k_tool = int(dia_mm * dpmm)
    
        kernel_step = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (k_step, k_step))
        kernel_tool = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (k_tool, k_tool))
    
        done = False
    
        paths = []
    
        # mask = cv2.dilate(img_mill_bin, kernel_tool)
        # mask = cv2.dilate(mask, kernel_tool)
        img_keep_pad = np.pad(img_keep_bin, pad_width=(k_tool, k_tool), mode="constant", constant_values=0)
    
        mask = np.copy(img_mill_bin)
        # cv2.imwrite(f"mask_init_{count:02d}_pre.png", mask)
        mask = cv2.dilate(mask, kernel_tool)
        # mask = cv2.dilate(mask, kernel_tool)
        mask[img_keep_bin > 0] = 0
        # cv2.imwrite(f"mask_init_{count:02d}.png", mask)
        # mask[img_keep > 0] = 0
        mask = np.pad(mask, pad_width=(k_tool, k_tool), mode="constant", constant_values=0)
    
        h_p, w_p = mask.shape
    
        edge_px = k_tool
    
        mask_diff = np.zeros_like(mask)
        mask_miss = np.zeros_like(mask)
        mask_next = np.zeros_like(mask)
    
        i = 0
        img_miss = np.zeros_like(img_mill_bin)
    
        while not done:
            if i == 0:
                dil = cv2.dilate(img_keep_pad, kernel_tool)
                mask_next[(mask > 0) & ~(dil > 0)] = 255
                mask_next_dil = cv2.dilate(mask_next, kernel_tool)
                missed = (mask > 0) & ~(mask_next_dil > 0)
                img_miss[missed[edge_px:-edge_px, edge_px:-edge_px]] = 255
                # ignore pixels that weren't marked to begin with
                img_miss[img_mill_bin == 0] = 0
            else:
                mask_next[:, :] = cv2.erode(mask, kernel_step)
                mask_diff[~(mask > 0)] = 0
                mask_diff[mask > 0] = 255
                mask_diff[mask_next > 0] = 0
                mask_miss[:, :] = cv2.erode(mask_diff, kernel_tool)
                contours, hierarchy = cv2.findContours(mask, cv2.RETR_TREE, cv2.CHAIN_APPROX_TC89_KCOS)
                for c in contours:
                    c_float = c.astype(np.float32)
                    c_mm = np.zeros_like(c_float)
                    c_mm[:, 0, 0] = (c_float[:, 0, 0] - edge_px) / dpmm
                    c_mm[:, 0, 1] = ((h_p - c_float[:, 0, 1]) - edge_px) / dpmm
                    paths.append(c_mm[:, 0, :])
                if once:
                    done = True
    
            mask[:, :] = mask_next[:, :]
            if i > 0:
                mask[mask_miss > 0] = 255
    
            i += 1
    
            if not np.any(mask > 0):
                done = True
    
            print(i)
    
        return paths, img_miss
    
    
    def add_gcode(f, paths, z_down=0.0, z_up=1.0):
        feedrate_cut = 480.0
        feedrate_plunge = 240.0
    
        for p in paths:
            x = p[0, 0]
            y = p[0, 1]
            f.write(f"G0 X{x:.4f} Y{y:.4f}\n")
            f.write(f"G1 Z{z_down:.4f} F{feedrate_plunge:.4f}\n")
    
            for c in p:
                x, y = c[:]
                f.write(f"G1 X{x:.4f} Y{y:.4f} F{feedrate_cut:.4f}\n")
    
            x = p[0, 0]
            y = p[0, 1]
            f.write(f"G1 X{x:.4f} Y{y:.4f} F{feedrate_cut:.4f}\n")
            f.write(f"G0 Z{z_up:.4f}\n")
    
    
    def main():
        filename_copper = "input/Cu.png"
        filename_edge = "input/Edge.png"
        filename_out = "result.nc"
    
        dpi = 1000.0
    
        img_copper = cv2.imread(filename_copper)
        img_edge = cv2.imread(filename_edge)
    
        if len(img_copper.shape) > 2:
            img_copper = cv2.cvtColor(img_copper, cv2.COLOR_BGR2GRAY)
    
        if len(img_edge.shape) > 2:
            img_edge = cv2.cvtColor(img_edge, cv2.COLOR_BGR2GRAY)
    
        img1 = np.zeros_like(img_edge)
        img1[(img_edge < 127) | (img_copper > 127)] = 255
        img1_keep = np.copy(img1)
        img1_keep[img1_keep >= 127] = 255
        img1_keep[img1_keep < 127] = 0
        img1_mill = 255 - img1_keep
    
        paths1, img_miss1 = mill(img1_mill, img1_keep, 1000, 0.794)
    
        paths2, img_miss2 = mill(img_miss1, img1_keep, 1000, 0.397)
    
        img_edge_keep = np.copy(img_edge)
        img_edge_keep[img_edge_keep >= 127] = 255
        img_edge_keep[img_edge_keep < 127] = 0
        img_edge_mill = 255 - img_edge_keep
        paths3, _ = mill(img_edge_mill, img_edge_keep, dpi, 0.794, once=True)
    
        f = open(filename_out, "w")
    
        f.write("%\n")
        f.write("G17\n")
        f.write("G21\n")
        f.write("G40\n")
        f.write("G49\n")
        f.write("G54\n")
        f.write("G80\n")
        f.write("G90\n")
        f.write("G94\n")
        f.write("T4 M06\n")
        f.write("S16000\n")
        f.write("G0 Z1\n")
        f.write("M03\n")
    
        add_gcode(f, paths2, z_down=-0.12)
    
        f.write("M05\n")
        f.write("T3 M06\n")
        f.write("S16000\n")
        f.write("G0 Z1\n")
        f.write("M03\n")
    
        add_gcode(f, paths1, z_down=-0.12)
    
        add_gcode(f, paths3, z_down=-0.6)
        add_gcode(f, paths3, z_down=-1.2)
        add_gcode(f, paths3, z_down=-1.75)
    
        f.write("M30\n")
        f.write("%\n")
        f.write("M6 T-1\n")
        f.write("M496.1\n")
    
        f.close()
    
        dpmm = dpi / 25.4
    
        h, w = np.shape(img1_mill)
        img_show = np.zeros((h, w, 3), dtype=np.uint8)
        img_show[img1_mill > 0, :] = 150
        img_show[img_miss2 > 0, :] = 0
        img_show[img_miss2 > 0, 0] = 255
    
        plt.figure()
        plt.imshow(img_show)
        for c in paths1:
            plt.plot(c[:, 0] * dpmm,  h-c[:, 1] * dpmm, "b")
            plt.plot([c[-1, 0] * dpmm, c[0, 0] * dpmm],  [h-c[-1, 1] * dpmm, h-c[0, 1] * dpmm], "b")
        for c in paths2:
            plt.plot(c[:, 0] * dpmm,  h-c[:, 1] * dpmm, "g")
            plt.plot([c[-1, 0] * dpmm, c[0, 0] * dpmm],  [h-c[-1, 1] * dpmm, h-c[0, 1] * dpmm], "g")
        for c in paths3:
            plt.plot(c[:, 0] * dpmm,  h-c[:, 1] * dpmm, "b")
            plt.plot([c[-1, 0] * dpmm, c[0, 0] * dpmm],  [h-c[-1, 1] * dpmm, h-c[0, 1] * dpmm], "b")
        plt.axis("equal")
        plt.show()
    
    
    if __name__ == "__main__":
        main()