Warning: file_get_contents(/data/phpspider/zhask/data//catemap/0/mercurial/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
使用端点和凸出距离绘制圆弧。在OpenCV或PIL中_Opencv_Image Processing_Python Imaging Library_Dxf - Fatal编程技术网

使用端点和凸出距离绘制圆弧。在OpenCV或PIL中

使用端点和凸出距离绘制圆弧。在OpenCV或PIL中,opencv,image-processing,python-imaging-library,dxf,Opencv,Image Processing,Python Imaging Library,Dxf,使用脚本将dxf转换为png,我需要绘制只有三个参数的圆弧,即圆弧起点、圆弧终点和凸出距离 我已经检查了OpenCV和PIL,它们需要开始和结束角度来绘制此弧。我可以使用一些几何图形找出这些角度,但我想知道是否还有其他解决方案我遗漏了。定义圆弧的信息有三条:圆上的两点(定义圆的弦)和凸出距离(称为圆弧的矢状线) 请参见下图: 这里s是矢状,l是弦长的一半,r当然是半径。其他重要的未标记位置是弦与圆相交的点、矢状与圆相交的点以及半径从其延伸的圆心 对于OpenCV的功能,我们将使用以下原型: c

使用脚本将dxf转换为png,我需要绘制只有三个参数的圆弧,即圆弧起点、圆弧终点和凸出距离


我已经检查了OpenCV和PIL,它们需要开始和结束角度来绘制此弧。我可以使用一些几何图形找出这些角度,但我想知道是否还有其他解决方案我遗漏了。

定义圆弧的信息有三条:圆上的两点(定义圆的弦)和凸出距离(称为圆弧的矢状线)

请参见下图:

这里s是矢状,l是弦长的一半,r当然是半径。其他重要的未标记位置是弦与圆相交的点、矢状与圆相交的点以及半径从其延伸的圆心

对于OpenCV的功能,我们将使用以下原型:

cv2.ellipse(img, center, axes, angle, startAngle, endAngle, color[, thickness[, lineType[, shift]]]) → img
其中大多数参数由下图描述:

因为我们画的是圆弧,而不是椭圆弧,长轴/短轴的大小相同,旋转时没有区别,所以轴的大小就是
(半径,半径)
,角度应该为零以简化。然后,我们需要的唯一参数是圆心、半径以及与弦点对应的绘图的开始角和结束角。角度很容易计算(它们只是圆上的一些角度)。所以最终我们需要找到圆的半径和中心

求半径和圆心和求圆的方程是一样的,所以有很多方法。但是因为我们在这里编程,IMO最简单的方法是定义圆上的第三个点,通过箭头与圆的接触点,然后从这三个点求解圆

首先,我们需要得到弦的中点,得到一条与该中点垂直的线,并将其延伸到矢状面的长度,以到达第三点,但这很容易。我将开始给定
pt1=(x1,y1)
pt2=(x2,y2)
作为我在圆上的两点,
sagitta
是“凸出深度”(即您拥有的参数):

现在我们得到了圆上的第三个点。请注意,矢状线只是一段长度,所以它可以向任意方向移动——如果矢状线为负,它将从弦向一个方向移动,如果矢状线为正,它将向另一个方向移动。不确定这是不是给你的距离

然后我们就可以简单地

最后,由于我们需要开始和结束角度来使用OpenCV绘制椭圆,因此我们可以使用
atan2()
来获取从中心到初始点的角度:

# calculate angles of pt1 and pt2 from center of circle
pt1_angle = 180*np.arctan2(y1 - cy, x1 - cx)/np.pi
pt2_angle = 180*np.arctan2(y2 - cy, x2 - cx)/np.pi
因此,我将所有这些打包为一个函数:

def convert_arc(pt1, pt2, sagitta):

    # extract point coordinates
    x1, y1 = pt1
    x2, y2 = pt2

    # find normal from midpoint, follow by length sagitta
    n = np.array([y2 - y1, x1 - x2])
    n_dist = np.sqrt(np.sum(n**2))

    if np.isclose(n_dist, 0):
        # catch error here, d(pt1, pt2) ~ 0
        print('Error: The distance between pt1 and pt2 is too small.')

    n = n/n_dist
    x3, y3 = (np.array(pt1) + np.array(pt2))/2 + sagitta * n

    # calculate the circle from three points
    # see https://math.stackexchange.com/a/1460096/246399
    A = np.array([
        [x1**2 + y1**2, x1, y1, 1],
        [x2**2 + y2**2, x2, y2, 1],
        [x3**2 + y3**2, x3, y3, 1]])
    M11 = np.linalg.det(A[:, (1, 2, 3)])
    M12 = np.linalg.det(A[:, (0, 2, 3)])
    M13 = np.linalg.det(A[:, (0, 1, 3)])
    M14 = np.linalg.det(A[:, (0, 1, 2)])

    if np.isclose(M11, 0):
        # catch error here, the points are collinear (sagitta ~ 0)
        print('Error: The third point is collinear.')

    cx = 0.5 * M12/M11
    cy = -0.5 * M13/M11
    radius = np.sqrt(cx**2 + cy**2 + M14/M11)

    # calculate angles of pt1 and pt2 from center of circle
    pt1_angle = 180*np.arctan2(y1 - cy, x1 - cx)/np.pi
    pt2_angle = 180*np.arctan2(y2 - cy, x2 - cx)/np.pi

    return (cx, cy), radius, pt1_angle, pt2_angle
使用这些值,您可以使用OpenCV的
eliple()
函数创建圆弧。但是,这些都是浮点值
ellipse()
确实允许您使用
shift
参数绘制浮点值,但如果您不熟悉它,则会有点奇怪,因此我们可以借用该解决方案来定义函数

def draw_ellipse(
        img, center, axes, angle,
        startAngle, endAngle, color,
        thickness=1, lineType=cv2.LINE_AA, shift=10):
    # uses the shift to accurately get sub-pixel resolution for arc
    # taken from https://stackoverflow.com/a/44892317/5087436
    center = (
        int(round(center[0] * 2**shift)),
        int(round(center[1] * 2**shift))
    )
    axes = (
        int(round(axes[0] * 2**shift)),
        int(round(axes[1] * 2**shift))
    )
    return cv2.ellipse(
        img, center, axes, angle,
        startAngle, endAngle, color,
        thickness, lineType, shift)
然后,要使用这些函数,只需执行以下操作:

img = np.zeros((500, 500), dtype=np.uint8)
pt1 = (50, 50)
pt2 = (350, 250)
sagitta = 50

center, radius, start_angle, end_angle = convert_arc(pt1, pt2, sagitta)
axes = (radius, radius)
draw_ellipse(img, center, axes, 0, start_angle, end_angle, 255)
cv2.imshow('', img)
cv2.waitKey()

再次注意,负矢状线给出了另一个方向的弧:

center, radius, start_angle, end_angle = convert_arc(pt1, pt2, sagitta)
axes = (radius, radius)
draw_ellipse(img, center, axes, 0, start_angle, end_angle, 255)
center, radius, start_angle, end_angle = convert_arc(pt1, pt2, -sagitta)
axes = (radius, radius)
draw_ellipse(img, center, axes, 0, start_angle, end_angle, 127)
cv2.imshow('', img)
cv2.waitKey()


最后,为了扩展,我在
convert\u arc()
函数中发现了两个错误案例。第一:

if np.isclose(n_dist, 0):
    # catch error here, d(pt1, pt2) ~ 0
    print('Error: The distance between pt1 and pt2 is too small.')
这里的错误是因为我们需要得到一个单位向量,所以我们需要除以不能为零的长度。当然,只有当
pt1
pt2
是同一点时才会发生这种情况,因此您可以在函数顶部检查它们是否唯一,而不是在此处检查

第二:

if np.isclose(M11, 0):
    # catch error here, the points are collinear (sagitta ~ 0)
    print('Error: The third point is collinear.')

这里只发生在三个点共线的情况下,这只发生在矢状为0的情况下。同样,您可以在函数顶部检查此项(可能会说,好吧,如果它是0,那么只需从
pt1
pt2
或您想做的任何事情划一条线即可)。

在没有给出任何理由的情况下否决一个问题是很糟糕的。让我知道原因,如果需要,我可以更新问题。使用几何有什么错?这并不是一件需要大量处理的事情,它只是一些操作和完成。请注意,至少对于OpenCV,其想法是绘制一个椭圆,但仅从一个起始角度到一个结束角度(这毕竟是圆弧)。如果您发布尝试上述解决方案的代码(使用您喜欢的任何库),并发布您对此有何问题?取一个向量(长度为给定的喇叭距离),从端点形成的直线的中间垂直延伸,以获得圆上的第三个点。众所周知,通过这三个点,可以计算通过这些点的圆的圆心和半径(即圆弧)。然后使用圆心,您可以简单地计算到原始端点的角度,给出角度、中心、半径等,以使用OpenCV或PIL绘制圆弧。感谢@AlexanderReynolds指出此解决方案。我是用椭圆曲线来解的,但是把三个点放在一个圆上(半径和圆心不同)的想法要好得多。谢谢你的精确解。
if np.isclose(n_dist, 0):
    # catch error here, d(pt1, pt2) ~ 0
    print('Error: The distance between pt1 and pt2 is too small.')
if np.isclose(M11, 0):
    # catch error here, the points are collinear (sagitta ~ 0)
    print('Error: The third point is collinear.')