图像分割实验(二值化,otsu、迭代法、区域生长、区域分割)vs2010+opencv2.4

图像分割实验

实验目的

  1. 理解和掌握图像分割的基本理论和算法;
  2. 掌握用阈值法进行图像分割的基本方法;
  3. 掌握基于区域分割的图像分割方法。

二、实验环境与设备

  1. 计算机;
  2. VC++程序;

三、实验内容
打开计算机,启动VisualC++程序建立工程,工程文件夹中应有待处理的图像文件;编程实现基于区域的图像分割,并与普通的阈值分割图像作比较;记录和整理实验报告。

四、实验原理与步骤
1.连通定义
在数字图像中,一个像素在空间上可能非常接近其它一些像素。在用网格表示的数字图像中,一个像素与其它四个像素有公共边界,并与另外四个像素共享顶角。如果两个像素有公共边界,则称它们互为4邻点(4-neighbors)。同样,如果两个像素至少共享一个顶点,则称它们互为8邻点。

  1. 连通成分标记
    在一幅图像中找出连通成分是数字图像中最常见的运算之一。连通区域内的点构成表示物体的候选区域。机器视觉中的大多数物体都有表面,显然,物体表面点投影到图像平面上会形成空间上密集的点集。这里应该指出,连通成分算法常常会在二值视觉系统中形成瓶颈效应,原因是连通成分运算是一个全局性的运算,这种算法在本质上是连贯的。如果图像中仅有一个物体,那么找连通成分就没有必要;如果图像中有许多物体,且需要求出物体的特性与位置,则必须确定连通成分。
    连通标记算法可以找到图像中的所有连通成分,并对同一连通成分中的所有点分配同一标记。

  2. 递归算法
    算法步骤如下:
    ① 扫描图像,找到没有标记的1点,给它分配一个新的标记L;
    ② 递归分配标记L给1点的邻点;
    ③ 如果不存在没标记的点,则停止;
    ④ 返回第①步。

  3. 区域分裂算法
    如果区域的某些特性不是恒定的,则区域应该分裂。基于分裂方法的图像分割过程是从最大的区域开始,在许多情况下,常把整个图像作为起始分裂的图像。
    算法步骤如下:
    ① 形成初始区域;
    ② 对图像的每一个区域,连续执行下面两步:
    •计算区域灰度值方差;
    •如果方差值大于某一阈值,则沿着某一适合的边界分裂区域。

  4. 区域增长算法
    在许多图像中,单个区域内的灰度值不是完全恒定的,因此需要更复杂的算法来进行图像分割。其中最好的算法是那些基于如下假设的算法,即图像可以划分成区域,而区域可以用简单函数模型化。
    可由分割问题导出如下算法:寻找初始区域核,并从区域核开始,逐渐增长核区域,形成满足一定约束的较大的区域。
    算法步骤如下:
    ① 把图像划分成初始区域核;
    ② 用平面模型拟合每一个区域核;
    ③ 对每一个区域,通过区域模型向邻接区域外插,求取与该区域兼容的所有点;
    ④ 如果没有兼容点,则增加模型阶数。如果模型阶数大于最大的模型阶数,停止区域增长;否则,回到第③步,继续区域增长;
    ⑤ 形成新的区域,重新用相同阶数的模型能够拟合新区域,计算拟合最佳度;
    ⑥ 计算区域模型的新老拟合最佳度之差;
    ⑦ 如果新老拟合最佳度之差小于某一给定阈值,回到第三步,继续区域增长;
    ⑧ 增加模型阶数,如果模型阶数大于最大的模型阶数,停止区域增长;
    用新的模型阶数再拟合区域模型。入伙拟合误差增加,接收新的模型阶数,回到第③步,继续区域增长,否则,停止区域增长。

实验代码

