2013-05-15 21:36:53 jia20003 阅读数 13433

图像处理之距离变换

概述

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

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

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

基本思想

根据度量距离的方法不同,距离变换有几种不同的方法,假设像素点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);
          }
    }
  }

2016-11-24 13:32:57 yangdashi888 阅读数 8129

1、象素间各种距离的定义及计算

我们知道,构成一幅数字图像最基本的元素是一个一个的像素点,也就是像素。理解像素间的一些基本关系是我们以后进行图形图形处理的基础和关键。如相邻像素(像素的邻域),像素的邻接性、连通性、区域和边界这些内容。下面我想写一下像素间各种距离的定义以及用C++编写的程序来计算像素间的距离。
 应用领域:如果应用到单幅图像上则是主要为目标细化、骨架提取、粘连物体的分离等;如果应用到原图跟模板图则是应用于图像匹配,如果光照条件不变的条件下,这种可以达到很好的匹配效果。鲁棒性最好的是要使用归一化相关系数匹配方法。
对于像素p(x y),q(s t),z(v w),用D(p q)来表示像素p q间的距离,有:
一 像素间距离的定义(D(x y)应满足的条件)
   D(p q) ≥ 0.(当且仅当p q);
   D(p q) D(q p);
   D(p q) D(q z) ≥ D(p z);
二 像素距离的分类及计算方法
   欧式距离(Euclidean Distance)
    (1)相信大家对这个距离公式是非常熟悉的,初中时就学了,也称它为两点间的距离。p和q之间的欧式距离定义如下:
De(p q) =       
 (2)距离直观描述:距点(x y)小于或等于某一值r的欧式距离是中心在(x y)半径为r的圆平面。
       城区距离(City-Block Distance)
        (1)p和q之间的城区距离定义如下:
D4(p q) |x s| |y t|
    (2)距离直观描述:距点(x y)小于或等于某一值r的城区距离是中心在(x y)对角线为2r的菱形。
  棋盘距离(Chess Board Distance)
    (1)p和q之间的棋盘距离定义如下:
D8(p q) max(|x s| |y t|)
    (2)距离直观描述:距点(x y)小于或等于某一值r的棋盘距离是中心在(x y)对角线为2r的正方形。

方法:

首先对图像进行二值化处理(其要处理成二值图),然后给每个像素赋值为离它最近的背景像素点与其距离(Manhattan距离or欧氏距离),得到distance metric(距离矩阵),那么离边界越远的点越亮。



实现:

[cpp] view plain copy
  1. Imgori=imread('test.jpg');  
  2. I=rgb2gray(Imgori);  
  3. subplot(2,3,1);imshow(I);title('origin');  
  4.   
  5. Threshold=100;  
  6. F=I>Threshold;%front  
  7. %B=I<=Threshold;%background  
  8. subplot(2,3,4);imshow(F,[]);title('binary');  
  9.   
  10. T=bwdist(F,'chessboard');  
  11. subplot(2,3,2);imshow(T,[]);title('chessboard distance transform')  
  12. %the chessboard distance between (x1,y1) and (x2,y2) is max(│x1 – x2│,│y1 – y2│).  
  13.   
  14. T=bwdist(F,'cityblock');  
  15. subplot(2,3,3);imshow(T,[]);title('chessboard distance transform')  
  16. %the cityblock distance between (x1,y1) and (x2,y2) is │x1 – x2│ + │y1 – y2│.  
  17.   
  18. T=bwdist(F,'euclidean');  
  19. subplot(2,3,5);imshow(T,[]);title('euclidean distance transform')  
  20. %use Euclidean distance  
  21.   
  22. T=bwdist(F,'quasi-euclidean');  
  23. subplot(2,3,6);imshow(T,[]);title('quasi-euclidean distance transform')  
  24. %use quasi-Euclidean distance  

或者单纯想看这几个距离函数的区别可以用以下code:

