Connected components on 2D and 3D images. Supports multiple labels.
Project description
cc3d: Connected Components on Multilabel 3D Images
Implementation of connected components in three dimensions using a 26, 18, or 6 connected neighborhood in 3D or 4 and 8connected in 2D. This package uses a 3D variant of the two pass method by Rosenfeld and Pflatz augmented with UnionFind and a decision tree based on the 2D 8connected work of Wu, Otoo, and Suzuki. This implementation is compatible with images containing many different labels, not just binary images. It can be used with 2D or 3D images.
I wrote this package because I was working on densely labeled 3D biomedical images of brain tissue (e.g. 512x512x512 voxels). Other off the shelf implementations I reviewed were limited to binary images. This rendered these other packages too slow for my use case as it required masking each label and running the connected components algorithm once each time. For reference, there are often between hundreds to thousands of labels in a given volume. The benefit of this package is that it labels all connected components in one shot, improving performance by one or more orders of magnitude.
In general, binary images are much more common (usually resulting from image thresholding), but multilabel images crop up in instance segmentation and semantic labeling as a classifier may label touching clusters of adjacent pixels differently. If a gap between different labels is guaranteed, then the problem degenerates into the binary version.
Check out benchmarks to see a comparison with SciPy on a few different tasks.
Python pip
Installaction
If compatible binaries are available for your platform, installation is particularly simple.
pip install connectedcomponents3d
If compatible binaries are not available, you can install from source as follows.
Requires a C++ compiler.
pip install numpy pip install connectedcomponents3d nobinary :all:
Occasionally, you may appear to successfully install cc3d, but on import you'll see an error that includes: numpy.ufunc size changed, may indicate binary incompatibility
. You can either try upgrading numpy or compiling cc3d from source in this case.
Python Manual Installation
Requires a C++ compiler.
pip install r requirements.txt python setup.py develop
Python Use
import cc3d import numpy as np labels_in = np.ones((512, 512, 512), dtype=np.int32) labels_out = cc3d.connected_components(labels_in) # 26connected connectivity = 6 # only 4,8 (2D) and 26, 18, and 6 (3D) are allowed labels_out = cc3d.connected_components(labels_in, connectivity=connectivity) # You can extract the number of labels (which is also the maximum # label value) like so: labels_out, N = cc3d.connected_components(labels_in, return_N=True) # free #  OR  labels_out = cc3d.connected_components(labels_in) N = np.max(labels_out) # costs a full read # You can extract individual components using numpy operators # This approach is slow, but makes a mutable copy. for segid in range(1, N+1): extracted_image = labels_out * (labels_out == segid) process(extracted_image) # If a readonly image is ok, this approach is MUCH faster # if the image has many contiguous regions. A random image # can be slower. binary=True yields binary images instead # of numbered images. for label, image in cc3d.each(labels_out, binary=False, in_place=True): process(image) # We also include a region adjacency graph function # that returns a set of undirected edges. edges = cc3d.region_graph(labels_out, connectivity=connectivity) # You can also generate a voxel connectivty graph that encodes # which directions are passable from a given voxel as a bitfield. # This could also be seen as a method of eroding voxels fractionally # based on their label adjacencies. # See help(cc3d.voxel_connectivity_graph) for details. graph = cc3d.voxel_connectivity_graph(labels, connectivity=connectivity)
If you know approximately how many labels you are going to generate, you can save some memory by specifying a number a safety factor above that range. The max label ID in your input labels must be less than max_labels
. This option is only recommended for expert users.
labels_out = connected_components(labels_in, max_labels=20000)
Note: C and Fortran order arrays will be processed in row major and column major order respectively, so the numbering of labels will be "transposed". The scare quotes are there because the dimensions of the array will not change.
C++ Use
#include "cc3d.hpp" // 3d array represented as 1d array int* labels = new int[512*512*512](); uint32_t* cc_labels = cc3d::connected_components3d<int>( labels, /*sx=*/512, /*sy=*/512, /*sz=*/512 ); // The default template parameter for output type is uint32_t uint64_t* cc_labels = cc3d::connected_components3d<int, uint64_t>( labels, /*sx=*/512, /*sy=*/512, /*sz=*/512 ); uint16_t* cc_labels = cc3d::connected_components3d<int, uint16_t>( labels, /*sx=*/512, /*sy=*/512, /*sz=*/512, /*connectivity=*/18 // default is 26 connected ); #include "cc3d_graphs.hpp" // edges is [ e11, e12, e21, e22, ... ] std::vector<uint64_t> edges = cc3d::extract_region_graph<uint64_t>( labels, /*sx=*/512, /*sy=*/512, /*sz=*/512, /*connectivity=*/18 // default is 26 connected ); // graph is a series of bitfields that describe intervoxel // connectivity based on adjacent labels. See "cc3d_graphs.hpp" // for details on the bitfield. uint32_t* graph = extract_voxel_connectivity_graph<T>( labels, /*sx=*/512, /*sy=*/512, /*sz=*/512, /*connectivity=*/6 // default is 26 connected );
26Connected CCL Algorithm
The algorithm contained in this package is an elaboration into 3D images of the 2D image connected components algorithm described by Rosenfeld and Pflatz (RP) in 1968 [1] (which is well illustrated by this youtube video) using an equivalency list implemented as Tarjan's UnionFind disjoint set with path compression and balancing [2] and augmented with a decision tree based on work by Wu, Otoo, and Suzuki (WOS), an approach commonly known as Scan plus Arraybased UnionFind (SAUF). [3] The description below describes the 26connected algorithm, but once you understand it, deriving 18 and 6 are simple. However, we recently made some changes that warrant further discursion on 6connected.
First Principles in 2D
In RP's 4connected twopass method for binary 2D images, the algorithm raster scans and every time it first encounters a foreground pixel (the pixels to its top and left are background), it marks it with a new label. If there is a preexisting label in its neighborhood, it uses that label instead. Whenever two labels are adjacent, it records they are equivalent so that they can be relabeled consistently in the second pass. This equivalency table can be constructed in several ways, but some popular approaches are UnionFind with path compression with balancing by rank and Selkow's algorithm (which can avoid pipeline stalls). [4] However, Selkow's algorithm is designed for two trees of depth two, appropriate for binary images. We would like to process multiple labels at the same time, making UnionFind preferable.
In the second pass, the pixels are relabeled using the equivalency table. UnionFind establishes one label as the root label of a tree, and the root is considered the representative label. Each pixel is then labeled with the representative label. UnionFind is therefore appropriate for representing disjoint sets. Path compression with balancing radically reduces the height of the tree, which accelerates the second pass.
WOS approached the problem of accelerating 8connected 2D connected components on binary images. 8connected labeling is achieved by extending RP's forward pass mask to the top left and top right corner pixels. In UnionFind based connected components algorithms, the unify step in the first pass is the most expensive step. WOS showed how to optimize away a large fraction of these calls using a decision tree that takes advantage of local topology. For example, since the topcenter neighbor of the current pixel is also adjacent to the other mask elements, all of which have already been processed by virtue of the raster scan direction, if it is present it is sufficient to copy its value and move on. If it is absent, pick one of the remaining foreground pixels, copy their value, and use unify for the mask element on the right as it is now known to be nonneighboring with the left hand side. WOS's algorithm continues in this fashion until a match is found or all mask elements are processed at which point a new label is created.
For several years, this algorithm was the world's fastest, though it has been superceded by a newer work that exchanges the static decision tree for a dynamic one or precalculated generated one amongst other improvements. However, WOS's work is significant for both its simplicity and speed and thus serves as the inspiration for this library. For 2D 8connected images, we provide a specialization using Wu et al's original decision tree for a slight performance boost.
We're interested in exploring the block based approaches of Grana, Borghesani, and Cucchiara ([5],[7]), however their approach appears to critically rely on binary images. We'll continue to think about ways to incorporate it. We also considered the approach of He et al [8] which is also supposed to modestly faster than than WOS. However, it substitutes the UnionFind data structure (one array) with three arrays, which imposes a memory requirement that is at odds with our goal of processing large images.
Extending to 3D
The approach presented below is very similar to that of Sutheebanjard [6]. To move to a 3D 26connected neighborhood, the mask must be extended into three dimensions in order to connect neighboring planes. Observe that the 8connected mask covers the trailing half of the neighborhood (the part that will have been already processed) such that the current pixel can rely on those labels. Thus the mask for the 26connected neighborhood covers only two out of three potential planes: the entire lower plane (nine voxels), and a mask identical to WOS's (four voxels) on the current plane. While some further optimizations are possible, to begin, the problem can be conceptually decomposed into two parts: establishing a 9connected link to the bottom plane and then an 8connected link to the current plane. This works because the current pixel functions as a hub that transmits the connection information from the 9connected step to the 8connected step.
Fig. 1: Mask for an 8connected plane. If J,K,L, and M are all eliminated, only N remains and a new label is assigned.
j  k  l 

