2018-07-24 10:01:29 DL960722 阅读数 1858
  • 图解Java数据结构和算法

    1.算法是程序的灵魂,优秀的程序在对海量数据处理时,依然保持高速计算,就需要高效的数据结构和算法支撑。2.网上数据结构和算法的课程不少,但存在两个问题: 1)授课方式单一,大多是照着代码念一遍,数据结构和算法本身就比较难理解,对基础好的学员来说,还好一点,对基础不好的学生来说,基本上就是听天书了 2)说是讲数据结构和算法,但大多是挂羊头卖狗肉,算法讲的很少。 本课程针对上述问题,有针对性的进行了升级  3)授课方式采用图解+算法游戏的方式,让课程生动有趣好理解 4)系统全面的讲解了数据结构和算法, 除常用数据结构和算法外,还包括程序员常用10大算法:二分查找算法(非递归)、分治算法、动态规划算法、KMP算法、贪心算法、普里姆算法、克鲁斯卡尔算法、迪杰斯特拉算法、弗洛伊德算法、马踏棋盘算法。可以解决面试遇到的最短路径、最小生成树、最小连通图、动态规划等问题及衍生出的面试题,让你秒杀其他面试小伙伴 3.如果你不想永远都是代码工人,就需要花时间来研究下数据结构和算法。教程内容: 本教程是使用Java来讲解数据结构和算法,考虑到数据结构和算法较难,授课采用图解加算法游戏的方式。内容包括: 稀疏数组、单向队列、环形队列、单向链表、双向链表、环形链表、约瑟夫问题、栈、前缀、中缀、后缀表达式、中缀表达式转换为后缀表达式、递归与回溯、迷宫问题、八皇后问题、算法的时间复杂度、冒泡排序、选择排序、插入排序、快速排序、归并排序、希尔排序、基数排序(桶排序)、堆排序、排序速度分析、二分查找、插值查找、斐波那契查找、散列、哈希表、二叉树、二叉树与数组转换、二叉排序树(BST)、AVL树、线索二叉树、赫夫曼树、赫夫曼编码、多路查找树(B树B+树和B*树)、图、图的DFS算法和BFS、程序员常用10大算法、二分查找算法(非递归)、分治算法、动态规划算法、KMP算法、贪心算法、普里姆算法、克鲁斯卡尔算法、迪杰斯特拉算法、弗洛伊德算法马踏棋盘算法。 学习目标:通过学习,学员能掌握主流数据结构和算法的实现机制,开阔编程思路,提高优化程序的能力。

    3237 人正在学习 去看看 佟刚

      图像是由像素点组成的矩阵,矩阵的每个点记录着图像的RGB值。数字图像处理即根据用户需求,使用计算机技术对图像进行处理得到所需效果。

1.采样

      我们获取到的图像一般为模拟图像,要让计算机进行处理需将其数字化,采样的作用就是将模拟图像转变为数字图像。一般来说,采样间隔越大,所得图像像素数越少,空间分辨率越低,质量差,严重时出现马赛克效应;采样间隔越小,所得图像像素数越多,空间分辨率越高,图像质量好,但数据量大。

2.量化

       模拟图像经过采样后,在时间和空间上离散化为像素,但采样所得的像素值(即灰度值)仍是连续量,把采样后所得的各像素的灰度值从模拟量到离散量的转换称为图像灰度的量化。量化等级越多,所得图像层次越丰富,灰度分辨率高,图像质量好,但数据量大;量化等级越少,图像层次欠丰富,灰度分辨率低,会出现假轮廓现象,图像质量变差,但数据量小。

一般,当限定数字图像的大小时, 为了得到质量较好的图像可采用如下原则:

   (1) 对缓变的图像, 应该细量化, 粗采样, 以避免假轮廓。

   (2) 对细节丰富的图像,应细采样,粗量化, 以避免模糊(混叠)。

3.邻域

图像邻域即相邻像素关系,有4-邻域、8-邻域、D邻域

(1)4-邻域

4-邻域即像素点的上下左右四个点构成的邻域。设p(i,j),则p(i-1,j)、p(i+1,j)、p(i,j+1)、p(i,j-1)为其4-领域点。

(2)8-邻域

8-邻域即像素点周围一圈的八个点构成的邻域。设p(i,j),则p(i-1,j)、p(i+1,j)、p(i,j+1)、p(i,j-1)、p(i+1,j+1)、p(i-1,j+1),p(i-1,j-1),p(i-1,j-1)为其8-邻域点。

(3)D邻域

D邻域及像素点对角上的点。设p(i,j),则p(i+1,j+1)、p(i-1,j+1),p(i-1,j-1),p(i-1,j-1)为其D邻域点。

4.连通性

两像素连通的必要条件:1)两像素位置是否相邻;2)两像素灰度值是否满足特定的相似性准则

(1)4连通:两个像素p和q,如果p在q的4邻域中,称这两个像素是4连通

(2)8连通:两个像素p和q,如果p在q的8邻域中,称这两个像素是8连通

(3)m连通:1)两个像素p和q,p在q的4邻域内,或者p在q的D邻域内   2)且p和q的4邻域的交集为空,即m连通是4连通和D连通的混合(mixture)连通,(特别注意第二个条件!)

 

2014-06-15 08:42:45 pc1377318286 阅读数 2899
  • 图解Java数据结构和算法

    1.算法是程序的灵魂,优秀的程序在对海量数据处理时,依然保持高速计算,就需要高效的数据结构和算法支撑。2.网上数据结构和算法的课程不少,但存在两个问题: 1)授课方式单一,大多是照着代码念一遍,数据结构和算法本身就比较难理解,对基础好的学员来说,还好一点,对基础不好的学生来说,基本上就是听天书了 2)说是讲数据结构和算法,但大多是挂羊头卖狗肉,算法讲的很少。 本课程针对上述问题,有针对性的进行了升级  3)授课方式采用图解+算法游戏的方式,让课程生动有趣好理解 4)系统全面的讲解了数据结构和算法, 除常用数据结构和算法外,还包括程序员常用10大算法:二分查找算法(非递归)、分治算法、动态规划算法、KMP算法、贪心算法、普里姆算法、克鲁斯卡尔算法、迪杰斯特拉算法、弗洛伊德算法、马踏棋盘算法。可以解决面试遇到的最短路径、最小生成树、最小连通图、动态规划等问题及衍生出的面试题,让你秒杀其他面试小伙伴 3.如果你不想永远都是代码工人,就需要花时间来研究下数据结构和算法。教程内容: 本教程是使用Java来讲解数据结构和算法,考虑到数据结构和算法较难,授课采用图解加算法游戏的方式。内容包括: 稀疏数组、单向队列、环形队列、单向链表、双向链表、环形链表、约瑟夫问题、栈、前缀、中缀、后缀表达式、中缀表达式转换为后缀表达式、递归与回溯、迷宫问题、八皇后问题、算法的时间复杂度、冒泡排序、选择排序、插入排序、快速排序、归并排序、希尔排序、基数排序(桶排序)、堆排序、排序速度分析、二分查找、插值查找、斐波那契查找、散列、哈希表、二叉树、二叉树与数组转换、二叉排序树(BST)、AVL树、线索二叉树、赫夫曼树、赫夫曼编码、多路查找树(B树B+树和B*树)、图、图的DFS算法和BFS、程序员常用10大算法、二分查找算法(非递归)、分治算法、动态规划算法、KMP算法、贪心算法、普里姆算法、克鲁斯卡尔算法、迪杰斯特拉算法、弗洛伊德算法马踏棋盘算法。 学习目标:通过学习,学员能掌握主流数据结构和算法的实现机制,开阔编程思路,提高优化程序的能力。

    3237 人正在学习 去看看 佟刚


