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

简介: 图像分析之连通组件标记算法

图像处理之连接组件标记算法



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


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


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


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


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


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



算法流程如下:


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


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



算法实现Class文件解释:


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


同时完成了一些公共方法


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


继承上面的自抽象类


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


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



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



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



算法运行结果:

1334936488_3384.png


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




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

public class Label {
  
  private int index;
  private Label root;
  
  public Label(int index) {
    this.index = index;
    this.root = this;
  }
  
  public Label getRoot() {
    if(this.root != this) {
      this.root = this.root.getRoot();
    }
    return root;
  }
 
  public int getIndex() {
    return index;
  }
 
  public void setIndex(int index) {
    this.index = index;
  }
 
  public void setRoot(Label root) {
    this.root = root;
  }
}

Pixelnfo的代码如下:

package com.gloomyfish.image.analysis;
 
public class PixelInfo {
  private int value; // pixel value
  private int xp;
  private int yp;
  
  public PixelInfo(int pixelValue, int yp, int xp) {
    this.value = pixelValue;
    this.yp = yp;
    this.xp = xp;
  }
  
  public int getValue() {
    return value;
  }
  public void setValue(int value) {
    this.value = value;
  }
  public int getXp() {
    return xp;
  }
  public void setXp(int xp) {
    this.xp = xp;
  }
  public int getYp() {
    return yp;
  }
  public void setYp(int yp) {
    this.yp = yp;
  }
}

抽象的组件连通标记算法Class如下:

public abstract class AbstractConnectedComponentLabel {
  
  protected int width;
  protected int height;
  protected Color fgColor;
  protected int[] inPixels;
  protected int[][] chessborad;
  protected Map<Integer, Integer> neighbourMap;
  
  public int getWidth() {
    return width;
  }
 
  public void setWidth(int width) {
    this.width = width;
  }
 
  public int getHeight() {
    return height;
  }
 
  public void setHeight(int height) {
    this.height = height;
  }
  
  public abstract Map<Integer, List<PixelInfo>> doConntectedLabel();
 
  public boolean isForeGround(int tr, int tg, int tb) {
    if(tr == fgColor.getRed() && tg == fgColor.getGreen() && tb == fgColor.getBlue()) {
      return true;
    } else {
      return false;
    }
    
  }
 
}

实现抽象类的算法one的代码如下:

import java.awt.Color;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
 
public class ConnectedComponentLabelAlgOne extends AbstractConnectedComponentLabel {
 
  public ConnectedComponentLabelAlgOne(Color fgColor, int[] srcPixel, int width, int height) {
    this.fgColor = fgColor;
    this.width = width;
    this.height = height;
    this.inPixels = srcPixel;
    this.chessborad = new int[height][width];
    for(int i=0; i<height; i++) {
      for(int j=0; j<width; j++) {
        chessborad[i][j] = 0;
      }
    }
    this.neighbourMap = new HashMap<Integer, Integer>(); 
  }
  
  // assume the input image data is binary image.
  public Map<Integer, List<PixelInfo>> doConntectedLabel() {
    System.out.println("start to do connected component labeling algorithm");
    int index = 0;
    int labelCount = 0;
    Label currentLabel = new Label(0);
    HashMap<Integer, Label> allLabels = new HashMap<Integer, Label>();
        for(int row=0; row<height; row++) {
          int ta = 0, tr = 0, tg = 0, tb = 0;
          for(int col=0; col<width; col++) {
            index = row * width + col;
            ta = (inPixels[index] >> 24) & 0xff;
                tr = (inPixels[index] >> 16) & 0xff;
                tg = (inPixels[index] >> 8) & 0xff;
                tb = inPixels[index] & 0xff;
                if(isForeGround(tr, tg, tb)) {
                  getNeighboringLabels(row, col);
                  if(neighbourMap.size() == 0) {
                    currentLabel.setIndex(++labelCount);
                    allLabels.put(labelCount,new Label(labelCount));
 
                  } else {
                    for(Integer pixelLabel : neighbourMap.keySet().toArray(new Integer[0])) {
                      currentLabel.setIndex(pixelLabel);
                      break;
                    }
                    mergeLabels(currentLabel.getIndex(), neighbourMap, allLabels);
                  }
                  chessborad[row][col] = currentLabel.getIndex();
                }
          }
        }
        
        Map<Integer, List<PixelInfo>> connectedLabels = consolidateAllLabels(allLabels);
    return connectedLabels;
  }
  
