距离变换 Distance Transformation

以下为该学习地址的学习笔记:Distance transformation in image - Python OpenCV - GeeksforGeeks

 其他学习资料:Morphology - Distance Transform

简介

距离变换是一种用于计算图像中每个像素与最近的非零像素之间距离的技术。它通常用于图像分割和物体识别任务,因为它可以帮助识别图像中物体的边界。(如下图片来源于Chamani, H., Rabbani, A., Russell, K. P., Zydney, A. L., Gomez, E. D., Hattrick-Simpers, J., & Werber, J. R. (2023). Rapid reconstruction of 3-D membrane pore structure using a single 2-D Micrograph. arXiv preprint arXiv:2301.10601.)

1. 距离变换在OpenCV中的实现

OpenCV 中的距离变换函数 cv2.distanceTransform() 接收二值图像并返回两个数组:距离图像和标签图像(the distance image and the label image)。距离图像包含每个像素与最近的非零像素的距离值,标签图像包含最近的非零像素的标签。

#简单表示一下
dist_transform, labels = cv2.distanceTransform(binary_image, distance_type, mask_size)
  • binary_image: 输入的二值图像,非零像素通常表示前景对象。
  • distance_type: 距离类型,可以使用常量如cv2.DIST_L1(曼哈顿距离)或cv2.DIST_L2(欧几里得距离)。
  • mask_size: 掩码大小,确定用于计算距离的掩码的大小,值越大计算越精确但速度越慢。

距离类型

  • cv2.DIST_L1: 曼哈顿距离the Manhattan distance,即只计算水平和垂直方向的距离。
  • cv2.DIST_L2: 欧几里得距离the Euclidean distance,即计算实际的几何距离,包含所有方向。

掩码大小

掩码大小(mask size)决定了计算距离时使用的邻域范围,常用的大小有3、5等。较大的掩码可以提供更精确的距离值,但计算开销也会增加。

距离变换的结果

距离变换的结果是一个与原始图像大小相同的距离图像,每个像素值表示该像素到最近前景像素的距离。通过这种方式,可以有效地识别图像中对象的边界。

距离图像的归一化

为了方便可视化,OpenCV提供了cv2.normalize()函数,可以将距离图像的值归一化到0到255的范围内。

归一化

#简单表示一下
normalized_dist_transform = cv2.normalize(dist_transform, None, 0, 255, cv2.NORM_MINMAX)
  • dist_transform: 输入的距离图像。
  • None: 输出数组,通常设为None表示原地操作。
  • 0: 归一化后的最小值。
  • 255: 归一化后的最大值。
  • cv2.NORM_MINMAX: 归一化类型,将最小值和最大值归一化到指定范围。

除了距离变换函数外,OpenCV 还提供了 cv2.normalize() 函数,用于对距离图像进行归一化处理,使距离值在 0 到 255 之间。这对距离图像的可视化非常有用。

距离变换是一种图像处理技术,可用于获取图像中每个像素与最近的非零像素之间的距离。它常用于图像分割和物体识别任务。

2. 具体步骤

在 OpenCV 中,我们可以使用 cv2.distanceTransform() 函数执行距离变换。

需要遵循的步骤

  1. 加载图像:使用 cv2.imread() 函数加载图像。
  2. 转换为灰度图像:使用 cv2.cvtColor() 函数将图像转换为灰度图像。这是因为距离变换函数仅适用于单通道图像
  3. 创建二值图像:进行阈值处理,使用cv2.threshold()函数对图像进行二值化,将像素强度值大于某个阈值的像素设为255,其他像素设为0。
  4. 计算距离变换:使用cv2.distanceTransform()函数计算每个像素到最近非零像素的距离。该函数接受三个参数:二值图像、距离类型(如cv2.DIST_L2表示欧几里得距离)和掩码大小(如3表示3×3掩码)。
  5. 返回结果:距离变换函数返回两个数组:距离图像和标签图像。距离图像包含每个像素与最近的非零像素的距离值,标签图像包含最近的非零像素的标签。

