Coder Social home page Coder Social logo

phcerdan / sgext Goto Github PK

View Code? Open in Web Editor NEW
4.0 2.0 2.0 21.18 MB

Spatial Graph Extractor. Library and scripts to study graphs extracted from binary images, or to generate graphs and analyze them completely in-silico. Used at least in biopolymers simulations and vascular networks.

License: GNU General Public License v3.0

Python 5.99% CMake 4.80% C++ 87.70% Jupyter Notebook 0.60% Dockerfile 0.09% Shell 0.72% PowerShell 0.04% C 0.06%
graph thinning skeletonization binarization segmentation image-processing itk dgtal vtk thin

sgext's Introduction

Build Status

SGEXT

SpatialGraph Extractor.

Library with utilities to handle graphs extracted from binary images.

Provides scripts to generate a thin/skeletonized image from binary images (segmentations). A thin image is a one pixel wide image, conserving the same topology (same number of holes, and shapes) than the input binary image.

A distance map can be used for the thin image to be in the centerline of the input.

The thinning algorithm used was contributed by the author to the DGtal library, based on the work of Couprie and Bertrand [1]

The thin output can also be converted to a Spatial Graph, this is a regular graph, an adjacency list holding the nodes and edges, plus all the geometrical information. In the case of a nodes/vertices, a spatial node with 3D location. In edges, a spatial edge, a data structure with a consecutive list of points connecting the nodes.

Using histo.hpp from: https://github.com/phcerdan/histo-header SHA: 556ada3ff79c0180a0cbec36ff29a30da5acb367

Python

SGEXT is wrapped to python using pybind11, and uploaded regularly to pypi for all platforms (Linux, Windows, MacOS) and multiple python version (from 3.5 to latest) using azure-pipelines.

pip install sgext
import sgext
# Read image from file into a sgext_image
input_filename="/path/to/binary_image.nrrd" # or any format that ITK can read
sgext_image = sgext.itk.read_as_binary(input_filename)
# Or from a numpy array:
sgext_image = sgext.itk.IUC3P()
sgext_image.from_pyarray(mask)
# Or convert from an existing ITK image via the numpy bridge:
sgext_image = sgext.itk.IUC3P()
sgext_image.from_pyarray(itk.GetArrayFromImage(itk_image))
thin_image = sgext.scripts.thin(input=sgext_image,
                   tables_folder= sgext.tables_folder,
                   skel_type="end",
                   select_type="first",
                   persistence=2,
                   visualize=False,
                   verbose=True
                   )
thin_filename ="/path/to/thin_image.nrrd"
sgext.itk.write(thin_image, thin_filename)

Usage

The C++ scripts are in folder cpp-scripts. The inputImage to these scripts is a label/binary image or sometimes a float grayscale image. All the scripts provide a --help or -h option for guidance.

Distance Map

Create a distance map using DGtal most precise way with Lp metric. The output is a float/double image with pixels storing the distance to the background (heavy image).

create_distance_map \
    -i inputImage.nrrd \
    -o outputFolder \
    -v # verbose flag --recommended--

In verbose mode, the output would be:

New Block [Create Distance Map]
EndBlock [Create Distance Map] (108686 ms)
Time elapsed: 108

the outputFolder will be populated with inputImage_DMAP.nrrd (heavy image).

Thinning

Using VoxelComplex in DGtal, based on Bertrand and Couprie research in digital topology. Ensures topology consistency, and implements a way to perform a prunning on the branches based on local information.

thin \
    -i inputImage.nrrd \
    -o outputFolder \
    -s 1isthmus \
    -c dmax \ # Requires distance map, ensures centrality of the thinning
    -d distanceMapImage.nrrd \
#    -p 2 \ Optional persistence, useful for noisy images, default to 0.
    -v # verbose flag --recommended--

Get radius of vesselnes

The distance map can also be used as a really good approximation to vesselnes radius. In order to get this information for our skeletonized image we can use the script mask_distance_map_with_thin_image

mask_distance_map_with_thin_image \
    -i inputDistanceMapImage.nrrd \
    -m inputSkeletonizedImage.nrrd \
    -o outputFolder \
    -v # verbose flag --recommended--

This will generate an image skeletonizedImage_DMAP_MASKED in the output folder

Graph processing