博客已转移至个人网站(http://www.p-chao.com)


C语言实现的二次遍历法提取连通分量


http://blog.csdn.net/pc1377318286/article/details/41414381            tip:这是这个算法的OpenCV接口版本链接地址



图像的最外框必须为黑色,添加黑色外框的部分代码我没有植入,使用时要特别注意

第一遍扫描检测联通状态,将其记录如静态线性链表中

第二遍扫描根据指针数组和静态线性链表恢复出图像

如果联通分量溢出,直接修改 int label[4096]={0};int labelcnt[4096]={0};int lcnt[4096]={0};

这三个数组的大小,关于图像数组的建立,请参见我的其它博客


如果提示堆栈溢出,在属性中修改堆栈大小即可,根据处理图像大小选择堆栈保留大小,建议在16M以上


输入图像:


提取结果:



操作源码:


void twopass(unsigned char image[Y_SIZE][X_SIZE],unsigned char imageget[Y_SIZE][X_SIZE])
{
	int i,j,cnt=0;
	int max=0,sec=0;
	int lmax;
	int lsec;
	int label[4096]={0};
	int labelcnt[4096]={0};
	int lcnt[4096]={0};
	int *plabel= label;
	int num,numt;
	int l=0;
	int *pimage[Y_SIZE][X_SIZE];
	int imageout[Y_SIZE][X_SIZE];
	unsigned char a,b,c,d;
	for(i=1;i<Y_SIZE;i++)
		for(j=1;j<X_SIZE-1;j++)
		{
			
			if( (i==1) && (j==1) )
			{	
				pimage[i-1][j-1]=NULL;
				pimage[i-1][j]=NULL;
				pimage[i-1][j+1]=NULL;
				pimage[i][j-1]=NULL;
			}
			else if( (i==1) && (j!=1) && (j!=X_SIZE-2) )
			{
				pimage[i-1][j+1]=NULL;
			}
			else if( (i==1) && (j==X_SIZE-2) )
			{
				pimage[i-1][j+1]=NULL;
				pimage[i][j+1]=NULL;
			}
			else if( (i!=1) && (j==1))
			{
				pimage[i][j-1]=NULL;
			}
			else if( (i!=1) && (j==X_SIZE-2) )
			{
				pimage[i][j+1]=NULL;
			}

 			if(image[i][j]==BLACK) pimage[i][j]=NULL;
			else
			{
				a=image[i-1][j-1];
				b=image[i-1][j];
				c=image[i-1][j+1];
				d=image[i][j-1];

				if( (a==0) && (b==0) && (c==0) && (d==0) )
				{
					plabel++;cnt++;
					pimage[i][j]=plabel;
				}//0
				else if( (a!=0) && (b==0) && (c==0) && (d==0) )
				{
					pimage[i][j]=pimage[i-1][j-1];
				}//1
				else if( (a==0) && (b!=0) && (c==0) && (d==0) )
				{
					pimage[i][j]=pimage[i-1][j];
				}//2
				else if( (a==0) && (b==0) && (c!=0) && (d==0) )
				{
					pimage[i][j]=pimage[i-1][j+1];
				}//3
				else if( (a==0) && (b==0) && (c==0) && (d!=0) )
				{
					pimage[i][j]=pimage[i][j-1];
				}//4
				else if( (a!=0) && (b!=0) && (c==0) && (d==0) )
				{
					pimage[i][j]=pimage[i-1][j-1];
				}//5
				else if( (a!=0) && (b==0) && (c!=0) && (d==0) )
				{
					pimage[i][j]=pimage[i-1][j-1];

					numt=*(pimage[i-1][j-1]);
					if(numt==0)
					{
						numt=(((int)pimage[i-1][j-1]-(int)label)/sizeof(int));
					}
					else
					{
						while(1)
						{
							if(label[numt]==0) break;
							else numt=label[numt];
						}
					}

					num=*(pimage[i-1][j+1]);
					if(num==0)
					{
						num=(((int)pimage[i-1][j+1]-(int)label)/sizeof(int));
					}
					else
					{
						while(1)
						{
							if(label[num]==0) break;
							else num=label[num];
						}
					}

					if(numt!=num) label[num]=numt;
				}//6
				else if( (a!=0) && (b==0) && (c==0) && (d!=0) )
				{
					pimage[i][j]=pimage[i-1][j-1];
				}//7
				else if( (a==0) && (b!=0) && (c!=0) && (d==0) )
				{
					pimage[i][j]=pimage[i-1][j];
				}//8
				else if( (a==0) && (b!=0) && (c==0) && (d!=0) )
				{
					pimage[i][j]=pimage[i-1][j];
				}//9
				else if( (a==0) && (b==0) && (c!=0) && (d!=0) )
				{
					pimage[i][j]=pimage[i-1][j+1];

					numt=*(pimage[i][j-1]);
					if(numt==0)
					{
						numt=(((int)pimage[i][j-1]-(int)label)/sizeof(int));
					}
					else
					{
						while(1)
						{
							if(label[numt]==0) break;
							else numt=label[numt];
						}
					}

					num=*(pimage[i-1][j+1]);
					if(num==0)
					{
						num=(((int)pimage[i-1][j+1]-(int)label)/sizeof(int));
					}
					else
					{
						while(1)
						{
							if(label[num]==0) break;
							else num=label[num];
						}
					}

					if(numt!=num) label[num]=numt;
				}//10
				else if( (a==0) && (b!=0) && (c!=0) && (d!=0) )
				{
					pimage[i][j]=pimage[i-1][j];
				}//11
				else if( (a!=0) && (b==0) && (c!=0) && (d!=0) )
				{
					pimage[i][j]=pimage[i-1][j-1];

					numt=*(pimage[i-1][j-1]);
					if(numt==0)
					{
						numt=(((int)pimage[i-1][j-1]-(int)label)/sizeof(int));
					}
					else
					{
						while(1)
						{
							if(label[numt]==0) break;
							else numt=label[numt];
						}
					}

					num=*(pimage[i-1][j+1]);
					if(num==0)
					{
						num=(((int)pimage[i-1][j+1]-(int)label)/sizeof(int));
					}
					else
					{
						while(1)
						{
							if(label[num]==0) break;
							else num=label[num];
						}
					}

					if(numt!=num) label[num]=numt;
				}//12
				else if( (a!=0) && (b!=0) && (c==0) && (d!=0) )
				{
					pimage[i][j]=pimage[i-1][j-1];
				}//13
				else if( (a!=0) && (b!=0) && (c!=0) && (d==0) )
				{
					pimage[i][j]=pimage[i-1][j-1];
				}//14
				else if( (a!=0) && (b!=0) && (c!=0) && (d!=0) )
				{
					pimage[i][j]=pimage[i-1][j-1];
				}//15

			}
		}

	for(i=0;i<Y_SIZE;i++)
	{
		for(j=0;j<X_SIZE;j++)
		{
			if(pimage[i][j]!=NULL)
			{
				num=*(pimage[i][j]);
				if(num==0)
				{
					num=(((int)pimage[i][j]-(int)label)/sizeof(int));
				}
				else
				{
					while(1)
					{
						if(label[num]==0) break;
						else num=label[num];
					}
				}
				if(labelcnt[num]==0)
				{
					l++;
					labelcnt[num]=l;
				}
				imageout[i][j]=labelcnt[num];
				lcnt[labelcnt[num]]++;
			}
			else imageout[i][j]=0;
		}
	}

	for(i=0;i<l+1;i++)
	{
		if(lcnt[i]>max)
		{
			max=lcnt[i];
			lmax=i;
		}
	}

	for(i=0;i<l;i++)
		if( (lcnt[i]>sec) && (lcnt[i]<max) )
		{
			sec=lcnt[i];
			lsec=i;
		}


	for(i=0;i<Y_SIZE;i++)
		for(j=0;j<X_SIZE;j++)
		{
			if(imageout[i][j]==lmax) imageget[i][j]=WHITE;
			else imageget[i][j]=BLACK;
		}
}


期中的逻辑还可以再优化,为便于分析,列出了15条,事实上可以简化为6条


if( (a==0) && (b==0) && (c==0) && (d==0) )
				{
					plabel++;cnt++;
					pimage[i][j]=plabel;
				}//0
				else if( (a==0) && (b==0) && (c!=0) && (d==0) )
				{
					pimage[i][j]=pimage[i-1][j+1];
				}//3
				else if( (a==0) && (b==0) && (c==0) && (d!=0) )
				{
					pimage[i][j]=pimage[i][j-1];
				}//4
				else if( (a!=0) && (b==0) && (c!=0)  )
				{
					pimage[i][j]=pimage[i-1][j-1];

					numt=*(pimage[i-1][j-1]);
					if(numt==0)
					{
						numt=(((int)pimage[i-1][j-1]-(int)label)/sizeof(int));
					}
					else
					{
						while(1)
						{
							if(label[numt]==0) break;
							else numt=label[numt];
						}
					}

					num=*(pimage[i-1][j+1]);
					if(num==0)
					{
						num=(((int)pimage[i-1][j+1]-(int)label)/sizeof(int));
					}
					else
					{
						while(1)
						{
							if(label[num]==0) break;
							else num=label[num];
						}
					}

					if(numt!=num) label[num]=numt;
				}//6
				else if( (a==0) && (b!=0) )
				{
					pimage[i][j]=pimage[i-1][j];
				}//8
				else if( (a==0) && (b==0) && (c!=0) && (d!=0) )
				{
					pimage[i][j]=pimage[i-1][j+1];

					numt=*(pimage[i][j-1]);
					if(numt==0)
					{
						numt=(((int)pimage[i][j-1]-(int)label)/sizeof(int));
					}
					else
					{
						while(1)
						{
							if(label[numt]==0) break;
							else numt=label[numt];
						}
					}

					num=*(pimage[i-1][j+1]);
					if(num==0)
					{
						num=(((int)pimage[i-1][j+1]-(int)label)/sizeof(int));
					}
					else
					{
						while(1)
						{
							if(label[num]==0) break;
							else num=label[num];
						}
					}

					if(numt!=num) label[num]=numt;
				}//10
				else
				{
					pimage[i][j]=pimage[i-1][j-1];
				}//15



2018-09-05 14:12:43 sinat_36330809 阅读数 3275
  • 图解Java数据结构和算法

    1.算法是程序的灵魂,优秀的程序在对海量数据处理时,依然保持高速计算,就需要高效的数据结构和算法支撑。2.网上数据结构和算法的课程不少,但存在两个问题: 1)授课方式单一,大多是照着代码念一遍,数据结构和算法本身就比较难理解,对基础好的学员来说,还好一点,对基础不好的学生来说,基本上就是听天书了 2)说是讲数据结构和算法,但大多是挂羊头卖狗肉,算法讲的很少。 本课程针对上述问题,有针对性的进行了升级  3)授课方式采用图解+算法游戏的方式,让课程生动有趣好理解 4)系统全面的讲解了数据结构和算法, 除常用数据结构和算法外,还包括程序员常用10大算法:二分查找算法(非递归)、分治算法、动态规划算法、KMP算法、贪心算法、普里姆算法、克鲁斯卡尔算法、迪杰斯特拉算法、弗洛伊德算法、马踏棋盘算法。可以解决面试遇到的最短路径、最小生成树、最小连通图、动态规划等问题及衍生出的面试题,让你秒杀其他面试小伙伴 3.如果你不想永远都是代码工人,就需要花时间来研究下数据结构和算法。教程内容: 本教程是使用Java来讲解数据结构和算法,考虑到数据结构和算法较难,授课采用图解加算法游戏的方式。内容包括: 稀疏数组、单向队列、环形队列、单向链表、双向链表、环形链表、约瑟夫问题、栈、前缀、中缀、后缀表达式、中缀表达式转换为后缀表达式、递归与回溯、迷宫问题、八皇后问题、算法的时间复杂度、冒泡排序、选择排序、插入排序、快速排序、归并排序、希尔排序、基数排序(桶排序)、堆排序、排序速度分析、二分查找、插值查找、斐波那契查找、散列、哈希表、二叉树、二叉树与数组转换、二叉排序树(BST)、AVL树、线索二叉树、赫夫曼树、赫夫曼编码、多路查找树(B树B+树和B*树)、图、图的DFS算法和BFS、程序员常用10大算法、二分查找算法(非递归)、分治算法、动态规划算法、KMP算法、贪心算法、普里姆算法、克鲁斯卡尔算法、迪杰斯特拉算法、弗洛伊德算法马踏棋盘算法。 学习目标:通过学习,学员能掌握主流数据结构和算法的实现机制,开阔编程思路,提高优化程序的能力。

    3237 人正在学习 去看看 佟刚

