(算法版)基于二值图像数字矩阵的距离变换算法

ops/2024/10/11 13:29:10/

Hi,大家好,我是半亩花海。本项目展示了欧氏距离、城市街区距离和棋盘距离变换的实现方法。通过定义一个距离变换类,对输入图像进行距离变换操作,并生成对应的距离矩阵。在示例中,展示了在一个480x480的全黑背景图像上设置三个前景像素点的距离变换结果。(距离变换算法函数版详见以下链接:计算机视觉 | 基于二值图像数字矩阵的距离变换算法


文章目录

  • 一、距离的定义及分类
  • 二、导入必要库
  • 三、距离变换算法
  • 四、定义距离变换类
  • 五、显示原始图像
  • 六、计算并输出距离变换矩阵
  • 七、可视化距离变换结果
  • 八、完整代码


一、距离的定义及分类

距离是描述图像两点像素之间的远近关系的度量,常见的度量距离有欧式距离、城市街区距离、棋盘距离。以下以两坐标点 a = ( i , j ) a = (i, j) a=(i,j) b = ( k , l ) b = (k, l) b=(k,l) 的距离为例,来说明各种距离的定义方式:

(1)欧式距离 D e D_e De 欧式距离的定义源于经典的几何学,与我们数学中所学的简单几何的两点之间的距离一致,为两个像素点坐标值的平方根。欧式距离的优点在于其定义非常地直观,是显而易见的,但缺点在于平方根的计算是非常耗时的。

D e ( a , b ) = ( ( i − k ) 2 ) + ( j − l ) 2 D_e(a, b)=\sqrt{\left((i-k)^2\right)+(j-l)^2} De(a,b)=((ik)2)+(jl)2

(2)城市街区距离 D 4 D_4 D4 距离描述的是只允许像素坐标系平面中横向和纵向的移动距离,4表示在这种定义下,像素点是 4 邻接的,即每个点只与它的上、下、左、右相邻的 4 个点之间的距离为 1。

D 4 ( a , b ) = ∣ i − k ∣ + ∣ j − l ∣ D_4(a, b)=|i-k|+|j-l| D4(a,b)=ik+jl

(3)棋盘距离 D 8 D_8 D8 如果允许在图像坐标系中像素点的对角线方向的移动,就可以得到棋盘距离,8 表示在这种定义下,像素点是 8 邻接的,即每个点只与它的上、下、左、右、四个对角线方向相邻的 8 个点之间的距离为 1。

D 8 ( a , b ) = max ⁡ { ∣ i − k ∣ , ∣ j − l ∣ } D_8(a, b)=\max \{|i-k|,|j-l|\} D8(a,b)=max{ik,jl}


二、导入必要库

import numpy as np
import matplotlib.pyplot as plt# 设置字体样式以正常显示中文标签
plt.rcParams['font.sans-serif'] = ['SimHei']
plt.rcParams['axes.unicode_minus'] = False

三、距离变换算法

  • 距离变换是图像中像素点与某个区域块的距离。区域块中的像素点值为 0,临近区域块的像素点有较小的值,离它越远值越大。

以二值图像为例,其中区域块内部的像素值为 1,其他像素值为 0。距离变换给出每个像素点到最近的区域块边界的距离,区域块内部的距离变换结果为0。输入图像如图 1 所示, D 4 D_4 D4 距离的距离变换结果如图 2 所示。

  • 距离变换算法核心是利用两个小的局部掩膜对图像进行遍历。第一遍利用掩模1,左上角开始,从左往右,从上往下;第二遍利用第二个掩模,右下角开始,从右往左,从下往上。掩模形状如下图所示:

按照某种距离(如: D 4 D_4 D4 距离或 D 8 D_8 D8 距离)对大小为 M × N M×N M×N 的图像中的区域块作距离变换,算法过程如下:

(1) 建立一个大小为 M × N M×N M×N 的数组 F F F,作如下的初始化:将区域块中的元素设置为 0,其余元素设置为无穷;

(2) 利用掩模1(mask1),左上角开始,从左往右,从上往下遍历数组,将掩模中P点对应的元素的值作如下更新:

F ( P ) = min ⁡ q ∈ mask1 ⁡ { F ( P ) , D ( P , q ) + F ( q ) } F(P)=\min _{q \in \operatorname{mask1}}\{F(P), D(P, q)+F(q)\} F(P)=qmask1min{F(P),D(P,q)+F(q)}

(3) 利用掩模2(mask2),右下角开始,从右往左,从下往上遍历数组,将掩模中P点对应的元素的值作如下更新:

F ( P ) = min ⁡ q ∈ mask2 ⁡ { F ( P ) , D ( P , q ) + F ( q ) } F(P)=\min _{q \in \operatorname{mask2}}\{F(P), D(P, q)+F(q)\} F(P)=qmask2min{F(P),D(P,q)+F(q)}

最终得到的更新后的数组即为距离变换的结果。

因在边界处掩模不能全部覆盖图像,可以将掩模中没有对应元素的位置的值当作 0 来处理,以此对图像边界处做出调整,即maskSize=0


四、定义距离变换类

在距离变换类当中分别初始化距离变换类、定义欧氏距离和城市街区距离及棋盘距离的函数。

# 定义距离变换类
class DistanceTransform:# 初始化距离变换类def __init__(self, image):self.image = imageself.height, self.width = image.shape# 定义欧氏距离变换def Euclidean_distance_transform(self):distance_matrix = np.zeros_like(self.image, dtype=float)# 获取前景像素点的坐标yy, xx = np.argwhere(self.image == 1).Tfor y in range(self.height):for x in range(self.width):if self.image[y, x] == 1:distance_matrix[y, x] = 0else:# 计算当前像素到前景像素点的欧氏距离distances = np.sqrt((y - yy) ** 2 + (x - xx) ** 2)# 取最小值作为当前像素的距离值distance_matrix[y, x] = np.min(distances)return distance_matrix# 定义城市街区距离变换def D4_distance_transform(self):distance_matrix = np.zeros_like(self.image, dtype=float)yy, xx = np.argwhere(self.image == 1).Tfor y in range(self.height):for x in range(self.width):if self.image[y, x] == 1:distance_matrix[y, x] = 0else:# 计算当前像素到前景像素点的曼哈顿距离distances = np.abs(y - yy) + np.abs(x - xx)# 取最小值作为当前像素的距离值distance_matrix[y, x] = np.min(distances)return distance_matrix# 定义棋盘距离变换def D8_distance_transform(self):distance_matrix = np.zeros_like(self.image, dtype=float)yy, xx = np.argwhere(self.image == 1).Tfor y in range(self.height):for x in range(self.width):if self.image[y, x] == 1:distance_matrix[y, x] = 0else:# 计算当前像素到前景像素点的棋盘距离distances = np.maximum(np.abs(y - yy), np.abs(x - xx))# 取最小值作为当前像素的距离值distance_matrix[y, x] = np.min(distances)return distance_matrix

五、显示原始图像

# 初始化输入图像:480x480的全黑背景
image = np.zeros((480, 480), dtype=np.uint8)
# 取三个前景像素点
image[100, 200] = 1
image[200, 100] = 1
image[300, 300] = 1# 显示原始图像
plt.figure(figsize=(5, 5))
plt.scatter([100, 200, 300], [200, 100, 300], color='white', marker='o')
plt.imshow(image, cmap='gray')
plt.title('原始图像', fontsize=15)

六、计算并输出距离变换矩阵

# 计算距离变换矩阵
dt = DistanceTransform(image)
euclidean_distance_matrix = dt.Euclidean_distance_transform()
manhattan_distance_matrix = dt.D4_distance_transform()
chessboard_distance_matrix = dt.D8_distance_transform()# 输出欧氏、城区和棋盘的距离矩阵
print("欧氏距离的变换矩阵:\n", euclidean_distance_matrix)
print("城区距离的变换矩阵:\n", manhattan_distance_matrix)
print("棋盘距离的变换矩阵:\n", chessboard_distance_matrix)

七、可视化距离变换结果

# 可视化距离变换结果
plt.figure(figsize=(15, 5))# 欧氏距离变换
plt.subplot(1, 3, 1)
plt.imshow(euclidean_distance_matrix, cmap='gray')
plt.colorbar(shrink=0.8)
plt.title('欧氏距离', fontsize=15)
plt.axis('off')# 城区距离变换
plt.subplot(1, 3, 2)
plt.imshow(manhattan_distance_matrix, cmap='gray')
plt.colorbar(shrink=0.8)
plt.title('城区距离', fontsize=15)
plt.axis('off')# 棋盘距离变换
plt.subplot(1, 3, 3)
plt.imshow(chessboard_distance_matrix, cmap='gray')
plt.colorbar(shrink=0.8)
plt.title('棋盘距离', fontsize=15)
plt.axis('off')plt.tight_layout()
plt.show()

八、完整代码

import numpy as np
import matplotlib.pyplot as plt# 设置字体样式以正常显示中文标签
plt.rcParams['font.sans-serif'] = ['SimHei']
plt.rcParams['axes.unicode_minus'] = False# 定义距离变换类
class DistanceTransform:# 初始化距离变换类def __init__(self, image):self.image = imageself.height, self.width = image.shape# 定义欧氏距离变换def Euclidean_distance_transform(self):distance_matrix = np.zeros_like(self.image, dtype=float)# 获取前景像素点的坐标yy, xx = np.argwhere(self.image == 1).Tfor y in range(self.height):for x in range(self.width):if self.image[y, x] == 1:distance_matrix[y, x] = 0else:# 计算当前像素到前景像素点的欧氏距离distances = np.sqrt((y - yy) ** 2 + (x - xx) ** 2)# 取最小值作为当前像素的距离值distance_matrix[y, x] = np.min(distances)return distance_matrix# 定义城市街区距离变换def D4_distance_transform(self):distance_matrix = np.zeros_like(self.image, dtype=float)yy, xx = np.argwhere(self.image == 1).Tfor y in range(self.height):for x in range(self.width):if self.image[y, x] == 1:distance_matrix[y, x] = 0else:# 计算当前像素到前景像素点的曼哈顿距离distances = np.abs(y - yy) + np.abs(x - xx)# 取最小值作为当前像素的距离值distance_matrix[y, x] = np.min(distances)return distance_matrix# 定义棋盘距离变换def D8_distance_transform(self):distance_matrix = np.zeros_like(self.image, dtype=float)yy, xx = np.argwhere(self.image == 1).Tfor y in range(self.height):for x in range(self.width):if self.image[y, x] == 1:distance_matrix[y, x] = 0else:# 计算当前像素到前景像素点的棋盘距离distances = np.maximum(np.abs(y - yy), np.abs(x - xx))# 取最小值作为当前像素的距离值distance_matrix[y, x] = np.min(distances)return distance_matrix# 初始化输入图像:480x480的全黑背景
image = np.zeros((480, 480), dtype=np.uint8)
# 取三个前景像素点
image[100, 200] = 1
image[200, 100] = 1
image[300, 300] = 1# 显示原始图像
plt.figure(figsize=(5, 5))
plt.scatter([100, 200, 300], [200, 100, 300], color='white', marker='o')
plt.imshow(image, cmap='gray')
plt.title('原始图像', fontsize=15)# 计算距离变换矩阵
dt = DistanceTransform(image)
euclidean_distance_matrix = dt.Euclidean_distance_transform()
manhattan_distance_matrix = dt.D4_distance_transform()
chessboard_distance_matrix = dt.D8_distance_transform()# 输出欧氏、城区和棋盘的距离矩阵
print("欧氏距离的变换矩阵:\n", euclidean_distance_matrix)
print("城区距离的变换矩阵:\n", manhattan_distance_matrix)
print("棋盘距离的变换矩阵:\n", chessboard_distance_matrix)# 可视化距离变换结果
plt.figure(figsize=(15, 5))# 欧氏距离变换
plt.subplot(1, 3, 1)
plt.imshow(euclidean_distance_matrix, cmap='gray')
plt.colorbar(shrink=0.8)
plt.title('欧氏距离', fontsize=15)
plt.axis('off')# 城区距离变换
plt.subplot(1, 3, 2)
plt.imshow(manhattan_distance_matrix, cmap='gray')
plt.colorbar(shrink=0.8)
plt.title('城区距离', fontsize=15)
plt.axis('off')# 棋盘距离变换
plt.subplot(1, 3, 3)
plt.imshow(chessboard_distance_matrix, cmap='gray')
plt.colorbar(shrink=0.8)
plt.title('棋盘距离', fontsize=15)
plt.axis('off')plt.tight_layout()
plt.show()

http://www.ppmy.cn/ops/5447.html

相关文章

ISP图像处理pipeline简介2

上一篇文章介绍了ISP pipeline中的3A和镜头传感器校正。现在继续介绍pipeline中的其它流程。 上一篇文章主要讲了镜头,sensor相关的处理流程,pipeline接下来的一步是颜色插值。 sensor直接输出的图片是raw格式,需要经过isp处理之后才是平时常…

reportlab 生成pdf文件 (python)

1 安装 pip install reportlab2 应用场景 通过网页动态生成PDF文档大量的报告和数据发布用XML一步生成PDF 官网案例 3 PLATYPUS Platypus是“Page Layout and Typography Using Scripts”,是使用脚本的页面布局和印刷术的缩写,这是一个高层次页面布局…

通过excel获取网站的数据信息

通过excel获取网站的数据信息,有时候网站数据的表格很多数据,需要梳理到表格中,如何用excel表格直接读取网站数据 备注问题:表格内容有时候在一行,很长,不方便处理 bug问题:不要多选&#xff0…

html5与css3前端学习笔记

一、前端页面开发流程 创建页面项目目录使用Photoshop对效果图切图,切出网页制作中需要的小图片将装饰类图像合并,制作成雪碧图结合Photoshop和代码编辑器,参照效果图,进行html和css代码书写,制作页面 二、CSS权重 …

C++进阶——继承

前言:从这篇文章开始,我们进入C进阶知识的分享,在此之前,我们需要先来回顾一个知识: C语言有三大特性,分别是封装、继承和多态,而我们前边所分享的各种容器类,迭代器等,…

vue admin pro 角色不同显示不同页面

从后端获取动态路由,并根据不同角色侧边栏有不同页面,此框架有写好我们只需做部分修改,文章最后贴出渲染页面代码 1,src/config/net.config.js中按照官方注释改后端接口 // 默认的接口地址,开发环境和生产环境都会走…

[阅读笔记18][CITING]LARGE LANGUAGE MODELS CREATE CURRICULUM FOR INSTRUCTION TUNING

这篇论文是23年10月提交到arxiv上的,也是用大模型蒸馏小模型的思路。 作者在这篇论文中提出了课程指令微调,大体流程如下图所示,教师模型给出一个问题,让学生模型回答一下,这时候学生回答大概率不够准确,这…

算法训练营第25天回溯(分割)

回溯算法(分割) 131.分割回文串 力扣题目链接(opens new window) 题目 给定一个字符串 s,将 s 分割成一些子串,使每个子串都是回文串。 返回 s 所有可能的分割方案。 示例: 输入: “aab” 输出: [ [“aa”,“b”], [“a”,“…