3. 示例

3.1 示例 1

import cv2  # 导入OpenCV库

# 加载图像
# 你可以提供图像的路径
image = cv2.imread(r"Mandala.jpg")  # 读取图像文件"Mandala.jpg"

# 将图像转换为灰度图像
grayScaleImage = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)  # 将彩色图像转换为灰度图像

# 对图像进行二值化处理,创建二值图像
_, threshold = cv2.threshold(grayScaleImage, 123, 255, cv2.THRESH_BINARY)  # 将灰度图像进行二值化,阈值为123

# 计算距离变换
# distTransform = cv2.distanceTransform(threshold, cv2.DIST_C, 3)  # 使用Chebyshev距离计算距离变换
# distTransform= cv2.distanceTransform(threshold, cv2.DIST_L1, 3)  # 使用曼哈顿距离计算距离变换
distTransform= cv2.distanceTransform(threshold, cv2.DIST_L2, 3)  # 使用欧几里得距离计算距离变换

# 显示距离变换后的图像
cv2.imshow('Transformed Distance Image1', distTransform)  # 显示距离变换后的图像

# 按任意键结束进程
cv2.waitKey(0)  # 等待按键输入
cv2.destroyAllWindows()  # 销毁所有窗口

# 保存距离变换后的图像
cv2.imwrite('distTransformed.jpg', distTransform)  # 将距离变换后的图像保存为"distTransformed.jpg"

此代码的输出将是一个包含距离图像的窗口,其中每个像素包含与最近非零像素的距离值。距离值的范围为 0 到 255,值越大表示距离越大。

3.2 示例2(标签图像)

下面是OpenCV中距离变换的第二个示例代码,演示了如何使用标签图像获取图像中每个像素的最近非零像素的坐标:

# importing necessary libraries 
# 导入必要的库
import cv2  # 导入OpenCV库
import numpy as np  # 导入NumPy库,用于数组操作

# Load the image 
# you can specify the path to image 
# 加载图像
# 你可以指定图像的路径
image = cv2.imread(r"Visual_arts.jpg")  # 读取图像文件"Visual_arts.jpg"

# Convert the image to grayscale 
# 使用cv2.cvtColor()函数将图像转换为灰度图像
grayScaleImage = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY) 

# Threshold the image to create a binary image 
# 使用cv2.threshold()函数对灰度图像进行二值化处理,创建二值图像
_, threshold = cv2.threshold(grayScaleImage, 127, 255, cv2.THRESH_BINARY) 

# Calculate the distance transform 
# 使用cv2.distanceTransform()函数计算二值图像的距离变换
dist, labels, *other_vars = cv2.distanceTransform(threshold, cv2.DIST_L2, 3) 

# Find the coordinates of the nearest non-zero pixels for each pixel in the image 
# 使用np.where()和np.column_stack()函数找到每个像素的最近非零像素的坐标
coords = np.column_stack(np.where(labels > 0)) 

# Print the coordinates 
# squeeze is used to remove the undesired dimension 
# 打印坐标
# 使用squeeze()函数去除不需要的维度
print(coords.squeeze())

输出:

[ 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
  18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
  36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53
  54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71
  72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89
  90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107
 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125
 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143
 144 151 152 156 157 158 159 160 161 162 163 167 168 169 170 171 172 179
 180 200 201 202 203 204 240 241 242 243 244 254 255 256 257 287 290 297]

代码片段补充介绍

_, threshold = cv2.threshold(grayScaleImage, 127, 255, cv2.THRESH_BINARY)
  • _:这个下划线通常用作占位符,表示在此处接收一个值但不需要使用它。在这里,它用于接收cv2.threshold()函数返回的第一个返回值,即阈值化操作后的阈值(threshold),但实际上在后续代码中并没有使用这个值。
  • threshold:这是一个变量名,用于接收cv2.threshold()函数返回的第二个返回值,即二值化后的图像。这是我们感兴趣的结果,它是一个灰度图像,其中像素值只有两种可能:0(黑色,表示背景)和255(白色,表示前景)。
  • cv2.THRESH_BINARY:这是cv2.threshold()函数的一个参数,用于指定阈值化类型。cv2.THRESH_BINARY表示二值阈值化类型,即将大于阈值的像素设置为一个值(这里是255),小于或等于阈值的像素设置为另一个值(这里是0)。
