【问题标题】:Get perimeter of pixels around centre pixel获取中心像素周围的像素周长
【发布时间】:2018-11-16 09:14:35
【问题描述】:

我正在尝试围绕中心像素获取一圈像素。即,就像 FAST 关键点检测器的工作原理一样,我想在给定半径的情况下获得它周围的周边像素。但是数学使我无法理解,理论上我知道如何使用三角函数来获得它。即,我可以使用 for 循环并以 15 度进行迭代。我知道三角形斜边长度是半径,我知道角度。

有什么建议可以获取给定像素周围的像素周长吗?

【问题讨论】:

    标签: python opencv trigonometry


    【解决方案1】:

    公式为:

    (x-cx)**2 + (y-cy)**2 = r**2
    

    其中 cx 和 cy 是圆心,x 和 y 是您要测试的坐标...现在我们可以遍历 x 并使用如下公式获得 y:

    y = sqrt(r**2 - (x-cx)**2) + cy
    

    另一种方法是迭代 360 度并计算 x 和 y 并添加偏移量(中心),如下所示:

    x = cos(radians) * radius + cx
    y = sin(radians) * radius + cy
    

    第二个版本在我的测试中给了我一个更完整的圈子。这是我在 python 中的测试脚本:

    import numpy as np
    import cv2
    import math
    
    img = np.zeros((480, 640, 1), dtype="uint8")
    img2 = np.zeros((480, 640, 1), dtype="uint8")
    
    center = (200, 200)
    radius = 100
    
    x = np.arange(center[0] - radius, center[0]+radius+1)
    y_off = np.sqrt(radius**2 - (x - center[0]) **2)
    y1 = np.int32(np.round(center[1] + y_off))
    y2 = np.int32(np.round(center[1] - y_off))
    img[y1, x] = 255
    img[y2, x] = 255
    
    
    degrees = np.arange(360)
    x = np.int32(np.round(np.cos(degrees) * radius)) + center[0]
    y = np.int32(np.round(np.sin(degrees) * radius)) + center[1]
    img2[y,x] = 255
    
    
    cv2.imshow("First method", img)
    cv2.imshow("Second method", img2)
    cv2.waitKey(0)
    cv2.destroyAllWindows()
    

    结果如下:

    方法一

    方法二

    还有第三种方法...您在大小为半径 x 半径的圆周围取一个框,并使用上面给出的圆公式评估每个点,如果它是真的那么它是一个圆点...但是那是很好地画出整个圆,因为你有整数,而且很可能没有多少点是相等的......


    更新:

    只是一个小提醒,确保你的点在图像中,在上面的例子中,如果你把中心放在 0,0 它将在每个角落绘制 1/4 的圆,因为它考虑了负值从数组的末尾开始。

    要删除重复项,您可以尝试以下代码:

    c = np.unique(np.array(list(zip(y,x))), axis=0  )
    img2[c[:,0], c[:,1]] = 255
    

    【讨论】:

    • 这两个函数都可以很容易地向量化。特别是第二个——只需将math 函数替换为numpy 函数并为d 使用一个数组。您可以使用 x 和 y 坐标的两对对应数组直接索引图像(在舍入并显式转换为 int 之后)。
    • @AlexanderReynolds 是的,我稍后会编辑它们
    • @api55 谢谢,您的第二个解决方案效果很好。然而,即使中心周围的点数小于 360,它也总是吐出 360 个点。例如,半径为 1,它会产生 360 个点而不是 4 或 8 个点。你知道我如何确定角度步长吗?即,如果半径为 1,则步长为 45 或 90。
    • @api55 也许以下是正确的?而不是 for d in range(360): 它将是 for d in range(0, 360, math.floor(360 / (radius * 4))): 替换 48 取决于。
    • @JakeM 问题是很多点都是一样的......你总是可以过滤重复的。但也许你可以做一些事情,比如除以半径 * 4,但是给它加上一个掩码
    【解决方案2】:

    只需将圆圈画在蒙版上:

    In [27]: mask = np.zeros((9, 9), dtype=np.uint8)
    
    In [28]: cv2.circle(mask, center=(4, 4), radius=4, color=255, thickness=1)
    Out[28]:
    array([[  0,   0,   0,   0, 255,   0,   0,   0,   0],
           [  0,   0, 255, 255,   0, 255, 255,   0,   0],
           [  0, 255,   0,   0,   0,   0,   0, 255,   0],
           [  0, 255,   0,   0,   0,   0,   0, 255,   0],
           [255,   0,   0,   0,   0,   0,   0,   0, 255],
           [  0, 255,   0,   0,   0,   0,   0, 255,   0],
           [  0, 255,   0,   0,   0,   0,   0, 255,   0],
           [  0,   0, 255, 255,   0, 255, 255,   0,   0],
           [  0,   0,   0,   0, 255,   0,   0,   0,   0]], dtype=uint8)
    

    现在您可以根据需要使用它来索引您的图像。例如,这是一张随机图片:

    In [33]: img
    Out[33]:
    array([[ 88, 239, 212, 160,  89,  85, 249, 242,  88],
           [ 47, 230, 206, 206,  63, 143, 152,  67,  58],
           [162, 212,   0, 213, 208, 169, 228,  14, 229],
           [230,  45, 103, 201, 188, 231,  80, 122, 131],
           [159,  31, 148, 158,  73, 215, 152, 158, 235],
           [213, 177, 148, 237,  92, 115, 152, 188, 223],
           [234,  67, 141, 173,  14,  18, 242, 208, 147],
           [ 53, 194, 229, 141,  37, 215, 230, 167,  82],
           [ 72,  78, 152,  76, 230, 128, 137,  25, 168]], dtype=uint8)
    

    这是周边的值:

    In [34]: img[np.nonzero(mask)]
    Out[34]:
    array([ 89, 206, 206, 143, 152, 212,  14,  45, 122, 159, 235, 177, 188,
            67, 208, 229, 141, 215, 230, 230], dtype=uint8)
    

    将图像在圆的周长处的值设置为0:

    In [35]: img[np.nonzero(mask)] = 0
    
    In [36]: img
    Out[36]:
    array([[ 88, 239, 212, 160,   0,  85, 249, 242,  88],
           [ 47, 230,   0,   0,  63,   0,   0,  67,  58],
           [162,   0,   0, 213, 208, 169, 228,   0, 229],
           [230,   0, 103, 201, 188, 231,  80,   0, 131],
           [  0,  31, 148, 158,  73, 215, 152, 158,   0],
           [213,   0, 148, 237,  92, 115, 152,   0, 223],
           [234,   0, 141, 173,  14,  18, 242,   0, 147],
           [ 53, 194,   0,   0,  37,   0,   0, 167,  82],
           [ 72,  78, 152,  76,   0, 128, 137,  25, 168]], dtype=uint8)
    

    您也可以轻松获取坐标:

    In [56]: np.where(mask)
    Out[56]:
    (array([0, 1, 1, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 7, 7, 8]),
     array([4, 2, 3, 5, 6, 1, 7, 1, 7, 0, 8, 1, 7, 1, 7, 2, 3, 5, 6, 4]))
    

    【讨论】:

    • 为务实的方法点赞。 :)
    【解决方案3】:

    假设img 是您的图像,radius 是圆的半径,x, y 是您要聚焦的中心的坐标。

    focus_img可以使用

    offset = math.ceil(radius * math.sqrt(2))
    focus_img = img[y-offset:y+offset, x-offset:x+offset]
    

    【讨论】:

    • OP 要求的是圆圈边缘的周边像素,而不是以圆圈为中心的 ROI。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-05-15
    • 1970-01-01
    • 2021-10-12
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多