利用Numpy有效地将分割后的地面真值转换为等高线图像

利用Numpy有效地将分割后的地面真值转换为等高线图像,numpy,optimization,image-segmentation,Numpy,Optimization,Image Segmentation,假设我有一个分割图像作为一个Numpy数组,其中图像中的每个条目都是一个从1开始的数字。。。C、 C+1,其中C是分段类的数量,类C+1是一些背景类。我想找到一种有效的方法将其转换为轮廓图像(一个二值图像,其中一个轮廓像素的值为1,其余的值为0),这样任何在其8邻域(或4邻域)中有邻居的像素都将是轮廓像素 效率低下的方式可能是: def isValidLocation(i, j, image_height, image_width): if i<0: return

假设我有一个分割图像作为一个Numpy数组,其中图像中的每个条目都是一个从1开始的数字。。。C、 C+1,其中C是分段类的数量,类C+1是一些背景类。我想找到一种有效的方法将其转换为轮廓图像(一个二值图像,其中一个轮廓像素的值为1,其余的值为0),这样任何在其8邻域(或4邻域)中有邻居的像素都将是轮廓像素

效率低下的方式可能是:

def isValidLocation(i, j, image_height, image_width):
    if i<0:
        return False
    if i>image_height-1:
        return False
    if j<0:
        return False
    if j>image_width-1:
        return False
    return True

def get8Neighbourhood(i, j, image_height, image_width):
    nbd = []
    for height_offset in [-1, 0, 1]:
        for width_offset in [-1, 0, 1]:
            if isValidLocation(i+height_offset, j+width_offset, image_height, image_width):
                nbd.append((i+height_offset, j+width_offset))
    return nbd


def getContourImage(seg_image):
    seg_image_height = seg_image.shape[0]
    seg_image_width = seg_image.shape[1]
    contour_image = np.zeros([seg_image_height, seg_image_width], dtype=np.uint8)
    for i in range(seg_image_height):
        for j in range(seg_image_width):
            nbd = get8Neighbourhood(i, j, seg_image_height, seg_image_width)
            for (m,n) in nbd:
                if seg_image[m][n] != seg_image[i][j]:
                    contour_image[i][j] = 1
                    break
    return contour_image
def isValidLocation(i、j、图像高度、图像宽度):
如果图像高度为-1:
返回错误
如果jimage_width-1:
返回错误
返回真值
def GET8邻域(i、j、图像高度、图像宽度):
nbd=[]
对于[-1,0,1]中的高度_偏移:
对于[-1,0,1]中的宽度_偏移:
如果isValidLocation(i+高度偏移、j+宽度偏移、图像高度、图像宽度):
nbd.追加((i+高度偏移,j+宽度偏移))
返回nbd
def getContourImage(seg_图像):
seg_image_height=seg_image.shape[0]
seg_image_width=seg_image.shape[1]
轮廓图像=np.0([seg\U图像高度,seg\U图像宽度],dtype=np.uint8)
对于范围内的i(分段图像高度):
对于范围内的j(分段图像宽度):
nbd=Get8邻域(i,j,分段图像高度,分段图像宽度)
对于nbd中的(m,n):
如果seg_图像[m][n]!=seg_image[i][j]:
等高线图像[i][j]=1
打破
返回轮廓图像
我正在寻找一种更有效的“矢量化”方法来实现这一点,因为我需要能够在运行时对深度学习环境中的8幅图像进行计算。任何见解都值得赞赏。下面是一个可视示例。第一幅图像是覆盖在地面真实分割遮罩上的原始图像(当然不是最好的分割…),第二幅是我的代码输出,看起来不错,但速度太慢了。使用intel 9900K cpu,每张图像大约需要10秒


来自SUN RGBD数据集的图像积分。

这可能有效,但可能存在一些限制,如果不测试实际数据,我无法确定这些限制,因此我将依赖您的反馈

将numpy导入为np
从scipy导入ndimage
将matplotlib.pyplot作为plt导入
#一些样本数据有几个矩形段展开
seg=np.ones((100100),dtype=np.int8)
赛格[3:10,3:10]=20
赛格[24:50,40:70]=30
赛格[55:80,62:79]=40
赛格[40:70,10:20]=50
plt.imshow(seg)
plt.show()

现在,为了找到轮廓,我们将使用一个内核对图像进行卷积,当在图像的同一段内进行卷积时,内核应给出
0
值,当在多段图像区域上进行卷积时,内核应给出
0

#用于卷积的内核
k=np.数组([[1,-1,-1],
[1, 0, -1],
[1, 1, -1]])
卷积=nImage.卷积(seg,k)
#轮廓像素
非零=np.argwhere(卷积!=0)
plt.scatter(非零值[:,1],非零值[:,0],c='r',marker='。)
plt.show()

正如您在这个示例数据中所看到的,由于数据的对称性,内核有一个很小的限制,并且没有识别两个轮廓像素(我认为这在实际分割输出中是罕见的)

为了更好地理解,这是一个场景(发生在矩形的左上角和右下角),其中内核卷积无法识别轮廓,即缺少一个像素

[ 1,  1,  1]
[ 1,  1,  1]
[ 1, 20, 20]

基于@sai的想法,我提出了这个代码片段,它产生了相同的结果,比我的原始代码快得多。运行时间为0.039秒,与之相比,我认为原始版本的运行时间接近8-10秒是相当快的

filters = []
for i in [0, 1, 2]:
    for j in [0, 1, 2]:
        filter = np.zeros([3,3], dtype=np.int)
        if i ==1 and j==1:
            pass
        else:
            filter[i][j] = -1
            filter[1][1] = 1
            filters.append(filter)


def getCountourImage2(seg_image):
    convolved_images = []
    for filter in filters:
        convoled_image = ndimage.correlate(seg_image, filter, mode='reflect')
        convolved_images.append(convoled_image)
    convoled_images = np.add.reduce(convolved_images)
    seg_image = np.where(convoled_images != 0, 255, 0)
    return seg_image

如果我错了,请纠正我。默认情况下,顶部和底部行以及左侧和右侧列中的所有像素不是都有8个相邻像素吗?我在这里的观点是,有很好的可能性消除
isvalid
函数和
get8Neigh….
函数。您还可以解释一下nbd中(m,n)的for循环
中发生了什么:
?(我的理解-您正在检查相邻像素是否与中心像素具有相同的像素值)@sai我不确定您所说的“默认情况下”顶行和底行中有8个相邻像素是什么意思。例如,在(0,0)(左上角)的顶行中,唯一的邻居是(1,0)、(0,1)和(1,1),因为显然我们不能到(0,0)的左侧或(0,0)的“顶部”。其次,在循环中,是的,你是正确的,我们正在检查一个像素的任何邻居是否与该像素不相等,在这种情况下,它是一个轮廓像素,因为它在两个不同的分段连接组件中有邻居。@sai我应该补充一点,而我的8Nexter和isValid函数可能不是最佳的,这里真正的瓶颈是,我正在以非矢量化的方式检查分割图像阵列中每个像素的邻域,这非常非常慢。我正在寻找一种numpy矢量化的方式来加速这个过程,类似于numpy.where函数比双for循环快得多。感谢您的解释,抱歉我之前的措辞。我的意思是,除了最上面一行、最下面一行、最左边一列和最右边一列中的像素外,所有像素都有8个邻域,它们的索引可以通过数学计算,它们的有效性不需要检查,对吗?非常有趣的想法!我将用你的想法进行实验,用8个不同的过滤器进行卷积,并对其进行基准测试(每个邻居一个)。谢谢请查看我的答案,再次感谢您的帮助。很高兴为您提供帮助,干杯!