圖像的骨架提取

 

細化方法當中,當屬經典的Zhang並行快速細化算法,細化以後的輪廓走勢與原圖保持得相對較好。算法

參照 http://blog.csdn.net/byxdaz/article/details/5642669 , 代碼改造結果以下, 數組

//將 DEPTH_8U型二值圖像進行細化  經典的Zhang並行快速細化算法
void thin(const Mat &src, Mat &dst, const int iterations) { const int height =src.rows -1; const int width =src.cols -1; //拷貝一個數組給另外一個數組 if(src.data != dst.data) { src.copyTo(dst); } int n = 0,i = 0,j = 0; Mat tmpImg; uchar *pU, *pC, *pD; BOOL isFinished =FALSE; for(n=0; n<iterations; n++) { dst.copyTo(tmpImg); isFinished =FALSE; //一次 先行後列掃描 開始 //掃描過程一 開始 for(i=1; i<height; i++) { pU = tmpImg.ptr<uchar>(i-1); pC = tmpImg.ptr<uchar>(i); pD = tmpImg.ptr<uchar>(i+1); for(int j=1; j<width; j++) { if(pC[j] > 0) { int ap=0; int p2 = (pU[j] >0); int p3 = (pU[j+1] >0); if (p2==0 && p3==1) { ap++; } int p4 = (pC[j+1] >0); if(p3==0 && p4==1) { ap++; } int p5 = (pD[j+1] >0); if(p4==0 && p5==1) { ap++; } int p6 = (pD[j] >0); if(p5==0 && p6==1) { ap++; } int p7 = (pD[j-1] >0); if(p6==0 && p7==1) { ap++; } int p8 = (pC[j-1] >0); if(p7==0 && p8==1) { ap++; } int p9 = (pU[j-1] >0); if(p8==0 && p9==1) { ap++; } if(p9==0 && p2==1) { ap++; } if((p2+p3+p4+p5+p6+p7+p8+p9)>1 && (p2+p3+p4+p5+p6+p7+p8+p9)<7) { if(ap==1) { if((p2*p4*p6==0)&&(p4*p6*p8==0)) { dst.ptr<uchar>(i)[j]=0; isFinished =TRUE; } // if((p2*p4*p8==0)&&(p2*p6*p8==0)) // { // dst.ptr<uchar>(i)[j]=0; // isFinished =TRUE; // } } } } } //掃描過程一 結束 dst.copyTo(tmpImg); //掃描過程二 開始 for(i=1; i<height; i++) //一次 先行後列掃描 開始 { pU = tmpImg.ptr<uchar>(i-1); pC = tmpImg.ptr<uchar>(i); pD = tmpImg.ptr<uchar>(i+1); for(int j=1; j<width; j++) { if(pC[j] > 0) { int ap=0; int p2 = (pU[j] >0); int p3 = (pU[j+1] >0); if (p2==0 && p3==1) { ap++; } int p4 = (pC[j+1] >0); if(p3==0 && p4==1) { ap++; } int p5 = (pD[j+1] >0); if(p4==0 && p5==1) { ap++; } int p6 = (pD[j] >0); if(p5==0 && p6==1) { ap++; } int p7 = (pD[j-1] >0); if(p6==0 && p7==1) { ap++; } int p8 = (pC[j-1] >0); if(p7==0 && p8==1) { ap++; } int p9 = (pU[j-1] >0); if(p8==0 && p9==1) { ap++; } if(p9==0 && p2==1) { ap++; } if((p2+p3+p4+p5+p6+p7+p8+p9)>1 && (p2+p3+p4+p5+p6+p7+p8+p9)<7) { if(ap==1) { // if((p2*p4*p6==0)&&(p4*p6*p8==0)) // { // dst.ptr<uchar>(i)[j]=0; // isFinished =TRUE; // } if((p2*p4*p8==0)&&(p2*p6*p8==0)) { dst.ptr<uchar>(i)[j]=0; isFinished =TRUE; } } } } } } //一次 先行後列掃描完成 //若是在掃描過程當中沒有刪除點,則提早退出 if(isFinished ==FALSE) { break; } } } }

 

另外一種實現方法,細化結果不大好,但看起來好像是簡潔些,適合通常的簡單應用。spa

BOOL isContourPoint(const int x, const int y, const Mat& bwImg) {  BOOL p[10] ={0}; //記錄當前點的8鄰域的有無狀況  const BYTE *pU= bwImg.ptr(y-1, x); //上一行  const BYTE *pC= bwImg.ptr(y, x); //當前行  const BYTE *pD= bwImg.ptr(y+1, x); //下一行  p[2]=*(pU) ? true:false;  p[3]=*(pU+1) ? true:false;  p[4]=*(pC+1) ? true:false;  p[5]=*(pD+1) ? true:false;  p[6]=*(pD) ? true:false;  p[7]=*(pD-1) ? true:false;  p[8]=*(pC-1) ? true:false;  p[9]=*(pU-1) ? true:false;  int Np=0;//鄰域不爲零節點總數  int Tp=0;//鄰域節點由0變成1的次數  for (int i=2; i<10; i++)  {  Np += p[i];  int k= (i<9) ? (i+1) : 2;  if ( p[k] -p[i]>0)  {  Tp++;  }  }  int p246= p[2] && p[4] && p[6];  int p468= p[4] && p[6] && p[8];  int p24= p[2] && !p[3] && p[4] && !p[5] && !p[6] && !p[7] && !p[8] && !p[9];  int p46= !p[2] && !p[3] && p[4] && !p[5] && p[6] && !p[7] && !p[8] && !p[9];  int p68= !p[2] && !p[3] && !p[4] && !p[5] && p[6] && !p[7] && p[8] && !p[9];  int p82= p[2] && !p[3] && !p[4] && !p[5] && !p[6] && !p[7] && p[8] && !p[9];  int p782= p[2] && !p[3] && !p[4] && !p[5] && !p[6] && p[7] && p[8] && !p[9];  int p924= p[2] && !p[3] && p[4] && !p[5] && !p[6] && !p[7] && !p[8] && p[9];  int p346= !p[2] && p[3] && p[4] && !p[5] && p[6] && !p[7] && !p[8] && !p[9];  int p568= !p[2] && !p[3] && !p[4] && p[5] && p[6] && !p[7] && p[8] && !p[9];  int p689= !p[2] && !p[3] && !p[4] && !p[5] && p[6] && !p[7] && p[8] && p[9];  int p823= p[2] && p[3] && !p[4] && !p[5] && !p[6] && !p[7] && p[8] && !p[9];  int p245= p[2] && !p[3] && p[4] && p[5] && !p[6] && !p[7] && !p[8] && !p[9];  int p467= !p[2] && !p[3] && p[4] && !p[5] && p[6] && p[7] && !p[8] && !p[9];  int p2468= p24 || p46 || p68 || p82;  int p3333= p782 || p924 || p346 || p568 || p689 || p823 || p245 || p467;  //斷定條件第一個由數字圖像處理上獲得,因爲結果不夠滿意,又加上兩個條件  return ( !p246 && !p468 && (Np<7) && (Np>1) && (Tp==1) ) || p2468 || p3333; } //細化二值圖像,獲得單像素連通域 void thin(Mat& bwImg) {  const int imgRows=bwImg.rows -1;  const int imgCols=bwImg.cols -1;  int Remove_Num;  int i, j;  do //循環調用,直至沒有能夠去掉的點  {   Remove_Num=0;   for (j = 1; j < imgRows; j++)   {    for(i = 1; i < imgCols; i++)    {     if ( *bwImg.ptr(j, i) && isContourPoint( i, j, bwImg))//符合條件,去掉     {      *bwImg.ptr(j, i)=0;      Remove_Num++;     } //if    } //for   } //for  } while( Remove_Num); }








====

 

// Algorithm.cpp : All the algorithms.
//.net

#include "stdafx.h"blog

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif索引

///
static int erasetable[256]={0,0,1,1,0,0,1,1, 1,1,0,1,1,1,0,1,
1,1,0,0,1,1,1,1, 0,0,0,0,0,0,0,1,
0,0,1,1,0,0,1,1, 1,1,0,1,1,1,0,1,
1,1,0,0,1,1,1,1, 0,0,0,0,0,0,0,1,
1,1,0,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,
1,1,0,0,1,1,0,0, 1,1,0,1,1,1,0,1,
0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,
0,0,1,1,0,0,1,1, 1,1,0,1,1,1,0,1,
1,1,0,0,1,1,1,1, 0,0,0,0,0,0,0,1,
0,0,1,1,0,0,1,1, 1,1,0,1,1,1,0,1,
1,1,0,0,1,1,1,1, 0,0,0,0,0,0,0,0,
1,1,0,0,1,1,0,0, 0,0,0,0,0,0,0,0,
1,1,0,0,1,1,1,1, 0,0,0,0,0,0,0,0,
1,1,0,0,1,1,0,0, 1,1,0,1,1,1,0,0,
1,1,0,0,1,1,1,0, 1,1,0,0,1,0,0,0};
//////////////////////////////////////////////////////////////////
BOOL Thinning(char *pBuffer)
{
BOOL Finished;
char *pPtr;
LONG x,y;
int num;
int nw,n,ne,w,e,sw,s,se;get

// w/b bitmap saved with 256 colors !
int NumColors;
int LineBytes;
char *pImgData;it


LPBITMAPFILEHEADER pBf;
LPBITMAPINFOHEADER pBi;
RGBQUAD *pRgb;
pBf=(LPBITMAPFILEHEADER) pBuffer;
pBi=(LPBITMAPINFOHEADER )(pBuffer +sizeof(BITMAPFILEHEADER));
pRgb=(RGBQUAD*)(pBuffer+sizeof(BITMAPFILEHEADER)+sizeof(BITMAPINFOHEADER));
//
if(pBi->biClrUsed==0) NumColors=1<<pBi->biBitCount;
//
LineBytes=pBi->biWidth;
//
pImgData=pBuffer+sizeof(BITMAPFILEHEADER)+sizeof(BITMAPINFOHEADER)+NumColors*sizeof(RGBQUAD);
//結束標誌置成假
Finished=FALSE;
while(!Finished)
{ //尚未結束
//結束標誌置成假
Finished=TRUE;
//先進行水平方向的細化
for (y=1;y<pBi->biHeight-1;y++)
{ //注意爲防止越界,y的範圍從1到高度-2
//pPtr指向原圖數據
pPtr=pImgData+y*LineBytes;
x=1; //注意爲防止越界,x的範圍從1到寬度-2
while(x<pBi->biWidth-1)
{
if(*(pPtr+x)==0)
{ //是黑點才作處理
w=(unsigned char)*(pPtr+x-1); //左鄰點
e=(unsigned char)*(pPtr+x+1); //右鄰點
if( (w==255)|| (e==255))
{
//若是左右兩個鄰居中至少有一個是白點才處理
nw=(unsigned char)*(pPtr+x+LineBytes-1); //左上鄰點
n=(unsigned char)*(pPtr+x+LineBytes); //上鄰點
ne=(unsigned char)*(pPtr+x+LineBytes+1); //右上鄰點
sw=(unsigned char)*(pPtr+x-LineBytes-1); //左下鄰點
s=(unsigned char)*(pPtr+x-LineBytes); //下鄰點
se=(unsigned char)*(pPtr+x-LineBytes+1); //右下鄰點
//計算索引
num=nw/255+n/255*2+ne/255*4+w/255*8+e/255*16+sw/255*32+s/255*64+se/255*128;
if(erasetable[num]==1)
{ //經查表,能夠刪除
//在原圖緩衝區中將該黑點刪除
*(pPtr+x)=(BYTE)255;

Finished=FALSE; //有改動,結束標誌置成假

x++; //水平方向跳過一個象素
}
}
}
x++; //掃描下一個象素
}
}
//再進行垂直方向的細化
for (x=1;x<pBi->biWidth-1;x++)
{ //注意爲防止越界,x的範圍從1到寬度-2
//pPtr指向原圖數據
y=1; //注意爲防止越界,y的範圍從1到高度-2
while(y<pBi->biHeight-1)
{
pPtr=pImgData+y*LineBytes;
if(*(pPtr+x)==0)
{ //是黑點才作處理
n=(unsigned char)*(pPtr+x+LineBytes);
s=(unsigned char)*(pPtr+x-LineBytes);
if( (n==255)|| (s==255))
{//若是上下兩個鄰居中至少有一個是白點才處理
nw=(unsigned char)*(pPtr+x+LineBytes-1);
ne=(unsigned char)*(pPtr+x+LineBytes+1);
w=(unsigned char)*(pPtr+x-1);
e=(unsigned char)*(pPtr+x+1);
sw=(unsigned char)*(pPtr+x-LineBytes-1);
se=(unsigned char)*(pPtr+x-LineBytes+1);
//計算索引
num=nw/255+n/255*2+ne/255*4+w/255*8+e/255*16+sw/255*32+s/255*64+se/255*128;
if(erasetable[num]==1)
{ //經查表,能夠刪除
//在原圖緩衝區中將該黑點刪除
*(pPtr+x)=(BYTE)255;

Finished=FALSE; //有改動,結束標誌置成假

y++;//垂直方向跳過一個象素
}
}
}
y++; //掃描下一個象素
}
}
}
return TRUE;
}
BOOL ManageBitmap(HWND hWnd)
{
FILE *fp;
FILE *new_fp;
LPBITMAPFILEHEADER pBf;
LPBITMAPINFOHEADER pBi;
char *pImgData=0;
int NumColors;
char filename[]="res\\bitmap1.bmp";// 229*79
//
fp=fopen("res\\bitmap1.bmp","rb");
if(fp== NULL)
{
MessageBox(hWnd,"Error open fiel!",filename,MB_OK|MB_ICONEXCLAMATION);
return FALSE;
}
// get length
fseek(fp,0L,SEEK_END);
long filelen=ftell(fp);
fseek(fp,0L,SEEK_SET);
char *pBuffer=new char[filelen];
fread(pBuffer,sizeof(char),filelen,fp);
fclose(fp);
//
pBf=(LPBITMAPFILEHEADER) pBuffer;
pBi=(LPBITMAPINFOHEADER )(pBuffer +sizeof(BITMAPFILEHEADER));io

if(pBi->biClrUsed==0) NumColors=1 << pBi->biBitCount;
//爲了處理的方便,仍採用256級灰度圖,不過只用調色板中0和255兩項
if( NumColors!=256 )
{
MessageBox(hWnd,"Must be a mono bitmap with grayscale palette!",
"Error Message",MB_OK|MB_ICONEXCLAMATION);
return FALSE;
}
//
if(!Thinning(pBuffer))
{
if(pBuffer) delete []pBuffer;
return FALSE;
}
// write new
new_fp=fopen("res\\thinning.bmp","wb");
fwrite(pBuffer,sizeof(char),filelen,new_fp);
fclose(new_fp);
//
if(pBuffer) delete []pBuffer;
return TRUE;
}圖像處理

相關文章
相關標籤/搜索