1、图像处理笔试面试题(1)

https://blog.csdn.net/m0_37407756/article/details/78800709

1.1、给定0-1矩阵,求连通域

二值图像分析最重要的方法就是连通区域标记,它是所有二值图像分析的基础,它通过对二值图像中白色像素(目标)的标记,让每个单独的连通区域形成一个被标识的块,进一步的我们就可以获取这些块的轮廓、外接矩形、质心、不变矩等几何参数。

连通域;连通区域的标记;

基于行程的标记(算法思路明白!;代码实现?);基于轮廓的标记(算法思路明白?;代码实现?)

2、图像处理算法工程师面试题

2.1、常用的图像空间

RGB/CMY/HSV/HSI/YUV(YCrCb)/Lab

HSL/HSB/Ycc/XYZ

https://blog.csdn.net/baidu_18891025/article/details/81742905

https://blog.csdn.net/skyereeee/article/details/7265415

HSV和HSI区别:

a 定义:

HSV:

HSI:

b 数学模型:

HSV:                                                       HSI:

https://blog.csdn.net/wxb1553725576/article/details/45827923

https://blog.csdn.net/jolinxia/article/details/27963675

YCrCb:

YCrCb即YUV, 其中“Y”表示明亮度(Luminance或Luma),也就是灰阶值;而“U”和“V” 表示的则是色度(Chrominance或Chroma),作用是描述影像色彩及饱和度,用于指定像素的颜色。

