Skip to main content

Implementation of Dijkstra's Shortest Path algorithm on 3D images.

Project description

Build Status PyPI version

dijkstra3d

Dijkstra's Shortest Path variants for 6, 18, and 26-connected 3D Image Volumes or 4 and 8-connected 2D images.

import dijkstra3d
import numpy as np

field = np.ones((512, 512, 512), dtype=np.int32)
source = (0,0,0)
target = (511, 511, 511)

# path is an [N,3] numpy array i.e. a list of x,y,z coordinates
# terminates early, default is 26 connected
path = dijkstra3d.dijkstra(field, source, target, connectivity=26) 
path = dijkstra3d.dijkstra(field, source, target, bidirectional=True) # 2x memory usage, faster

# Use distance from target as a heuristic (A* search)
# Does nothing if bidirectional=True (it's just not implemented)
path = dijkstra3d.dijkstra(field, source, target, compass=True) 

parents = dijkstra3d.parental_field(field, source=(0,0,0), connectivity=6) # default is 26 connected
path = dijkstra3d.path_from_parents(parents, target=(511, 511, 511))
print(path.shape)

# Given a boolean label "field" and a source vertex, compute 
# the anisotropic euclidean distance from the source to all labeled vertices.
dist_field = dijkstra3d.euclidean_distance_field(field, source=(0,0,0), anisotropy=(4,4,40))

# Given a numerical field, for each directed edge from adjacent voxels A and B, 
# use B as the edge weight. In this fashion, compute the distance from a source 
# point for all finite voxels.
dist_field = dijkstra3d.distance_field(field, source=(0,0,0))

Perform dijkstra's shortest path algorithm on a 3D image grid. Vertices are voxels and edges are the nearest neighbors. For 6 connected images, these are the faces of the voxel (L1: manhattan distance), 18 is faces and edges, 26 is faces, edges, and corners (L: chebyshev distance). For given input voxels A and B, the edge weight from A to B is B and from B to A is A. All weights must be finite and non-negative (incl. negative zero).

What Problem does this Package Solve?

This package was developed in the course of exploring TEASAR skeletonization of 3D image volumes (now available in Kimimaro). Other commonly available packages implementing Dijkstra used matricies or object graphs as their underlying implementation. In either case, these generic graph packages necessitate explicitly creating the graph's edges and vertices, which turned out to be a significant computational cost compared with the search time. Additionally, some implementations required memory quadratic in the number of vertices (e.g. an NxN matrix for N nodes) which becomes prohibitive for large arrays. In some cases, a compressed sparse matrix representation was used to remain within memory limits.

Neither of graph construction nor quadratic memory pressure are necessary for an image analysis application. The edges between voxels (3D pixels) are regular and implicit in the rectangular structure of the image. Additionally, the cost of each edge can be stored a single time instead of 26 times in contiguous uncompressed memory regions for faster performance.

C++ Use

#include <vector>
#include "dijkstra3d.hpp"

// 3d array represented as 1d array
float* labels = new float[512*512*512](); 

// x + sx * y + sx * sy * z
int source = 0 + 512 * 5 + 512 * 512 * 3; // coordinate <0, 5, 3>
int target = 128 + 512 * 128 + 512 * 512 * 128; // coordinate <128, 128, 128>

vector<unsigned int> path = dijkstra::dijkstra3d<float>(
  labels, /*sx=*/512, /*sy=*/512, /*sz=*/512,
  source, target, /*connectivity=*/26 // 26 is default
);

vector<unsigned int> path = dijkstra::bidirectional_dijkstra3d<float>(
  labels, /*sx=*/512, /*sy=*/512, /*sz=*/512,
  source, target, /*connectivity=*/26 // 26 is default
);

// A* search using a distance to target heuristic
vector<unsigned int> path = dijkstra::compass_guided_dijkstra3d<float>(
  labels, /*sx=*/512, /*sy=*/512, /*sz=*/512,
  source, target, /*connectivity=*/26 // 26 is default
);

uint32_t* parents = dijkstra::parental_field3d<float>(
  labels, /*sx=*/512, /*sy=*/512, /*sz=*/512, 
  source, /*connectivity=*/26 // 26 is default
);
vector<unsigned int> path = dijkstra::query_shortest_path(parents, target);


float* field = dijkstra::euclidean_distance_field3d<float>(
  labels, 
  /*sx=*/512, /*sy=*/512, /*sz=*/512, 
  /*wx=*/4, /*wy=*/4, /*wz=*/40, 
  source
);

