图像处理之Zhang Suen细化算法

简介: 图像处理之Zhang Suen细化算法

在二值图像处理特别是OCR识别与匹配中,都要通过对字符进行细化以便获得图像的骨架,通过zhang-suen细化算法获得图像,作为图像的特征之一,常用来作为识别或者模式匹配。


一:算法介绍


Zhang-Suen细化算法通常是一个迭代算法,整个迭代过程分为两步:


Step One:循环所有前景像素点,对符合如下条件的像素点标记为删除:


1.      2 <= N(p1) <=6


2.      S(P1) = 1


3.      P2 * P4 * P6 = 0


4.      P4 * P6 * P8 = 0


其中N(p1)表示跟P1相邻的8个像素点中,为前景像素点的个数


S(P1)表示从P2 ~ P9 ~ P2像素中出现0~1的累计次数,其中0表示背景,1表示前景


完整的P1 ~P9的像素位置与举例如下:

其中 N(p1) = 4, S(P1) = 3, P2*P4*P6=0*0*0=0, P4*P6*P8=0*0*1=0, 不符合条件,无需标记为删除。

Step Two:跟Step One很类似,条件1、2完全一致,只是条件3、4稍微不同,满足如下条件的像素P1则标记为删除,条件如下:


1.      2 <= N(p1) <=6


2.      S(P1) = 1


3.      P2 * P4 * P8 = 0


4.      P2 * P6 * P8 = 0


循环上述两步骤,直到两步中都没有像素被标记为删除为止,输出的结果即为二值图像细化后的骨架。


二:代码实现步骤


1.      二值化输入图像,初始化图像像素对应的标记映射数组

                BufferedImage binaryImage = super.process(image);
    int width = binaryImage.getWidth();
    int height = binaryImage.getHeight();
    int[] pixels = new int[width*height];
    int[] flagmap = new int[width*height];
    getRGB(binaryImage, 0, 0, width, height, pixels);
    Arrays.fill(flagmap, 0);

2.      迭代细化算法(Zhang-Suen)

a.      Step One

  private boolean step1Scan(int[] input, int[] flagmap, int width, int height) {
    boolean stop = true;
    int bc = 255 - fcolor;
    int p1=0, p2=0, p3=0;
    int p4=0, p5=0, p6=0;
    int p7=0, p8=0, p9=0;
    int offset = 0;
    for(int row=1; row<height-1; row++) {
      offset = row*width;
      for(int col=1; col<width-1; col++) {
        p1 = (input[offset+col]>>16)&0xff;
        if(p1 == bc) continue;
        p2 = (input[offset-width+col]>>16)&0xff;
        p3 = (input[offset-width+col+1]>>16)&0xff;
        p4 = (input[offset+col+1]>>16)&0xff;
        p5 = (input[offset+width+col+1]>>16)&0xff;
        p6 = (input[offset+width+col]>>16)&0xff;
        p7 = (input[offset+width+col-1]>>16)&0xff;
        p8 = (input[offset+col-1]>>16)&0xff;
        p9 = (input[offset-width+col-1]>>16)&0xff;
        // match 1 - 前景像素  0 - 背景像素
        p1 = (p1 == fcolor) ? 1 : 0;
        p2 = (p2 == fcolor) ? 1 : 0;
        p3 = (p3 == fcolor) ? 1 : 0;
        p4 = (p4 == fcolor) ? 1 : 0;
        p5 = (p5 == fcolor) ? 1 : 0;
        p6 = (p6 == fcolor) ? 1 : 0;
        p7 = (p7 == fcolor) ? 1 : 0;
        p8 = (p8 == fcolor) ? 1 : 0;
        p9 = (p9 == fcolor) ? 1 : 0;
        
        int con1 = p2+p3+p4+p5+p6+p7+p8+p9;
        String sequence = "" + String.valueOf(p2) + String.valueOf(p3) + String.valueOf(p4) + String.valueOf(p5) +
            String.valueOf(p6) + String.valueOf(p7) + String.valueOf(p8) + String.valueOf(p9) + String.valueOf(p2);
        int index1 = sequence.indexOf("01");
        int index2 = sequence.lastIndexOf("01");
        
        int con3 = p2*p4*p6;
        int con4 = p4*p6*p8;
        
        if((con1 >= 2 && con1 <= 6) && (index1 == index2) && con3 == 0 && con4 == 0) {
          flagmap[offset+col] = 1;
          stop = false;
        }
 
      }
    }
    return stop;
  }