“色度”则定义了颜色的两个方面─色调与饱和度,分别用Cr和CB来表示。其中,Cr反映了RGB输入信号红色部分与RGB信号亮度值之间的差异。而CB反映的是RGB输入信号蓝色部分与RGB信号亮度值之间的差异。

采用YUV色彩空间的重要性是它的亮度信号Y和色度信号U、V是分离的。如果只有Y信号分量而没有U、V分量,那么这样表示的图像就是黑白灰度图像。彩色电视采用YUV空间正是为了用亮度信号Y解决彩色电视机与黑白电视机的兼容问题,使黑白电视机也能接收彩色电视信号。

肤色YCbCr颜色空间是一种常用的肤色检测的色彩模型,其中Y代表亮度,Cr代表光源中的红色分量,Cb代表光源中的蓝色分量。人的肤色在外观上的差异是由色度引起的,不同人的肤色分布集中在较小的区域内。

2.2.简述你熟悉的聚类算法并说明其优缺点。

1. K-Means(K均值)聚类

https://blog.csdn.net/Katherine_hsr/article/details/79382249

https://blog.csdn.net/guoziqing506/article/details/59057427

优点:

1)原理简单,容易实现
2)可解释度较强

缺点:

1)K值很难确定
2)局部最优
3)对噪音和异常点敏感
4)需样本存在均值(限定数据种类)
5)聚类效果依赖于聚类中心的初始化
6)对于非凸数据集或类别规模差异太大的数据效果不好

7)K-Means的缺点在于对聚类中心均值的简单使用。

https://blog.csdn.net/baidu_33566882/article/details/79886598

2. 均值漂移聚类

https://blog.csdn.net/Katherine_hsr/article/details/79382249

优点:(1)不同于K-Means算法,均值漂移聚类算法不需要我们知道有多少类/组。 
(2)基于密度的算法相比于K-Means受均值影响较小。 
缺点:(1)窗口半径r的选择可能是不重要的。

3. 基于密度的聚类方法(DBSCAN)

密度:统计某一半径内的点数,直到找到最大点数位置。

4. 用高斯混合模型(GMM)的最大期望(EM)聚类

相对应K-Means假设数据点是圆形的,使用高斯混合模型(GMM)做聚类首先假设数据点是呈高斯分布的,高斯分布(椭圆形)给出了更多的可能性。我们有两个参数来描述簇的形状:均值和标准差。所以这些簇可以采取任何形状的椭圆形,因为在x,y方向上都有标准差。因此,每个高斯分布被分配给单个簇。 

5. 凝聚层次聚类

6. 图团体检测(Graph Community Detection)

参考:

https://blog.csdn.net/Katherine_hsr/article/details/79382249

https://blog.csdn.net/abc200941410128/article/details/78541273?locationNum=1&fps=1

http://blog.chinaunix.net/uid-10289334-id-3758310.html

https://www.jianshu.com/p/2fa67f9bad60

2.3.请描述以下任一概念:SIFT/SURF  LDA/PCA

 

 

2017-11-11 10:13:07 u013162035 阅读数 650
  • 图解Java数据结构和算法

    1.算法是程序的灵魂,优秀的程序在对海量数据处理时,依然保持高速计算,就需要高效的数据结构和算法支撑。2.网上数据结构和算法的课程不少,但存在两个问题: 1)授课方式单一,大多是照着代码念一遍,数据结构和算法本身就比较难理解,对基础好的学员来说,还好一点,对基础不好的学生来说,基本上就是听天书了 2)说是讲数据结构和算法,但大多是挂羊头卖狗肉,算法讲的很少。 本课程针对上述问题,有针对性的进行了升级  3)授课方式采用图解+算法游戏的方式,让课程生动有趣好理解 4)系统全面的讲解了数据结构和算法, 除常用数据结构和算法外,还包括程序员常用10大算法:二分查找算法(非递归)、分治算法、动态规划算法、KMP算法、贪心算法、普里姆算法、克鲁斯卡尔算法、迪杰斯特拉算法、弗洛伊德算法、马踏棋盘算法。可以解决面试遇到的最短路径、最小生成树、最小连通图、动态规划等问题及衍生出的面试题,让你秒杀其他面试小伙伴 3.如果你不想永远都是代码工人,就需要花时间来研究下数据结构和算法。教程内容: 本教程是使用Java来讲解数据结构和算法,考虑到数据结构和算法较难,授课采用图解加算法游戏的方式。内容包括: 稀疏数组、单向队列、环形队列、单向链表、双向链表、环形链表、约瑟夫问题、栈、前缀、中缀、后缀表达式、中缀表达式转换为后缀表达式、递归与回溯、迷宫问题、八皇后问题、算法的时间复杂度、冒泡排序、选择排序、插入排序、快速排序、归并排序、希尔排序、基数排序(桶排序)、堆排序、排序速度分析、二分查找、插值查找、斐波那契查找、散列、哈希表、二叉树、二叉树与数组转换、二叉排序树(BST)、AVL树、线索二叉树、赫夫曼树、赫夫曼编码、多路查找树(B树B+树和B*树)、图、图的DFS算法和BFS、程序员常用10大算法、二分查找算法(非递归)、分治算法、动态规划算法、KMP算法、贪心算法、普里姆算法、克鲁斯卡尔算法、迪杰斯特拉算法、弗洛伊德算法马踏棋盘算法。 学习目标:通过学习,学员能掌握主流数据结构和算法的实现机制,开阔编程思路,提高优化程序的能力。

    3237 人正在学习 去看看 佟刚

1.5漫水填充

1.5.1漫水填充的定义

漫水填充法是一种用特定的颜色填充联通区域,通过设置可连通像素的上下限以及连通方式来达到不同的填充效果的方法。漫水填充经常被用来标记或分离图像的一部分以便对其进行进一步处理或分析,也可以用来从输入图像获取掩码区域,掩码会加速处理过程,或只处理掩码指定的像素点,操作的结果总是某个连续的区域。

1.5.2漫水填充法的基本思想

所谓漫水填充,简单来说,就是自动选中了和种子点相连的区域,接着将该区域替换成指定的颜色,这是个非常有用的功能,经常用来标记或者分离图像的一部分进行处理或分析.漫水填充也可以用来从输入图像获取掩码区域,掩码会加速处理过程,或者只处理掩码指定的像素点.
以此填充算法为基础,类似photoshop的魔术棒选择工具就很容易实现了。漫水填充(FloodFill)是查找和种子点联通的颜色相同的点,魔术棒选择工具则是查找和种子点联通的颜色相近的点,将和初始种子像素颜色相近的点压进栈作为新种子
在OpenCV中,漫水填充是填充算法中最通用的方法。且在OpenCV 2.X中,使用C++重写过的FloodFill函数有两个版本。一个不带掩膜mask的版本,和一个带mask的版本。这个掩膜mask,就是用于进一步控制哪些区域将被填充颜色(比如说当对同一图像进行多次填充时)。这两个版本的FloodFill,都必须在图像中选择一个种子点,然后把临近区域所有相似点填充上同样的颜色,不同的是,不一定将所有的邻近像素点都染上同一颜色,漫水填充操作的结果总是某个连续的区域。当邻近像素点位于给定的范围(从loDiff到upDiff)内或在原始seedPoint像素值范围内时,FloodFill函数就会为这个点涂上颜色。

1.5.3 floodFill函数在OpenCV中的实现源代码