#include
#include
#include 
#include 
#include
#include
#include 
#include
using namespace cv;
using namespace std;
int T,To;//分别为迭代阈值法和otsu法计算的最佳阈值
int myotsu(Mat &src);//otsu法确定阈值函数
void RegionGrowing(Mat src,Mat &src1,Point pt);//区域生长法
void RegionCut(Mat src,Mat &src1);//区域分裂法
int main(int argc, char* argv[])
{
	const char* image="C:\\Users\\dell\\Desktop\\test3.jpg";//读取图像文件
	Mat img = imread(image); 
	if (!img.data)
	{
		cout << "读入图像出错" << endl;
		return -1;
	}
	Mat src;
	Mat src1(img.rows,img.cols,CV_8UC1);
	Mat src2(img.rows,img.cols,CV_8UC1);
	cvtColor(img, src, COLOR_BGR2GRAY);//转化为灰度图
	src.copyTo(src1);
	src.copyTo(src2);
	To=myotsu(src);
	Mat src3(src.rows,src.cols,CV_8UC1,Scalar(0));//新建一个背景全黑的图像
	RegionGrowing(src,src3,Point(100,130));
	Mat src4(src.rows,src.cols,CV_8UC1,Scalar(255));//新建一个背景全黑的图像
	RegionCut(src,src4);
	for(int i=0;i(i,j)>=128)
				src.at(i,j)=255;
			else
				src.at(i,j)=0;
		}
	}
	namedWindow("原图", CV_WINDOW_NORMAL);
	imshow("原图",img);
	namedWindow("阈值分割图(二值化)", CV_WINDOW_NORMAL);
	imshow("阈值分割图(二值化)",src);
	int T1=128;int T2=0;
	//计算最优阈值
	while(1){
		int num1=0,num2=0;
		int sum1=0,sum2=0;
		int ave1=0,ave2=0;
		for(int i=0;i(i,j)>=T1){
					num1++;
					sum1+=src1.at(i,j);
				}else{
					num2++;
					sum2+=src1.at(i,j);
				}
			}
		}
		ave1=sum1/num1;
		ave2=sum2/num2;
		T2=(ave1+ave2)/2;
		if(abs(T2-T1)<0.1){
			T=T2;
			break;
		}else{
			T1=T2;
		}
	}
	cout<<"迭代阈值法确定的最优阈值为: "<(i,j)>=T)
				src1.at(i,j)=255;
			else
				src1.at(i,j)=0;
		}
	}
	namedWindow("阈值分割图(迭代阈值法)", CV_WINDOW_NORMAL);
	imshow("阈值分割图(迭代阈值法)",src1);

	cout<<"otsu法确定的最优阈值为: "<(i,j)>=To)
				src2.at(i,j)=255;
			else
				src2.at(i,j)=0;
		}
	}
	namedWindow("阈值分割图(otsu法)", CV_WINDOW_NORMAL);
	imshow("阈值分割图(otsu法)",src2);
	waitKey(0);
	return 0;
}
int myotsu(Mat &src){
	int th;
	const int Gray = 256;
	int pixCount[Gray] = {0};//每个灰度值所占像素个数
	int pixSum = src.cols * src.rows;//图像总像素点
	float pixPro[Gray] = {0};//每个灰度值所占总像素比例
	float w0, w1, u0tmp, u1tmp, u0, u1, deltaTmp, deltaMax = 0;
	for(int i=0;i(i,j)]++;//统计每个灰度级的像素个数
		}
	}
	for(int i=0;i deltaMax) 
		{
			deltaMax = deltaTmp;
			th = i;  
		}  
	}
	return th;
}
void RegionGrowing(Mat src,Mat &src1,Point pt){
	//8邻域
	Point connects[8]={Point(-1,-1),Point(0,-1),Point(1,-1),Point(-1,0),Point(1,0),Point(-1,1),Point(0,1),Point(1,1)};
	int startValue=0;//生长点灰度值
	int currentValue=0;//当前种子灰度值
	Mat flag;//用于标记当前像素点是否被便利过
	int exp=6;//可以生长的阈值范围
	src1.copyTo(flag);
	Point Ptnew;
	stack growPt;

	growPt.push(Point(pt.x,pt.y));//将种子点入栈

	src1.at(Point(pt.x,pt.y))=255;//将种子点标记为白色

	flag.at(pt.x,pt.y)=255;//将当前点标记为1
	while(!growPt.empty()){
		Point currpt=growPt.top();//将栈顶元素返回
		growPt.pop();
		startValue=src.at(currpt.x,currpt.y);//获取当前元素的灰度值,作为起始种子的灰度值
		for(int i=0;i<8;i++){
			Ptnew.x=currpt.x+connects[i].x;
			Ptnew.y=currpt.y+connects[i].y;
			//判断是否为边缘点
			if((Ptnew.x>0) && (Ptnew.y>0) && (Ptnew.x(Ptnew.x,Ptnew.y)==0){
					currentValue = src.at(Ptnew.x, Ptnew.y);	
					if(abs(startValue - currentValue)(Ptnew.x,Ptnew.y)=255;//将该点标记为白色
						flag.at(Ptnew.x,Ptnew.y)=255;//标记该点
						growPt.push(Point(Ptnew.x,Ptnew.y));//将该点入栈
					}
				}
			}
		}
	}
	namedWindow("区域生长法", CV_WINDOW_NORMAL);
	imshow("区域生长法",src1);
}
void RegionCut(Mat src,Mat &src1){
	namedWindow("huidutu", CV_WINDOW_NORMAL);
	imshow("huidutu",src);
	struct SplitStruct{
		int width;  //图像的宽度
		int height;  //图像的高度
		int x;    //相对原图像数据的偏移宽度
		int y;    //相对原图像数据的偏移高度
	};
	stack splitPt;
	SplitStruct ss,ssTemp;
	ss.width=src.rows;
	ss.height=src.cols;
	ss.x=0;
	ss.y=0;
	splitPt.push(ss);

	int values[2][2];//存储每次分类后的区间
	int Height[3],Width[3];//存储每次分裂后没个块的大小

	while(!splitPt.empty()){
		ss=splitPt.top();//将栈顶元素幅值给结构体
		splitPt.pop();   //栈顶元素出栈

		Height[0]=0;
		Height[1]=(ss.height+1)/2;//第一个块的高
		Height[2]=ss.height-Height[1];//第二个块的高

		Width[0]=0;  
		Width[1]=(ss.width+1)/2;  //第一个块的宽
		Width[2]=ss.width-Width[1];//第二个块的宽

		for(int i=1;i<3;i++){
			for(int j=1;j<3;j++){
				//计算每一个区域的平均灰度值
				int Sumgray=0;
				for(int p=0;p(p+ss.x,q+ss.y);
					}
				}
				if(Width[i]*Height[j]==0)
					continue;
				if(Width[i]*Height[j]==1){    //即分到最小像素
					if(src.at(Width[i-1]+ss.x,Height[j-1]+ss.y)<100){
						src1.at(Width[i-1]+ss.x,Height[j-1]+ss.y)=0;
					}else{
						src1.at(Width[i-1]+ss.x,Height[j-1]+ss.y)=255;
					}
					continue;
				}
				values[i-1][j-1]=Sumgray/(Height[j]*Width[i]);
				//判断是否需要继续分裂
				for(int p=0;p(p+Width[i-1]+ss.x,q+Height[j-1]+ss.y)-values[i-1][j-1])*(src.at(p+Width[i-1]+ss.x,q+Height[j-1]+ss.y)-values[i-1][j-1]);
					}
				}
				sn[i-1][j-1]=sngray/(Height[j]*Width[i]);

				//判断是否需要继续分裂
				if(sn[i-1][j-1]>16){   //如果该区域内的灰度值不满足设定阈值,则继续分裂,否则合并
					//设置新的区域的大小以及相对原点像素点偏移的位置
					ssTemp.width=Width[i];
					ssTemp.height=Height[j];
					ssTemp.x=ss.x+Width[i-1];
					ssTemp.y=ss.y+Height[j-1];
					splitPt.push(ssTemp);          //将新的结构体元素入栈
				}else{     //如果不继续分裂则合并
					for(int xx=0;xx(xx+Width[i-1]+ss.x,yy+Height[j-1]+ss.y)=0;   //即把该区域内的所有像素在src1中以0的灰度绘制出来
						}
					}
				}
			}
		}
	}
	namedWindow("区域分裂合并法", CV_WINDOW_NORMAL);
	imshow("区域分裂合并法",src1);
}