dist, labels, *other_vars = cv2.distanceTransform(threshold, cv2.DIST_L2, 3)
  • dist:这是一个变量名,用于接收cv2.distanceTransform()函数返回的第一个数组,即距离图像(distance image)。距离图像包含了每个像素到最近非零像素的距离值。
  • labels:这是一个变量名,用于接收cv2.distanceTransform()函数返回的第二个数组,即标签图像(label image)。标签图像包含了每个像素所属的最近非零像素的标签。
  • other_vars:这个是Python中的可变参数形式,用于接收cv2.distanceTransform()函数可能返回的额外的参数。在这个特定的情况下,cv2.distanceTransform()函数实际上只返回两个值(距离图像和标签图像),所以other_vars在这里实际上是一个空列表,因为没有额外的参数需要接收。
  • cv2.distanceTransform():这是OpenCV库中的一个函数,用于计算图像的距离变换。它接受以下参数:
    • threshold:输入的二值化图像,这里是前面阈值化得到的二值图像。
    • cv2.DIST_L2:距离的类型,这里选择欧几里得距离(L2范数),表示每个像素到最近非零像素的欧几里得距离。
    • 3:掩码大小,即用于计算距离的卷积核的大小,这里是3×3的卷积核。

补充掩码

  • 控制掩码的大小和形状可以影响操作的结果,例如在距离变换中,掩码的大小决定了计算每个像素到最近非零像素距离的精度和效率。
  • 在距离变换中,掩码用于计算每个像素到最近非零像素的距离。通常使用的掩码大小是一个正方形(3×3、5×5等),它定义了像素周围的邻域。
coords = np.column_stack(np.where(labels > 0)) 

np.where(labels > 0)

  • labels > 0:这是一个条件表达式,返回一个布尔类型的数组,数组的每个元素都是 labels 中对应位置的像素值是否大于0的判断结果。如果 labels 中的像素值大于0,则对应位置为True,否则为False。
  • np.where():这是一个NumPy函数,用于根据指定的条件返回符合条件的元素的索引。它可以接受一个条件表达式作为参数,并返回一个包含符合条件元素索引的元组。
    • 如果条件表达式是一个二维数组,np.where() 返回的是一个包含两个一维数组的元组,分别代表符合条件的行索引和列索引。
    • 如果条件表达式是一个一维数组,np.where() 直接返回符合条件的元素的索引。

在这里,np.where(labels > 0) 返回的是一个元组,其中包含了 labels 中大于0的像素点的索引。

np.column_stack()

  • np.column_stack():这是NumPy函数,用于将多个一维数组按列堆叠成一个二维数组。它接受一个元组或列表作为参数,参数中的每个元素是一个一维数组。

在这里,np.column_stack(np.where(labels > 0)) 接受了 np.where(labels > 0) 返回的元组作为参数,将其中的每个一维数组按列堆叠成一个二维数组。因为 np.where(labels > 0) 返回的是一个包含行索引和列索引的元组,所以 np.column_stack() 的作用是将这两个一维数组按列排列成一个二维数组,即将行索引和列索引对应堆叠在一起。

coords

  • coords:这是一个变量名,用于接收 np.column_stack(np.where(labels > 0)) 的结果。它表示了所有 labels 中大于0的像素点的坐标。

综合起来,coords = np.column_stack(np.where(labels > 0)) 这行代码的作用是找到 labels 图像中所有像素值大于0的像素点的坐标,并将这些坐标按列堆叠成一个二维数组,存储在 coords 变量中。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值