float* field = dijkstra::distance_field3d<float>(labels, /*sx=*/512, /*sy=*/512, /*sz=*/512, source);

Python pip Binary Installation

pip install dijkstra3d

Python pip Source Installation

Requires a C++ compiler.

pip install numpy
pip install dijkstra3d

Python Direct Installation

Requires a C++ compiler.

git clone https://github.com/seung-lab/dijkstra3d.git
cd dijkstra3d
virtualenv -p python3 venv
source venv/bin/activate
pip install -r requirements.txt
python setup.py develop

Performance

I ran three algorithms on a field of ones from the bottom left corner to the top right corner of a 512x512x512 int8 image using a 3.7 GHz Intel i7-4920K CPU. Unidirectional search takes about 42 seconds (3.2 MVx/sec) with a maximum memory usage of about 1300 MB. In the unidirectional case, this test forces the algorithm to process nearly all of the volume (dijkstra aborts early when the target is found). In the bidirectional case, the volume is processed in about 11.8 seconds (11.3 MVx/sec) with a peak memory usage of about 2300 MB. The A* version processes the volume in 0.5 seconds (268.4 MVx/sec) with an identical memory profile to unidirectional search. A* works very well in this simple case, but may not be superior in all configurations.

Theoretical unidirectional memory allocation breakdown: 128 MB source image, 512 MB distance field, 512 MB parents field (1152 MB). Theoretical bidirectional memory allocation breakdown: 128 MB source image, 2x 512 distance field, 2x 512 MB parental field (2176 MB).

Fig. 1: A benchmark of dijkstra.dijkstra run on a 512<sup>3</sup> voxel field of ones from bottom left source to top right target. (black) unidirectional search (blue) bidirectional search (red) A* search aka compass=True.
Fig. 1: A benchmark of dijkstra.dijkstra run on a 5123 voxel field of ones from bottom left source to top right target. (black) unidirectional search (blue) bidirectional search (red) A* search aka compass=True.

import numpy as np
import time
import dijkstra3d

field = np.ones((512,512,512), order='F', dtype=np.int8)
source = (0,0,0)
target = (511,511,511)

path = dijkstra3d.dijkstra(field, source, target) # black line
path = dijkstra3d.dijkstra(field, source, target, bidirectional=True) # blue line
path = dijkstra3d.dijkstra(field, source, target, compass=True) # red line

Fig. 2: A benchmark of dijkstra.dijkstra run on a 50<sup>3</sup> voxel field of random integers of increasing variation from random source to random target. (blue/squares) unidirectional search (yellow/triangles) bidirectional search (red/diamonds) A* search aka .compass=True.
Fig. 2: A benchmark of dijkstra.dijkstra run on a 503 voxel field of random integers of increasing variation from random source to random target. (blue/squares) unidirectional search (yellow/triangles) bidirectional search (red/diamonds) A* search aka compass=True.

import numpy as np
import time
import dijkstra3d

N = 250
sx, sy, sz = 50, 50, 50

def trial(bi, compass):
  for n in range(0, 100, 1):
    accum = 0
    for i in range(N):
      if n > 0:
        values = np.random.randint(1,n+1, size=(sx,sy,sz))
      else:
        values = np.ones((sx,sy,sz))
      values = np.asfortranarray(values)
      start = np.random.randint(0,min(sx,sy,sz), size=(3,))
      target = np.random.randint(0,min(sx,sy,sz), size=(3,))  

      s = time.time()
      path_orig = dijkstra3d.dijkstra(values, start, target, bidirectional=bi, compass=compass)
      accum += (time.time() - s)

    MVx_per_sec = N * sx * sy * sz / accum / 1000000
    print(n, ',', '%.3f' % MVx_per_sec)

print("Unidirectional")
trial(False, False)
print("Bidirectional")
trial(True, False)
print("Compass")
trial(False, True)

What is that pairing_heap.hpp?

Early on, I anticipated using decrease key in my heap and implemented a pairing heap, which is supposed to be an improvement on the Fibbonacci heap. However, I ended up not using decrease key, and the STL priority queue ended up being faster. If you need a pairing heap outside of boost, check it out.