[cpp] view plain copy
  1. bw = zeros(200,200); bw(50,50) = 1; bw(50,150) = 1;  
  2. bw(150,100) = 1;  
  3. D1 = bwdist(bw,'euclidean');  
  4. D2 = bwdist(bw,'cityblock');  
  5. D3 = bwdist(bw,'chessboard');  
  6. D4 = bwdist(bw,'quasi-euclidean');  
  7. figure  
  8. subplot(2,2,1), subimage(mat2gray(D1)), title('Euclidean')  
  9. hold on, imcontour(D1)  
  10. subplot(2,2,2), subimage(mat2gray(D2)), title('City block')  
  11. hold on, imcontour(D2)  
  12. subplot(2,2,3), subimage(mat2gray(D3)), title('Chessboard')  
  13. hold on, imcontour(D3)  
  14. subplot(2,2,4), subimage(mat2gray(D4)), title('Quasi-Euclidean')  
  15. hold on, imcontour(D4)  


其MATLAB函数bwdist函数的使用:

bwdist是距离变换函数,如果不提供第二参数method,默认计算二值图中当前像素点与最近的非0像素点的距离,并返回与原二值图同大小的结果矩阵,如果返回值指定为2个,第二返回值是与当前位置最近的非0像素的一维坐标(列优先存储).

给个例子如下:
C/C++ code
bw =
     0     0     0     0     0
     0     1     0     0     0
     0     0     0     0     0
     0     0     0     1     0
     0     0     0     0     0
 
[D,L] = bwdist(bw)
 
D =
    1.4142    1.0000    1.4142    2.2361    3.1623
    1.0000         0    1.0000    2.0000    2.2361
    1.4142    1.0000    1.4142    1.0000    1.4142
    2.2361    2.0000    1.0000         0    1.0000
    3.1623    2.2361    1.4142    1.0000    1.4142
 
L =
     7     7     7     7     7
     7     7     7     7    19
     7     7     7    19    19
     7     7    19    19    19
     7    19    19    19    19


3、通俗的程序代码是:

#include<math.h>
#include <iostream.h>
class Point
{
public:
    Point(int xValue=0,int yValue=0)
    {
        x = xValue;
        y = yValue;
    }
    int getX()
    {
        return x;
    }
    int getY()
    {
        return y;
    }
private:
    int x,y;
};
class pixelDistance
{
public:
    pixelDistance(Point pointValueA,Point pointValueB,int distanceTypeValue)
    {
        pointA = pointValueA;
        pointB = pointValueB;
        distanceType = distanceTypeValue;
    }
    pixelDistance(){};
    double getPixelDistance();
private:
    Point pointA;
    Point pointB;
    int distanceType;
};
double pixelDistance::getPixelDistance()
{
    switch(distanceType) {
    //欧式距离
    case 0:
        return sqrt((pointA.getX() - pointB.getX()) * (pointA.getX() - pointB.getX()) + (pointA.getY() - pointB.getY()) * (pointA.getY() - pointB.getY()));
        break;
    //城区距离
    case 1:
        return abs(pointA.getX() - pointB.getX()) + abs(pointA.getY() - pointB.getY());
        break;
    //棋盘距离
    case 2:
        return abs(pointA.getX() - pointB.getX()) > abs(pointA.getY() - pointB.getY()) ? abs(pointA.getX() - pointB.getX()) : abs(pointA.getY() - pointB.getY());
        break;
    default:
        return 0;
        break;
    }
}
 
void main()
{
    pixelDistance pd;
    Point p1,p2;
    int p1x,p1y,p2x,p2y;
    double dValue;
    int dType;
    char * dTypeStr;
    cout << "Please choice the type of distanse and two points' value. 0--Euclidean Distance; 1--City Block Distance; 2--Chess Board Distance." << endl;
    cin >> dType >> p1x >> p1y >> p2x >> p2y;
     
    while ((dType>3) || (dType <0)) {
        cout << "Sorry! You choice wrongly. Please choice again."<< endl;
        cin >> dType;
    }
 
    switch(dType) {
    case 0:
        dTypeStr = "Euclidean Distance";
        break;
    case 1:
        dTypeStr = "City Block Distance";
        break;
    case 2:
        dTypeStr = "Chess Board Distance";
        break;
    }
    p1 = Point(p1x,p1y);
    p2 = Point(p2x,p2y);
    pd = pixelDistance(p1,p2,dType);
    dValue = pd.getPixelDistance();
    cout << "The Type Of Distance is " ;
    cout << dTypeStr;
    cout << ",The Value Of Distance is ";
    cout << dValue <<endl;
}

4、性能评估:

由于其直接按公式来处理计算的话,时间需求量很大,所以对算法的加速方法是进行模板计算,

其博客里有讲:二值图像的距离变换研究