运行结果:
原图:
图像分割实验(二值化,otsu、迭代法、区域生长、区域分割)vs2010+opencv2.4_第1张图片图像分割实验(二值化,otsu、迭代法、区域生长、区域分割)vs2010+opencv2.4_第2张图片

  1. 使用阈值分割图像结果:
    (1)、指定阈值的简单二值分割:
    图像分割实验(二值化,otsu、迭代法、区域生长、区域分割)vs2010+opencv2.4_第3张图片
    图像分割实验(二值化,otsu、迭代法、区域生长、区域分割)vs2010+opencv2.4_第4张图片
    (2)、迭代阈值法分割:
    图像分割实验(二值化,otsu、迭代法、区域生长、区域分割)vs2010+opencv2.4_第5张图片
    图像分割实验(二值化,otsu、迭代法、区域生长、区域分割)vs2010+opencv2.4_第6张图片
    (3)、otsu阈值分割法:

图像分割实验(二值化,otsu、迭代法、区域生长、区域分割)vs2010+opencv2.4_第7张图片
图像分割实验(二值化,otsu、迭代法、区域生长、区域分割)vs2010+opencv2.4_第8张图片
两种方法的计算出的阈值:

图像分割实验(二值化,otsu、迭代法、区域生长、区域分割)vs2010+opencv2.4_第9张图片
图像分割实验(二值化,otsu、迭代法、区域生长、区域分割)vs2010+opencv2.4_第10张图片
通过otsu法和迭代法计算出的阈值相近,其效果较简单二值分割要优。
2、基于区域的图像分割:
区域生长法:

图像分割实验(二值化,otsu、迭代法、区域生长、区域分割)vs2010+opencv2.4_第11张图片
图像分割实验(二值化,otsu、迭代法、区域生长、区域分割)vs2010+opencv2.4_第12张图片
图像分割实验(二值化,otsu、迭代法、区域生长、区域分割)vs2010+opencv2.4_第13张图片
图像分割实验(二值化,otsu、迭代法、区域生长、区域分割)vs2010+opencv2.4_第14张图片

该算法仅对灰度图操作时很受灰度变换率和种子点的选取有关。
区域分裂合并法:
图像分割实验(二值化,otsu、迭代法、区域生长、区域分割)vs2010+opencv2.4_第15张图片
图像分割实验(二值化,otsu、迭代法、区域生长、区域分割)vs2010+opencv2.4_第16张图片
图像分割实验(二值化,otsu、迭代法、区域生长、区域分割)vs2010+opencv2.4_第17张图片

你可能感兴趣的:(笔记)