References

  1. E. W. Dijkstra. "A Note on Two Problems in Connexion with Graphs" Numerische Mathematik 1. pp. 269-271. (1959)
  2. E. W. Dijkstra. "Go To Statement Considered Harmful". Communications of the ACM. Vol. 11, No. 3, pp. 147-148. (1968)
  3. Pohl, Ira. "Bi-directional Search", in Meltzer, Bernard; Michie, Donald (eds.), Machine Intelligence, 6, Edinburgh University Press, pp. 127–140. (1971)

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

dijkstra3d-1.4.0.tar.gz (301.1 kB view details)

Uploaded Source

Built Distributions

dijkstra3d-1.4.0-cp38-cp38-manylinux1_x86_64.whl (1.1 MB view details)

Uploaded CPython 3.8

dijkstra3d-1.4.0-cp37-cp37m-manylinux1_x86_64.whl (1.1 MB view details)

Uploaded CPython 3.7m

dijkstra3d-1.4.0-cp36-cp36m-manylinux1_x86_64.whl (1.1 MB view details)

Uploaded CPython 3.6m

dijkstra3d-1.4.0-cp35-cp35m-manylinux1_x86_64.whl (1.0 MB view details)

Uploaded CPython 3.5m

dijkstra3d-1.4.0-cp27-cp27m-manylinux1_x86_64.whl (1.0 MB view details)

Uploaded CPython 2.7m

dijkstra3d-1.4.0-cp27-cp27m-macosx_10_14_intel.whl (259.1 kB view details)

Uploaded CPython 2.7m macOS 10.14+ intel

File details

Details for the file dijkstra3d-1.4.0.tar.gz.

File metadata

  • Download URL: dijkstra3d-1.4.0.tar.gz
  • Upload date:
  • Size: 301.1 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/1.12.1 pkginfo/1.4.2 requests/2.20.1 setuptools/40.6.2 requests-toolbelt/0.8.0 tqdm/4.28.1 CPython/3.6.6

File hashes

Hashes for dijkstra3d-1.4.0.tar.gz
Algorithm Hash digest
SHA256 a9a52fa28eacc057060f81728aeb37d3e49c0974bf58699c189dfa5e4b4dc0e6
MD5 f7efd312745ca2e9ace6f6c63631a1f9
BLAKE2b-256 e058ec760d0409e1e1f9d39c53f3edc2a27c41895eb3b1f905982708b780a78a

See more details on using hashes here.

Provenance

File details

Details for the file dijkstra3d-1.4.0-cp38-cp38-manylinux1_x86_64.whl.

File metadata

  • Download URL: dijkstra3d-1.4.0-cp38-cp38-manylinux1_x86_64.whl
  • Upload date:
  • Size: 1.1 MB
  • Tags: CPython 3.8
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/1.12.1 pkginfo/1.4.2 requests/2.20.1 setuptools/40.6.2 requests-toolbelt/0.8.0 tqdm/4.28.1 CPython/3.6.6

File hashes

Hashes for dijkstra3d-1.4.0-cp38-cp38-manylinux1_x86_64.whl
Algorithm Hash digest
SHA256 62efa80a25a5df75ce37f59b225f6e757d985613748b54dad8922ba15b2fb1ed
MD5 b2cad8f5a354331e100209a639a19df1
BLAKE2b-256 7372fb2626d2227c72dd18761c1f331a0a6a7a0fce389c434681f1226efdfa0f

See more details on using hashes here.

Provenance

File details

Details for the file dijkstra3d-1.4.0-cp37-cp37m-manylinux1_x86_64.whl.

File metadata

  • Download URL: dijkstra3d-1.4.0-cp37-cp37m-manylinux1_x86_64.whl
  • Upload date:
  • Size: 1.1 MB
  • Tags: CPython 3.7m
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/1.12.1 pkginfo/1.4.2 requests/2.20.1 setuptools/40.6.2 requests-toolbelt/0.8.0 tqdm/4.28.1 CPython/3.6.6

File hashes

Hashes for dijkstra3d-1.4.0-cp37-cp37m-manylinux1_x86_64.whl
Algorithm Hash digest
SHA256 37a0d2e514779b3f967ac8c36d9b9e1c0605a5ca6802d35a7ad2013537486062
MD5 bc6701b24db7356951bd93b98816ef53
BLAKE2b-256 67425bdaff1b1d3018f5f7013ec4f71bfeaf7aabf5b168dfcc1b0c5d6909a7ee

See more details on using hashes here.

Provenance

File details

