【图像处理】灰度加权距离变换(GWDT)

 

【fishing-pan:https://blog.csdn.net/u013921430转载请注明出处】

前言

       距离变换(distance transform,DT)在图像处理、计算机视觉等领域有非常多的运用,例如骨架化、目标细化、黏连目标分离等。在Matlab和OpenCV中都有进行距离变换的函数。Matlab中有bwdist函数用于二值化图像的距离变换, OpenCV中有cvDistTransform函数用于进行二值化图像的距离变换。

      除此之外,Matlab中还提供了graydist函数用于灰度加权距离变换,但是graydist居然要输入一个种子点,这让我觉得很神奇,因为在我的印象中灰度加权距离变换是不需要种子点的!自己测试运行后就发现,这是扯淡呢!算出来的结果是图像中各个点到种子点的距离加权变换。所以,我必须来讲讲我所理解的不需要种子点的灰度加权距离变换。

灰度加权距离变换

       一般的距离变换需要首先对图像进行二值化,这样的过程会损失掉图像中某些有用的信息,因为单纯的距离变换获得的是前景点到背景的最短距离,损失了灰度信息所代表的物理意义。因此在距离变换的基础上加上图像灰度信息,就有了灰度加权距离变换——GWDT(gray-weighted image distance transform),也可以称之为灰度尺度距离变换——GSDT(gray-scale image distance transform)。

       理解了距离变换,灰度加权距离变换就很好理解了,就是在距离变换的基础上加上了灰度信息。那么如何操作呢?我采用类似于区域增长的方法,在快速行进法的框架下来进行实现它,基本步骤如下;

1.    设定阈值分离前景和背景,将背景点的状态设置为ALIVE(其实所有的背景点都是seed point),将前景设为FAR,前景点的值初始化为无限大;

2.    初始化边缘点,在FAR点中选取8邻域(对二维图像进行处理)中有背景点的点,这些点就是边缘点,将边缘点的值赋予原图中的值,更改其状态为TRAIL,并将其放入一个队列Q中;

