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中专门提到输入只能是单通道的,因此如果是多通道只能通过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

  • 3
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

00000cj

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值