Details for the file dijkstra3d-1.4.0-cp36-cp36m-manylinux1_x86_64.whl.

File metadata

  • Download URL: dijkstra3d-1.4.0-cp36-cp36m-manylinux1_x86_64.whl
  • Upload date:
  • Size: 1.1 MB
  • Tags: CPython 3.6m
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/1.12.1 pkginfo/1.4.2 requests/2.20.1 setuptools/40.6.2 requests-toolbelt/0.8.0 tqdm/4.28.1 CPython/3.6.6

File hashes

Hashes for dijkstra3d-1.4.0-cp36-cp36m-manylinux1_x86_64.whl
Algorithm Hash digest
SHA256 1e31843a8fd312bd7069f3949a85fd475e43262b960109e9f0ea35c88895273d
MD5 d4d1edab592e02ae54c6e0607306eb58
BLAKE2b-256 510afbf568e8c58f41a94e38dbb377e9522872e162b0526d4f4f0efff34b2441

See more details on using hashes here.

Provenance

File details

Details for the file dijkstra3d-1.4.0-cp35-cp35m-manylinux1_x86_64.whl.

File metadata

  • Download URL: dijkstra3d-1.4.0-cp35-cp35m-manylinux1_x86_64.whl
  • Upload date:
  • Size: 1.0 MB
  • Tags: CPython 3.5m
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/1.12.1 pkginfo/1.4.2 requests/2.20.1 setuptools/40.6.2 requests-toolbelt/0.8.0 tqdm/4.28.1 CPython/3.6.6

File hashes

Hashes for dijkstra3d-1.4.0-cp35-cp35m-manylinux1_x86_64.whl
Algorithm Hash digest
SHA256 5e3a41076ccd1865846a58a9a50d34ba31b07381615176957e688c8ae45c320e
MD5 e7f64634d66169b76f5807bba46d98d2
BLAKE2b-256 38c4d73a23fbc3e9a9880cb24446cbbfe143151d6717ffebfb3bbdd35289ab01

See more details on using hashes here.

Provenance

File details

Details for the file dijkstra3d-1.4.0-cp27-cp27m-manylinux1_x86_64.whl.

File metadata

  • Download URL: dijkstra3d-1.4.0-cp27-cp27m-manylinux1_x86_64.whl
  • Upload date:
  • Size: 1.0 MB
  • Tags: CPython 2.7m
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/1.12.1 pkginfo/1.4.2 requests/2.20.1 setuptools/40.6.2 requests-toolbelt/0.8.0 tqdm/4.28.1 CPython/3.6.6

File hashes

Hashes for dijkstra3d-1.4.0-cp27-cp27m-manylinux1_x86_64.whl
Algorithm Hash digest
SHA256 9345e953315c2a0bf14314b452de27eb5ebb89e3a0a9354afa1b372add469a21
MD5 e8dec1a4bf41b172b9dededc659f9532
BLAKE2b-256 3c4483112b9d023739023ccaecc5fad299709ea587ffa1806a6cb37bbf0cba16

See more details on using hashes here.

Provenance

File details

Details for the file dijkstra3d-1.4.0-cp27-cp27m-macosx_10_14_intel.whl.

File metadata

  • Download URL: dijkstra3d-1.4.0-cp27-cp27m-macosx_10_14_intel.whl
  • Upload date:
  • Size: 259.1 kB
  • Tags: CPython 2.7m, macOS 10.14+ intel
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/1.13.0 pkginfo/1.5.0.1 requests/2.22.0 setuptools/40.6.2 requests-toolbelt/0.9.1 tqdm/4.32.1 CPython/3.7.2

File hashes

Hashes for dijkstra3d-1.4.0-cp27-cp27m-macosx_10_14_intel.whl
Algorithm Hash digest
SHA256 6e4ef58d4b3b50389d056f499634f51f94a72a0b8322817c48fb61b06fedda5a
MD5 838e85ebe76f6a32b32a38476d9cd339
BLAKE2b-256 3f45bb7fd54eb64117c9f835376c8d059bbbc6a0ea357c6da1b3445455783d73

See more details on using hashes here.

Provenance

Supported by

AWS AWS Cloud computing and Security Sponsor Datadog Datadog Monitoring Fastly Fastly CDN Google Google Download Analytics Microsoft Microsoft PSF Sponsor Pingdom Pingdom Monitoring Sentry Sentry Error logging StatusPage StatusPage Status page