3.    在队列Q中不放回地取出点,假设取出的点记为y,计算y周围的8个点x,根据以下公式计算x,如果x的值被更新了,判断其是否已经在队列中,如果不在,那么就将其放入队列中。(在我实现的时候,考虑到效率的因素,并没有判断其是否已经在队列中,而是将其直接放入队列中,这个跟queue类和C++函数传参有关系

4.    循环第三步,直到队列Q为空。

       从上面的步骤中可以看出,实现过程与快速行进算法还是存在一定的差异的,如果按照快速行进算法的思想,那么对于点y,只会计算周围8临域中属性是FAR的点的值,不会对ALIVE的点进行更新。但是上述的过程,会对ALIVE点进行更新。我这样做是为了尽量保证每个点在变换过后值尽量小,大家可以自行修改代码来决定要不要对ALIVE点进行更新。 


       如下图所示,假设图中蓝色的点为初始化后的边缘点,他的状态为TRAIL,点A的状态为FAR;依次遍历S1-S4周围的8个点,很显然A是这四个点的共同邻域,A取四个点对其进行变换后的最小值;

 

【图像处理】灰度加权距离变换(GWDT)_第1张图片

                                                                         图 1 迭代过程示意图标题标题

 

处理结果

【图像处理】灰度加权距离变换(GWDT)_第2张图片

                                                                                        图 2 原始图像

 

 

【图像处理】灰度加权距离变换(GWDT)_第3张图片

                                                                                  图 3  处理后的图像


结果分析

       原图中我加入了一些噪声,在GWDT后,前景点特别是远离背景的前景点的灰度值相对被增强了,这样背景点就变相的被抑制了。此外,通过设定阈值可以分离区域内的黏连的大米,从而自动进行记数。等等等等,GWDT的应用还有很多。总的来说,GWDT是在传统的距离变换的基础上保留和利用了灰度信息,更加具有物理意义和价值。

代码

       为了方便大家学习,为大家提供源码;

//-------------------------
//潘正宇 ,2018.05.10
//灰度尺度距离变换,GSDT/GWDT
//-------------------------

#include 
#include 
#include 
#include 
#include 
#include 
#include 

using namespace std;
using namespace cv;


#define INF 3.4e+38                        // 无穷大的点
enum{ ALIVE = -1, TRIAL = 0, FAR = 1 };    //设定三种状态;


bool find(queue que,int value)
{
	int size = que.size();
	for (int i = 0; i (0, 0);   //获取图像均值

	int *state= new int[col*row];

	for (int i = 0; i < row; i++)
	{
		for (int j = 0; j < col; j++)
		{
			if (Scr.at(i, j)<(mean))
			{
				gsdtImg.at(i, j) = Scr.at(i, j);       //背景点是ALIVE的
				state[i*col + j] = ALIVE;
			}
			else
			{
				gsdtImg.at(i, j) = INF;
				state[i*col + j] = FAR;
			}

		}
	}
	
	queue TrailQue;                     //定义队列用于存放TRAIL的点
	for (int i = 1; i < row-1; i++)
	{
		for (int j = 1; j < col-1; j++)
		{
			if (state[i*col + j] == FAR)     //如果这个点是前景点,那么搜寻这个点周围是否有背景点,如果是那么它就是边缘点;
			{
				for (int o = -1; o <= 1; o++)
				{
					for (int p = -1; p <= 1; p++)
					{
						if (state[(i + o)*col + j + p] == ALIVE)        //找到所有的边缘点,并且将其放入队列TrailQue;
						{
							state[i*col + j] = TRIAL;                   
							gsdtImg.at(i, j) = Scr.at(i, j);
							TrailQue.push(i*col + j);
							break;
						}
					}
				}
			}
			
		}
	}
	

	while (!TrailQue.empty())
	{
		int P_row = TrailQue.front() / col;    ///获取TrailQue中点的坐标
		int P_col = TrailQue.front() % col;

		if (P_row < 1){ P_row = 1; }if (P_row>row - 2){ P_row = row - 2; }
		if (P_col < 1){ P_col = 1; }if (P_col>col - 2){ P_col = col - 2; }

		for (int o = -1; o <= 1; o++)
		{
			for (int p = -1; p <= 1; p++)
			{
				int N = (P_row + o)*col + P_col + p;

				double len = sqrt(o*o + p*p);
				float gs = gsdtImg.at(P_row, P_col) + Scr.at(P_row + o, P_col + p)*len;

				//---比较该点现有的GSDT值与P点给它的值;
				if (gsdtImg.at(P_row + o, P_col + p) > gs)
				{
					state[N] = TRIAL;
					gsdtImg.at(P_row + o, P_col + p) = gs;

					TrailQue.push(N);

					/*if (!find(TrailQue, N))
					{
						TrailQue.push(N);
					}*/
					
				}
					
				
			}
		}
		

		TrailQue.pop();                        //从TrailQue中删除这个点
	}

	//寻找最大值;
	float Max = 0;
	for (int i = 0; i < row; i++)
	{
		for (int j = 0; j < col; j++)
		{
			if (gsdtImg.at(i, j)>(INF / 100))
			{
				gsdtImg.at(i, j) = 0;
			}
			if (gsdtImg.at(i, j)>Max)//&&gsdtImg.at(i, j)<(INF/10)
			{
				Max = gsdtImg.at(i, j);
			}
			
		}
	}

	//将图像像素区间压缩到0-255
	Mat Dst;
	Dst = Scr.clone();
	for (int i = 0; i < row; i++)
	{
		for (int j = 0; j < col; j++)
		{
			Dst.at(i, j) = 255 * gsdtImg.at(i, j) / (Max+1);
		}
	}

	imwrite("temp.bmp", Dst);
	
	//归一化
	gsdtImg = gsdtImg / (Max+1);
	
	
	cout << gsdtImg.at(20, 206);
	imshow("GSDT",gsdtImg);
	waitKey(0);

	return true;
}


void main()
{
	string picPath = "G:\\博客\\图像处理\\灰度尺度距离变换\\rice.jpg";
	GSDT(picPath);

	system("pause");
	return;
}

 

你可能感兴趣的:(OpenCV,图像处理,灰度加权距离变换,距离变换,GSDT)