5、图像处理之倒角距离变换

图像处理中的倒角距离变换(Chamfer Distance Transform)在对象匹配识别中经常用到,

算法基本上是基于3x3的窗口来生成每个像素的距离值,分为两步完成距离变换,第一

步从左上角开始,从左向右、从上到下移动窗口扫描每个像素,检测在中心像素x的周

围0、1、2、3四个像素,保存最小距离与位置作为结果,图示如下:


第二步从底向上、从右向左,对每个像素,检测相邻像素4、5、6、7保存最小距离与

位置作为结果,如图示所:


完成这两步以后,得到的结果输出即为倒角距离变换的结果。完整的图像倒角距离变

换代码实现可以分为如下几步:

1.      对像素数组进行初始化,所有背景颜色像素点初始距离为无穷大,前景像素点距

  离为0

2.      开始倒角距离变换中的第一步,并保存结果

3.      基于第一步结果完成倒角距离变换中的第二步

4.      根据距离变换结果显示所有不同灰度值,形成图像

最终结果显示如下(左边表示原图、右边表示CDT之后结果)


完整的二值图像倒角距离变换的源代码如下:

[java] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. package com.gloomyfish.image.transform;  
  2.   
  3. import java.awt.Color;  
  4. import java.awt.image.BufferedImage;  
  5. import java.util.Arrays;  
  6.   
  7. import com.gloomyfish.filter.study.AbstractBufferedImageOp;  
  8.   
  9. public class CDTFilter extends AbstractBufferedImageOp {  
  10.     private float[] dis; // nn-distances  
  11.     private int[] pos; // nn-positions, 32 bit index  
  12.     private Color bakcgroundColor;  
  13.       
  14.     public CDTFilter(Color bgColor)  
  15.     {  
  16.         this.bakcgroundColor = bgColor;  
  17.     }  
  18.   
  19.     @Override  
  20.     public BufferedImage filter(BufferedImage src, BufferedImage dest) {  
  21.         int width = src.getWidth();  
  22.         int height = src.getHeight();  
  23.   
  24.         if (dest == null)  
  25.             dest = createCompatibleDestImage(src, null);  
  26.   
  27.         int[] inPixels = new int[width * height];  
  28.         pos = new int[width * height];  
  29.         dis = new float[width * height];  
  30.         src.getRGB(00, width, height, inPixels, 0, width);  
  31.         // 随机生成距离变换点  
  32.         int index = 0;  
  33.         Arrays.fill(dis, Float.MAX_VALUE);  
  34.         int numOfFC = 0;  
  35.         for (int row = 0; row < height; row++) {  
  36.             for (int col = 0; col < width; col++) {  
  37.                 index = row * width + col;  
  38.                 if (inPixels[index] != bakcgroundColor.getRGB()) {  
  39.                     dis[index] = 0;  
  40.                     pos[index] = index;  
  41.                     numOfFC++;  
  42.                 }  
  43.             }  
  44.         }  
  45.         final float d1 = 1;  
  46.         final float d2 = (float) Math.sqrt(d1 * d1 + d1 * d1);  
  47.         System.out.println(numOfFC);  
  48.         float nd, nd_tmp;  
  49.         int i, in, cols, rows, nearestPixel;  
  50.   
  51.         // 1 2 3  
  52.         // 0 i 4  
  53.         // 7 6 5  
  54.         // first pass: forward -> L->R, T-B  
  55.         for (rows = 1; rows < height - 1; rows++) {  
  56.             for (cols = 1; cols < width - 1; cols++) {  
  57.                 i = rows * width + cols;  
  58.   
  59.                 nd = dis[i];  
  60.                 nearestPixel = pos[i];  
  61.                 if (nd != 0) { // skip background pixels  
  62.                     in = i;  
  63.   
  64.                     in += -1// 0  
  65.                     if ((nd_tmp = d1 + dis[in]) < nd) {  
  66.                         nd = nd_tmp;  
  67.                         nearestPixel = pos[in];  
  68.                     }  
  69.   
  70.                     in += -width; // 1  
  71.                     if ((nd_tmp = d2 + dis[in]) < nd) {  
  72.                         nd = nd_tmp;  
  73.                         nearestPixel = pos[in];  
  74.                     }  
  75.   
  76.                     in += +1// 2  
  77.                     if ((nd_tmp = d1 + dis[in]) < nd) {  
  78.                         nd = nd_tmp;  
  79.                         nearestPixel = pos[in];  
  80.                     }  
  81.   
  82.                     in += +1// 3  
  83.                     if ((nd_tmp = d2 + dis[in]) < nd) {  
  84.                         nd = nd_tmp;  
  85.                         nearestPixel = pos[in];  
  86.                     }  
  87.   
  88.                     dis[i] = nd;  
  89.                     pos[i] = nearestPixel;  
  90.                 }  
  91.             }  
  92.         }  
  93.   
  94.         // second pass: backwards -> R->L, B-T  
  95.         // exactly same as first pass, just in the reverse direction  
  96.         for (rows = height - 2; rows >= 1; rows--) {  
  97.             for (cols = width - 2; cols >= 1; cols--) {  
  98.                 i = rows * width + cols;  
  99.   
  100.                 nd = dis[i];  
  101.                 nearestPixel = pos[i];  
  102.                 if (nd != 0) {  
  103.                     in = i;  
  104.   
  105.                     in += +1// 4  
  106.                     if ((nd_tmp = d1 + dis[in]) < nd) {  
  107.                         nd = nd_tmp;  
  108.                         nearestPixel = pos[in];  
  109.                     }  
  110.   
  111.                     in += +width; // 5  
  112.                     if ((nd_tmp = d2 + dis[in]) < nd) {  
  113.                         nd = nd_tmp;  
  114.                         nearestPixel = pos[in];  
  115.                     }  
  116.   
  117.                     in += -1// 6  
  118.                     if ((nd_tmp = d1 + dis[in]) < nd) {  
  119.                         nd = nd_tmp;  
  120.                         nearestPixel = pos[in];  
  121.                     }  
  122.   
  123.                     in += -1// 7  
  124.                     if ((nd_tmp = d2 + dis[in]) < nd) {  
  125.                         nd = nd_tmp;  
  126.                         nearestPixel = pos[in];  
  127.                     }  
  128.   
  129.                     dis[i] = nd;  
  130.                     pos[i] = nearestPixel;  
  131.   
  132.                 }  
  133.             }  
  134.         }  
  135.   
  136.         for (int row = 0; row < height; row++) {  
  137.             for (int col = 0; col < width; col++) {  
  138.                 index = row * width + col;  
  139.                 if (Float.MAX_VALUE != dis[index]) {  
  140.                     int gray = clamp((int) (dis[index]));  
  141.                     inPixels[index] = (255 << 24) | (gray << 16) | (gray << 8)  
  142.                             | gray;  
  143.                 }  
  144.             }  
  145.         }  
  146.         setRGB(dest, 00, width, height, inPixels);  
  147.         return dest;  
  148.     }  
  149.   
  150.     private int clamp(int i) {  
  151.         return i > 255 ? 255 : (i < 0 ? 0 : i);  
  152.     }  
  153.   
  154. }  


