1. 程式人生 > >opencv學習系列(七)--- 細化演算法

opencv學習系列(七)--- 細化演算法

演算法思想:


公式: y = p0*2^0 + p1*2^1+ p2*2^2 + p3*2^3 + p4*2^4 + p5*2^5 + p6*2^6 +p7*2^7

         前輩們對此作出了總結,得出每個點周圍8領域的256種情況,放在一個char data[256]的陣列中,不可以刪除用0來表示,能被刪除的用1來表示。然後對影象進行處理得到二值影象<0和1>,掃描影象,根據公式得出y,依次用data[y]判斷該點是否可以被刪除,直到所有的點都不可以被刪除為止。

演算法步驟:


程式碼

#include <stdlib.h>
#include <string.h>

#include "cv.h"
#include "highgui.h"
#include "cxcore.h"

//基於索引表的細化細化演算法
//功能:對圖象進行細化
//引數:lpDIBBits:代表圖象的一維陣列
//      lWidth:圖象高度
//      lHeight:圖象寬度
//      無返回值
bool ThiningDIBSkeleton (unsigned char* lpDIBBits, int lWidth, int lHeight)
{	
	//迴圈變數
	long i;
	long j;
	long lLength;

	unsigned char deletemark[256] = {      // 這個即為前人據8領域總結的是否可以被刪除的256種情況
		0,0,0,0,0,0,0,1,	0,0,1,1,0,0,1,1,
		0,0,0,0,0,0,0,0,	0,0,1,1,1,0,1,1,
		0,0,0,0,0,0,0,0,	1,0,0,0,1,0,1,1,
		0,0,0,0,0,0,0,0,	1,0,1,1,1,0,1,1,
		0,0,0,0,0,0,0,0,	0,0,0,0,0,0,0,0,
		0,0,0,0,0,0,0,0,	0,0,0,0,0,0,0,0,
		0,0,0,0,0,0,0,0,	1,0,0,0,1,0,1,1,
		1,0,0,0,0,0,0,0,	1,0,1,1,1,0,1,1,
		0,0,1,1,0,0,1,1,	0,0,0,1,0,0,1,1,
		0,0,0,0,0,0,0,0,	0,0,0,1,0,0,1,1,
		1,1,0,1,0,0,0,1,	0,0,0,0,0,0,0,0,
		1,1,0,1,0,0,0,1,	1,1,0,0,1,0,0,0,
		0,1,1,1,0,0,1,1,	0,0,0,1,0,0,1,1,
		0,0,0,0,0,0,0,0,	0,0,0,0,0,1,1,1,
		1,1,1,1,0,0,1,1,	1,1,0,0,1,1,0,0,
		1,1,1,1,0,0,1,1,	1,1,0,0,1,1,0,0
	};//索引表

	unsigned char p0, p1, p2, p3, p4, p5, p6, p7;
	unsigned char *pmid, *pmidtemp;    // pmid 用來指向二值影象  pmidtemp用來指向存放是否為邊緣
	unsigned char sum;
	bool bStart = true;
	lLength = lWidth * lHeight;
	unsigned char *pTemp = new uchar[sizeof(unsigned char) * lWidth * lHeight]();  //動態建立陣列 並且初始化
	
	//    P0 P1 P2
	//    P7    P3
	//    P6 P5 P4

	while(bStart)
	{
		bStart = false;

		//首先求邊緣點
		pmid = (unsigned char *)lpDIBBits + lWidth + 1;
		memset(pTemp,  0, lLength);
		pmidtemp = (unsigned char *)pTemp + lWidth + 1; //  如果是邊緣點 則將其設為1
		for(i = 1; i < lHeight -1; i++)     
		{
			for(j = 1; j < lWidth - 1; j++)
			{
				if( *pmid == 0)                   //是0 不是我們需要考慮的點
				{
					pmid++;
					pmidtemp++;
					continue;
				}
				p3 = *(pmid + 1);
				p2 = *(pmid + 1 - lWidth);
				p1 = *(pmid - lWidth);
				p0 = *(pmid - lWidth -1);
				p7 = *(pmid - 1);
				p6 = *(pmid + lWidth - 1);
				p5 = *(pmid + lWidth);
				p4 = *(pmid + lWidth + 1);				
				sum = p0 & p1 & p2 & p3 & p4 & p5 & p6 & p7;
				if(sum == 0)
				{
					*pmidtemp = 1;       // 這樣周圍8個都是1的時候  pmidtemp==1 表明是邊緣     					
				}

				pmid++;
				pmidtemp++;
			}
			pmid++;
			pmid++;
			pmidtemp++;
			pmidtemp++;
		}
		
		//現在開始刪除
		pmid = (unsigned char *)lpDIBBits + lWidth + 1;
		pmidtemp = (unsigned char *)pTemp + lWidth + 1;

		for(i = 1; i < lHeight -1; i++)   // 不考慮影象第一行 第一列 最後一行 最後一列
		{
			for(j = 1; j < lWidth - 1; j++)
			{
				if( *pmidtemp == 0)     //1表明是邊緣 0--周圍8個都是1 即為中間點暫不予考慮
				{
					pmid++;
					pmidtemp++;
					continue;
				}

				p3 = *(pmid + 1);
				p2 = *(pmid + 1 - lWidth);
				p1 = *(pmid - lWidth);
				p0 = *(pmid - lWidth -1);
				p7 = *(pmid - 1);
				p6 = *(pmid + lWidth - 1);
				p5 = *(pmid + lWidth);
				p4 = *(pmid + lWidth + 1);
				
				p1 *= 2;
				p2 *= 4;
				p3 *= 8;
				p4 *= 16;
				p5 *= 32;
				p6 *= 64;
				p7 *= 128;

				sum = p0 | p1 | p2 | p3 | p4 | p5 | p6 | p7;
			//	sum = p0 + p1 + p2 + p3 + p4 + p5 + p6 + p7;
				if(deletemark[sum] == 1)
				{
					*pmid = 0;
					bStart = true;      //  表明本次掃描進行了細化
				}
				pmid++;
				pmidtemp++;
			}

			pmid++;
			pmid++;
			pmidtemp++;
			pmidtemp++;
		}
	}
	delete []pTemp;
	return true;
}

int main(int argc, char* argv[])
{
	IplImage* src = cvLoadImage("E:\\study_opencv_video\\testthin\\char2.png",0);
	cvThreshold(src,src,100,255,CV_THRESH_BINARY);
	unsigned char* imagedata ;

	cvNamedWindow("s",0);
	cvShowImage("s" , src);
	imagedata = new uchar[sizeof(char)*src->width*src->height]();
	int x , y;
	for(y=0;y<src->height;y++)
	{
		unsigned char* ptr  = (unsigned char*)(src->imageData + y*src->widthStep);
		for(x=0;x<src->width;x++)
		{
			imagedata[y*src->width+x] = ptr[x] > 0 ? 1 : 0;
		}
	}
	ThiningDIBSkeleton(imagedata,src->width,src->height);

	for(y=0;y<src->height;y++)
	{
		unsigned char* ptr  = (unsigned char*)(src->imageData + y*src->widthStep);
		for(x=0;x<src->width;x++)
		{
			ptr[x] = imagedata[y*src->width + x]>0? 255 : 0;
		}

	}
	cvNamedWindow("src",0);
	cvShowImage("src" , src);
	cvWaitKey(0);
	delete []imagedata;
	return 0;
}

例子 左邊為輸入影象 右邊為細化的效果圖


轉載自http://blog.csdn.net/lu597203933/article/details/14397605