m  n  . 
.  .  . 
The very first Z plane (Z=0) the algorithm runs against is special: the edge effect omits the bottom plane of the mask. Therefore, as the remaining mask is only comprosed of the 8connected 2D mask, after this pass, the bottom of the image is 8connected. At Z=1, the 9connected part of the mask kicks in, forming connections to Z=0, making the current plane now (8 + 9) 17connected. At Z=2, the 9connected bottom mask now forms connections from Z=1 to Z=2 on the top, making Z=1 (17 + 9) 26connected. By induction, when this process proceeds to completion it results in a 26connected labeling of the volume.
Following inspiration from WOS, we construct a decision tree on the densely labeled bottom plane that minimizes the number of unifications we need to perform.
Fig 2. The mask for the lower plane in 3D.
a  b  c 

d  e  f 
g  h  i 
As e
is connected to all other voxels, if present, it can simply be copied. If e
is absent, b
and h
fully cover the mask. If b
is absent, h
, a
, c
comprise a covering. If h
is absent, b
, g
, i
are one. Below is a list of coverings such that each proceeding entry in the list assumes the first letters in the entries above are background.
e
k
, (h
g
,i
)b
, (h
g
,i
)h
,a
,c
m
, (f
c
,i
)d
, (f
c
,i
)f
,g
,a
a
,c
,g
,i
c
,g
,i
g
,i
i
The decision tree is then constructed such that each of these coverings will be evaluated using the fewest unifications possible. It's possible to further optimize this by noting that e
and b
are both fully connected to the upper 2D mask. Therefore, if either of them are present, we can skip the 8connected unification step. It's also possible to try the DF covering first if B is background, which would save one unification versus HAC given even statistics, but it seems to be slightly slower on the dataset I attempted. To move from binary data to multilabel data, I simply replaced tests for foreground and background with tests for matching labels.
In order to make a reasonably fast implementation, I implemented unionfind with path compression. I conservatively used an IDs array qual to the size of the image for the unionfind data structure instead of a sparse map. The unionfind data structure plus the output labels means the memory consumption will be input + output + rank + equivalences. If your input labels are 32bit, the memory usage will be 4x the input size. This becomes more problematic when 64bit labels are used, but if you know something about your data, you can decrease the size of the unionfind data structure. I previously used unionbysize but for some reason it merely reduced performance and increased memory usage so it was removed.
For more information on the history of connected components algorithms, and an even faster approach for 2D 8connected components, consult Grana et al's paper on Block Based Decision Trees. [5,7]
Phantom Labels
In the course of thinking of improvements to several algorithms, we developed a technique we term "Phantom Labeling" for improving the SAUF method directly.
Definition: Phantom Labels are elements of a CCL mask that
transmit connectivity information between other elements of the
mask but cannot directly pass their value to the current pixel
during the first pass of a SAUF derived algorithm.
Reproducing Fig. 1 again, but with new letters for the more limited problem, the standard SAUF mask appears like so:
Fig. 3: Mask for an 8connected plane.
a  b  c 