2019-10-29 19:16:14 qq_33446100 阅读数 23

说在前面

  • 编译环境:vs2017
  • opencv版本:4.0.1
  • 参考:《图像处理、分析与机器视觉(第4版)》

概念

  • 数字图像

    对于一个图像函数f(x,y)f(x,y),若其定义域以及值域都是离散的,那么称之为数字的;
    通常,通过采样将连续图像进行数字化。
    在这里插入图片描述
    一个连续图像在采样点处被数字化;采样点之间构成的关系为栅格。
  • 距离

    • 定义
      满足以下条件的函数:
      D(p,q)0,p=q,D(p,q)=0D(p,q)=D(q,p)D(p,r)D(p,q)+D(q,r)D(\bold p,\bold q)\geq 0,当且仅当\bold p=\bold q时,D(\bold p,\bold q)=0\\ D(\bold p,\bold q)=D(\bold q,\bold p)\\ D(\bold p,\bold r)\leq D(\bold p,\bold q)+D(\bold q,\bold r)
    • 欧式距离
      DE[(i,j),(h,k)]=(ih)2+(jk)2D_E[(i,j),(h,k)]=\sqrt{(i-h)^2+(j-k)^2}
      在这里插入图片描述
    • 城市街区距离
      只允许横向以及纵向的移动
      在这里插入图片描述
      D4[(i,j),(h,k)]=ih+jkD_4[(i,j),(h,k)]=|i-h|+|j-k|
      在这里插入图片描述
    • 棋盘距离
      允许横向、纵向以及对角线上的移动
      在这里插入图片描述
      D8[(i,j),(h,k)]=max{ih+jk}D_8[(i,j),(h,k)]=max\{|i-h|+|j-k|\}
      在这里插入图片描述
  • 距离变换算法

    • 按照一种距离度量D,D是D4或者D8,对大小为MxN的图像的一个子集S进行距离变换,建立一个MxN的数组F并作初始化:子集S中的元素置为0,其他位置为无穷。
    • 按行遍历图像,从上到下,从左到右。对于上方和左边的邻接像素,设
      F(p)=min[F(p),D(p,q)+F(q)]F(\bold p)=min[F(\bold p), D(p,q)+F(q)]
      在这里插入图片描述
    • 按行遍历图像,从下到上,从右到左。对于下方和右边的邻接像素,设
      F(p)=min[F(p),D(p,q)+F(q)]F(\bold p)=min[F(\bold p), D(p,q)+F(q)]
      在这里插入图片描述
    • 数组F中得到的是子集S的斜切

