n维数字图像欧氏距离变换算法

算法简介

该算法主要用于在三维图像中计算有效体素之间的最短欧几里得距离。

算法出处:NEW ALGORITHMS FOR EUCLIDEAN DISTANCE TRANSFORMATION OF AN n-DIMENSIONAL DIGITIZED PICTURE WITH APPLICATIONS
算法在体渲染加速中的应用:Accelerated Volume Rendering with Chebyshev Distance Maps

算法过程

公式表示

符号说明
图像维度: L × M × N L \times M \times N L×M×N,其中 1 ≤ i ≤ L , 1 ≤ j ≤ M , 1 ≤ k ≤ N 1\leq i \leq L,1\leq j \leq M, 1\leq k \leq N 1iL,1jM,1kN
输入图像: F = { f i j k } F=\{f_{ijk}\} F={fijk}
中间图像: G = { g i j k } G=\{g_{ijk}\} G={gijk} H = { h i j k } H=\{h_{ijk}\} H={hijk}
输出图像: S = { s i j k } S=\{s_{ijk}\} S={sijk}

步骤1

输入:F
输出:G
公式:当 f x j k = 0 , 1 ≤ x ≤ L f_{xjk}=0, 1 \leq x \leq L fxjk=0,1xL,求 g i j k = m i n { ( i − x ) 2 } g_{ijk}=min \{ (i-x)^2 \} gijk=min{(ix)2}

f x j k = 0 f_{xjk}=0 fxjk=0 表示在图像中体素值为0是有效体素

计算示例图:
在这里插入图片描述

步骤2

输入:G
输出:H
公式:当 1 ≤ y ≤ M 1 \leq y \leq M 1yM,求 h i j k = m i n { g i j k + ( j − y ) 2 } h_{ijk}=min\{ g_{ijk} + (j-y)^2 \} hijk=min{gijk+(jy)2}

计算示例图:
在这里插入图片描述

步骤3

输入:H
输出:S
公式:当 1 ≤ z ≤ N 1 \leq z \leq N 1zN,求 s i j k = m i n { h i j k + ( k − z ) 2 } s_{ijk}=min\{ h_{ijk} + (k-z)^2 \} sijk=min{hijk+(kz)2}

计算过程和步骤2类似,只是换了维度,因此不再赘述

算法示例

为了描述方便,这里使用二维图像作为操作目标,且每个像素是二值的。由于是二维的,所以步骤会少一步,即无步骤3,最终的输出结果是H

原始图像F,值为0表示有效像素
在这里插入图片描述

中间图像G,因为最后一行无有效像素,所以保持默认值
在这里插入图片描述

最终图像H,像素内存储最近有效像素的距离的平方
在这里插入图片描述


我们将图像H中的值开方,即可得到每个像素的最近有效像素的欧式距离
在这里插入图片描述

代码实现

在计算机上实现

步骤1

前向扫描
输入:F
输出:G’
g 0 j k ′ = L 2 g'_{0jk}=L^2 g0jk=L2
要求 i i i 1 1 1 L L L,如果 f i j k ≠ 0 f_{ijk} \neq 0 fijk=0,则 g i j k ′ = ( g ( i − 1 ) j k ′ + 1 ) 2 g'_{ijk} = ( \sqrt {g'_{(i-1)jk}} + 1)^2 gijk=(g(i1)jk +1)2;否则 g i j k ′ = 0 g'_{ijk}=0 gijk=0

反向扫描
输入:G’
输出:G
g ( L + 1 ) j k = L 2 g_{(L+1)jk}=L^2 g(L+1)jk=L2
要求 i i i L L L 1 1 1,如果 g i j k ′ ≠ 0 g'_{ijk} \neq 0 gijk=0,则 g i j k = m i n { ( g ( i + 1 ) j k + 1 ) 2 , g i j k ′ } g_{ijk} = min \{ ( \sqrt { g_{(i+1)jk} } + 1)^2 , g'_{ijk}\} gijk=min{(g(i+1)jk +1)2,gijk};否则 g i j k = 0 g_{ijk}=0 gijk=0

步骤2

输入:G
输出:H

要求 n n n − r 1 -r_1 r1 r 2 r_2 r2,其中 r 1 ≤ r , r 2 ≤ r , r = g i j k r_1 \leq r, r_2 \leq r, r=\sqrt {g_{ijk}} r1r,r2r,r=gijk ,求 h i j k = m i n { g i ( j + n ) k + n 2 } h_{ijk}=min\{ g_{i(j+n)k} + n^2 \} hijk=min{gi(j+n)k+n2}
注意 1 ≤ j + n ≤ M 1 \leq j+n \leq M 1j+nM,所以要保证 j − r 1 ≥ 1 , j + r 2 ≤ M j-r_1 \geq 1,j+r_2 \leq M jr11,j+r2M,从而 r 1 ≤ j − 1 , r 2 ≤ M − j r_1 \leq j-1, r_2 \leq M-j r1j1,r2Mj
所以 r 1 = m i n { r , j − 1 } , r 2 = m i n { r , M − j } r_1 = min \{ r, j-1 \},r_2 = min\{ r,M-j \} r1=min{r,j1},r2=min{r,Mj}

步骤3

输入:H
输出:S

要求 n n n − r -r r r r r,其中 r = h i j k r=\sqrt {h_{ijk}} r=hijk ,求 s i j k = m i n { h i j ( k + n ) + n 2 } s_{ijk}=min\{ h_{ij(k+n)}+ n^2 \} sijk=min{hij(k+n)+n2}
注意 1 ≤ k + n ≤ N 1 \leq k+n \leq N 1k+nN,所以要保证 k − r 1 ≥ 1 , k + r 2 ≤ N k-r_1 \geq 1,k+r_2 \leq N kr11,k+r2N,从而 r 1 ≤ k − 1 , r 2 ≤ M − k r_1 \leq k-1, r_2 \leq M-k r1k1,r2Mk
所以 r 1 = m i n { r , k − 1 } , r 2 = m i n { r , M − k } r_1 = min \{ r, k-1 \},r_2 = min\{ r,M-k \} r1=min{r,k1},r2=min{r,Mk}

