• 图像处理基本算法 链码 边界跟踪


    链码在图像提取的后期即模式识别是一个很重要的特征,比如进行数字识别或者文字识别都会用到链码的特征,而链码的提取则可以借助于边界跟踪算法获取边界序列,注意是边界序列而不是边界,边界很容易获取,但是要想把边界的点按照一定的顺序输出则要费些功夫。下面采用边界跟踪算法获取边界,并存储在堆栈中,(这里的堆栈实际是C++容器类,是虚拟堆栈)。


    利用点的八邻域信息,选择下一个点作为边界点,这个算法需要选择一个开始点,可以选择图像上是目标点,在最上,最左的点。然后查看它的八邻域的点,从右下方45°的位置开始寻找,如果是目标点,将沿顺时针90°作为下一次寻找的方向,如果不是,则逆时针45°继续寻找,一旦找到重复上面的过程。


    具体的步骤在算法中有讲解。

    /************************************************************************/
    /* 查找物体的边界,输出已排序的边界序列 适应于单一区域        */
    /************************************************************************/
    //若能够输出边界点的序列则是比较有用的
    #include<cv.h>
    #include <highgui.h>
    #include <iostream>
    #include <stack>
    using namespace std;
    
    
    
    int main(){ 
    	IplImage * image,*image2;
    	image = cvLoadImage("E:\image\mapleleaf.tif",0);
    	cvNamedWindow("image",1);
    	cvShowImage("image",image);
    
    	image2 = cvCreateImage(cvSize(image->width, image->height),image->depth,1);
    	cvZero(image2);//image2 赋值为0
    	//寻找区域的左上角点
    	CvPoint startPoint = cvPoint(0,0);
    	bool bFindStartpoint = false;
    	int i ,j;
    	unsigned char * ptr,*dst;
    	stack<int> board;//奇数位存储x坐标,偶数位存储y坐标
    
    	//当前扫描点
    	CvPoint currentPoint = cvPoint(0,0);
    	//邻域的8个点的方向
    	int directions[8][2] = {{0,1},{1,1},{1,0},{1,-1},{0,-1},{-1,-1},{-1,0},{-1,1}}; 
    	int beginDirection = 0;
    	bool bFindBoardpoint = false;//寻找到邻域的边界点的判定
    	for (i = 0 ; i< image->height && bFindStartpoint == false; i++)
    	{
    		for (j = 0 ; j< image->width && bFindStartpoint == false; j++)
    		{
    			ptr = (unsigned char *)(image->imageData + i*image->widthStep + j);
    			if (*ptr == 255)
    			{
    				startPoint.x = j;
    				startPoint.y = i;
    				bFindStartpoint = true;
    				//cout<<"x:  " << j <<"y :  " <<i <<endl;  
    			}
    		}
    	}
    
    	//进行边界跟踪 每次搜索8个方向的点 找到了即停止
    	currentPoint = startPoint;
        bFindStartpoint = false;
    	beginDirection = 0;
    	board.push(startPoint.x);
    	board.push(startPoint.y);
    	while (!bFindStartpoint)
    	{
    		bFindBoardpoint = false;
    		//在8个方向寻找符合条件的边界点
    		while (!bFindBoardpoint)
    		{   
    			//进行出界判定  不对啊 这张图不可能出界啊
    			ptr = (unsigned char *)(image->imageData + (currentPoint.y + directions[beginDirection][1])* image->widthStep + currentPoint.x + directions[beginDirection][0]);
    			if (*ptr == 255)
    			{
    				bFindBoardpoint = true;
    				currentPoint.x +=  directions[beginDirection][0];
    				currentPoint.y  += directions[beginDirection][1];
    				/************************************************************************/
    				/*  此处添加序列存储的代码                    */
    				/************************************************************************/
    				//一、将边界存储到图片中
    				dst  = (unsigned char *)image2->imageData + currentPoint.y * image2->widthStep + currentPoint.x;
    				*dst = 255;
    
    				//二、将边界点的序列存储到一个堆栈中
    				board.push(currentPoint.x);
    				board.push(currentPoint.y);
    
    				if (currentPoint.x == startPoint.x  && currentPoint.y == startPoint.y )
    				{
    					bFindStartpoint = true;
    				}
    				//改变下次首先开始扫描的方向
    				beginDirection -= 2;
    				if (beginDirection < 0)
    				{
    					beginDirection += 8;
    				}
    				
    				
    				
    			}
    			else
    			{
    				beginDirection ++;
    				beginDirection = beginDirection%8;
    			}
    		}
    		//cout<<"currentPoint    "<<currentPoint.x <<"     "<< currentPoint.y<<endl;
    	}
    	cvNamedWindow("image2",1);
    	cvShowImage("image2",image2);
    	
    
    	//显示堆栈中的数据 顺时针存储,逆时针显示
    	//注意:显示时候堆栈中已经没有数据了
    /*	int x,y;
    	while(!board.empty())
    	{
    		y = board.top();
    		board.pop();
    		x = board.top();
    		board.pop();
    		cout<<"x   "<<x<<"    y    "<<y<<endl;
    	}
    */
    	cvWaitKey(0);
    	return 0;
    }

      
    /************************************************************************/
    /* 轮廓跟踪算法获取物体的轮廓序列 生成边界链码   */
    /************************************************************************/
    #include<cv.h>
    #include <highgui.h>
    #include <iostream>
    #include <stack>
    using namespace std;
    
    
    
    int main(){ 
    	IplImage * image,*image2,*image3;
    	image = cvLoadImage("E:\image\bottle2.tif",0);
    	cvNamedWindow("image",1);
    	cvShowImage("image",image);
    
    	image2 = cvCreateImage(cvSize(image->width, image->height),image->depth,1);
    	image3 = cvCreateImage(cvSize(image->width, image->height),image->depth,1);
    	cvZero(image2);//image2 赋值为0
    	cvZero(image3);
    	//寻找区域的左上角点
    	CvPoint startPoint = cvPoint(0,0);
    	bool bFindStartpoint = false;
    	int i ,j;
    	unsigned char * ptr,*dst;
    	stack<int> board;//奇数位存储x坐标,偶数位存储y坐标
    
    	//当前扫描点
    	CvPoint currentPoint = cvPoint(0,0);
    	//邻域的8个点的方向
    	int directions[8][2] = {{0,1},{1,1},{1,0},{1,-1},{0,-1},{-1,-1},{-1,0},{-1,1}}; 
    	int beginDirection = 0;
    	bool bFindBoardpoint = false;//寻找到邻域的边界点的判定
    	for (i = 0 ; i< image->height && bFindStartpoint == false; i++)
    	{
    		for (j = 0 ; j< image->width && bFindStartpoint == false; j++)
    		{
    			ptr = (unsigned char *)(image->imageData + i*image->widthStep + j);
    			if (*ptr == 255)
    			{
    				startPoint.x = j;
    				startPoint.y = i;
    				bFindStartpoint = true;
    				//cout<<"x:  " << j <<"y :  " <<i <<endl;  
    			}
    		}
    	}
    
    	//进行边界跟踪 每次搜索8个方向的点 找到了即停止
    	currentPoint = startPoint;
        bFindStartpoint = false;
    	beginDirection = 0;
    	board.push(startPoint.x);
    	board.push(startPoint.y);
    	while (!bFindStartpoint)
    	{
    		bFindBoardpoint = false;
    		//在8个方向寻找符合条件的边界点
    		while (!bFindBoardpoint)
    		{   
    			//进行出界判定  不对啊 这张图不可能出界啊
    			ptr = (unsigned char *)(image->imageData + (currentPoint.y + directions[beginDirection][1])* image->widthStep + currentPoint.x + directions[beginDirection][0]);
    			if (*ptr == 255)
    			{
    				bFindBoardpoint = true;
    				currentPoint.x +=  directions[beginDirection][0];
    				currentPoint.y  += directions[beginDirection][1];
    				/************************************************************************/
    				/*  此处添加序列存储的代码                    */
    				/************************************************************************/
    				//一、将边界存储到图片中
    				dst  = (unsigned char *)image2->imageData + currentPoint.y * image2->widthStep + currentPoint.x;
    				*dst = 255;
    
    				//二、将边界点的序列存储到一个堆栈中
    				board.push(currentPoint.x);
    				board.push(currentPoint.y);
    
    				if (currentPoint.x == startPoint.x  && currentPoint.y == startPoint.y )
    				{
    					bFindStartpoint = true;
    				}
    				//改变下次首先开始扫描的方向
    				beginDirection -= 2;
    				if (beginDirection < 0)
    				{
    					beginDirection += 8;
    				}
    				
    				
    				
    			}
    			else
    			{
    				beginDirection ++;
    				beginDirection = beginDirection%8;
    			}
    		}
    		//cout<<"currentPoint    "<<currentPoint.x <<"     "<< currentPoint.y<<endl;
    	}
    	cvNamedWindow("image2",1);
    	cvShowImage("image2",image2);
    	
    
    	//显示堆栈中的数据 顺时针存储,逆时针显示
    	//注意:显示时候堆栈中已经没有数据了
    /*	int x,y;
    	while(!board.empty())
    	{
    		y = board.top();
    		board.pop();
    		x = board.top();
    		board.pop();
    		cout<<"x   "<<x<<"    y    "<<y<<endl;
    	}
    */
    
    	//Board中存储着边界的序列 转化为8邻域链码,每隔10个点取样 显示
    
    	int lianmaLength = (board.size()+5)/10;
        int* lianma = new int[lianmaLength];
    
    	for (i = 0 ; i< lianmaLength  && !board.empty();i += 2)
    	{
    		lianma[i+1] = board.top();
    		board.pop();
    		lianma[i] = board.top();
    		board.pop();
    
    		for (j = 0; j< 18 && !board.empty();j++)
    		{
    			board.pop();
    		}
    	}
    	//将数据在image3中显示
    	int t;
    	for ( t = 0; t < lianmaLength;t += 2)
    	{
    		 i = lianma[t+1];
    		 j = lianma[t];
    		 ptr = (unsigned char *)image3->imageData + i*image->widthStep + j;
    		 *ptr = 255;
    		
    	}
        cvNamedWindow("image3",1);
    	cvSaveImage("E:\image\bottle2lianma.bmp",image3);
    	cvShowImage("image3",image3);
    
    
    	cvWaitKey(0);
    	return 0;
    }

    Keep it simple!
    作者:N3verL4nd
    知识共享,欢迎转载。
  • 相关阅读:
    树莓派研究笔记(2)-- 安装Nginx 服务器,PHP 和 SQLite
    树莓派研究笔记(1)-- 安装Mono
    Qemu虚拟机 玩树莓派最新版系统 (截止2017-04-10)
    CLRInjection
    CLRMonitor
    Xamarin Mono for VS开发窗体标题(Title)乱码解决方案
    精美3D中国象棋
    怀旧系列(5)----大学时代的疯狂
    怀旧系列(4)----文曲星编程GV-Basic
    怀旧系列(3)----Pascal
  • 原文地址:https://www.cnblogs.com/lgh1992314/p/6616300.html
Copyright © 2020-2023  润新知