geodesic distance

1、python实现geodesic距离

其中见到的出现最多的名词之一就是 geodesic distance ,这个单词的中文翻译是测地距离,其实测地距离的意思就是在三维空间中,两点之间的最短路径,归根究底就是最短路径,在三维中间从一个点到另外一个点的路径有无数种,但是最短路径只有一条,那么这个最短路径的长度就是测地距离 geodesic distance

# -*- coding: utf-8 -*-
"""
Created on Mon Jan 24 15:00:35 2022

@author: wmz
"""
import numpy as np
import matplotlib.pyplot as plt
import numpy.ma as ma

# %pylab
from scipy.ndimage.morphology import distance_transform_edt
l = 100
x, y = np.indices((l, l))
center1 = (50, 20)
center2 = (28, 24)
center3 = (30, 50)
center4 = (60,48)
radius1, radius2, radius3, radius4 = 15, 12, 19, 12
circle1 = (x - center1[0])**2 + (y - center1[1])**2 < radius1**2
circle2 = (x - center2[0])**2 + (y - center2[1])**2 < radius2**2
circle3 = (x - center3[0])**2 + (y - center3[1])**2 < radius3**2
circle4 = (x - center4[0])**2 + (y - center4[1])**2 < radius4**2
# 3 circles
img = circle1 + circle2 + circle3 + circle4
mask = ~img.astype(bool)
img = img.astype(float)
m = np.ones_like(img)
m[center1] = 0
#imshow(distance_transform_edt(m), interpolation='nearest')
m = ma.masked_array(distance_transform_edt(m), mask)
plt.imshow(m, interpolation='nearest')
plt.show()

在这里插入图片描述

I implemented the Dijkstra algorithm to apply a geodesic distance transform to the shape above:

# -*- coding: utf-8 -*-
"""
Created on Mon Jan 24 15:43:44 2022

@author: wmz
"""

import numpy
import numpy as np
import matplotlib.pyplot as plt
import numpy.ma as ma
from math import *
from numpy import *

# %pylab
from scipy.ndimage.morphology import distance_transform_edt


def geodesic_distance_transform(m):
    visit_mask = mask.copy() # mask visited cells
    m = m.filled(numpy.inf)
    m[m!=0] = numpy.inf
    distance_increments = numpy.asarray([sqrt(2), 1., sqrt(2), 1., 1., sqrt(2), 1., sqrt(2)])
    connectivity = [(i,j) for i in [-1, 0, 1] for j in [-1, 0, 1] if (not (i == j == 0))]
    cc = unravel_index(m.argmin(), m.shape) # current_cell
    while (~visit_mask).sum() > 0:
        neighbors = [tuple(e) for e in asarray(cc) - connectivity 
                     if not visit_mask[tuple(e)]]
        tentative_distance = [distance_increments[i] for i,e in enumerate(asarray(cc) - connectivity) 
                              if not visit_mask[tuple(e)]]
        for i,e in enumerate(neighbors):
            d = tentative_distance[i] + m[cc]
            if d < m[e]:
                m[e] = d
        visit_mask[cc] = True
        m_mask = ma.masked_array(m, visit_mask)
        cc = unravel_index(m_mask.argmin(), m.shape)
    return m


if __name__ == "__main__":
    
    l = 100
    x, y = np.indices((l, l))
    center1 = (50, 20)
    center2 = (28, 24)
    center3 = (30, 50)
    center4 = (60,48)
    radius1, radius2, radius3, radius4 = 15, 12, 19, 12
    circle1 = (x - center1[0])**2 + (y - center1[1])**2 < radius1**2
    circle2 = (x - center2[0])**2 + (y - center2[1])**2 < radius2**2
    circle3 = (x - center3[0])**2 + (y - center3[1])**2 < radius3**2
    circle4 = (x - center4[0])**2 + (y - center4[1])**2 < radius4**2
    # 3 circles
    img = circle1 + circle2 + circle3 + circle4
    mask = ~img.astype(bool)
    img = img.astype(float)
    m = np.ones_like(img)
    m[center1] = 0
    m = ma.masked_array(distance_transform_edt(m), mask)
    gdt = geodesic_distance_transform(m)
    plt.imshow(gdt, interpolation='nearest')
    plt.colorbar()
    plt.show()