  private Map<Integer, List<PixelInfo>> consolidateAllLabels(HashMap<Integer, Label> allLabels) {
    Map<Integer, List<PixelInfo>> patterns = new HashMap<Integer, List<PixelInfo>>();
        int patternNumber;
        List<PixelInfo> shape;
        for (int i = 0; i < this.height; i++)
        {
            for (int j = 0; j < this.width; j++)
            {
                patternNumber = chessborad[i][j];
                if (patternNumber != 0)
                {
                    patternNumber = allLabels.get(patternNumber).getRoot().getIndex();
                    if (!patterns.containsKey(patternNumber))
                    {
                        shape = new ArrayList<PixelInfo>();
                        shape.add(new PixelInfo(Color.BLUE.getRGB(), i, j));
                    }
                    else
                    {
                        shape = patterns.get(patternNumber);
                        shape.add(new PixelInfo(Color.BLUE.getRGB(), i, j));
                    }
                    patterns.put(patternNumber, shape);
                }
            }
        }
    return patterns;
  }
 
  private void mergeLabels(int index, Map<Integer, Integer> neighbourMap,
      HashMap<Integer, Label> allLabels) {
    Label root = allLabels.get(index).getRoot();
    Label neighbour;
    for(Integer key : neighbourMap.keySet().toArray(new Integer[0])) {
       if (key != index)
             {
         neighbour = allLabels.get(key);
         if(neighbour.getRoot() != root) {
           neighbour.setRoot(neighbour.getRoot());// thanks zhen712,
         }
             }
    }
    
  }
 
  /**
   * get eight neighborhood pixels
   * 
   * @param row
   * @param col
   * @return
   */
  public  void getNeighboringLabels(int row, int col) {
    neighbourMap.clear();
    for(int i=-1; i<=1; i++) {
      int yp = row + i;
      if(yp >=0 && yp < this.height) {
        for(int j=-1; j<=1; j++) {
          if(i == 0 && j==0) continue; // ignore/skip center pixel/itself
          int xp = col + j;
          if(xp >=0 && xp < this.width) {
            if(chessborad[yp][xp] != 0) {
              if(!neighbourMap.containsKey(chessborad[yp][xp])) {
                neighbourMap.put(chessborad[yp][xp],0);
              }
            }
          }
        }
      }
    }
  }
}

测试代码可以参考以前的文章 -  

Java数字图像处理基础知识 - 必读