d  x  . 
.  .  . 
This results in a decision tree like so assuming x is a foreground pixel.
if b:
x := b
elif a:
x := a
if c:
unify(a,c)
elif d:
x := d
if c:
unify(c,d)
elif c:
x := c
else:
x := new label
There is an opportunity here for eliminating up to half of the unify calls, one of the more expensive operations in modern CCL by slightly modifying the mask:
Fig. 4: 8connected mask modified to include phantom label P.
.  P  . 

a  b  c 
d  x  . 
.  .  . 
This results in a modified decision tree.
if b:
x := b
elif a:
x := a
if c and not P: < change here
unify(a,c)
elif d:
x := d
if c:
unify(c,d)
elif c:
x := c
else:
x := new label
The novelty of this technique is unclear, but it is very simple to apply and results in substantial speed ups for the 4 and 6 connected problems, a minor improvement for 8connected, and is readily compatible with the multilabel approach unlike block based approaches.
4 and 6Connected CCL Algorithm
Here is where the phantom label technique shines. It's a bit harder to find 4 and 6 connected algorithms in the literature, I assume because many of the techniques invented for the 8way problem, such as the UnionFind data structure for the equivalency table and runbased approaches, are applicable to the simpler problem. However, the SAUF decision tree approach was lacking as every pixel required a unify call in the 4way problem and two in the 6way problem.
Fig. 5: 4connected mask modified to include phantom label P.
P  b  . 