执行时间:

0.22861695289611816 s

在这里插入图片描述

安装软件包

conda install sscikit-fmm
# -*- coding: utf-8 -*-
"""
Created on Mon Jan 24 15:43:44 2022

@author: wmz
"""

import numpy
import numpy as np
import matplotlib.pyplot as plt
import numpy.ma as ma
from math import *
from numpy import *
import skfmm
import time
# %pylab
from scipy.ndimage.morphology import distance_transform_edt


def geodesic_distance_transform(m):
    visit_mask = mask.copy() # mask visited cells
    m = m.filled(numpy.inf)
    m[m!=0] = numpy.inf
    distance_increments = numpy.asarray([sqrt(2), 1., sqrt(2), 1., 1., sqrt(2), 1., sqrt(2)])
    connectivity = [(i,j) for i in [-1, 0, 1] for j in [-1, 0, 1] if (not (i == j == 0))]
    cc = unravel_index(m.argmin(), m.shape) # current_cell
    while (~visit_mask).sum() > 0:
        neighbors = [tuple(e) for e in asarray(cc) - connectivity 
                     if not visit_mask[tuple(e)]]
        tentative_distance = [distance_increments[i] for i,e in enumerate(asarray(cc) - connectivity) 
                              if not visit_mask[tuple(e)]]
        for i,e in enumerate(neighbors):
            d = tentative_distance[i] + m[cc]
            if d < m[e]:
                m[e] = d
        visit_mask[cc] = True
        m_mask = ma.masked_array(m, visit_mask)
        cc = unravel_index(m_mask.argmin(), m.shape)
    return m


if __name__ == "__main__":
    
    l = 100
    x, y = np.indices((l, l))
    center1 = (50, 20)
    center2 = (28, 24)
    center3 = (30, 50)
    center4 = (60,48)
    radius1, radius2, radius3, radius4 = 15, 12, 19, 12
    circle1 = (x - center1[0])**2 + (y - center1[1])**2 < radius1**2
    circle2 = (x - center2[0])**2 + (y - center2[1])**2 < radius2**2
    circle3 = (x - center3[0])**2 + (y - center3[1])**2 < radius3**2
    circle4 = (x - center4[0])**2 + (y - center4[1])**2 < radius4**2
    # 3 circles
    img = circle1 + circle2 + circle3 + circle4
    mask = ~img.astype(bool)
    img = img.astype(float)
    m = np.ones_like(img)
    m[center1] = 0
    m = ma.masked_array(m, mask) # distance_transform_edt(m)
    t1 = time.time()
    # gdt = geodesic_distance_transform(m)
    distance = skfmm.distance(m)
    t2 = time.time()
    eclipsed = t2 - t1
    print(eclipsed, "s")
    # plt.imshow(gdt, interpolation='nearest')
    plt.imshow(distance, interpolation='nearest')

    plt.colorbar()
    plt.show()

执行时间:

0.0009877681732177734 s

在这里插入图片描述
速度skfmm.distance是前者的231倍,效果skfmm.distance还要好于前者。

参考:关于 geodesic distance 的通俗解释
参考:geodesic distance transform in python

2、c++实现geodesic距离

编译与安装参考 GeographicLib 1.52

测试程序:
In order to use GeographicLib from C++ code, you will need to

Include the header files for the GeographicLib classes in your code. E.g.,

#include <GeographicLib/LambertConformalConic.hpp> 

Include the GeographicLib:: namespace prefix to the GeographicLib classes, or include

using namespace GeographicLib; 

in your code.

  • Finally compile and link your code. You have two options here.
    • Use cmake to build your package. If you are familiar with cmake this typically will be far the simplest option.
    • Set the include paths and linking options “manually”.
  • Building your code with cmake. In brief, the necessary steps are:

include in your CMakeLists.txt files

	find_package (GeographicLib REQUIRED)
    add_executable (program source1.cpp source2.cpp)
    target_link_libraries (program ${GeographicLib_LIBRARIES}) 
  • configure your package, e.g., with
 mkdir BUILD
    cd BUILD
    cmake -G "Visual Studio 14" -A x64 \
      -D CMAKE_PREFIX_PATH="C:/Program Files" \
      -D CMAKE_INSTALL_PREFIX="C:/Program Files/testgeog" \
      .. 

