图像处理之距离变换

简介: 图像处理之距离变换

图像处理之距离变换

概述

距离变换是二值图像处理与操作中常用手段,在骨架提取,图像窄化中常有应用。距离

变换的结果是得到一张与输入图像类似的灰度图像,但是灰度值只出现在前景区域。并

且越远离背景边缘的像素灰度值越大。

基本思想

根据度量距离的方法不同,距离变换有几种不同的方法,假设像素点p1(x1, y1),

p2(x2, y2)计算距离的方法常见的有:

1.      欧几里德距离,Distance =


2.      曼哈顿距离(City Block Distance),公式如下:Distance = |x2-x1|+|y2-y1|


3.      象棋格距离(Chessboard Distance),公式如下:Distance = max(|x2-x1|,|y2-y1|)


一旦距离度量公式选择,就可以在二值图像的距离变换中使用。一个最常见的距离变换


算法就是通过连续的腐蚀操作来实现,腐蚀操作的停止条件是所有前景像素都被完全


腐蚀。这样根据腐蚀的先后顺序,我们就得到各个前景像素点到前景中心骨架像素点的


距离。根据各个像素点的距离值,设置为不同的灰度值。这样就完成了二值图像的距离变换。

注意点:

腐蚀操作结构体的选取会影响距离变换的效果,例子使用3*3的矩阵完成。有很多快速

的距离变换算法,感兴趣的可以自己研究。

运行结果:

关键代码解析:

初始化二值图像,读取像素,获取前景边缘像素与背景边缘像素

  public DistanceTransform(float scaleValue, float offsetValue, BufferedImage src)
  {
    this.scaleValue = scaleValue;
    this.offsetValue = offsetValue;
    this.inputImage = src;
    this.width = src.getWidth();
    this.height = src.getHeight();
        int[] inPixels = new int[width*height];
        getRGB( src, 0, 0, width, height, inPixels );
        int index = 0;
        pixels2D = new int[height][width]; // row, column
        greyLevel = new int[height][width];
        for(int row=0; row < height; row++)
        {
          for(int col=0; col<width; col++) 
          {
            index = row * width + col;
            int grayValue = (inPixels[index] >> 16) & 0xff;
            pixels2D[row][col] = grayValue;
            greyLevel[row][col] = 0;
          }
        }
        
        generateForegroundEdge();
        generateBackgroundEdgeFromForegroundEdge();
        
  }

现实距离变换的代码如下:

@Override
public BufferedImage filter(BufferedImage src, BufferedImage dest) {
  
  // calculate the distance here!!
  int index = 1;
    while (foregroundEdgePixels.size() > 0) {
      distanceSingleIteration(index);
        ++index;
    }
    
    // loop the each pixel and assign the color value according to distance value
  for (int row = 0; row < inputImage.getHeight(); row++) {
        for (int col = 0; col < inputImage.getWidth(); col++) {
          if(greyLevel[row][col] > 0) {
            int colorValue = (int)Math.round(greyLevel[row][col] * scaleValue + offsetValue);
            colorValue = colorValue > 255 ? 255 : ((colorValue < 0) ? 0 : colorValue);
            this.pixels2D[row][col] = colorValue;
          }
          
        }
  }
  
  // build the result pixel data at here !!!
    if ( dest == null )
        dest = createCompatibleDestImage(inputImage, null );
    
    index = 0;
    int[] outPixels = new int[width*height];
    for(int row=0; row<height; row++) {
      int ta = 0, tr = 0, tg = 0, tb = 0;
      for(int col=0; col<width; col++) {
        if(row == 75 && col > 60) {
          System.out.println("ddddd");
        }
        index = row * width + col;
        tr = tg = tb = this.pixels2D[row][col];
        ta = 255;
        outPixels[index] = (ta << 24) | (tr << 16) | (tg << 8) | tb;
      }
    }
    setRGB( dest, 0, 0, width, height, outPixels );
  return dest;
}