编写C++代码

输入数据是三维向量

void edt(vector<vector<vector<float>>>& f)
{
	int N = f.size();
	int M = f[0].size();
	int L = f[0][0].size();

	///步骤1
	///前向扫描
	for (int k = 0; k < N; k++)
	{
		for (int j = 0; j < M; j++)
		{
			float df = L;
			for (int i = 0; i < L; i++)
			{
				if (f[k][j][i] != 0)
				{
					df = df + 1;
				}
				else
				{
					df = 0;
				}
				f[k][j][i] = df * df;
			}
		}
		///反向扫描
		for (int j = 0; j < M; j++)
		{
			float db = L;
			for (int i = L - 1; i >= 0; i--)
			{
				if (f[k][j][i] != 0)
				{
					db = db + 1;
				}
				else
				{
					db = 0;
				}
				f[k][j][i] = std::fmin(f[k][j][i], db * db);
			}
		}
	}
	

	///步骤2
	for (int k = 0; k < N; k++)
	{		
		for (int i = 0; i < L; i++)
		{
			vector<float> buff(M);
			for (int j = 0; j < M; j++)
			{
				buff[j] = f[k][j][i];
			}

			for (int j = 0; j < M; j++)
			{
				float d = buff[j];
				if (d != 0)
				{
					float rMax = int(std::sqrt(d)) + 1;
					float rStart = std::min(rMax, float(j));
					float rEnd = std::min(rMax, float(M - 1 - j));

					for (int n = -rStart; n < rEnd; n++)
					{
						float w = buff[j + n] + n * n;
						if (w < d)
						{
							d = w;
						}
					}
				}
				f[k][j][i] = d;
			}
		}
	}
	
	///步骤3,与步骤2类似
	for (int j = 0; j < M; j++)
	{
		for (int i = 0; i < L; i++)
		{
			vector<float> buff(N);
			for (int k = 0; k < N; k++)
			{
				buff[k] = f[k][j][i];
			}

			for (int k = 0; k < N; k++)
			{
				float d = buff[k];
				if (d != 0)
				{
					float rMax = int(std::sqrt(d)) + 1;
					float rStart = std::min(rMax, float(k));
					float rEnd = std::min(rMax, float(N - 1 - k));

					for (int n = -rStart; n < rEnd; n++)
					{
						float w = buff[k + n] + n * n;
						if (w < d)
						{
							d = w;
						}
					}
				}
				f[k][j][i] = d;
			}
		}
	}
}

测试代码

#include<iostream>
#include<cmath>
#include<vector>
using namespace std;

int main(int argc, char *argv[])
{
	//图像维度4x4x1
	vector<vector<vector<float>>> F(1, vector<vector<float>>(4, vector<float>(4)));
	
	//输入图像数据
	F[0][0][0] = 1; F[0][0][1] = 1; F[0][0][2] = 1; F[0][0][3] = 0;
	F[0][1][0] = 1; F[0][1][1] = 0; F[0][1][2] = 1; F[0][1][3] = 0;
	F[0][2][0] = 0; F[0][2][1] = 1; F[0][2][2] = 1; F[0][2][3] = 1;
	F[0][3][0] = 1; F[0][3][1] = 1; F[0][3][2] = 1; F[0][3][3] = 1;

	edt(F);

	//输出计算数据
	for (int k = 0; k < F.size(); k++)
	{
		for (int j = 0; j < F[k].size(); j++)
		{
			for (int i = 0; i < F[k][j].size(); i++)
			{
				cout << F[k][j][i] << " ";
			}
			cout << endl;
		}		
	}

	return 0;
}

结果
在这里插入图片描述

相关推荐

  1. C++ 点云单木分割(距离法)

    2024-03-20 20:50:03       37 阅读

最近更新

  1. docker php8.1+nginx base 镜像 dockerfile 配置

    2024-03-20 20:50:03       94 阅读
  2. Could not load dynamic library ‘cudart64_100.dll‘

    2024-03-20 20:50:03       100 阅读
  3. 在Django里面运行非项目文件

    2024-03-20 20:50:03       82 阅读
  4. Python语言-面向对象

    2024-03-20 20:50:03       91 阅读

热门阅读

  1. 动态加载CSS文件

    2024-03-20 20:50:03       45 阅读
  2. 如何从零开始拆解uni-app开发的vue项目(二)

    2024-03-20 20:50:03       40 阅读
  3. Python 中可以用来生成 SVG 图的库

    2024-03-20 20:50:03       43 阅读
  4. linux系统中的PS命令详解

    2024-03-20 20:50:03       47 阅读
  5. 主流开发语言和开发环境介绍

    2024-03-20 20:50:03       39 阅读
  6. DNS劫持怎么预防?

    2024-03-20 20:50:03       45 阅读
  7. 去除项目git的控制 端口号的关闭

    2024-03-20 20:50:03       39 阅读
  8. 对建造者模式的理解

    2024-03-20 20:50:03       35 阅读
  9. 《建造者模式(极简c++)》

    2024-03-20 20:50:03       47 阅读
  10. Doris案例篇—美团外卖数仓中的应用实践

    2024-03-20 20:50:03       43 阅读
  11. 前端面试小节

    2024-03-20 20:50:03       40 阅读