Code

  • c++实现

    并未处理图像,仅用于展示算法;
    时间复杂度:O(n2)O(n^2)
    #include <iostream>
    #include <vector>
    #include <algorithm>
    using namespace std;
    /*
    @{func} 判断i,j是否在范围内
    */
    bool InArea(int i, int j, int rows, int cols)
    {
    	if (i<0 || i>=rows)
    		return false;
    	if (j<0 || j>=cols)
    		return false;
    
    	return true;
    }
    /*
    @{param: i j} 点位置
    @{param: rows cols} 图像大小
    @{param: f} 目标数组
    @{func} 处理上以及左边的近邻像素
    */
    void D4AL(int i,int j, int rows, int cols, vector<vector<int>> &f)
    {
    	//上
    	if (InArea(i - 1, j, rows, cols))
    		f[i][j] = min(f[i][j], 1 + f[i - 1][j]);
    	//左上
    	if (InArea(i - 1, j - 1, rows, cols))
    		f[i][j] = min(f[i][j], 2 + f[i - 1][j - 1]);
    	//左
    	if (InArea(i, j - 1, rows, cols))
    		f[i][j] = min(f[i][j], 1 + f[i][j - 1]);
    	//左下
    	if (InArea(i + 1, j - 1, rows, cols))
    		f[i][j] = min(f[i][j], 2 + f[i + 1][j - 1]);
    }
    /*
    @{param: i j} 点位置
    @{param: rows cols} 图像大小
    @{param: f} 目标数组
    @{func} 处理下以及右边的近邻像素
    */
    void D4BR(int i, int j, int rows, int cols, vector<vector<int>> &f)
    {
    	//下
    	if (InArea(i + 1, j, rows, cols))
    		f[i][j] = min(f[i][j], 1 + f[i + 1][j]);
    	//右下
    	if (InArea(i + 1, j + 1, rows, cols))
    		f[i][j] = min(f[i][j], 2 + f[i + 1][j + 1]);
    	//右
    	if (InArea(i, j + 1, rows, cols))
    		f[i][j] = min(f[i][j], 1 + f[i][j + 1]);
    	//右上
    	if (InArea(i - 1, j + 1, rows, cols))
    		f[i][j] = min(f[i][j], 2 + f[i - 1][j + 1]);
    }
    
    /*
    @{param:src} 源图像
    @{param:f}   目标数组
    */
    void DistanceTransformD4(vector<vector<int>> &src, vector<vector<int>> &f)
    {
    	int cols = src[0].size();
    	int rows = src.size();
    
    	//初始化
    	for (int i = 0; i < rows; ++i)
    		for (int j = 0; j < cols; ++j)
    			if (src[i][j] == 1)
    				f[i][j] = 0;
    			else
    				f[i][j] = INT_MAX - 2;//简单的防止溢出
    	//按行遍历图像,从上到下,从左到右
    	for (int i = 0; i < rows; ++i)
    		for (int j = 0; j < cols; ++j)
    			D4AL(i, j, rows, cols, f);
    
    	//按行遍历图像,从下到上,从右到左
    	for (int i = rows - 1; i >= 0; --i)
    		for (int j = cols - 1; j >= 0; --j)
    			D4BR(i, j, rows, cols, f);
    }
    
    int main()
    {
    	vector<vector<int>> src = { 
    	{0,1,1,0,0,0,1,0 },
    	{0,1,0,0,0,0,0,1 },
    	{0,1,0,0,0,0,0,0 },
    	{0,1,0,0,0,0,0,0 }
    	};
    	int rows = src.size();
    	int cols = src[0].size();
    	vector<vector<int>> f(rows, vector<int>(cols, 0));
    
    	cout << "SRC:" << endl;
    	for (int i = 0; i < rows; ++i)
    	{
    		for (int j = 0; j < cols; ++j)
    			cout << src[i][j] << " ";
    		cout << endl;
    	}
    
    	DistanceTransformD4(src, f);
    
    	cout << "\nResult:" << endl;
    	for (int i = 0; i < rows; ++i)
    	{
    		for (int j = 0; j < cols; ++j)
    			cout << f[i][j] << " ";
    		cout << endl;
    	}
    
    	return 0;
    }
    
    在这里插入图片描述
  • opencv

    void cv::distanceTransform	(	
    InputArray 	src,
    OutputArray 	dst,
    OutputArray 	labels,
    int 	distanceType,
    int 	maskSize,
    int 	labelType = DIST_LABEL_CCOMP 
    )	
    
    栗子?
    在这里插入图片描述