a  x  . 
if a:
x := a
if b and not P:
unify(a,b)
elif b:
x := b
else:
x := new label
This gives a decent improvement on the order of 1020%. If you're lucky, you might not incur even a single label merge operation. In the 6way problem, there are three phantom labels that can be exploited and the improvement is closer to 50% on our data, a fairly substantial amount. Again, with luck you might avoid any unify operations at all.
Fig. 6: Mask for the 6way problem with phantom labels P, Q, and R added.
P  b 

a  x 
.  Q 

R  c 
You can even use multiple routes to propagate information if a label is missing. For example, if path (a,P,b) is unavailable due to a missing P, you could potentially transmit information using path (a,R,c,Q,b).
Four Pass Algorithm
We introduce two additional passes over the image label prior to running the twopass SAUF algorithm. These additional passes are used to collect statistcs for optimizing the SAUF passes.
Estimating Provisional Labels
The first additional pass is used to overestimate the number of provisional labels generated by the first SAUF pass. A better estimation allows a smaller allocation for the UnionFind datastructure. For some operating systems, the reduced size of the allocation and improved caching recovers more time than is spent collecting statistics.
This can be computed by counting the number of transitions between labels along each row of the image. This scan is easily written such that the instructions can be vectorized to minimize the cost of the scan. The number of transitions is guaranteed to be larger than or equal to the number of provisional labels as all provisional labels are generated in this fashion and then reduced by stealing a label from a neighboring voxel.
A hierarchy of estimators can be written as:
0 <= provisional labels <= X transitions <= static estimate <= voxels
Binary images can also be estimated statically as voxels / 2
for 4 and 6way, voxels / 4
for 8 and 18 way, and voxels / 8
for 26 connected. For multilabel images, the best static estimate is voxels
as no assumptions can be made about how labels connect to each other (in the worst case all eight voxels in a cube have different labels).
It is also possible to check XY and XYZ transitions to get a tighter bound, but in experiments, the amount of time spent checking those directions exceeded the benefit obtained by checking the X pass. Often the X pass alone results in factors as high as voxels / 100
.
Estimation of the number of labels also allows aborting processing before the first SAUF pass in the case of an all background cube.
Estimating Foreground Location
The second additional pass is estimating the location of the foreground. In the literature, this strategy is sometimes referred to as a "oneandahalf pass" where the foreground location is computed during the first SAUF pass and then used to skip processing of background voxels during the relabeling pass.
Here we perform this check up front so that it can be performed minimally. Instead of integrating the calculation into the first pass which could force some computation on every voxel, we scan each row from the left to find the first foreground voxel and then scan from the right to the find the foreground voxel at the end. The results are tabulated in a uint32 table of starts and ends to each row of size 2 * sy * sz
. This ensures that the volume is scanned at most once, and most likely much less if the shapes fill the space reasonably well. Then, both passes of the SAUF method scan only the part of each row indicated by this table.
Certain shapes and distributions defeat the efficiency of scanning only the starts and ends of the row (such as random images or an image with foreground on the start and end of each row and nowhere else). However, for a great many shapes, this provides substantial efficiencies and minimal downside for a dense multilabel image as only two YZ slices of the images are scanned before the table is completed.
Early Abortion Points
There are three locations in the algorithm at which further processing can be aborted early without changing the result.
 After estimating provisional labels if zero transitions are detected (an all zeros volume). A black image is returned.
 After the first SAUF pass if the number of provisional labels is zero or one. In this case, the provisional labels are guaranteed to be identical to final labels.
 After assigning final labels to each provisional label in a translation array. If the number of final labels equals the number of provisional labels, the provisional labels were accurately assigned and the relabeling scan can be skipped.
