对于二值图像的连通域标记算法,常见的使用方法是opencv
里的connectedComponents()
以及connectedComponentsWithStats()
,这个实现方法很快,使用也便捷,但无法适用于3D图像。skimage
中的skimage.measure.label()
以及skimage.measure.regionprops()
可以得到2D和3D图像的连通域和相关的统计信息,但这个实现方法较慢,对于尺寸大的3D图像(比如512*512*512
尺寸的医学影像)并非最优解。
这里简单介绍一个提速的2D/3D图像连通域标记的Python
/C++
开源包:connected-components-3d
(link)。这个包的实现结合了two-pass算法,Array-Based Union-Find并查集来存储label信息,对于3D图像的处理更快,还能够直接处理非二值图像,下载方便,使用方法简单,强烈推荐~
下载和使用
Python版本直接pip install
下载,C++版本可以去github(link)上下载源码。
pip install connected-components-3d
使用:
import cc3d
import numpy as np
labels_in = np.ones((512, 512, 512), dtype=np.int32)
labels_out = cc3d.connected_components(labels_in, connectivity=26)
详情请直接去github上并支持原作者:)
2D的情况
我们先用较为简单直观的2D图像来做示例,再扩展到3D图像。这里举例的是8邻域的连通域。主要思路是Two Pass算法,这个算法包括两次扫描,大体过程是这样的:
- 第一次扫描:依次扫描每个像素。每遇到一个前景像素,判断是否周围像素已有label。如果已有标记过label,就用这个label;如果没有,则标记上新的label。如果有两个label相邻,记录下相邻关系。
- 第二次扫描:遇到有label的前景像素,根据之前记录的相邻关系进行重新label。
这个算法的实现中有可以提速的点:
- 相邻关系的记录使用Union-Find并查集,也能适用于非二值图,可以用一个array来实现。
- 在第一遍扫描的时候,对于前景像素的判断,需要对于其周围的8个像素都遍历取值,这个过程是比较慢的。我们是从上到下、从左到右扫描的,有些点的状态是已知的,经过对于拓扑关系的总结,我们可以只用8邻域中左上角的4个点来对当前前景点做出判断。这个判断关系可以用决策树来表示,并通过优化决策树结构(判断步骤最少),可以得到对于一个前景点标记的最终判断条件。
值得一提的是,对于2D的情况,可以对于多个像素合成block,以block为单位来一次性判断和赋值多个像素点来提高速度(Blocked Based Decision Tree)。
PS. 这个方法在2D图像的情况下很快,但目前还未有很好的3D图像版本的实现。
3D的情况
我们可以想象,把之前的2D图像扩展成3D,这里以26邻域连通域举例。我们的步骤是一样的,仍旧是Two Pass,但在第一次扫描时,判断每个前景像素点的时间更久了,因为周围有26个点需要取值来进行判断。我们用之前相同的优化逻辑,同样利用拓扑和优化决策树找到最优的判断条件。如果要看详细的具体判断条件可以直接看论文或者看代码。
这个包的作者还添加了一些代码实现上的加速:
- 算法的具体逻辑代码使用
C++
,2D或者3D图像都转换成1D array处理。Python
版本接口使用Cython
来进行数据结构转换。 - 从Two Pass改成了Four Pass。在开始之前提到的Two Pass算法之前,多加了两遍遍历来得到一些已知的统计信息,以对后续的操作进行剪枝。
- 最开始的额外第一遍遍历:预估之后的Two Pass算法会有多少个临时的label。因为我们要用一个array来当作并查集来存储所有label的相邻关系,大概知道会有多少label可以直接申请多大空间的array,节省空间以及减少caching的时间。预估的方法是遍历每一行,记录label之间可能出现多少次转换(relabel)。
- 额外第二遍遍历:预估前景点的位置。比如说对于每一行,记录下前景点出现的index区间,这样在之后的遍历不需要遍历整行。因为大部分图片的前景点可能是比背景点的个数少得多,这样可以节省后续的很多时间。但可以根据数据的具体情况调整。
- Two Pass算法的两次遍历
- 多设置一些剪枝条件,以减少不必要的时间开销。比如,如果额外第一次遍历所得到的临时label个数为0,就可以直接返回,因为没有前景点。
Reference
- W. Silversmith. “cc3d: Connected Components on Multilabel 3D Images”. January 2021. (link)
- A. Rosenfeld and J. Pfaltz. “Sequential Operations in Digital Picture Processing”. Journal of the ACM. Vol. 13, Issue 4, Oct. 1966, Pg. 471-494. doi: 10.1145/321356.321357 (link)
- R. E. Tarjan. “Efficiency of a good but not linear set union algorithm”. Journal of the ACM, 22:215-225, 1975. (link)
- K. Wu, E. Otoo, K. Suzuki. “Two Strategies to Speed up Connected Component Labeling Algorithms”. Lawrence Berkeley National Laboratory. LBNL-29102, 2005. (link)
- S. Selkow. “The Tree-to-Tree Editing Problem”. Information Processing Letters. Vol. 6, No. 6. June 1977. doi: 10.1016/0020-0190(77)90064-3 (link)
- C. Grana, D. Borghesani, R. Cucchiara. “Optimized Block-based 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 3-D 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 Linear-Time Two-Scan Labeling Algorithm”, IEEE International Conference on Image Processing, vol. 5, pp. 241-244, 2007.