这个部分贴出OpenCV中本文相关函数的源码实现细节,来给想了解实现细节的小伙伴们参考。
<1>OpenCV3.0中两个版本的floodFill函数源码
第一个,不mask版本的floodFill。

/*【floodFill( )函数源代码】********************************************************
 * @Version:OpenCV 3.0.0(Opnencv2和Opnencv3差别不大,Linux和PC的对应版本源码完全一样,均在对应的安装目录下)  
 * @源码路径:…\opencv\sources\modules\imgproc\src\ floodfill.cpp
 * @起始行数:458行   
********************************************************************************/
int cv::floodFill( InputOutputArray _image, InputOutputArray _mask,
                  Point seedPoint, Scalar newVal, Rect* rect,
                  Scalar loDiff, Scalar upDiff, int flags )
{
    ConnectedComp comp;
    std::vector<FFillSegment> buffer;

    if( rect )
        *rect = Rect();

    int i, connectivity = flags & 255;
    union {
        uchar b[4];
        int i[4];
        float f[4];
        double _[4];
    } nv_buf;
    nv_buf._[0] = nv_buf._[1] = nv_buf._[2] = nv_buf._[3] = 0;

    struct { Vec3b b; Vec3i i; Vec3f f; } ld_buf, ud_buf;
    Mat img = _image.getMat(), mask;
    if( !_mask.empty() )
        mask = _mask.getMat();
    Size size = img.size();

    int type = img.type();
    int depth = img.depth();
    int cn = img.channels();

    if ( (cn != 1) && (cn != 3) )
    {
        CV_Error( CV_StsBadArg, "Number of channels in input image must be 1 or 3" );
    }

    if( connectivity == 0 )
        connectivity = 4;
    else if( connectivity != 4 && connectivity != 8 )
        CV_Error( CV_StsBadFlag, "Connectivity must be 4, 0(=4) or 8" );

    bool is_simple = mask.empty() && (flags & FLOODFILL_MASK_ONLY) == 0;

    for( i = 0; i < cn; i++ )
    {
        if( loDiff[i] < 0 || upDiff[i] < 0 )
            CV_Error( CV_StsBadArg, "lo_diff and up_diff must be non-negative" );
        is_simple = is_simple && fabs(loDiff[i]) < DBL_EPSILON && fabs(upDiff[i]) < DBL_EPSILON;
    }

    if( (unsigned)seedPoint.x >= (unsigned)size.width ||
       (unsigned)seedPoint.y >= (unsigned)size.height )
        CV_Error( CV_StsOutOfRange, "Seed point is outside of image" );

    scalarToRawData( newVal, &nv_buf, type, 0);
    size_t buffer_size = MAX( size.width, size.height ) * 2;
    buffer.resize( buffer_size );

    if( is_simple )
    {
        size_t elem_size = img.elemSize();
        const uchar* seed_ptr = img.ptr(seedPoint.y) + elem_size*seedPoint.x;

        size_t k = 0;
        for(; k < elem_size; k++)
            if (seed_ptr[k] != nv_buf.b[k])
                break;

        if( k != elem_size )
        {
            if( type == CV_8UC1 )
                floodFill_CnIR(img, seedPoint, nv_buf.b[0], &comp, flags, &buffer);
            else if( type == CV_8UC3 )
                floodFill_CnIR(img, seedPoint, Vec3b(nv_buf.b), &comp, flags, &buffer);
            else if( type == CV_32SC1 )
                floodFill_CnIR(img, seedPoint, nv_buf.i[0], &comp, flags, &buffer);
            else if( type == CV_32FC1 )
                floodFill_CnIR(img, seedPoint, nv_buf.f[0], &comp, flags, &buffer);
            else if( type == CV_32SC3 )
                floodFill_CnIR(img, seedPoint, Vec3i(nv_buf.i), &comp, flags, &buffer);
            else if( type == CV_32FC3 )
                floodFill_CnIR(img, seedPoint, Vec3f(nv_buf.f), &comp, flags, &buffer);
            else
                CV_Error( CV_StsUnsupportedFormat, "" );
            if( rect )
                *rect = comp.rect;
            return comp.area;
        }
    }

    if( mask.empty() )
    {
        Mat tempMask( size.height + 2, size.width + 2, CV_8UC1 );
        tempMask.setTo(Scalar::all(0));
        mask = tempMask;
    }
    else
    {
        CV_Assert( mask.rows == size.height+2 && mask.cols == size.width+2 );
        CV_Assert( mask.type() == CV_8U );
    }

    memset( mask.ptr(), 1, mask.cols );
    memset( mask.ptr(mask.rows-1), 1, mask.cols );

    for( i = 1; i <= size.height; i++ )
    {
        mask.at<uchar>(i, 0) = mask.at<uchar>(i, mask.cols-1) = (uchar)1;
    }

    if( depth == CV_8U )
        for( i = 0; i < cn; i++ )
        {
            ld_buf.b[i] = saturate_cast<uchar>(cvFloor(loDiff[i]));
            ud_buf.b[i] = saturate_cast<uchar>(cvFloor(upDiff[i]));
        }
    else if( depth == CV_32S )
        for( i = 0; i < cn; i++ )
        {
            ld_buf.i[i] = cvFloor(loDiff[i]);
            ud_buf.i[i] = cvFloor(upDiff[i]);
        }
    else if( depth == CV_32F )
        for( i = 0; i < cn; i++ )
        {
            ld_buf.f[i] = (float)loDiff[i];
            ud_buf.f[i] = (float)upDiff[i];
        }
    else
        CV_Error( CV_StsUnsupportedFormat, "" );

    uchar newMaskVal = (uchar)((flags & ~0xff) == 0 ? 1 : ((flags >> 8) & 255));

    if( type == CV_8UC1 )
        floodFillGrad_CnIR<uchar, uchar, int, Diff8uC1>(
                img, mask, seedPoint, nv_buf.b[0], newMaskVal,
                Diff8uC1(ld_buf.b[0], ud_buf.b[0]),
                &comp, flags, &buffer);
    else if( type == CV_8UC3 )
        floodFillGrad_CnIR<Vec3b, uchar, Vec3i, Diff8uC3>(
                img, mask, seedPoint, Vec3b(nv_buf.b), newMaskVal,
                Diff8uC3(ld_buf.b, ud_buf.b),
                &comp, flags, &buffer);
    else if( type == CV_32SC1 )
        floodFillGrad_CnIR<int, uchar, int, Diff32sC1>(
                img, mask, seedPoint, nv_buf.i[0], newMaskVal,
                Diff32sC1(ld_buf.i[0], ud_buf.i[0]),
                &comp, flags, &buffer);
    else if( type == CV_32SC3 )
        floodFillGrad_CnIR<Vec3i, uchar, Vec3i, Diff32sC3>(
                img, mask, seedPoint, Vec3i(nv_buf.i), newMaskVal,
                Diff32sC3(ld_buf.i, ud_buf.i),
                &comp, flags, &buffer);
    else if( type == CV_32FC1 )
        floodFillGrad_CnIR<float, uchar, float, Diff32fC1>(
                img, mask, seedPoint, nv_buf.f[0], newMaskVal,
                Diff32fC1(ld_buf.f[0], ud_buf.f[0]),
                &comp, flags, &buffer);
    else if( type == CV_32FC3 )
        floodFillGrad_CnIR<Vec3f, uchar, Vec3f, Diff32fC3>(
                img, mask, seedPoint, Vec3f(nv_buf.f), newMaskVal,
                Diff32fC3(ld_buf.f, ud_buf.f),
                &comp, flags, &buffer);
    else
        CV_Error(CV_StsUnsupportedFormat, "");

    if( rect )
        *rect = comp.rect;
    return comp.area;
}