To convert the skeletonized image into a graph we used boost graph. The following script

analyze_graph \
    -i inputSkeletonizedImage.nrrd
    -o outputFolder \ # This will generate a .dot file with all the graph information
    -r \ # reduce graph, convert chain nodes (degree 2) into edge points.
    -c \ # removeExtraEdges (remove edges created because full connectivity)
    -m \ # mergeThreeConnectedNodes
    -v

More options are possible, use --help for details.

Contributors

  • Pablo Hernandez-Cerdan

[1]: Couprie and Bertrand, “Asymmetric Parallel 3D Thinning Scheme and Algorithms Based on Isthmuses.” Pattern Recognition Letters. June, 2016. DOI:10.1016/j.patrec.2015.03.014

sgext's People

Contributors

labode avatar phcerdan avatar

Stargazers

 avatar  avatar  avatar  avatar

Watchers

 avatar  avatar

sgext's Issues

Handle GRAPH_SPACING more consistently.

The better approach would be to store the points/geometry of the spatial graph in the Image Index domain. And then compute graph properties having into account graph_spacing / image_spacing.

analyze_graph

Hi. So happy to use this package for graph conversion from image mask. Just wondering if the analyze_graph method incorporated within the python library? If not, can you suggest how I can use it within python?

metadata of skeletonized image

3D Segmented image:

NRRD0005
# Complete NRRD file format specification at:
# http://teem.sourceforge.net/nrrd/format.html
type: unsigned char
dimension: 3
space: right-anterior-superior
sizes: 211 272 315
space directions: (-1,-0,0) (-0,-1,-0) (0,0,1)
kinds: domain domain domain
encoding: gzip
space origin: (-52,-58,100)
measurement frame: (1,0,0) (0,1,0) (0,0,1)

Skeletonized image:

NRRD0004
# Complete NRRD file format specification at:
# http://teem.sourceforge.net/nrrd/format.html
type: unsigned char
dimension: 3
space: left-posterior-superior
sizes: 207 268 299
space directions: (1,0,0) (0,1,0) (0,0,1)
kinds: domain domain domain
encoding: raw
space origin: (1,2,14)

During skeletonization (after applying the 'thin'script), size of image and space origin change.

Radius calculation issue

Hi, I want to express my gratitude for providing such a useful package. I'm particularly interested in using the Sgext package to create graphs. However, I've encountered an issue with the radius calculation. Previously, I was using the branch_radius function for this purpose, but it seems to be unavailable now. Could you please guide me on how to calculate the radius value in the current version?
Additionally, I attempted to use 'create_distance_map' followed by 'create_vertex_to_radius_map', but all the radius values returned as one. I suspect there might be an error in my approach to calculating the radius value.

Make ITK Optional

ITK is only used in some cpp-scripts, in VISUALIZE blocks and only in random test in core (the header is templated on TImage): test_transform_to_physical_point.cpp.

This should involve CMake work only.

zlib RuntimeError in Windows

Description

I ran across this package through the ITK discourse (https://discourse.itk.org/t/geometric-medial-axis-representation-from-binary-thinning/1702/3) and thought it looked perfect for some of the my workflows.

I have a decent amount of experience with the python side of ITK/VTK, and was able to get the distance map out without a problem. However, when I try to run the thin script it throws an input/output zlib error:

RuntimeError: loadTable error in: D:\Desktop\itk_conda\env\MARS\lib\site-packages\sgext\tables\simplicity_table26_6.zlib with exception: zlib error: iostream stream error

I tried by implicitly stating the tables_folder, and also importing the tables folder, as shown in #46.

I was able to decompress the file within python after importing zlib, but it seems like the required information can't be passed to the algorithm directly.

I've tried:

  • with and without the implicit typing (e.g. skel_type=str("isthmus") ).
  • variations on the variables skel_type and select_type.
  • with and without the optional arguments (i.e. profile, verbose, visualize).

I am trying to build the project currently, because this does seem very useful. Still, I'd like to just tell my lab that they can install the package off pypi. Thanks for your assistance and the awesome library!

How to reproduce

Windows 10 with anaconda python
python 3.7.7

Package versions in python environment:
itk 5.1.0.post2
sgext 0.9.10
zlib 1.2.11.1

import os
import pathlib
import SimpleITK as sitk
import sgext
from sgext import tables_folder

directory = pathlib.Path(r"D:\Desktop")

os.chdir(directory)

input_filename = "Seg_sphereVOI.mhd"
binary_img = sgext.itk.read_as_binary(input_filename)

distance_map = sgext.scripts.create_distance_map(input=binary_img, use_itk=False, verbose=True)

thin = sgext.scripts.thin(input=binary_img,
                          skel_type=str("isthmus"),
                          select_type=str("dmax"),
                          tables_folder=tables_folder,
                          persistence=int(2),
                          input_distance_map_image=distance_map,
                          profile=bool(True),
                          verbose=bool(True),
                          visualize=bool(False))


Output

New Block [thin_function parameters:]
  skel_type_str: isthmus
  skel_select_type_str: dmax
   -- provided distance_map_image.
  persistence: 2
  profile: 1
  verbose: 1
  visualize: 0
  ----------
EndBlock [thin_function parameters:] (12 ms)
New Block [construct with table]
---------------------------------------------------------------------------
RuntimeError                              Traceback (most recent call last)
<ipython-input-7-7336ac649fb4> in <module>
      7                           profile=bool(True),
      8                           verbose=bool(True),
----> 9                        visualize=bool(False))