应用

  • 移动机器人领域的路径规划以及障碍躲避
  • 图像中寻找最近特征,骨架抽取
  • 待补充
2019-10-04 19:01:53 Vichael_Chan 阅读数 104

数字图像处理:图像变换的基本模型

一、常用图象的变换模型

变换模型是指根据待匹配图像与背景图像之间几何畸变的情况,所选择的能最佳拟合两幅图像之间变化的几何变换模型。可采用的变换模型有如下几种:刚性变换、仿射变换、透视变换和非线形变换等,如下图:
在这里插入图片描述
(1) 刚体变换
如果一幅图像中的两点间的距离经变换到另一幅图像中后仍然保持不变,则这种变换称为刚体变换(Rigid Transform)。刚体变换仅局限于平移、旋转和反转(镜像)。在二维空间中,点(x,y)力经过刚体变换到点(x’,y’)的变换公式为:
在这里插入图片描述
(2) 仿射变换
如果一幅图像中的直线经过后映射到另一幅图像上仍为直线,并且保持平行关系,则这种变换称为仿射变换(Affine Transform。仿射变换适应于平移、旋转、缩放和反转(镜像)情况。可以用以下公式表示:
在这里插入图片描述
(3) 投影变换
如果一幅图像中的直线经过后映射到另一幅图像上仍为直线,但平行关系基本不保持,则这种变换称为投影变换(Projective Transform )。二维平面投影变换是关于齐次三维矢量的线性变换,在齐次坐标系下,二维平面上的投影变换具体可用下面的非奇异3x3矩阵形式来描述,即:
在这里插入图片描述

(4) 非线性变换
非线性变换又称为弯曲变换(Curved Transform),经过非线性变换,一幅图像上的直线映射到另一幅图像上不一定是直线,可能是曲线,在二维空间中,可以用以下公式表示:
在这里插入图片描述
在得到两幅图像间的变换模型参数后,要将输入图像做相应参数的变换使之与参考图像处于同一坐标系下,则可实现目标图像与背景的图像的匹配,这里目标图像变换后所得点坐标不一定为整像素数,此时应进行插值处理。

2019-06-28 10:37:13 zhouzongzong 阅读数 531

距离变换是二值图像处理与操作中常用手段,在骨架提取,图像窄化中常有应用。距离变换的结果是得到一张与输入图像类似的灰度图像,但是灰度值只出现在前景区域。并且越远离背景边缘的像素灰度值越大。

根据度量距离的方法不同,距离变换有几种不同的方法,假设像素点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|)
    在这里插入图片描述

一旦距离度量公式选择,就可以在二值图像的距离变换中使用。一个最常见的距离变换算法就是通过连续的腐蚀操作来实现,腐蚀操作的停止条件是所有前景像素都被完全腐蚀这样根据腐蚀的先后顺序,我们就得到各个前景像素点到前景中心骨架像素点的距离。根据各个像素点的距离值,设置为不同的灰度值。这样就完成了二值图像的距离

距离变换

阅读数 342

没有更多推荐了,返回首页