Here is a very simple test code, which uses the Geodesic class:

// Small example of using the GeographicLib::Geodesic class
 
#include <iostream>
#include <GeographicLib/Geodesic.hpp>
 
using namespace std;
using namespace GeographicLib;
 
int main() {
  const Geodesic& geod = Geodesic::WGS84();
  // Distance from JFK to LHR
  double
    lat1 = 40.6, lon1 = -73.8, // JFK Airport
    lat2 = 51.6, lon2 = -0.5;  // LHR Airport
  double s12;
  geod.Inverse(lat1, lon1, lat2, lon2, s12);
  cout << s12 / 1000 << " km\n";
}

完整CMakeLists.txt

project (geodesictest)
cmake_minimum_required (VERSION 3.1.0)

find_package (GeographicLib REQUIRED)

if (NOT MSVC)
  set (CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE)
endif ()

add_executable (${PROJECT_NAME} example-Geodesic-small.cpp)
target_link_libraries (${PROJECT_NAME} ${GeographicLib_LIBRARIES})

if (MSVC)
  get_target_property (_LIBTYPE ${GeographicLib_LIBRARIES} TYPE)
  if (_LIBTYPE STREQUAL "SHARED_LIBRARY")
    # On Windows systems, copy the shared library to build directory
    add_custom_command (TARGET ${PROJECT_NAME} POST_BUILD
      COMMAND ${CMAKE_COMMAND} -E
      copy $<TARGET_FILE:${GeographicLib_LIBRARIES}> ${CMAKE_CFG_INTDIR}
      COMMENT "Copying shared library for GeographicLib")
  endif ()
endif () 

参考:C library for Geodesics 1.52
参考:GeographicLib 1.52
参考:

### Geodesic Loss Function Explanation and Implementation In the context of machine learning, geodesic distance refers to the shortest path between two points on a curved surface or manifold. This concept is particularly relevant when working with data that lies on non-Euclidean spaces such as graphs or manifolds[^1]. The geodesic loss function measures how well predictions align with these distances. #### Mathematical Background The geodesic loss can be formulated based on Riemannian geometry principles where it penalizes deviations from true geodesics computed over surfaces defined by input features. For instance, given predicted values \( \hat{y} \) and ground truth labels \( y \), one common approach involves minimizing differences along intrinsic paths rather than direct Euclidean norms: \[ L_{geo}(y,\hat{y}) = ||d_g(y)- d_g(\hat{y})||_p, \] where \( d_g(x_i,x_j) \) represents the geodesic distance between any pair of elements within dataset space according to their embedding structure. Below follows an example Python implementation leveraging PyTorch for computing this type of custom geometric error metric during training processes involving neural networks like those mentioned under graph-based architectures (e.g., Graph Neural Networks). ```python import torch from scipy.spatial.distance import pdist,squareform import numpy as np def compute_geodesic_distance(X): """Compute pairwise geodesic distances.""" dist_matrix = squareform(pdist(X.detach().cpu().numpy(), 'euclidean')) n = X.shape[0] # Floyd-Warshall algorithm approximation here... D = dist_matrix.copy() for k in range(n): for i in range(n): for j in range(n): if D[i,j] > D[i,k]+D[k,j]: D[i,j]=D[i,k]+D[k,j] return torch.tensor(D).float() class GeodesicLoss(torch.nn.Module): def __init__(self,p=2): super(GeodesicLoss,self).__init__() self.p=p def forward(self,y_pred,y_true): pred_geo_dist = compute_geodesic_distance(y_pred) true_geo_dist = compute_geodesic_distance(y_true) diff = torch.abs(pred_geo_dist - true_geo_dist)**self.p loss_val = torch.mean(diff)/torch.numel(diff) return loss_val ``` This code defines both utility functions needed alongside actual class definition implementing desired behavior via inheritance pattern typical among deep-learning frameworks' modules design patterns today.
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

落花逐流水

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

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

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

打赏作者

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

抵扣说明:

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

余额充值