b.      Step Two

  private boolean step2Scan(int[] input, int[] flagmap, int width, int height) {
    boolean stop = true;
    int bc = 255 - fcolor;
    int p1=0, p2=0, p3=0;
    int p4=0, p5=0, p6=0;
    int p7=0, p8=0, p9=0;
    int offset = 0;
    for(int row=1; row<height-1; row++) {
      offset = row*width;
      for(int col=1; col<width-1; col++) {
        p1 = (input[offset+col]>>16)&0xff;
        if(p1 == bc) continue;
        p2 = (input[offset-width+col]>>16)&0xff;
        p3 = (input[offset-width+col+1]>>16)&0xff;
        p4 = (input[offset+col+1]>>16)&0xff;
        p5 = (input[offset+width+col+1]>>16)&0xff;
        p6 = (input[offset+width+col]>>16)&0xff;
        p7 = (input[offset+width+col-1]>>16)&0xff;
        p8 = (input[offset+col-1]>>16)&0xff;
        p9 = (input[offset-width+col-1]>>16)&0xff;
        // match 1 - 前景像素  0 - 背景像素
        p1 = (p1 == fcolor) ? 1 : 0;
        p2 = (p2 == fcolor) ? 1 : 0;
        p3 = (p3 == fcolor) ? 1 : 0;
        p4 = (p4 == fcolor) ? 1 : 0;
        p5 = (p5 == fcolor) ? 1 : 0;
        p6 = (p6 == fcolor) ? 1 : 0;
        p7 = (p7 == fcolor) ? 1 : 0;
        p8 = (p8 == fcolor) ? 1 : 0;
        p9 = (p9 == fcolor) ? 1 : 0;
        
        int con1 = p2+p3+p4+p5+p6+p7+p8+p9;
        String sequence = "" + String.valueOf(p2) + String.valueOf(p3) + String.valueOf(p4) + String.valueOf(p5) +
            String.valueOf(p6) + String.valueOf(p7) + String.valueOf(p8) + String.valueOf(p9) + String.valueOf(p2);
        int index1 = sequence.indexOf("01");
        int index2 = sequence.lastIndexOf("01");
        
        int con3 = p2*p4*p8;
        int con4 = p2*p6*p8;
        
        if((con1 >= 2 && con1 <= 6) && (index1 == index2) && con3 == 0 && con4 == 0) {
          flagmap[offset+col] = 1;
          stop = false;
        }
 
      }
    }
    return stop;
  }

c.      检查如果上述两部没有任何像素被标记,则停止迭代,否则继续执行a, b

3.      返回细化后的图像,并显示

三:运行效果

四:完整的Zhang-suen算法代码实现:

import java.awt.image.BufferedImage;
import java.util.Arrays;
 
public class ZhangSuenThinFilter extends BinaryFilter {
  private int fcolor;
  public ZhangSuenThinFilter() {
    fcolor = 0;
  }
 
  public int getFcolor() {
    return fcolor;
  }
 
 
  public void setFcolor(int fcolor) {
    this.fcolor = fcolor;
  }
  
  @Override
  public BufferedImage process(BufferedImage image) {
    BufferedImage binaryImage = super.process(image);
    int width = binaryImage.getWidth();
    int height = binaryImage.getHeight();
    int[] pixels = new int[width*height];
    int[] flagmap = new int[width*height];
    getRGB(binaryImage, 0, 0, width, height, pixels);
    Arrays.fill(flagmap, 0);
    
    
    // 距离变化
    boolean stop = false;
    while(!stop) {
      // step one
      boolean s1 = step1Scan(pixels, flagmap, width, height);
      deletewithFlag(pixels, flagmap);
      Arrays.fill(flagmap, 0);
      // step two
      boolean s2 = step2Scan(pixels, flagmap, width, height);
      deletewithFlag(pixels, flagmap);
      Arrays.fill(flagmap, 0);
      if(s1 && s2) {
        stop = true;
      }
    }
 
    // 结果
    BufferedImage bi = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
    setRGB(bi, 0, 0, width, height, pixels);
    return bi;
  }
  
  private void deletewithFlag(int[] pixels, int[] flagmap) {
    int bc = 255 - fcolor;
    for(int i=0; i<pixels.length; i++) {
      if(flagmap[i] == 1) {
        pixels[i] = (0xff << 24) | ((bc&0xff) << 16) | ((bc&0xff) << 8) | (bc&0xff);
      }
    }
    
  }
 