第二个,不带mask版本的floodFill。

/*【floodFill( )函数源代码】********************************************************
 * @Version:OpenCV 3.0.0(Opnencv2和Opnencv3差别不大,Linux和PC的对应版本源码完全一样,均在对应的安装目录下)  
 * @源码路径:…\opencv\sources\modules\imgproc\src\ floodfill.cpp
 * @起始行数:628行   
********************************************************************************/
int cv::floodFill( InputOutputArray _image, Point seedPoint,
                  Scalar newVal, Rect* rect,
                  Scalar loDiff, Scalar upDiff, int flags )
{
    return floodFill(_image, Mat(), seedPoint, newVal, rect, loDiff, upDiff, flags);
}

从3.0的版本可以看出不带mask版本的floodFill就是调用带mask版本的floodFill函数,而在3.0以下版本有些许不同,请看下文。
<2>OpenCV2.X中两个版本的floodFill函数源码
第一个,不带mask版本的floodFill:

int cv::floodFill( InputOutputArray _image,
Point seedPoint,  
Scalar newVal, Rect* rect,  
Scalar loDiff, ScalarupDiff, int flags )  
{  
   CvConnectedComp ccomp;  
   CvMat c_image = _image.getMat();  
   cvFloodFill(&c_image, seedPoint, newVal, loDiff, upDiff, &ccomp,flags, 0);  
   if( rect )  
       *rect = ccomp.rect;  
   return cvRound(ccomp.area);  
}  

第二个,带mask版本的floodFill:

int cv::floodFill( InputOutputArray _image,
InputOutputArray _mask,  
Point seedPoint, ScalarnewVal, Rect* rect,  
Scalar loDiff, ScalarupDiff, int flags )  
{  
   CvConnectedComp ccomp;  
   CvMat c_image = _image.getMat(), c_mask = _mask.getMat();  
    cvFloodFill(&c_image, seedPoint, newVal,loDiff, upDiff, &ccomp, flags, c_mask.data.ptr ? &c_mask : 0);  
   if( rect )  
       *rect = ccomp.rect;  
   return cvRound(ccomp.area);  
}  

我们依然可以发现,其内部实现是基于OpenCV 1.X旧版的cvFloodFill函数,我们再来看看其旧版函数的源码。
<3>OpenCV2.X中cvFloodFill()函数的实现源码

CV_IMPL void  
cvFloodFill( CvArr* arr, CvPointseed_point,  
            CvScalar newVal, CvScalar lo_diff, CvScalar up_diff,  
            CvConnectedComp* comp, int flags, CvArr* maskarr )  
{  
   cv::Ptr<CvMat> tempMask;  
   std::vector<CvFFillSegment> buffer;  

   if( comp )  
       memset( comp, 0, sizeof(*comp) );  

   int i, type, depth, cn, is_simple;  
   int buffer_size, connectivity = flags & 255;  
   union {  
       uchar b[4];  
       int i[4];  
       float f[4];  
       double _[4];  
    }nv_buf;  
   nv_buf._[0] = nv_buf._[1] = nv_buf._[2] = nv_buf._[3] = 0;  

   struct { cv::Vec3b b; cv::Vec3i i; cv::Vec3f f; } ld_buf, ud_buf;  
   CvMat stub, *img = cvGetMat(arr, &stub);  
   CvMat maskstub, *mask = (CvMat*)maskarr;  
   CvSize size;  

   type = CV_MAT_TYPE( img->type );  
   depth = CV_MAT_DEPTH(type);  
   cn = CV_MAT_CN(type);  

   if( connectivity == 0 )  
       connectivity = 4;  
   else if( connectivity != 4 && connectivity != 8 )  
       CV_Error( CV_StsBadFlag, "Connectivity must be 4, 0(=4) or 8");  

   is_simple = mask == 0 && (flags & CV_FLOODFILL_MASK_ONLY) ==0;  

   for( i = 0; i < cn; i++ )  
    {  
       if( lo_diff.val[i] < 0 || up_diff.val[i] < 0 )  
           CV_Error( CV_StsBadArg, "lo_diff and up_diff must benon-negative" );  
       is_simple &= fabs(lo_diff.val[i]) < DBL_EPSILON &&fabs(up_diff.val[i]) < DBL_EPSILON;  
    }  

   size = cvGetMatSize( img );  

   if( (unsigned)seed_point.x >= (unsigned)size.width ||  
        (unsigned)seed_point.y >=(unsigned)size.height )  
       CV_Error( CV_StsOutOfRange, "Seed point is outside of image");  

   cvScalarToRawData( &newVal, &nv_buf, type, 0 );  
   buffer_size = MAX( size.width, size.height ) * 2;  
   buffer.resize( buffer_size );  

   if( is_simple )  
    {  
       int elem_size = CV_ELEM_SIZE(type);  
       const uchar* seed_ptr = img->data.ptr + img->step*seed_point.y +elem_size*seed_point.x;  

       for(i = 0; i < elem_size; i++)  
           if (seed_ptr[i] != nv_buf.b[i])  
                break;  

       if (i != elem_size)  
       {  
           if( type == CV_8UC1 )  
               icvFloodFill_CnIR(img->data.ptr, img->step, size, seed_point,nv_buf.b[0],  
                                  comp, flags, &buffer);  
           else if( type == CV_8UC3 )  
               icvFloodFill_CnIR(img->data.ptr, img->step, size, seed_point,cv::Vec3b(nv_buf.b),  
                                  comp, flags,&buffer);  
           else if( type == CV_32SC1 )  
               icvFloodFill_CnIR(img->data.ptr, img->step, size, seed_point,nv_buf.i[0],  
                                  comp, flags,&buffer);  
           else if( type == CV_32FC1 )  
               icvFloodFill_CnIR(img->data.ptr, img->step, size, seed_point,nv_buf.f[0],  
                                  comp, flags,&buffer);  
           else if( type == CV_32SC3 )  
               icvFloodFill_CnIR(img->data.ptr, img->step, size, seed_point,cv::Vec3i(nv_buf.i),  
                                  comp, flags, &buffer);  
           else if( type == CV_32FC3 )  
               icvFloodFill_CnIR(img->data.ptr, img->step, size, seed_point,cv::Vec3f(nv_buf.f),  
                                  comp, flags,&buffer);  
           else  
                CV_Error(CV_StsUnsupportedFormat, "" );  
           return;  
       }  
    }  

   if( !mask )  
    {  
       /* created mask will be 8-byte aligned */  
       tempMask = cvCreateMat( size.height + 2, (size.width + 9) & -8,CV_8UC1 );  
       mask = tempMask;  
    }  
   else  
    {  
       mask = cvGetMat( mask, &maskstub );  
       if( !CV_IS_MASK_ARR( mask ))  
           CV_Error( CV_StsBadMask, "" );  

       if( mask->width != size.width + 2 || mask->height != size.height +2 )  
           CV_Error( CV_StsUnmatchedSizes, "mask must be 2 pixel wider "  
                                   "and 2pixel taller than filled image" );  
    }  

   int width = tempMask ? mask->step : size.width + 2;  
   uchar* mask_row = mask->data.ptr + mask->step;  
    memset(mask_row - mask->step, 1, width );  

   for( i = 1; i <= size.height; i++, mask_row += mask->step )  
    {  
       if( tempMask )  
           memset( mask_row, 0, width );  
       mask_row[0] = mask_row[size.width+1] = (uchar)1;  
    }  
   memset( mask_row, 1, width );  

   if( depth == CV_8U )  
       for( i = 0; i < cn; i++ )  
       {  
           int t = cvFloor(lo_diff.val[i]);  
           ld_buf.b[i] = CV_CAST_8U(t);  
           t = cvFloor(up_diff.val[i]);  
           ud_buf.b[i] = CV_CAST_8U(t);  
       }  
   else if( depth == CV_32S )  
       for( i = 0; i < cn; i++ )  
       {  
           int t = cvFloor(lo_diff.val[i]);  
           ld_buf.i[i] = t;  
           t = cvFloor(up_diff.val[i]);  
           ud_buf.i[i] = t;  
       }  
    else if( depth == CV_32F )  
       for( i = 0; i < cn; i++ )  
       {  
           ld_buf.f[i] = (float)lo_diff.val[i];  
           ud_buf.f[i] = (float)up_diff.val[i];  
       }  
   else  
       CV_Error( CV_StsUnsupportedFormat, "" );  

   if( type == CV_8UC1 )  
       icvFloodFillGrad_CnIR<uchar, int, Diff8uC1>(  
                              img->data.ptr,img->step, mask->data.ptr, mask->step,  
                              size, seed_point,nv_buf.b[0],  
                              Diff8uC1(ld_buf.b[0],ud_buf.b[0]),  
                              comp, flags,&buffer);  
   else if( type == CV_8UC3 )  
       icvFloodFillGrad_CnIR<cv::Vec3b, cv::Vec3i, Diff8uC3>(  
                              img->data.ptr,img->step, mask->data.ptr, mask->step,  
                              size, seed_point,cv::Vec3b(nv_buf.b),  
                             Diff8uC3(ld_buf.b, ud_buf.b),  
                              comp, flags,&buffer);  
   else if( type == CV_32SC1 )  
       icvFloodFillGrad_CnIR<int, int, Diff32sC1>(  
                              img->data.ptr,img->step, mask->data.ptr, mask->step,  
                              size, seed_point,nv_buf.i[0],  
                             Diff32sC1(ld_buf.i[0], ud_buf.i[0]),  
                              comp, flags, &buffer);  
   else if( type == CV_32SC3 )  
       icvFloodFillGrad_CnIR<cv::Vec3i, cv::Vec3i, Diff32sC3>(  
                              img->data.ptr,img->step, mask->data.ptr, mask->step,  
                              size, seed_point,cv::Vec3i(nv_buf.i),  
                             Diff32sC3(ld_buf.i, ud_buf.i),  
                              comp, flags,&buffer);  
   else if( type == CV_32FC1 )  
       icvFloodFillGrad_CnIR<float, float, Diff32fC1>(  
                              img->data.ptr,img->step, mask->data.ptr, mask->step,  
                              size, seed_point,nv_buf.f[0],  
                             Diff32fC1(ld_buf.f[0], ud_buf.f[0]),  
                              comp, flags,&buffer);  
   else if( type == CV_32FC3 )  
       icvFloodFillGrad_CnIR<cv::Vec3f, cv::Vec3f, Diff32fC3>(  
                              img->data.ptr,img->step, mask->data.ptr, mask->step,  
                              size, seed_point,cv::Vec3f(nv_buf.f),  
                             Diff32fC3(ld_buf.f,ud_buf.f),  
                              comp, flags,&buffer);  
   else  
       CV_Error(CV_StsUnsupportedFormat, "");  
}  

