图像分析------连通组件标记算法

简介: <p style="color: rgb(51, 51, 51); font-family: Arial; font-size: 14px; line-height: 26px;">连接组件标记算法(connected component labeling algorithm)是图像分析中最常用的算法之一,</p><p style="color: rgb(51, 51, 51); font-

连接组件标记算法(connected component labeling algorithm)是图像分析中最常用的算法之一,

算法的实质是扫描一幅图像的每个像素,对于像素值相同的分为相同的组(group),最终得到

图像中所有的像素连通组件。扫描的方式可以是从上到下,从左到右,对于一幅有N个像

素的图像来说,最大连通组件个数为N/2。扫描是基于每个像素单位,对于二值图像而言,

连通组件集合可以是V={1}或者V={0}, 取决于前景色与背景色的不同。对于灰度图像来说,

连图组件像素集合可能是一系列在0 ~ 255之间的灰度值。

 

算法流程如下:

1.      首先扫描当前像素相邻的八邻域像素值,发现连通像素加以标记。

2.      完全扫描所有像素点之后,根据标记将所有连通组件合并。

 

算法实现Class文件解释:

AbstractConnectedComponentLabel一个抽象的Class定义了抽象方法doConntectedLabel()

同时完成了一些公共方法

ConnectedComponentLabelAlgOne一个容易读懂的连接组件算法完成,没有任何优化,

继承上面的自抽象类

ConnectedComponentLabelAlgTwo一个快速的连接组件算法,基于算法优化,取当前像素

的四邻域完成扫描与标记合并。

 

LabelPixelInfo是两个数据结构,用来存储算法计算过程中的中间变量。

 

ImageLabelFilter用来测试算法的驱动类,ImageAnalysisUI是现实测试结果的UI

 

算法运行结果:

 

根据标记的索引将组件着色。


定义数据结构的代码如下:

[java]  view plain copy
  1. public class Label {  
  2.       
  3.     private int index;  
  4.     private Label root;  
  5.       
  6.     public Label(int index) {  
  7.         this.index = index;  
  8.         this.root = this;  
  9.     }  
  10.       
  11.     public Label getRoot() {  
  12.         if(this.root != this) {  
  13.             this.root = this.root.getRoot();  
  14.         }  
  15.         return root;  
  16.     }  
  17.   
  18.     public int getIndex() {  
  19.         return index;  
  20.     }  
  21.   
  22.     public void setIndex(int index) {  
  23.         this.index = index;  
  24.     }  
  25.   
  26.     public void setRoot(Label root) {  
  27.         this.root = root;  
  28.     }  
  29. }  

Pixelnfo的代码如下:

[java]  view plain copy
  1. package com.gloomyfish.image.analysis;  
  2.   
  3. public class PixelInfo {  
  4.     private int value; // pixel value  
  5.     private int xp;  
  6.     private int yp;  
  7.       
  8.     public PixelInfo(int pixelValue, int yp, int xp) {  
  9.         this.value = pixelValue;  
  10.         this.yp = yp;  
  11.         this.xp = xp;  
  12.     }  
  13.       
  14.     public int getValue() {  
  15.         return value;  
  16.     }  
  17.     public void setValue(int value) {  
  18.         this.value = value;  
  19.     }  
  20.     public int getXp() {  
  21.         return xp;  
  22.     }  
  23.     public void setXp(int xp) {  
  24.         this.xp = xp;  
  25.     }  
  26.     public int getYp() {  
  27.         return yp;  
  28.     }  
  29.     public void setYp(int yp) {  
  30.         this.yp = yp;  
  31.     }  
  32. }  
抽象的组件连通标记算法Class如下:

[java]  view plain copy
  1. public abstract class AbstractConnectedComponentLabel {  
  2.       
  3.     protected int width;  
  4.     protected int height;  
  5.     protected Color fgColor;  
  6.     protected int[] inPixels;  
  7.     protected int[][] chessborad;  
  8.     protected Map<Integer, Integer> neighbourMap;  
  9.       
  10.     public int getWidth() {  
  11.         return width;  
  12.     }  
  13.   
  14.     public void setWidth(int width) {  
  15.         this.width = width;  
  16.     }  
  17.   
  18.     public int getHeight() {  
  19.         return height;  
  20.     }  
  21.   
  22.     public void setHeight(int height) {  
  23.         this.height = height;  
  24.     }  
  25.       
  26.     public abstract Map<Integer, List<PixelInfo>> doConntectedLabel();  
  27.   
  28.     public boolean isForeGround(int tr, int tg, int tb) {  
  29.         if(tr == fgColor.getRed() && tg == fgColor.getGreen() && tb == fgColor.getBlue()) {  
  30.             return true;  
  31.         } else {  
  32.             return false;  
  33.         }  
  34.           
  35.     }  
  36.   
  37. }  
实现抽象类的算法one的代码如下:

[java]  view plain copy
  1. import java.awt.Color;  
  2. import java.util.ArrayList;  
  3. import java.util.HashMap;  
  4. import java.util.List;  
  5. import java.util.Map;  
  6.   
  7. public class ConnectedComponentLabelAlgOne extends AbstractConnectedComponentLabel {  
  8.   
  9.     public ConnectedComponentLabelAlgOne(Color fgColor, int[] srcPixel, int width, int height) {  
  10.         this.fgColor = fgColor;  
  11.         this.width = width;  
  12.         this.height = height;  
  13.         this.inPixels = srcPixel;  
  14.         this.chessborad = new int[height][width];  
  15.         for(int i=0; i<height; i++) {  
  16.             for(int j=0; j<width; j++) {  
  17.                 chessborad[i][j] = 0;  
  18.             }  
  19.         }  
  20.         this.neighbourMap = new HashMap<Integer, Integer>();   
  21.     }  
  22.       
  23.     // assume the input image data is binary image.  
  24.     public Map<Integer, List<PixelInfo>> doConntectedLabel() {  
  25.         System.out.println("start to do connected component labeling algorithm");  
  26.         int index = 0;  
  27.         int labelCount = 0;  
  28.         Label currentLabel = new Label(0);  
  29.         HashMap<Integer, Label> allLabels = new HashMap<Integer, Label>();  
  30.         for(int row=0; row<height; row++) {  
  31.             int ta = 0, tr = 0, tg = 0, tb = 0;  
  32.             for(int col=0; col<width; col++) {  
  33.                 index = row * width + col;  
  34.                 ta = (inPixels[index] >> 24) & 0xff;  
  35.                 tr = (inPixels[index] >> 16) & 0xff;  
  36.                 tg = (inPixels[index] >> 8) & 0xff;  
  37.                 tb = inPixels[index] & 0xff;  
  38.                 if(isForeGround(tr, tg, tb)) {  
  39.                     getNeighboringLabels(row, col);  
  40.                     if(neighbourMap.size() == 0) {  
  41.                         currentLabel.setIndex(++labelCount);  
  42.                         allLabels.put(labelCount,new Label(labelCount));  
  43.   
  44.                     } else {  
  45.                         for(Integer pixelLabel : neighbourMap.keySet().toArray(new Integer[0])) {  
  46.                             currentLabel.setIndex(pixelLabel);  
  47.                             break;  
  48.                         }  
  49.                         mergeLabels(currentLabel.getIndex(), neighbourMap, allLabels);  
  50.                     }  
  51.                     chessborad[row][col] = currentLabel.getIndex();  
  52.                 }  
  53.             }  
  54.         }  
  55.           
  56.         Map<Integer, List<PixelInfo>> connectedLabels = consolidateAllLabels(allLabels);  
  57.         return connectedLabels;  
  58.     }  
  59.       
  60.     private Map<Integer, List<PixelInfo>> consolidateAllLabels(HashMap<Integer, Label> allLabels) {  
  61.         Map<Integer, List<PixelInfo>> patterns = new HashMap<Integer, List<PixelInfo>>();  
  62.         int patternNumber;  
  63.         List<PixelInfo> shape;  
  64.         for (int i = 0; i < this.height; i++)  
  65.         {  
  66.             for (int j = 0; j < this.width; j++)  
  67.             {  
  68.                 patternNumber = chessborad[i][j];  
  69.                 if (patternNumber != 0)  
  70.                 {  
  71.                     patternNumber = allLabels.get(patternNumber).getRoot().getIndex();  
  72.                     if (!patterns.containsKey(patternNumber))  
  73.                     {  
  74.                         shape = new ArrayList<PixelInfo>();  
  75.                         shape.add(new PixelInfo(Color.BLUE.getRGB(), i, j));  
  76.                     }  
  77.                     else  
  78.                     {  
  79.                         shape = patterns.get(patternNumber);  
  80.                         shape.add(new PixelInfo(Color.BLUE.getRGB(), i, j));  
  81.                     }  
  82.                     patterns.put(patternNumber, shape);  
  83.                 }  
  84.             }  
  85.         }  
  86.         return patterns;  
  87.     }  
  88.   
  89.     private void mergeLabels(int index, Map<Integer, Integer> neighbourMap,  
  90.             HashMap<Integer, Label> allLabels) {  
  91.         Label root = allLabels.get(index).getRoot();  
  92.         Label neighbour;  
  93.         for(Integer key : neighbourMap.keySet().toArray(new Integer[0])) {  
  94.              if (key != index)  
  95.              {  
  96.                  neighbour = allLabels.get(key);  
  97.                  if(neighbour.getRoot() != root) {  
  98.                      neighbour.setRoot(neighbour.getRoot());// thanks zhen712,  
  99.                  }  
  100.              }  
  101.         }  
  102.           
  103.     }  
  104.   
  105.     /** 
  106.      * get eight neighborhood pixels 
  107.      *  
  108.      * @param row 
  109.      * @param col 
  110.      * @return 
  111.      */  
  112.     public  void getNeighboringLabels(int row, int col) {  
  113.         neighbourMap.clear();  
  114.         for(int i=-1; i<=1; i++) {  
  115.             int yp = row + i;  
  116.             if(yp >=0 && yp < this.height) {  
  117.                 for(int j=-1; j<=1; j++) {  
  118.                     if(i == 0 && j==0continue// ignore/skip center pixel/itself  
  119.                     int xp = col + j;  
  120.                     if(xp >=0 && xp < this.width) {  
  121.                         if(chessborad[yp][xp] != 0) {  
  122.                             if(!neighbourMap.containsKey(chessborad[yp][xp])) {  
  123.                                 neighbourMap.put(chessborad[yp][xp],0);  
  124.                             }  
  125.                         }  
  126.                     }  
  127.                 }  
  128.             }  
  129.         }  
  130.     }  
  131. }  
相关文章
|
16天前
|
XML JavaScript 前端开发
学习react基础(1)_虚拟dom、diff算法、函数和class创建组件
本文介绍了React的核心概念,包括虚拟DOM、Diff算法以及如何通过函数和类创建React组件。
16 2
|
18天前
|
算法 搜索推荐 开发者
别再让复杂度拖你后腿!Python 算法设计与分析实战,教你如何精准评估与优化!
在 Python 编程中,算法的性能至关重要。本文将带您深入了解算法复杂度的概念,包括时间复杂度和空间复杂度。通过具体的例子,如冒泡排序算法 (`O(n^2)` 时间复杂度,`O(1)` 空间复杂度),我们将展示如何评估算法的性能。同时,我们还会介绍如何优化算法,例如使用 Python 的内置函数 `max` 来提高查找最大值的效率,或利用哈希表将查找时间从 `O(n)` 降至 `O(1)`。此外,还将介绍使用 `timeit` 模块等工具来评估算法性能的方法。通过不断实践,您将能更高效地优化 Python 程序。
32 4
|
16天前
|
算法 程序员 Python
程序员必看!Python复杂度分析全攻略,让你的算法设计既快又省内存!
在编程领域,Python以简洁的语法和强大的库支持成为众多程序员的首选语言。然而,性能优化仍是挑战。本文将带你深入了解Python算法的复杂度分析,从时间与空间复杂度入手,分享四大最佳实践:选择合适算法、优化实现、利用Python特性减少空间消耗及定期评估调整,助你写出高效且节省内存的代码,轻松应对各种编程挑战。
26 1
|
1月前
|
算法 数据可视化
基于SSA奇异谱分析算法的时间序列趋势线提取matlab仿真
奇异谱分析(SSA)是一种基于奇异值分解(SVD)和轨迹矩阵的非线性、非参数时间序列分析方法,适用于提取趋势、周期性和噪声成分。本项目使用MATLAB 2022a版本实现从强干扰序列中提取趋势线,并通过可视化展示了原时间序列与提取的趋势分量。代码实现了滑动窗口下的奇异值分解和分组重构,适用于非线性和非平稳时间序列分析。此方法在气候变化、金融市场和生物医学信号处理等领域有广泛应用。
|
2月前
|
算法
算法设计与分析作业
这篇文章是关于算法设计与分析的作业,其中包含了两个算法实现:一个是使用分治算法实现的十进制大整数相乘(包括加法、减法和乘法函数),并进行了正确性和健壮性测试;另一个是使用快速排序思想实现的分治查找第K小元素的程序,并分析了其平均和最坏时间复杂度。
算法设计与分析作业
|
22天前
|
机器学习/深度学习 存储 人工智能
文本情感识别分析系统Python+SVM分类算法+机器学习人工智能+计算机毕业设计
使用Python作为开发语言,基于文本数据集(一个积极的xls文本格式和一个消极的xls文本格式文件),使用Word2vec对文本进行处理。通过支持向量机SVM算法训练情绪分类模型。实现对文本消极情感和文本积极情感的识别。并基于Django框架开发网页平台实现对用户的可视化操作和数据存储。
25 0
文本情感识别分析系统Python+SVM分类算法+机器学习人工智能+计算机毕业设计
|
3天前
|
算法 数据安全/隐私保护
织物图像的配准和拼接算法的MATLAB仿真,对比SIFT,SURF以及KAZE
本项目展示了织物瑕疵检测中的图像拼接技术,使用SIFT、SURF和KAZE三种算法。通过MATLAB2022a实现图像匹配、配准和拼接,最终检测并分类织物瑕疵。SIFT算法在不同尺度和旋转下保持不变性;SURF算法提高速度并保持鲁棒性;KAZE算法使用非线性扩散滤波器构建尺度空间,提供更先进的特征描述。展示视频无水印,代码含注释及操作步骤。
|
1月前
|
算法 数据可视化 数据安全/隐私保护
基于LK光流提取算法的图像序列晃动程度计算matlab仿真
该算法基于Lucas-Kanade光流方法,用于计算图像序列的晃动程度。通过计算相邻帧间的光流场并定义晃动程度指标(如RMS),可量化图像晃动。此版本适用于Matlab 2022a,提供详细中文注释与操作视频。完整代码无水印。
|
1月前
|
编解码 算法 图形学
同一路RTSP|RTMP流如何同时回调YUV和RGB数据实现渲染和算法分析
我们播放RTSP|RTMP流,如果需要同时做渲染和算法分析的话,特别是渲染在上层实现(比如Unity),算法是python这种情况,拉两路流,更耗费带宽和性能,拉一路流,同时回调YUV和RGB数据也可以,但是更灵活的是本文提到的按需转算法期望的RGB数据,然后做算法处理
|
2月前
|
人工智能 算法
第一周算法设计与分析 G : 排队援救
这篇文章介绍了解决算法问题"排队援救"的方法,通过使用队列和映射来模拟救援点的排队过程,并确定最终得到救援的人的顺序和编号。