Python 计算二元遮罩阵列上封闭圆的半径

Python 计算二元遮罩阵列上封闭圆的半径,python,numpy,image-processing,Python,Numpy,Image Processing,我有一个图像的numpy数组: 红色是特定部分上的二进制遮罩,我的目标是计算此遮罩中的最大直径长度。我尝试过使用边界框并测量框的直径(从一端到另一端),但它并不像我希望的那样精确,特别是当遮罩的形状接近圆形时 我的下一个想法是使用一个边界圆而不是一个长方体,并测量半径,这将是非常准确的。问题是我不知道有什么包或函数可以给我一个边界圈。即使有这样一个函数,我也不知道如何测量给定圆的半径,因为圆中没有可以端到端的尖点 欣赏任何想法。如果您愿意接受近似值,请使用此方法并使用“长轴长度”属性: 关于我

我有一个图像的numpy数组:

红色是特定部分上的二进制遮罩,我的目标是计算此遮罩中的最大直径长度。我尝试过使用边界框并测量框的直径(从一端到另一端),但它并不像我希望的那样精确,特别是当遮罩的形状接近圆形时

我的下一个想法是使用一个边界圆而不是一个长方体,并测量半径,这将是非常准确的。问题是我不知道有什么包或函数可以给我一个边界圈。即使有这样一个函数,我也不知道如何测量给定圆的半径,因为圆中没有可以端到端的尖点


欣赏任何想法。

如果您愿意接受近似值,请使用此方法并使用“长轴长度”属性:

关于我的解决方案的一些注意事项:

(0)它接受二进制图像作为输入

(1) 它会为图像中的所有区域找到它

(2) 它找到该区域凸包的最大直径。我认为这是一个合理的做法,但如果您愿意,可以调整实现

(3) 我使用skimage.data“coins”图像,以便您可以复制自己

import sys

# To find the diameters
from skimage.measure import regionprops, label
from sklearn.metrics import pairwise_distances
from scipy import ndimage as ndi
import numpy as np

# To generate test data
from skimage import data
from skimage.filters import sobel
from skimage.morphology import watershed

# For visualization
import matplotlib.pyplot as plt

STREL_4 = np.array([[0, 1, 0],
                    [1, 1, 1],
                    [0, 1, 0]], dtype=np.bool)


def get_border_image(region):
    convex_hull_mask = region.convex_image
    eroded_image = ndi.binary_erosion(convex_hull_mask, STREL_4, border_value=0)
    border_image = np.logical_xor(convex_hull_mask, eroded_image)
    return border_image

def get_region_diameters(img):

    assert img.dtype == np.bool and len(img.shape) == 2

    label_img = label(img, connectivity=img.ndim)

    for region in regionprops(label_img):
        border_image = get_border_image(region)
        perimeter_coordinates = np.transpose(np.nonzero(border_image))
        pairwise_distances_matrix = pairwise_distances(perimeter_coordinates)
        i, j = np.unravel_index(np.argmax(pairwise_distances_matrix), pairwise_distances_matrix.shape)
        ptA, ptB = perimeter_coordinates[i], perimeter_coordinates[j]
        region_offset = np.asarray([region.bbox[0], region.bbox[1]])
        ptA += region_offset
        ptB += region_offset
        yield pairwise_distances_matrix[i, j], ptA, ptB

if __name__ == "__main__":

    # Create a segmentation of the coins image, for testing purposes.
    # You should create a binary image
    coins = data.coins()
    elevation_map = sobel(coins)
    markers = np.zeros_like(coins)
    markers[coins < 30] = 1
    markers[coins > 150] = 2
    segmentation = (watershed(elevation_map, markers) > 1)

    for distance, ptA, ptB in get_region_diameters(segmentation):
        plt.imshow(segmentation)
        x1, x2, y1, y2 = ptA[1], ptB[1], ptA[0], ptB[0]
        plt.plot([x1, x2], [y1, y2], color='k', linestyle='-', linewidth=2)
        print(distance, ptA, ptB)

    plt.show()
导入系统 #求直径 从skimage.measure导入区域道具,标签 从sklearn.metrics导入成对距离 从scipy导入ndi图像作为ndi 将numpy作为np导入 #生成测试数据的步骤 从浏览导入数据 从skimage.filters导入sobel 从skimage.com导入分水岭 #用于可视化 将matplotlib.pyplot作为plt导入 STREL_4=np.array([[0,1,0], [1, 1, 1], [0,1,0]],dtype=np.bool) def get_border_图像(区域): 凸面外壳遮罩=区域。凸面图像 侵蚀图像=ndi。二值侵蚀(凸面外壳遮罩,STREL 4,边界值=0) border\u image=np.逻辑异或(凸壳屏蔽,腐蚀图像) 返回边框图像 def获取区域直径(img): 断言img.dtype==np.bool和len(img.shape)==2 label\u img=label(img,连接性=img.ndim) 对于regionprops中的区域(标签): 边框图像=获取边框图像(区域) 周长坐标=np.转置(np.非零(边界图像)) 成对距离矩阵=成对距离(周长坐标) i、 j=np.Unlavel_索引(np.argmax(成对距离矩阵),成对距离矩阵形状) ptA,ptB=周长坐标[i],周长坐标[j] region_offset=np.asarray([region.bbox[0],region.bbox[1])) ptA+=区域偏移量 ptB+=区域偏移量 产生成对距离矩阵[i,j],ptA,ptB 如果名称=“\uuuuu main\uuuuuuuu”: #为测试目的,创建硬币图像的分割。 #您应该创建一个二进制图像 硬币=数据。硬币() 立面图=索贝尔(硬币) 马克=np.零(硬币) 马克币[硬币<30]=1 马克币[硬币>150]=2 分段=(流域(高程图、标记)>1) 对于get_区域直径中的距离、ptA、ptB(分段): plt.imshow(细分) x1,x2,y1,y2=ptA[1],ptB[1],ptA[0],ptB[0] plt.绘图([x1,x2],[y1,y2],颜色='k',线型='-',线宽=2) 打印(距离、ptA、ptB) plt.show()
您需要计算形状的最大Feret直径(最大投影长度)。我在这里解释一个算法:可能与这个问题重复,我认为考虑到周长相对较小(以像素为单位),最有效的解决方案可能最终是蛮力解决方案,即计算周长像素p_1,…,p_n的一些顺序,并比较所有对。最坏情况下的时间O(n^2),其中n是区域的总大小(长瘦区域=所有像素都在周长上)。谢谢你的回答,但我已经有了一个近似值。我想要一个更精确的值。关于我的解决方案的一些注释:(1)它为图像中的所有区域找到它。(2) 它找到该区域凸包的最大直径。我认为这是一个合理的做法,但是如果你愿意,你可以调整实现。编辑为@CrisLuengo SuggestHank感谢你的努力,感谢你。您的逻辑似乎很准确,但对于这样一个简单的任务,代码太多了。特别是当我看到你使用二进制腐蚀和异或只是为了得到边界,这似乎是不必要的。最后,我按照@Dr.H.Lecter的建议使用了openCV(cv2)。使用“contours,hierarchy=cv2.findContours(图像,1,2)”获取轮廓,并使用“(x,y),radius=cv2.MineConclosingCircle(轮廓[0])获取边界圆的半径