scipy.ndimage.distance_transform_edt 和 cv2.distanceTransform用法

scipy.ndimage.distance_transform_edt cv2.distanceTransform 的作用都是计算一张图上每个前景像素点到背景的最近距离。

import cv2
import numpy as np
from scipy.ndimage import distance_transform_edt

a = np.array(([0, 1, 1, 1, 1],
              [0, 0, 1, 1, 1],
              [0, 1, 1, 1, 1],
              [0, 1, 1, 1, 0],
              [0, 1, 1, 0, 0]))
print(a.shape)  # (5, 5)
y1 = distance_transform_edt(a)
print(y1)
# [[0.         1.         1.41421356 2.23606798 3.        ]
#  [0.         0.         1.         2.         2.        ]
#  [0.         1.         1.41421356 1.41421356 1.        ]
#  [0.         1.         1.41421356 1.         0.        ]
#  [0.         1.         1.         0.         0.        ]]
y2 = cv2.distanceTransform(a.astype(np.uint8), cv2.DIST_L2, maskSize=cv2.DIST_MASK_PRECISE)
print(y2)
# [[0.        1.        1.4142135 2.236068  3.       ]
#  [0.        0.        1.        2.        2.       ]
#  [0.        1.        1.4142135 1.4142135 1.       ]
#  [0.        1.        1.4142135 1.        0.       ]
#  [0.        1.        1.        0.        0.       ]]

这里输入中前景为1,背景为0。

cv2.distanceTransform的参数需要注意一下

  • 输入的数据类型需要是8-bit的,这里输入a默认数据类型为int32,因此需要通过.astype(np.uint8)先转换一下,否则会报错。而在distance_transform_edt中没有这点要求。
  • 第二个参数cv2.DIST_L2表示欧式距离
  • 第三个参数maskSize表示掩码大小,决定了距离计算的精度

maskSize参数

这里网上找了半天没找到比较详细的解释,这里也不涉及实际计算距离时用到的算法,只结合实例介绍一下自己的理解

maskSize有三个可选值分别为DIST_MASK_3、DIST_MASK_5、cv2.DIST_MASK_PRECISE,分别表示3x3 mask、5x5 mask、精确计算。

下图中的L2表示我们这里例子中的欧式距离,其中a表示水平或者竖直方向移动一个像素的距离,cv2.DIST_MASK_PRECISE即精确距离a=1,当采用5x5 mask时,a也是1,但当采用3x3 mask时,水平或者竖直移动一个像素的距离a=0.955,至于背后采用的是什么加速算法以及这个值是怎么得来的这里就不多做讨论了。b表示对角线方向移动一个像素的距离,精确值 \(b=\sqrt{1^{2}+1^{2}}=\sqrt{2}\approx 1.4142135\),5x5 mask下b=1.4,3x3 mask下b=1.3693。当采用5x5 mask时还有一个c值,这里c的距离是象棋中日字对角线的距离,即水平和竖直方向一个方向移动一个像素,另一个方向移动两个像素,精确值为 \(b=\sqrt{1^{2}+2^{2}}=\sqrt{5}\approx 2.236068\),这里近似为c=2.1969。

有了a,b,c值,mask size内所有位置的距离都可以根据周围像素的距离递推得到,比如下面例子中3x3 mask输出结果中的右上角的2.8650055就可以通过最右列第二行的像素向上移动一个像素得到即2.8650055=1.9100037+0.95500183。

scipy.ndimage.distance_transform_edt 和 cv2.distanceTransform用法_第1张图片

多通道

上面的输入都是单通道的情况,scipy.ndimage.distance_transform_edt是支持多通道的,但cv2.distanceTransform中专门提到输入只能是单通道的,因此如果是多通道只能通过for循环实现了。

在训练模型中,当有多类别或想要计算一整个batch时,scipy.ndimage.distance_transform_edt会更方便些。

import cv2
import numpy as np
from scipy.ndimage import distance_transform_edt

a = np.array((([0, 1, 1, 1, 1],
              [0, 0, 1, 1, 1],
              [0, 1, 1, 1, 1],
              [0, 1, 1, 1, 0],
              [0, 1, 1, 0, 0]),
             ([0, 1, 1, 1, 1],
              [0, 0, 1, 1, 1],
              [0, 1, 1, 1, 1],
              [0, 1, 1, 1, 0],
              [0, 1, 1, 0, 0]))
             )

y1 = distance_transform_edt(a)
print(y1.shape)  # (2, 5, 5)
print(y1)
# [[[0.         1.         1.41421356 2.23606798 3.        ]
#   [0.         0.         1.         2.         2.        ]
#   [0.         1.         1.41421356 1.41421356 1.        ]
#   [0.         1.         1.41421356 1.         0.        ]
#   [0.         1.         1.         0.         0.        ]]
#  [[0.         1.         1.41421356 2.23606798 3.        ]
#   [0.         0.         1.         2.         2.        ]
#   [0.         1.         1.41421356 1.41421356 1.        ]
#   [0.         1.         1.41421356 1.         0.        ]
#   [0.         1.         1.         0.         0.        ]]]
for single_c in a:
    y2 = cv2.distanceTransform(single_c.astype(np.uint8), cv2.DIST_L2, maskSize=cv2.DIST_MASK_PRECISE)
    print(y2.shape)  # (5, 5)
    print(y2)
    # [[0.        1.        1.4142135 2.236068  3.       ]
    #  [0.        0.        1.        2.        2.       ]
    #  [0.        1.        1.4142135 1.4142135 1.       ]
    #  [0.        1.        1.4142135 1.        0.       ]
    #  [0.        1.        1.        0.        0.       ]]

参考

cv.distanceTransform - mexopencv

OpenCV学习三十五:distanceTransform 距离变换函数_Thomas会写字的博客-CSDN博客_distancetransform

你可能感兴趣的:(OpenCV,scipy,python,opencv)