由于篇幅有限,在这里笔者就不继续贴出1.X版本的源码,请感兴趣的朋友自行去查看吧。可以看出高版本是在低版本的基础上修改而来的,只是更加规范和完善了。

1.5.4 floodFill函数详解

在OpenCV中,漫水填充算法由floodFill函数实现,其作用是用我们指定的颜色从种子点开始填充一个连接域。连通性由像素值的接近程度来衡量。OpenCV2.X有两个C++重写版本的floodFill。
第一个版本的floodFill:

C++: int floodFill(InputOutputArray image, 
                   Point seed, 
                   Scalar newVal, 
                   Rect* rect=0, 
                   Scalar loDiff=Scalar(),
                   Scalar upDiff=Scalar(), 
                   int flags=4 )

第二个版本的floodFill:

C++: int floodFill(InputOutputArray image, 
                   InputOutputArray mask, 
                   Point seed, 
                   Scalar newVal, 
                   Rect* rect=0, 
                   Scalar loDiff=Scalar(), 
                   Scalar upDiff=Scalar(), 
                   int flags=4 )

下面是一起介绍的参数详解。除了第二个参数外,其他的参数都是共用的。
第一个参数,InputOutputArray类型的image, 输入/输出1通道或3通道,8位或浮点图像,具体参数由之后的参数具体指明。
第二个参数, InputOutputArray类型的mask,这是第二个版本的floodFill独享的参数,表示操作掩模,。它应该为单通道、8位、长和宽上都比输入图像 image 大两个像素点的图像。第二个版本的floodFill需要使用以及更新掩膜,所以这个mask参数我们一定要将其准备好并填在此处。需要注意的是,漫水填充不会填充掩膜mask的非零像素区域。例如,一个边缘检测算子的输出可以用来作为掩膜,以防止填充到边缘。同样的,也可以在多次的函数调用中使用同一个掩膜,以保证填充的区域不会重叠。另外需要注意的是,掩膜mask会比需填充的图像大,所以 mask 中与输入图像(x,y)像素点相对应的点的坐标为(x+1,y+1)。
第三个参数,Point类型的seedPoint,漫水填充算法的起始点。
第四个参数,Scalar类型的newVal,像素点被染色的值,即在重绘区域像素的新值。
第五个参数,Rect*类型的rect,有默认值0,一个可选的参数,用于设置floodFill函数将要重绘区域的最小边界矩形区域。
第六个参数,Scalar类型的loDiff,有默认值Scalar( ),表示当前观察像素值与其部件邻域像素值或者待加入该部件的种子像素之间的亮度或颜色之负差(lower brightness/color difference)的最大值。
第七个参数,Scalar类型的upDiff,有默认值Scalar( ),表示当前观察像素值与其部件邻域像素值或者待加入该部件的种子像素之间的亮度或颜色之正差(lower brightness/color difference)的最大值。
第八个参数,int类型的flags,操作标志符,此参数包含三个部分,比较复杂,我们一起详细看看。
•低八位(第0~7位)用于控制算法的连通性,可取4 (4为缺省值) 或者 8。如果设为4,表示填充算法只考虑当前像素水平方向和垂直方向的相邻点;如果设为 8,除上述相邻点外,还会包含对角线方向的相邻点。
•高八位部分(16~23位)可以为0 或者如下两种选项标识符的组合:
FLOODFILL_FIXED_RANGE - 如果设置为这个标识符的话,就会考虑当前像素与种子像素之间的差,否则就考虑当前像素与其相邻像素的差。也就是说,这个范围是浮动的。
FLOODFILL_MASK_ONLY - 如果设置为这个标识符的话,函数不会去填充改变原始图像 (也就是忽略第三个参数newVal), 而是去填充掩模图像(mask)。这个标识符只对第二个版本的floodFill有用,因第一个版本里面压根就没有mask参数。
•中间八位部分,上面关于高八位FLOODFILL_MASK_ONLY标识符中已经说的很明显,需要输入符合要求的掩码。Floodfill的flags参数的中间八位的值就是用于指定填充掩码图像的值的。但如果flags中间八位的值为0,则掩码会用1来填充。