生成前景边缘像素与背景边缘像素的代码如下:

  private void generateForegroundEdge()
  {
    foregroundEdgePixels.clear();
 
    for (int row = 0; row < height; row++)
      for (int col = 0; col < width; col++)
        if (this.pixels2D[row][col] == foreground) {
          Point localPoint = new Point(col, row);
          for (int k = -1; k < 2; ++k) // 3*3 matrix
            for (int l = -1; l < 2; ++l) {
              if ((localPoint.x + l < 0) || (localPoint.x + l >= this.width) || (localPoint.y + k < 0) || (localPoint.y + k >= this.height) || 
                (this.pixels2D[(localPoint.y + k)][(localPoint.x + l)] != background) || (this.foregroundEdgePixels.contains(localPoint)))
                continue;
              this.foregroundEdgePixels.add(localPoint);
            }
        }
  }
  
  private void generateBackgroundEdgeFromForegroundEdge()
  {
    this.backgroundEdgePixels.clear();
 
    Iterator<Point> localIterator = this.foregroundEdgePixels.iterator();
    while (localIterator.hasNext()) {
      Point localPoint1 = new Point((Point)localIterator.next());
      for (int i = -1; i < 2; ++i)
        for (int j = -1; j < 2; ++j)
          if ((localPoint1.x + j >= 0) && (localPoint1.x + j < this.width) && (localPoint1.y + i >= 0) && (localPoint1.y + i < this.height)) {
            Point localPoint2 = new Point(localPoint1.x + j, localPoint1.y + i);
            if (this.pixels2D[localPoint2.y][localPoint2.x] == background)
              this.backgroundEdgePixels.add(localPoint2);
          }
    }
  }

相关文章
|
计算机视觉 Python
Opencv学习笔记(一):如何将得到的图片保存在指定目录以及如何将文件夹里所有图片以数组形式输出
这篇博客介绍了如何使用OpenCV库在Python中将图片保存到指定目录,以及如何将文件夹中的所有图片读取并以数组形式输出。
836 0
Opencv学习笔记(一):如何将得到的图片保存在指定目录以及如何将文件夹里所有图片以数组形式输出
halcon的灰度变换(图像增强)
halcon的灰度变换(图像增强)
960 1
Halcon中关于角度计算和测量拟合的算子详解
Halcon中关于角度计算和测量拟合的算子详解
2129 0
|
算法 数据可视化
Halcon边缘检测和线条检测(3),文章含BLOB检测常用方法和shape_trans内接和外接算子的说明
Halcon边缘检测和线条检测(3),文章含BLOB检测常用方法和shape_trans内接和外接算子的说明
2724 0
Halcon边缘检测和线条检测(3),文章含BLOB检测常用方法和shape_trans内接和外接算子的说明
|
算法 计算机视觉
图像处理之倒角距离变换
图像处理之倒角距离变换
238 1
halcon算子模板匹配(一)基于形状的模板匹配
halcon算子模板匹配(一)基于形状的模板匹配
3218 0
|
编解码 数据可视化 定位技术
60行代码就可以训练/微调 Segment Anything 2 (SAM 2)
本文演示了如何在仅60行代码内(不包括标注和导入)对SAM2进行微调。
866 1
60行代码就可以训练/微调 Segment Anything 2 (SAM 2)
|
机器学习/深度学习 文字识别 算法
[Halcon&图像] 缺陷检测的一些思路、常规检测算法
[Halcon&图像] 缺陷检测的一些思路、常规检测算法
5762 1
|
机器学习/深度学习 PyTorch TensorFlow
ONNX 与量化:提高模型效率
【8月更文第27天】随着人工智能技术的广泛应用,模型部署变得越来越重要。为了在资源受限的设备上运行复杂的机器学习模型,模型量化技术成为了一种有效的手段。Open Neural Network Exchange (ONNX) 作为一种开放格式,支持在不同框架之间交换训练好的模型,同时也支持模型量化。本文将探讨如何结合 ONNX 和模型量化技术来提高模型的效率,减少模型大小并加快推理速度。
2095 2
【qt】如何添加背景图片?
【qt】如何添加背景图片?
309 0