Papers Using cc3d
A number of papers are using cc3d now. Many of them seem to be deep learning applications as instance segmentation is liable to generate touching nonbinary labels. Some are in geoscience, neuroscience, and medical fields. If cc3d is helpful to you, please feel free to email us and let us know. We might be able to offer some tips if its performance critical (though we can't guarantee timeliness of response). There are so many variations of the CCL problem, you might be surprised at what you can do.
https://scholar.google.com/scholar?as_ylo=2019&q=connectedcomponents3d&hl=en&as_sdt=0,31
Please cite cc3d as:
W. Silversmith. "cc3d: Connected Components on Multilabel 3D Images".
January 2021. https://github.com/seunglab/connectedcomponents3d/
References
 A. Rosenfeld and J. Pfaltz. "Sequential Operations in Digital Picture Processing". Journal of the ACM. Vol. 13, Issue 4, Oct. 1966, Pg. 471494. doi: 10.1145/321356.321357 (link)
 R. E. Tarjan. "Efficiency of a good but not linear set union algorithm". Journal of the ACM, 22:215225, 1975. (link)
 K. Wu, E. Otoo, K. Suzuki. "Two Strategies to Speed up Connected Component Labeling Algorithms". Lawrence Berkeley National Laboratory. LBNL29102, 2005. (link)
 S. Selkow. "The TreetoTree Editing Problem". Information Processing Letters. Vol. 6, No. 6. June 1977. doi: 10.1016/00200190(77)900643 (link)
 C. Grana, D. Borghesani, R. Cucchiara. "Optimized Blockbased Connected Components Labeling with Decision Trees". IEEE Transactions on Image Processing. Vol. 19, Iss. 6. June 2010. doi: 10.1109/TIP.2010.2044963 (link)
 P. Sutheebanjard. "Decision Tree for 3D Connected Components Labeling". Proc. 2012 International Symposium on Information Technology in Medicine and Education. doi: 10.1109/ITiME.2012.6291402 (link)
 C. Grana, D. Borghesani, R. Cucchiara. "Fast Block Based Connected Components Labeling". Proc. 16th IEEE Intl. Conf. on Image Processing. 2009. doi: 10.1109/ICIP.2009.5413731 (link)
 L. He, Y. Chao and K. Suzuki, "A LinearTime TwoScan Labeling Algorithm", IEEE International Conference on Image Processing, vol. 5, pp. 241244, 2007.
Project details
Release history Release notifications  RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Filename, size  File type  Python version  Upload date  Hashes 

Filename, size connected_components_3d3.2.0cp36cp36mmanylinux1_x86_64.whl (1.6 MB)  File type Wheel  Python version cp36  Upload date  Hashes View 
Filename, size connected_components_3d3.2.0cp36cp36mmanylinux2010_x86_64.whl (1.7 MB)  File type Wheel  Python version cp36  Upload date  Hashes View 
Filename, size connected_components_3d3.2.0cp36cp36mmanylinux2014_x86_64.whl (1.9 MB)  File type Wheel  Python version cp36  Upload date  Hashes View 
Filename, size connected_components_3d3.2.0cp36cp36mwin32.whl (250.3 kB)  File type Wheel  Python version cp36  Upload date  Hashes View 
Filename, size connected_components_3d3.2.0cp36cp36mwin_amd64.whl (243.1 kB)  File type Wheel  Python version cp36  Upload date  Hashes View 
Filename, size connected_components_3d3.2.0cp37cp37mmanylinux1_x86_64.whl (1.6 MB)  File type Wheel  Python version cp37  Upload date  Hashes View 
Filename, size connected_components_3d3.2.0cp37cp37mmanylinux2010_x86_64.whl (1.7 MB)  File type Wheel  Python version cp37  Upload date  Hashes View 
Filename, size connected_components_3d3.2.0cp37cp37mmanylinux2014_x86_64.whl (1.9 MB)  File type Wheel  Python version cp37  Upload date  Hashes View 
Filename, size connected_components_3d3.2.0cp37cp37mwin32.whl (250.4 kB)  File type Wheel  Python version cp37  Upload date  Hashes View 
Filename, size connected_components_3d3.2.0cp37cp37mwin_amd64.whl (243.4 kB)  File type Wheel  Python version cp37  Upload date  Hashes View 
Filename, size connected_components_3d3.2.0cp38cp38macosx_10_9_x86_64.whl (340.2 kB)  File type Wheel  Python version cp38  Upload date  Hashes View 
Filename, size connected_components_3d3.2.0cp38cp38manylinux1_x86_64.whl (1.7 MB)  File type Wheel  Python version cp38  Upload date  Hashes View 
Filename, size connected_components_3d3.2.0cp38cp38manylinux2010_x86_64.whl (1.9 MB)  File type Wheel  Python version cp38  Upload date  Hashes View 
Filename, size connected_components_3d3.2.0cp38cp38manylinux2014_x86_64.whl (2.1 MB)  File type Wheel  Python version cp38  Upload date  Hashes View 
Filename, size connected_components_3d3.2.0cp38cp38win32.whl (257.9 kB)  File type Wheel  Python version cp38  Upload date  Hashes View 
Filename, size connected_components_3d3.2.0cp38cp38win_amd64.whl (251.2 kB)  File type Wheel  Python version cp38  Upload date  Hashes View 
Filename, size connected_components_3d3.2.0cp39cp39macosx_10_9_universal2.whl (608.5 kB)  File type Wheel  Python version cp39  Upload date  Hashes View 
Filename, size connected_components_3d3.2.0cp39cp39manylinux2014_x86_64.whl (2.0 MB)  File type Wheel  Python version cp39  Upload date  Hashes View 
Filename, size connected_components_3d3.2.0cp39cp39win32.whl (255.2 kB)  File type Wheel  Python version cp39  Upload date  Hashes View 
Filename, size connected_components_3d3.2.0cp39cp39win_amd64.whl (248.6 kB)  File type Wheel  Python version cp39  Upload date  Hashes View 
Filename, size connectedcomponents3d3.2.0.tar.gz (250.8 kB)  File type Source  Python version None  Upload date  Hashes View 
Hashes for connected_components_3d3.2.0cp36cp36mmanylinux1_x86_64.whl
Algorithm  Hash digest  

SHA256  bc5ef39fdaf8039ec36d44ed6acf29011022192d8b8ba61fdcbfa6faa6d10611 

MD5  b0d60abcb662ee557958752cf75bf29f 

BLAKE2256  0c616597a922d1e9d44fd7dd886b4efa7a1a3b62a37ec8997b3f9b5135013d0a 
Hashes for connected_components_3d3.2.0cp36cp36mmanylinux2010_x86_64.whl
Algorithm  Hash digest  

SHA256  c00ef2ec4e657c916043b0757878ec18b37b4221448db7f98258e3faca0501b9 

MD5  52c97197f747d699d623d5e442c8dfcd 

BLAKE2256  c0617c2c13b0cc3a4145484875d8f242ef1d31d53a40d24fc6d5eeaf398ff718 
Hashes for connected_components_3d3.2.0cp36cp36mmanylinux2014_x86_64.whl
Algorithm  Hash digest  

SHA256  c1bb730c65ca4d3ff87d2ea2a8287068f9f59ce4573cdb2df5406edcb3bc6efb 

MD5  47eaffedf597b66d09155e1affadb56e 

BLAKE2256  5cd84ecdc29e093b355f7094dd18bcb5ba9574224a446157902cb74ca781fda3 
Hashes for connected_components_3d3.2.0cp36cp36mwin32.whl
Algorithm  Hash digest  

SHA256  d3b11e7591e64f06207d32f9a1b48c3041c828f446fe0f47259f789b7c37ed28 

MD5  4a76c880ffd4e798a3f0def14b8f97de 

BLAKE2256  a1a067f24e4f25bcf8be17fe8adf249e6d50147ccd740e86186ed94d72bc0ce3 
Hashes for connected_components_3d3.2.0cp36cp36mwin_amd64.whl
Algorithm  Hash digest  

SHA256  d205f3d64ee7eeb1a8b55fb99bb590a5e59b1bc82a778ea7f8afa29b3deb28ff 

MD5  489bc03208ebd27dddd861f13ddd9a22 

BLAKE2256  0c589d75882cd20e6cf673c738ee992171365fb3c5072619e0a0c41318f07174 
Hashes for connected_components_3d3.2.0cp37cp37mmanylinux1_x86_64.whl
Algorithm  Hash digest  

SHA256  897ea154a6db2caf1fd33288082a96208969b0e18411b9569f007620d0de2a6f 

MD5  9305f2363dbc803c9f2c4e5cf86ca049 

BLAKE2256  ed2108c3d5df47aeddb6bf93e1bfe58a43a6776d042aaa80be904bd7560b8ef2 
Hashes for connected_components_3d3.2.0cp37cp37mmanylinux2010_x86_64.whl
Algorithm  Hash digest  

SHA256  b88ff8aa1a8e795afae64bada6e1bda9643e6222c964053ec9185429a1eb7787 

MD5  d787b1dfcd6be5da4e2fbcaa0f01844f 

BLAKE2256  b0c29869d3534fb6091643f4ad39b8152b294b00e010d5941e17f49e7069468e 
Hashes for connected_components_3d3.2.0cp37cp37mmanylinux2014_x86_64.whl
Algorithm  Hash digest  

SHA256  3a31f972b2d0fef04ac3a4645c8919e8eb751badd7cfa966e7daf7f8b65d5263 

MD5  89370d39ee0ad313c2680d8b6cb406f1 

BLAKE2256  4e9493ccc7f9d4e83154124dfdb194391af06b9bd39e2d43c83bbca19896c913 
Hashes for connected_components_3d3.2.0cp37cp37mwin32.whl
Algorithm  Hash digest  

SHA256  eae211fede21b79565d06382c334334c784753624c6a1192b82299ba53abb96c 

MD5  780df5252b2add29a82673445e0e2598 

BLAKE2256  18927d5dba23f729927ecd4d16461c4e596d8072ae03caa07ce4f6802934da75 
Hashes for connected_components_3d3.2.0cp37cp37mwin_amd64.whl
Algorithm  Hash digest  

SHA256  4e204497cab61d759702fd125eb033a7e728ea8ec503ebe3476bab8528b866b6 

MD5  6df4ed167ce8ea072cf877241f54c3eb 

BLAKE2256  9a85e95844aabdf345253a47231847a129688afb2c723ba80973b0f8fbcd1a3d 
Hashes for connected_components_3d3.2.0cp38cp38macosx_10_9_x86_64.whl
Algorithm  Hash digest  

SHA256  14d1d5cf9641edc27997c3dc5081348ed421cda850927b615ac43e618a918510 

MD5  128c816eb2d44457607a752141865d26 

BLAKE2256  47b8241c1eccf55af3c326346df9496f3452f90653d1fd6d98c1956269a2ddd4 
Hashes for connected_components_3d3.2.0cp38cp38manylinux1_x86_64.whl
Algorithm  Hash digest  

SHA256  8b841a3b2614bc50e3982c026d567d2e7e9d33115010151cdf5abaf2259a9109 

MD5  1fbaad2b8ddd31a866513545dd95cc69 

BLAKE2256  a7bd57fc950fa81e34b3b8f22ef62eab913ecc198d39aefbc10dc9bc44b1360b 
Hashes for connected_components_3d3.2.0cp38cp38manylinux2010_x86_64.whl
Algorithm  Hash digest  

SHA256  5050dd5dd3f9d47d13c17b544b2f540f3f5772295e51797afce4927bb48ff447 

MD5  73fecea26491d6407032a5fb69e8db20 

BLAKE2256  364f8f72c5374b72f3075d0e87c6c8991bac454f70940c83490f6675a65531ce 
Hashes for connected_components_3d3.2.0cp38cp38manylinux2014_x86_64.whl
Algorithm  Hash digest  

SHA256  074ffada785d4a964e2b989285f5b58f04b79478e392ca9a4f3b5ed4cc160b9e 

MD5  2ea4d71ff42c68031247680e798723ab 

BLAKE2256  034d3a929f6f113943ea17e3099786afe5f7b329960511e43d2521820c38bf20 
Hashes for connected_components_3d3.2.0cp38cp38win32.whl
Algorithm  Hash digest  

SHA256  031fd8e775c7f192ba23d97b6e69ff20646a06da71b320e2b290bcb128691d1e 

MD5  569bfce5df94141499ecb088eb839bee 

BLAKE2256  76d7c99f9d186e94e2bc2393afee02b53e0e8ca202c9b128c865a938b2aa580c 
Hashes for connected_components_3d3.2.0cp38cp38win_amd64.whl
Algorithm  Hash digest  

SHA256  2298396a895b55cb6d8b76e98cb63af7992cd39ba2c41c13c14c2e8deaa9cd78 

MD5  26635c0bb5f654974faedced35e18722 

BLAKE2256  b55d4c759795442df66b59bd25f7ce24958ed6414b90e42da4eddf9e8ebba07a 
Hashes for connected_components_3d3.2.0cp39cp39macosx_10_9_universal2.whl
Algorithm  Hash digest  

SHA256  8046f326bbe24342e9114f6a1c7e6107a75c4cf3038363fa0872e6dba8c24c78 

MD5  8a9ffba068c0017a8114d5d6d4a0cb17 

BLAKE2256  4a7810f268be01d6c3dd25b5ad99b025b3e2fdf975f94dcccd65db5780134380 
Hashes for connected_components_3d3.2.0cp39cp39manylinux2014_x86_64.whl
Algorithm  Hash digest  

SHA256  9afb2a495c2bab2a02cada18f264a70bd380140962ad820d31c583a454f89cae 

MD5  a2fe838c26a7707880bf1b163ed5a7b2 

BLAKE2256  fbee3b18d44860f411dde895dcf7c8298923abc2dd957f5bbea3e7e75621ecf5 
Hashes for connected_components_3d3.2.0cp39cp39win32.whl
Algorithm  Hash digest  

SHA256  c6b313ebb1cb3503f25979966a35962d8a7396a0af466130f5f680d937630551 

MD5  ea3f21ced2cb5215abc82de38c5674d8 

BLAKE2256  be3926a8fc320adb74e548446265527ca6823c52f431c361bd4a79c384a360cf 
Hashes for connected_components_3d3.2.0cp39cp39win_amd64.whl
Algorithm  Hash digest  

SHA256  0eddcd807c1a57fd5f1c57ebe296bd5ad0190f00d7b5a1dd43936ab1454a8dc3 

MD5  f8b7c62f0d46e22ed893a1d577a2830a 

BLAKE2256  67116c4adafa2bc85cf54cd5a13e2603a3ad45ffd2de7ea4c89b0c4b1cac70d2 
Hashes for connectedcomponents3d3.2.0.tar.gz
Algorithm  Hash digest  

SHA256  097099aa5747010cb5633f27335eb202c0716376dc765f373219bb29f8a9ebe7 

MD5  aa3c02c1644258b8c30f2c98018e17ee 

BLAKE2256  78de77e1122f5295327b83adb90652fc93096de3ef296f9c56ed4d3b97aacf23 