而所有flags可以用or操作符连接起来,即“|”。例如,如果想用8邻域填充,并填充固定像素值范围,填充掩码而不是填充源图像,以及设填充值为38,那么输入的参数是这样:
flags=8 | FLOODFILL_MASK_ONLY | FLOODFILL_FIXED_RANGE | (38<<8)
接着,来看一个关于Floodfill的简单的调用范例。
代码参见附件【demo1】,运行截图。

这里写图片描述

图1

看看效果你就知道能干什么了,通过此种方法可以不想要的内容。

1.5.5 SetMouseCallback( )函数详解

因为下面示例程序中有用到SetMouseCallback函数,我们在这里讲一讲。SetMouseCallback函数为指定的窗口设置鼠标回调函数。

 C++: void setMouseCallback(conststring& winname,
                            MouseCallback onMouse, 
                            void* userdata=0 )  

【参数】
第一个参数,const string&类型的winname,为窗口的名字。
第二个参数,MouseCallback类型的onMouse,指定窗口里每次鼠标时间发生的时候,被调用的函数指针。这个函数的原型应该为voidFoo(int event, int x, int y, int flags, void* param);其中event是 CV_EVENT_*变量之一, x和y是鼠标指针在图像坐标系的坐标(不是窗口坐标系), flags是CV_EVENT_FLAG的组合, param是用户定义的传递到cvSetMouseCallback函数调用的参数。
第三个参数,void*类型的userdata,用户定义的传递到回调函数的参数,有默认值0。

1.5.5漫水填充综合实例

本次的综合示例为OpenCV文档中自带的一个程序。笔者将其做了适当的修改并详细注释,放出来供大家消化理解。此程序着不少的按键功能。而我们拿着鼠标对窗口中的图形一顿狂点,就可以得到类似PhotoShop中魔棒的效果,当然,就这短短的两百来行代码写出来的东西,体验是比不上PS的魔棒工具的。
参见附件【demo2】。

这里写图片描述

图2程序功能

这里写图片描述

图3灰度图

这里写图片描述

图4漫水填充

本章附件:

请点击参考链接

【注意】博主在附件中的代码只有Linux版本的,如何使用Windows使用该代码请参看博主的另一篇博文
Opencv环境搭建(Visual Studio+Windows)- 请点击
有任何问题请联系博主。

2018-02-23 21:22:51 revitalise 阅读数 1692
  • 图解Java数据结构和算法

    1.算法是程序的灵魂,优秀的程序在对海量数据处理时,依然保持高速计算,就需要高效的数据结构和算法支撑。2.网上数据结构和算法的课程不少,但存在两个问题: 1)授课方式单一,大多是照着代码念一遍,数据结构和算法本身就比较难理解,对基础好的学员来说,还好一点,对基础不好的学生来说,基本上就是听天书了 2)说是讲数据结构和算法,但大多是挂羊头卖狗肉,算法讲的很少。 本课程针对上述问题,有针对性的进行了升级  3)授课方式采用图解+算法游戏的方式,让课程生动有趣好理解 4)系统全面的讲解了数据结构和算法, 除常用数据结构和算法外,还包括程序员常用10大算法:二分查找算法(非递归)、分治算法、动态规划算法、KMP算法、贪心算法、普里姆算法、克鲁斯卡尔算法、迪杰斯特拉算法、弗洛伊德算法、马踏棋盘算法。可以解决面试遇到的最短路径、最小生成树、最小连通图、动态规划等问题及衍生出的面试题,让你秒杀其他面试小伙伴 3.如果你不想永远都是代码工人,就需要花时间来研究下数据结构和算法。教程内容: 本教程是使用Java来讲解数据结构和算法,考虑到数据结构和算法较难,授课采用图解加算法游戏的方式。内容包括: 稀疏数组、单向队列、环形队列、单向链表、双向链表、环形链表、约瑟夫问题、栈、前缀、中缀、后缀表达式、中缀表达式转换为后缀表达式、递归与回溯、迷宫问题、八皇后问题、算法的时间复杂度、冒泡排序、选择排序、插入排序、快速排序、归并排序、希尔排序、基数排序(桶排序)、堆排序、排序速度分析、二分查找、插值查找、斐波那契查找、散列、哈希表、二叉树、二叉树与数组转换、二叉排序树(BST)、AVL树、线索二叉树、赫夫曼树、赫夫曼编码、多路查找树(B树B+树和B*树)、图、图的DFS算法和BFS、程序员常用10大算法、二分查找算法(非递归)、分治算法、动态规划算法、KMP算法、贪心算法、普里姆算法、克鲁斯卡尔算法、迪杰斯特拉算法、弗洛伊德算法马踏棋盘算法。 学习目标:通过学习,学员能掌握主流数据结构和算法的实现机制,开阔编程思路,提高优化程序的能力。

    3237 人正在学习 去看看 佟刚

在此用矩形连通域为例

当获取车牌位置信息时,连通域的长宽比是一种非常有效的辅助定位方法。

大致步骤:

1.输入图像获取灰度图像。

2.选取合适的阈值将灰度图像转化为二值图像。

3.对二值图像进行形态学处理,主要任务是去除连通域面积较小的区域以及降低筛选难度。

4.利用bwlabel()函数对连通区域进行标记

5.得到连通域的长宽比


matlab实现程序:

clear all;close all;clc
I= imread('F:\matlab\MATLAB上机操作\源代码\Fig0903(a)(utk).tif');
I = rgb2gray(RGB);
threshold = graythresh(I);
bw = bwareaopen(bw,50);%去除连通域面积小于50的区域(连通域面积与对象的像素数目不一定相等)
se = strel('disk',2);
bw = imclose(bw,se);%闭运算
bw = imfill(bw,'holes');%填充
ed=edge(bw);
L = bwlabel(bw);%标记连通域
L1 = bwlabel(ed);
p=zeros(1,max(L1(:)));
for i=1:max(L(:))%得到连通域的长宽比
p(i)=sum(ed(L==i));
[y,x]=find(L==i);
x0=min(x(:));
x1=max(x(:));
y0=min(y(:));
y1=max(y(:));
bl=(x1-x0)/(y1-y0);
disp(bl)
end


图像填充imfill()

bw2=imfill(bw)该函数对二值图像进行填充操作,对于二维图像允许通过鼠标选择填充的点。

[bw2,locations]=imfill(bw)  locations包含交互式选择时的点坐标。

bw2=imfill(bw,’holes’)通过参数holes可以填充二值图像的空洞。

连通域标记bwlabel()

L=bwlabel(bw,n)该函数先对二值图像的连通区域进行标记,参数n为联通类型,可取值48,默认为8,即为8连通,函数返回值为标记矩阵和原来的二值图像有相同的大小

[l,num]=bwlabel(bw,n)  num为连通域的数目。

Bwlabel()返回的标记矩阵可以通过函数label2rgb()进行显示。


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