相关文章
|
1月前
|
算法 数据安全/隐私保护 计算机视觉
基于FPGA的图像双线性插值算法verilog实现,包括tb测试文件和MATLAB辅助验证
本项目展示了256×256图像通过双线性插值放大至512×512的效果,无水印展示。使用Matlab 2022a和Vivado 2019.2开发,提供完整代码及详细中文注释、操作视频。核心程序实现图像缩放,并在Matlab中验证效果。双线性插值算法通过FPGA高效实现图像缩放,确保质量。
|
12天前
|
供应链 算法 搜索推荐
从公布的前十一批其他算法备案通过名单分析
2025年3月12日,国家网信办发布算法备案信息,深度合成算法通过395款,其他算法45款。前10次备案中,深度合成算法累计3234款,其他类别647款。个性化推送类占比49%,涵盖电商、资讯、视频推荐;检索过滤类占31.53%,用于搜索优化和内容安全;调度决策类占9.12%,集中在物流配送等;排序精选类占8.81%,生成合成类占1.55%。应用领域包括电商、社交媒体、物流、金融、医疗等,互联网科技企业主导,技术向垂直行业渗透,内容安全和多模态技术成新增长点。未来大模型检索和多模态生成或成重点。
从公布的前十一批其他算法备案通过名单分析
|
12天前
|
人工智能 自然语言处理 供应链
从第十批算法备案通过名单中分析算法的属地占比、行业及应用情况
2025年3月12日,国家网信办公布第十批深度合成算法通过名单,共395款。主要分布在广东、北京、上海、浙江等地,占比超80%,涵盖智能对话、图像生成、文本生成等多行业。典型应用包括医疗、教育、金融等领域,如觅健医疗内容生成算法、匠邦AI智能生成合成算法等。服务角色以面向用户为主,技术趋势为多模态融合与垂直领域专业化。
|
2月前
|
算法 数据安全/隐私保护 计算机视觉
基于Retinex算法的图像去雾matlab仿真
本项目展示了基于Retinex算法的图像去雾技术。完整程序运行效果无水印,使用Matlab2022a开发。核心代码包含详细中文注释和操作步骤视频。Retinex理论由Edwin Land提出,旨在分离图像的光照和反射分量,增强图像对比度、颜色和细节,尤其在雾天条件下表现优异,有效解决图像去雾问题。
|
11天前
|
自然语言处理 算法 安全
境内深度合成服务算法备案通过名单分析报告
本报告基于《境内深度合成服务算法备案通过名单》,分析了2023年6月至2025年3月公布的10批备案数据,涵盖属地分布、行业应用及产品形式等多个维度。报告显示,深度合成算法主要集中于经济发达地区,如北京、广东、上海等地,涉及教育、医疗、金融、娱乐等多行业。未来趋势显示技术将向多模态融合、行业定制化和安全合规方向发展。建议企业加强技术研发、拓展应用场景、关注政策动态,以在深度合成领域抢占先机。此分析旨在为企业提供参考,助力把握技术发展机遇。
境内深度合成服务算法备案通过名单分析报告
|
26天前
|
存储 缓存 监控
企业监控软件中 Go 语言哈希表算法的应用研究与分析
在数字化时代,企业监控软件对企业的稳定运营至关重要。哈希表(散列表)作为高效的数据结构,广泛应用于企业监控中,如设备状态管理、数据分类和缓存机制。Go 语言中的 map 实现了哈希表,能快速处理海量监控数据,确保实时准确反映设备状态,提升系统性能,助力企业实现智能化管理。
33 3
|
27天前
|
算法 数据安全/隐私保护
基于信息论的高动态范围图像评价算法matlab仿真
本项目基于信息论开发了一种高动态范围(HDR)图像评价算法,并通过MATLAB 2022A进行仿真。该算法利用自然图像的概率模型,研究图像熵与成像动态范围的关系,提出了理想成像动态范围的计算公式。核心程序实现了图像裁剪处理、熵计算等功能,展示了图像熵与动态范围之间的关系。测试结果显示,在[μ-3σ, μ+3σ]区间内图像熵趋于稳定,表明系统动态范围足以对景物成像。此外,还探讨了HDR图像亮度和对比度对图像质量的影响,为HDR图像评价提供了理论基础。
|
1月前
|
传感器 算法 数据安全/隐私保护
基于Affine-Sift算法的图像配准matlab仿真
本项目展示了Affine-SIFT算法的运行效果(无水印),适用于图像配准任务,能够处理旋转、缩放、平移及仿射变换。程序基于MATLAB2022A开发,包含完整代码与操作视频。核心步骤为:先用SIFT提取特征点,再通过仿射变换实现高精度对准。
|
14天前
|
人工智能 自然语言处理 算法
从第九批深度合成备案通过公示名单分析算法备案属地、行业及应用领域占比
2024年12月20日,中央网信办公布第九批深度合成算法名单。分析显示,教育、智能对话、医疗健康和图像生成为核心应用领域。文本生成占比最高(57.56%),涵盖智能客服、法律咨询等;图像/视频生成次之(27.32%),应用于广告设计、影视制作等。北京、广东、浙江等地技术集中度高,多模态融合成未来重点。垂直行业如医疗、教育、金融加速引入AI,提升效率与用户体验。
|
25天前
|
人工智能 编解码 算法
使用 PAI-DSW x Free Prompt Editing图像编辑算法,开发个人AIGC绘图小助理
使用 PAI-DSW x Free Prompt Editing图像编辑算法,开发个人AIGC绘图小助理