  private boolean step1Scan(int[] input, int[] flagmap, int width, int height) {
    boolean stop = true;
    int bc = 255 - fcolor;
    int p1=0, p2=0, p3=0;
    int p4=0, p5=0, p6=0;
    int p7=0, p8=0, p9=0;
    int offset = 0;
    for(int row=1; row<height-1; row++) {
      offset = row*width;
      for(int col=1; col<width-1; col++) {
        p1 = (input[offset+col]>>16)&0xff;
        if(p1 == bc) continue;
        p2 = (input[offset-width+col]>>16)&0xff;
        p3 = (input[offset-width+col+1]>>16)&0xff;
        p4 = (input[offset+col+1]>>16)&0xff;
        p5 = (input[offset+width+col+1]>>16)&0xff;
        p6 = (input[offset+width+col]>>16)&0xff;
        p7 = (input[offset+width+col-1]>>16)&0xff;
        p8 = (input[offset+col-1]>>16)&0xff;
        p9 = (input[offset-width+col-1]>>16)&0xff;
        // match 1 - 前景像素  0 - 背景像素
        p1 = (p1 == fcolor) ? 1 : 0;
        p2 = (p2 == fcolor) ? 1 : 0;
        p3 = (p3 == fcolor) ? 1 : 0;
        p4 = (p4 == fcolor) ? 1 : 0;
        p5 = (p5 == fcolor) ? 1 : 0;
        p6 = (p6 == fcolor) ? 1 : 0;
        p7 = (p7 == fcolor) ? 1 : 0;
        p8 = (p8 == fcolor) ? 1 : 0;
        p9 = (p9 == fcolor) ? 1 : 0;
        
        int con1 = p2+p3+p4+p5+p6+p7+p8+p9;
        String sequence = "" + String.valueOf(p2) + String.valueOf(p3) + String.valueOf(p4) + String.valueOf(p5) +
            String.valueOf(p6) + String.valueOf(p7) + String.valueOf(p8) + String.valueOf(p9) + String.valueOf(p2);
        int index1 = sequence.indexOf("01");
        int index2 = sequence.lastIndexOf("01");
        
        int con3 = p2*p4*p6;
        int con4 = p4*p6*p8;
        
        if((con1 >= 2 && con1 <= 6) && (index1 == index2) && con3 == 0 && con4 == 0) {
          flagmap[offset+col] = 1;
          stop = false;
        }
 
      }
    }
    return stop;
  }
  
  private boolean step2Scan(int[] input, int[] flagmap, int width, int height) {
    boolean stop = true;
    int bc = 255 - fcolor;
    int p1=0, p2=0, p3=0;
    int p4=0, p5=0, p6=0;
    int p7=0, p8=0, p9=0;
    int offset = 0;
    for(int row=1; row<height-1; row++) {
      offset = row*width;
      for(int col=1; col<width-1; col++) {
        p1 = (input[offset+col]>>16)&0xff;
        if(p1 == bc) continue;
        p2 = (input[offset-width+col]>>16)&0xff;
        p3 = (input[offset-width+col+1]>>16)&0xff;
        p4 = (input[offset+col+1]>>16)&0xff;
        p5 = (input[offset+width+col+1]>>16)&0xff;
        p6 = (input[offset+width+col]>>16)&0xff;
        p7 = (input[offset+width+col-1]>>16)&0xff;
        p8 = (input[offset+col-1]>>16)&0xff;
        p9 = (input[offset-width+col-1]>>16)&0xff;
        // match 1 - 前景像素  0 - 背景像素
        p1 = (p1 == fcolor) ? 1 : 0;
        p2 = (p2 == fcolor) ? 1 : 0;
        p3 = (p3 == fcolor) ? 1 : 0;
        p4 = (p4 == fcolor) ? 1 : 0;
        p5 = (p5 == fcolor) ? 1 : 0;
        p6 = (p6 == fcolor) ? 1 : 0;
        p7 = (p7 == fcolor) ? 1 : 0;
        p8 = (p8 == fcolor) ? 1 : 0;
        p9 = (p9 == fcolor) ? 1 : 0;
        
        int con1 = p2+p3+p4+p5+p6+p7+p8+p9;
        String sequence = "" + String.valueOf(p2) + String.valueOf(p3) + String.valueOf(p4) + String.valueOf(p5) +
            String.valueOf(p6) + String.valueOf(p7) + String.valueOf(p8) + String.valueOf(p9) + String.valueOf(p2);
        int index1 = sequence.indexOf("01");
        int index2 = sequence.lastIndexOf("01");
        
        int con3 = p2*p4*p8;
        int con4 = p2*p6*p8;
        
        if((con1 >= 2 && con1 <= 6) && (index1 == index2) && con3 == 0 && con4 == 0) {
          flagmap[offset+col] = 1;
          stop = false;
        }
 
      }
    }
    return stop;
  }
 
}