RuntimeError: loadTable error in: D:\Desktop\itk_conda\env\MARS\lib\site-packages\sgext\tables\simplicity_table26_6.zlib with exception: zlib error: iostream stream error

Exception in create_distance_map in Windows

Reported in https://discourse.vtk.org/t/vmtk-center-lines-extraction-how-to-accelerate-it-and-how-to-extract-center-lines-from-small-vessel/4103/13

sgextImg = sgext.itk.IUC3P()
sgextImg.from_pyarray(mask*255)
dmap_image = sgext.scripts.create_distance_map(input=sgextImg, verbose=True)
thin_image = sgext.scripts.thin(input=sgextImg,
                   table_folder= sgext.tables_folder,
                   skel_type="end",
                   select_type="dmax",
                   input_distance_map_image=dmap_image,
                   persistence=2,
                   visualize=False,
                   verbose=True
                   )

dmap_image = sgext.scripts.create_distance_map(input=sgextImg, verbose=True)

unhandled win32 exception occurred in python.exe

My environment is:

win10
Python 3.7.5 (default, Oct 31 2019, 15:18:51) [MSC v.1916 64 bit (AMD64)] :: Anaconda, Inc. on win32
sgext: I don't know the version. I think it is the latest version.

Issues with gcc10.1 with Release and RelWithDebInfo in thin_function

Solved after downgrading to gcc9.3.

Segfault. It happen in DGtal, in addFaces when constructing the voxel complex from the image_set. The recursion never ends.

gdb --args ./cpp-scripts/thin -i ../src/images/bX3D.tif -o results -f black --skel end --select dmax -d ./results/bX3D_DMAP.nrrd -p 2 -v -t

I don't really want to go deeper, just in case is a gcc bug.
it doesn't happen in Debug (DGtal and sgext in Debug mode)

I reported in gcc a bug showing a regression when using initializer lists, but not sure if related.

sgext file format IUC3P()

__Hi! I have used sgext to compute the thinning algorithm on my dicom series segmented images. Comparing the result with others coming from the use of other libraries (scikit-image and itk) I noticed that there were differences lying in the input matrix, due to the conversion in the IUC3P format, which changed the inside coefficient of the input matrix array. I didn't even find any useful information about this file format.
Do you have any suggestions to solve this problem?

Thank you in advance

TypeError: thin(): incompatible function arguments.

I run almost the same code in #51 , and get this error.

My platform:

sys.platform            linux
Python                  3.7.3 (default, Mar 27 2019, 22:11:17) [GCC 7.3.0]
sgext                   0.9.13

the codes I use:

import sgext
import numpy as np

mask = np.zeros(shape=[50, 50, 40], dtype=np.uint8)
mask[25:35, 25:35, 10:30] = 255
sgextImg.from_pyarray(mask)
dmap_image = sgext.scripts.create_distance_map(input=sgextImg, verbose=True)
thin_image = sgext.scripts.thin(input=sgextImg,
                   table_folder= sgext.tables_folder,
                   skel_type="end",
                   select_type="dmax",
                   input_distance_map_image=dmap_image,
                   persistence=2,
                   visualize=False,
                   verbose=True
                   )

