如何将轮廓扩大特定数量的像素(无需遍历每个像素)?

How to dilate a contour by a specific number of pixels (without iterating over every pixel)?

我正在编写一个 Python 脚本,用于将图像转换为 CNC 机器的 G 代码。 CNC 机器使用直径为 0.250 英寸的圆柱形钻头。我的脚本在图像中找到轮廓,然后将轮廓中的坐标转换为机器的方向。

效果很好,除了雕刻的形状比设计部分小 0.125"。钻头的中心直接在轮廓上,因此生成的形状太小了钻头直径的一半。

我想将每个轮廓扩大 x 像素。 我想制作一个输出图像,其中每个像素都是白色的源图像在输出图像中也是白色的,而且,输入图像中白色像素的 x 像素范围内的每个像素在输出图像中都应该是白色的。

这是我的源图片:

使用 cv2.dilate() 扩大轮廓不会产生我正在寻找的结果,因为它往往会使圆边变成方形。

img = cv2.dilate(img, (15,15), 5)

我尝试逐个像素地遍历图像,然后使用 cv2.pointPolygontest(contour, (x,y), True) 测试到轮廓的距离。这行得通,但是这个 hack 非常慢。

import cv2
import numpy as np

def erode_contours_by_cutter_size(img, contours, cutter_size):
    # Create an output image
    outputImage = np.zeros_like(img)
    # Iterate through every pixel in the image
    for x in range(img.shape[0]):
        for y in range(img.shape[1]):
            # Check if the pixel is black
            if img[y,x] != 255:
                # Check if the distance from this pixel to a contour is smaller than the cutter size
                for contour in contours:
                    dist = cv2.pointPolygonTest(contour, (x,y), True)                    
                    if abs(dist) < cutter_size:
                        outputImage[y,x] = 255
    return outputImage

img = 255-cv2.imread('/home/stephen/Desktop/t2.png',0)
img = cv2.resize(img, (234,234))
cutter_size = 50
contours, _ = cv2.findContours(img, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
img = erode_contours_by_cutter_size(img, contours, cutter_size)
cv2.imshow('img', img)
cv2.waitKey(0)
cv2.destroyAllWindows()

这是输出图像:

这可能不是最干净的解决方案,但它很简单并且适用于所有形状。通过一些额外的工作,您也可以将其用于模板内的切口。所以这可能足以满足您的需求。

找到等高线,在其上画一条 2 倍钻头大小的白线。现在使用 impute 图像的反转版本作为掩码,只留下路由路径 - 就像在输出图像中一样。相反,您还可以组合图像以创建更大的白色形状(如您的问题所述)。

结果:

代码:

import cv2
import numpy as np

bitsize = 100
# load image
img = cv2.imread('PluT1.png',0)
# create empty image
res = np.zeros(img.shape[:2], dtype=np.uint8)
# find countours. use a more complex CHAIN_APPROX if SIMPLE is not enough 
contours, hier = cv2.findContours(img,cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
# draw contour
for cnt in contours:
    cv2.drawContours(res,[cnt],0,(255),bitsize)

#invert input image to create mask
img_inverted = cv2.bitwise_not(img)
# apply mask to get routing path
path = cv2.bitwise_and(res,res, mask=img_inverted)
# join drawn contour and input image to get solid
solid = cv2.bitwise_or(img,res)
# show images
cv2.imshow('Input',img)
cv2.imshow('Path',path)
cv2.imshow('Solid',solid)
cv2.waitKey(0)
cv2.destroyAllWindows()