觉得不错请支持一下!

相关文章
|
20天前
|
算法 计算机视觉
图像处理之积分图应用四(基于局部均值的图像二值化算法)
图像处理之积分图应用四(基于局部均值的图像二值化算法)
89 0
|
20天前
|
监控 算法 图计算
图像处理之积分图应用三(基于NCC快速相似度匹配算法)
图像处理之积分图应用三(基于NCC快速相似度匹配算法)
20 0
|
20天前
|
算法 Java 计算机视觉
图像处理之积分图算法
图像处理之积分图算法
14 2
|
20天前
|
资源调度 算法 计算机视觉
图像处理之积分图应用二(快速边缘保留滤波算法)
图像处理之积分图应用二(快速边缘保留滤波算法)
11 0
|
20天前
|
算法 BI 计算机视觉
图像处理之积分图应用一(半径无关的快速模糊算法)
图像处理之积分图应用一(半径无关的快速模糊算法)
13 0
|
2天前
|
机器学习/深度学习 自然语言处理 算法
m基于深度学习的OFDM+QPSK链路信道估计和均衡算法误码率matlab仿真,对比LS,MMSE及LMMSE传统算法
**摘要:** 升级版MATLAB仿真对比了深度学习与LS、MMSE、LMMSE的OFDM信道估计算法,新增自动样本生成、复杂度分析及抗频偏性能评估。深度学习在无线通信中,尤其在OFDM的信道估计问题上展现潜力,解决了传统方法的局限。程序涉及信道估计器设计,深度学习模型通过学习导频信息估计信道响应,适应频域变化。核心代码展示了信号处理流程,包括编码、调制、信道模拟、降噪、信道估计和解调。
23 8
|
4天前
|
算法
基于GA遗传优化的混合发电系统优化配置算法matlab仿真
**摘要:** 该研究利用遗传算法(GA)对混合发电系统进行优化配置,旨在最小化风能、太阳能及电池储能的成本并提升系统性能。MATLAB 2022a用于实现这一算法。仿真结果展示了一系列图表,包括总成本随代数变化、最佳适应度随代数变化,以及不同数据的分布情况,如负荷、风速、太阳辐射、弃电、缺电和电池状态等。此外,代码示例展示了如何运用GA求解,并绘制了发电单元的功率输出和年变化。该系统原理基于GA的自然选择和遗传原理,通过染色体编码、初始种群生成、适应度函数、选择、交叉和变异操作来寻找最优容量配置,以平衡成本、效率和可靠性。
|
5天前
|
机器学习/深度学习 算法
基于鲸鱼优化的knn分类特征选择算法matlab仿真
**基于WOA的KNN特征选择算法摘要** 该研究提出了一种融合鲸鱼优化算法(WOA)与K近邻(KNN)分类器的特征选择方法,旨在提升KNN的分类精度。在MATLAB2022a中实现,WOA负责优化特征子集,通过模拟鲸鱼捕食行为的螺旋式和包围策略搜索最佳特征。KNN则用于评估特征子集的性能。算法流程包括WOA参数初始化、特征二进制编码、适应度函数定义(以分类准确率为基准)、WOA迭代搜索及最优解输出。该方法有效地结合了启发式搜索与机器学习,优化特征选择,提高分类性能。
|
5天前
|
机器学习/深度学习 算法 数据可视化
基于BP神经网络的64QAM解调算法matlab性能仿真
**算法预览图省略** MATLAB 2022A版中,运用BP神经网络进行64QAM解调。64QAM通过6比特映射至64复数符号,提高数据速率。BP网络作为非线性解调器,学习失真信号到比特的映射,对抗信道噪声和多径效应。网络在处理非线性失真和复杂情况时展现高适应性和鲁棒性。核心代码部分未显示。
|
3天前
|
算法 计算机视觉
基于Chan-Vese算法的图像边缘提取matlab仿真
**算法预览展示了4幅图像,从边缘检测到最终分割,体现了在matlab2022a中应用的Chan-Vese水平集迭代过程。核心代码段用于更新水平集并显示迭代效果,最后生成分割结果及误差曲线。Chan-Vese模型(2001)是图像分割的经典方法,通过最小化能量函数自动检测平滑区域和清晰边界的图像分割,适用于复杂环境,广泛应用于医学影像和机器视觉。**