And the bug is:

TypeError                                 Traceback (most recent call last)
<ipython-input-8-aa898cecb7fa> in <module>
     10                    persistence=2,
     11                    visualize=False,
---> 12                    verbose=True
     13                    )

TypeError: thin(): incompatible function arguments. The following argument types are supported:
    1. (input: sgext._sgext.itk.IUC3P, skel_type: str, select_type: str, tables_folder: str, persistence: int = 0, input_distance_map_image: sgext._sgext.itk.IF3P = Dimension: 3
LargestPossibleRegion:
  Index: [0, 0, 0]
  Size (i,j,k) (c_array): [0, 0, 0]
Origin: [0, 0, 0]
Spacing: [1, 1, 1]
Direction:
1 0 0
0 1 0
0 0 1
Buffer:
ImportImageContainer (0x55847df26b20)
  RTTI typeinfo:   itk::ImportImageContainer<unsigned long, float>
  Reference Count: 1
  Modified Time: 42
  Debug: Off
  Object Name:
  Observers:
    none
  Pointer: 0
  Container manages memory: true
  Size: 0
  Capacity: 0
, profile: bool = False, verbose: bool = False, visualize: bool = False) -> sgext._sgext.itk.IUC3P

Invoked with: kwargs: input=Dimension: 3
LargestPossibleRegion:
  Index: [0, 0, 0]
  Size (i,j,k) (c_array): [40, 50, 50]
Origin: [0, 0, 0]
Spacing: [1, 1, 1]
Direction:
1 0 0
0 1 0
0 0 1
Buffer:
ImportImageContainer (0x55847e2df030)
  RTTI typeinfo:   itk::ImportImageContainer<unsigned long, unsigned char>
  Reference Count: 1
  Modified Time: 160
  Debug: Off
  Object Name:
  Observers:
    none
  Pointer: 0x55847e34fbf0
  Container manages memory: false
  Size: 100000
  Capacity: 100000
, table_folder='/home/wangd/anaconda3/lib/python3.7/site-packages/sgext/tables', skel_type='end', select_type='dmax', input_distance_map_image=Dimension: 3
LargestPossibleRegion:
  Index: [0, 0, 0]
  Size (i,j,k) (c_array): [40, 50, 50]
Origin: [0, 0, 0]
Spacing: [1, 1, 1]
Direction:
1 0 0
0 1 0
0 0 1
Buffer:
ImportImageContainer (0x55847df412b0)
  RTTI typeinfo:   itk::ImportImageContainer<unsigned long, float>
  Reference Count: 1
  Modified Time: 173
  Debug: Off
  Object Name:
  Observers:
    none
  Pointer: 0x55847e36be60
  Container manages memory: true
  Size: 100000
  Capacity: 100000
, persistence=2, visualize=False, verbose=True

Segmentation Fault while running thinning.

Hi, I try to use sgext on an image mask, using an Ubuntu. Install is sgext-0.9.15 via the manylinux2014 wheel.

I tried all the steps suggest here:
https://discourse.vtk.org/t/vmtk-center-lines-extraction-how-to-accelerate-it-and-how-to-extract-center-lines-from-small-vessel/4103/14

but every time I tun he sgext.scripts.thin line my kernel crashes.

So, I run:

sgext_image = sgext.itk.IUC3P()
sgext_image.from_pyarray(e14_l_erosion*255)
dmap_image = sgext.itk.IF3P()

thin_image = sgext.scripts.thin(input=sgext_image,
                   tables_folder= sgext.tables_folder,
                   skel_type="end",
                   select_type="first",
                   input_distance_map_image=dmap_image,
                   persistence=2,
                   visualize=False,
                   verbose=True
                   )

and get

New Block [thin_function parameters:]
  skel_type_str: end
  skel_select_type_str: dmax
   -- provided distance_map_image.
  persistence: 2
  profile: 0
  verbose: 1
  visualize: 0
  ----------
EndBlock [thin_function parameters:] (0.08536 ms)
New Block [construct with table]
Segmentation fault (core dumped)

Yea, no idea how to approach this issue.

Best,

Malte

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.