SegApi.cpp
上传用户:wzyhdz
上传日期:2010-04-06
资源大小:164k
文件大小:42k
源码类别:

2D图形编程

开发平台:

Visual C++

  1. #include "GlobalApi.h"
  2. #include "stdafx.h"
  3. #include "cdib.h"
  4. #include "math.h"
  5. #include <direct.h>
  6. #include <complex>
  7. using namespace std;
  8. /*************************************************************************
  9.  *
  10.  * 函数名称:
  11.  *   RegionSegFixThreshold()
  12.  *
  13.  * 输入参数:
  14.  *   CDib * pDib - 指向CDib类的指针,含有原始图象信息
  15.  *   int nThreshold - 区域分割的阈值
  16.  *
  17.  * 返回值:
  18.  *   无
  19.  *
  20.  * 说明:
  21.  *   1(逻辑)表示对应象素为前景区域,0表示背景
  22.  *   阈值分割的关键问题在于阈值的选取。阈值的选取一般应该视实际的应用而
  23.  *   灵活设定。
  24.  *
  25.  *************************************************************************
  26.  */
  27. void RegionSegFixThreshold(CDib * pDib, int nThreshold)
  28. {
  29. //遍历图象的纵坐标
  30. int y;
  31. //遍历图象的横坐标
  32. int x;
  33. //图象的长宽大小
  34. CSize sizeImage = pDib->GetDimensions();
  35. int nWidth = sizeImage.cx ;
  36. int nHeight = sizeImage.cy ;
  37. //图像在计算机在存储中的实际大小
  38. CSize sizeImageSave = pDib->GetDibSaveDim();
  39. //图像在内存中每一行象素占用的实际空间
  40. int nSaveWidth = sizeImageSave.cx;
  41. //图像数据的指针
  42. LPBYTE  pImageData = pDib->m_lpImage;
  43. for(y=0; y<nHeight ; y++ )
  44. for(x=0; x<nWidth ; x++ )
  45. {
  46. if( *(pImageData+y*nSaveWidth+x) < nThreshold)
  47. *(pImageData+y*nSaveWidth+x) = 0;
  48. }
  49. }
  50. /*************************************************************************
  51.  *
  52.  * 函数名称:
  53.  *   RegionSegAdaptive()
  54.  *
  55.  * 输入参数:
  56.  *   CDib * pDib - 指向CDib类的指针,含有原始图象信息
  57.  *
  58.  * 返回值:
  59.  *   无
  60.  *
  61.  * 说明:
  62.  *   1(逻辑)表示对应象素为前景区域,0表示背景
  63.  *   阈值分割的关键问题在于阈值的选取。阈值的选取一般应该视实际的应用而
  64.  *   灵活设定。本函数中,阈值不是固定的,而是根据图象象素的实际性质而设定的。
  65.  *   这个函数把图像分成四个子图象,然后计算每个子图象的均值,根据均值设置阈值
  66.  *   阈值只是应用在对应的子图象
  67.  *
  68.  *************************************************************************
  69.  */
  70. void RegionSegAdaptive(CDib * pDib)
  71. {
  72. //遍历图象的纵坐标
  73. int y;
  74. //遍历图象的横坐标
  75. int x;
  76. //图象的长宽大小
  77. CSize sizeImage = pDib->GetDimensions();
  78. int nWidth = sizeImage.cx ;
  79. int nHeight = sizeImage.cy ;
  80. //图像在计算机在存储中的实际大小
  81. CSize sizeImageSave = pDib->GetDibSaveDim();
  82. //图像在内存中每一行象素占用的实际空间
  83. int nSaveWidth = sizeImageSave.cx;
  84. //图像数据的指针
  85. LPBYTE  lpImage = pDib->m_lpImage;
  86. // 局部阈值
  87. int nThd[2][2] ;
  88. // 子图象的平均值
  89. int nLocAvg ;
  90. // 对左上图像逐点扫描:
  91. nLocAvg = 0 ;
  92. // y方向
  93. for(y=0; y<nHeight/2 ; y++ )
  94. {
  95. // x方向
  96. for(x=0; x<nWidth/2 ; x++ )
  97. {
  98. nLocAvg += lpImage[y*nSaveWidth + x];
  99. }
  100. }
  101. // 计算均值
  102. nLocAvg /= ( (nHeight/2) * (nWidth/2) ) ;
  103. // 设置阈值为子图象的平均值
  104. nThd[0][0] = nLocAvg ;
  105. // 对左上图像逐点扫描进行分割:
  106. // y方向
  107. for(y=0; y<nHeight/2 ; y++ )
  108. {
  109. // x方向
  110. for(x=0; x<nWidth/2 ; x++ )
  111. {
  112. if(lpImage[y*nSaveWidth + x]<nThd[0][0])
  113. lpImage[y*nSaveWidth + x] = 255 ;
  114. else
  115. {
  116. lpImage[y*nSaveWidth + x] = 0 ;
  117. }
  118. }
  119. }
  120. // =============================================
  121. // 对左下图像逐点扫描:
  122. nLocAvg = 0 ;
  123. // y方向
  124. for(y=nHeight/2; y<nHeight ; y++ )
  125. {
  126. // x方向
  127. for(x=0; x<nWidth/2 ; x++ )
  128. {
  129. nLocAvg += lpImage[y*nSaveWidth + x];
  130. }
  131. }
  132. // 计算均值
  133. nLocAvg /= ( (nHeight - nHeight/2) * (nWidth/2) ) ;
  134. // 设置阈值为子图象的平均值
  135. nThd[1][0] = nLocAvg ;
  136. // 对左下图像逐点扫描进行分割:
  137. // y方向
  138. for(y=nHeight/2; y<nHeight ; y++ )
  139. {
  140. // x方向
  141. for(x=0; x<nWidth/2 ; x++ )
  142. {
  143. if(lpImage[y*nSaveWidth + x]<nThd[1][0])
  144. lpImage[y*nSaveWidth + x] = 255 ;
  145. else
  146. {
  147. lpImage[y*nSaveWidth + x] = 0 ;
  148. }
  149. }
  150. }
  151. // =============================================
  152. // 对右上图像逐点扫描:
  153. nLocAvg = 0 ;
  154. // y方向
  155. for(y=0; y<nHeight/2 ; y++ )
  156. {
  157. // x方向
  158. for(x=nWidth/2; x<nWidth ; x++ )
  159. {
  160. nLocAvg += lpImage[y*nSaveWidth + x];
  161. }
  162. }
  163. // 计算均值
  164. nLocAvg /= ( (nHeight/2) * (nWidth - nWidth/2) ) ;
  165. // 设置阈值为子图象的平均值
  166. nThd[0][1] = nLocAvg ;
  167. // 对右上图像逐点扫描进行分割:
  168. // y方向
  169. for(y=0; y<nHeight/2 ; y++ )
  170. {
  171. // x方向
  172. for(x=nWidth/2; x<nWidth ; x++ )
  173. {
  174. if(lpImage[y*nSaveWidth + x]<nThd[0][1])
  175. lpImage[y*nSaveWidth + x] = 255 ;
  176. else
  177. {
  178. lpImage[y*nSaveWidth + x] = 0 ;
  179. }
  180. }
  181. }
  182. // =============================================
  183. // 对右下图像逐点扫描:
  184. nLocAvg = 0 ;
  185. // y方向
  186. for(y=nHeight/2; y<nHeight ; y++ )
  187. {
  188. // x方向
  189. for(x=nWidth/2; x<nWidth ; x++ )
  190. {
  191. nLocAvg += lpImage[y*nSaveWidth + x];
  192. }
  193. }
  194. // 计算均值
  195. nLocAvg /= ( (nHeight - nHeight/2) * (nWidth - nWidth/2) ) ;
  196. // 设置阈值为子图象的平均值
  197. nThd[1][1] = nLocAvg ;
  198. // 对右下图像逐点扫描进行分割:
  199. // y方向
  200. for(y=nHeight/2; y<nHeight ; y++ )
  201. {
  202. // x方向
  203. for(x=nWidth/2; x<nWidth ; x++ )
  204. {
  205. if(lpImage[y*nSaveWidth + x]<nThd[1][1])
  206. lpImage[y*nSaveWidth + x] = 255 ;
  207. else
  208. {
  209. lpImage[y*nSaveWidth + x] = 0 ;
  210. }
  211. }
  212. }
  213. // 为了显示方便显示,逻辑1用黑色显示,逻辑0用白色显示
  214. for(y=0; y<nHeight ; y++ )
  215. {
  216. // x方向
  217. for(x=0; x<nWidth ; x++ )
  218. {
  219. lpImage[y*nSaveWidth + x] = 255 - lpImage[y*nSaveWidth + x] ;
  220. }
  221. }
  222. }
  223. /*************************************************************************
  224.  *
  225.  * 函数名称:
  226.  *   RobertsOperator()
  227.  *
  228.  * 输入参数:
  229.  *   CDib * pDib - 指向CDib类的指针,含有原始图象信息
  230.  *   double * pdGrad - 指向梯度数据的指针,含有图像的梯度信息
  231.  *
  232.  * 返回值:
  233.  *   无
  234.  *
  235.  * 说明:
  236.  *   Roberts算子
  237.  *
  238.  *************************************************************************
  239.  */
  240. void RobertsOperator(CDib * pDib, double * pdGrad)
  241. {
  242. // 遍历图象的纵坐标
  243. int y;
  244. // 遍历图象的横坐标
  245. int x;
  246. // 图象的长宽大小
  247. CSize sizeImage = pDib->GetDimensions();
  248. int nWidth = sizeImage.cx ;
  249. int nHeight = sizeImage.cy ;
  250. // 图像在计算机在存储中的实际大小
  251. CSize sizeImageSave = pDib->GetDibSaveDim();
  252. // 图像在内存中每一行象素占用的实际空间
  253. int nSaveWidth = sizeImageSave.cx;
  254. // 图像数据的指针
  255. LPBYTE  pImageData = pDib->m_lpImage;
  256. // 初始化
  257. for(y=0; y<nHeight ; y++ )
  258. for(x=0 ; x<nWidth ; x++ )
  259. {
  260. *(pdGrad+y*nWidth+x)=0;
  261. }
  262. // 下面开始利用Roberts算子进行计算,为了保证计算所需要的
  263. // 的数据位于图像数据的内部,下面的两重循环的条件是
  264. // y<nHeight-1 而不是y<nHeight,相应的x方向也是x<nWidth-1
  265. // 而不是x<nWidth
  266. //这两个变量用来表示Roberts算子第一个模板的两个象素值
  267. int nUpLeft;
  268. int nDownRight;
  269. // 这两个变量用来表示Roberts算子第二个模板的两个象素值
  270. int nUpRight;
  271. int nDownLeft;
  272. // 这两个变量用来表示Roberts算子计算的结果
  273. int nValueOne;
  274. int nValueTwo;
  275. // 临时变量
  276. double dGrad;
  277. for(y=0; y<nHeight-1 ; y++ )
  278. for(x=0 ; x<nWidth-1 ; x++ )
  279. {
  280. // Roberts算子第一个模板需要的象素值
  281. nUpLeft =*(pImageData+y*nSaveWidth+x) ; 
  282. nDownRight =*( pImageData+(y+1)*nSaveWidth+x+1 );
  283. nDownRight *=-1;
  284. //Roberts算子的第一个模板计算结果
  285. nValueOne =nUpLeft+nDownRight ;
  286. // Roberts算子第二个模板需要的象素值
  287. nUpRight =*( pImageData+y*nSaveWidth+x+1 ) ;
  288. nDownLeft =*( pImageData+(y+1)*nSaveWidth+x );
  289. nDownLeft *=-1;
  290. // Roberts算子的第二个模板计算结果
  291. nValueTwo =nUpRight+nDownLeft;
  292. // 计算两个偏导数的平方和
  293. dGrad=nValueOne*nValueOne + nValueTwo*nValueTwo;
  294. // 开方
  295. dGrad=pow(dGrad,0.5);
  296. // 范数采用欧式距离
  297. *(pdGrad+y*nWidth+x)=dGrad;
  298. }
  299. }
  300. /*************************************************************************
  301.  *
  302.  * 函数名称:
  303.  *   LaplacianOperator()
  304.  *
  305.  * 输入参数:
  306.  *   CDib * pDib - 指向CDib类的指针,含有原始图象信息
  307.  *   double * pdGrad - 指向梯度数据的指针,含有图像的梯度信息
  308.  *
  309.  * 返回值:
  310.  *   无
  311.  *
  312.  * 说明:
  313.  *   LaplacianOperator算子,是二阶算子,不想Roberts算子那样需要两个模板计算
  314.  *   梯度,LaplacianOperator算子只要一个算子就可以计算梯度。但是因为利用了
  315.  *   二阶信息,对噪声比较敏感
  316.  *
  317.  *************************************************************************
  318.  */
  319. void LaplacianOperator(CDib * pDib, double * pdGrad)
  320. {
  321. // 遍历图象的纵坐标
  322. int y;
  323. // 遍历图象的横坐标
  324. int x;
  325. // 图象的长宽大小
  326. CSize sizeImage = pDib->GetDimensions();
  327. int nWidth = sizeImage.cx ;
  328. int nHeight = sizeImage.cy ;
  329. // 图像在计算机在存储中的实际大小
  330. CSize sizeImageSave = pDib->GetDibSaveDim();
  331. // 图像在内存中每一行象素占用的实际空间
  332. int nSaveWidth = sizeImageSave.cx;
  333. // 图像数据的指针
  334. LPBYTE  lpImage = pDib->m_lpImage;
  335. // 初始化
  336. for(y=0; y<nHeight ; y++ )
  337. for(x=0 ; x<nWidth ; x++ )
  338. {
  339. *(pdGrad+y*nWidth+x)=0;
  340. }
  341. // 设置模板系数
  342. static int nWeight[3][3] ;
  343. nWeight[0][0] = -1 ;   
  344. nWeight[0][1] = -1 ;   
  345. nWeight[0][2] = -1 ;   
  346. nWeight[1][0] = -1 ;   
  347. nWeight[1][1] =  8 ;   
  348. nWeight[1][2] = -1 ;   
  349. nWeight[2][0] = -1 ;   
  350. nWeight[2][1] = -1 ;   
  351. nWeight[2][2] = -1 ;   
  352. //这个变量用来表示Laplacian算子象素值
  353. int nTmp[3][3];
  354. // 临时变量
  355. double dGrad;
  356. // 模板循环控制变量
  357. int yy ;
  358. int xx ;
  359. // 下面开始利用Laplacian算子进行计算,为了保证计算所需要的
  360. // 的数据位于图像数据的内部,下面的两重循环的条件是
  361. // y<nHeight-2 而不是y<nHeight,相应的x方向也是x<nWidth-2
  362. // 而不是x<nWidth
  363. for(y=1; y<nHeight-2 ; y++ )
  364. for(x=1 ; x<nWidth-2 ; x++ )
  365. {
  366. dGrad = 0 ; 
  367. // Laplacian算子需要的各点象素值
  368. // 模板第一行
  369. nTmp[0][0] = lpImage[(y-1)*nSaveWidth + x - 1 ] ; 
  370. nTmp[0][1] = lpImage[(y-1)*nSaveWidth + x     ] ; 
  371. nTmp[0][2] = lpImage[(y-1)*nSaveWidth + x + 1 ] ; 
  372. // 模板第二行
  373. nTmp[1][0] = lpImage[y*nSaveWidth + x - 1 ] ; 
  374. nTmp[1][1] = lpImage[y*nSaveWidth + x     ] ; 
  375. nTmp[1][2] = lpImage[y*nSaveWidth + x + 1 ] ; 
  376. // 模板第三行
  377. nTmp[2][0] = lpImage[(y+1)*nSaveWidth + x - 1 ] ; 
  378. nTmp[2][1] = lpImage[(y+1)*nSaveWidth + x     ] ; 
  379. nTmp[2][2] = lpImage[(y+1)*nSaveWidth + x + 1 ] ; 
  380. // 计算梯度
  381. for(yy=0; yy<3; yy++)
  382. for(xx=0; xx<3; xx++)
  383. {
  384. dGrad += nTmp[yy][xx] * nWeight[yy][xx] ;
  385. }
  386. // 梯度值写入内存
  387. *(pdGrad+y*nWidth+x)=dGrad;
  388. }
  389. }
  390. /*************************************************************************
  391.  *
  392.  * 函数名称:
  393.  *   RegionGrow()
  394.  *
  395.  * 输入参数:
  396.  *   CDib * pDib - 指向CDib类的指针,含有原始图象信息
  397.  *   unsigned char * pUnRegion - 指向区域生长结果的指针
  398.  *
  399.  * 返回值:
  400.  *   无
  401.  *
  402.  * 说明:
  403.  *   pUnRegion指针指向的数据区存储了区域生长的结果,其中1(逻辑)表示
  404.  *  对应象素为生长区域,0表示为非生长区域
  405.  *   区域生长一般包含三个比较重要的问题:
  406.  * 1. 种子点的选取
  407.  * 2. 生长准则
  408.  * 3. 终止条件
  409.  *  可以认为,这三个问题需要具体分析,而且每个问题解决的好坏直接关系到
  410.  *  区域生长的结果。
  411.  *  本函数的种子点选取为图像的中心,生长准则是相邻象素的象素值小于
  412.  *  nThreshold, 终止条件是一直进行到再没有满足生长准则需要的象素时为止
  413.  *
  414.  *************************************************************************
  415.  */
  416. void RegionGrow(CDib * pDib, unsigned char * pUnRegion, int nThreshold)
  417. {
  418. static int nDx[]={-1,0,1,0};
  419. static int nDy[]={0,1,0,-1};
  420. // 遍历图象的纵坐标
  421. // int y;
  422. // 遍历图象的横坐标
  423. // int x;
  424. // 图象的长宽大小
  425. CSize sizeImage = pDib->GetDimensions();
  426. int nWidth = sizeImage.cx ;
  427. int nHeight = sizeImage.cy ;
  428. // 图像在计算机在存储中的实际大小
  429. CSize sizeImageSave = pDib->GetDibSaveDim();
  430. // 图像在内存中每一行象素占用的实际空间
  431. int nSaveWidth = sizeImageSave.cx;
  432. // 初始化
  433. memset(pUnRegion,0,sizeof(unsigned char)*nWidth*nHeight);
  434. // 种子点
  435. int nSeedX, nSeedY;
  436. // 设置种子点为图像的中心
  437. nSeedX = nWidth /2 ;
  438. nSeedY = nHeight/2 ;
  439. // 定义堆栈,存储坐标
  440. int * pnGrowQueX ;
  441. int * pnGrowQueY ;
  442. // 分配空间
  443. pnGrowQueX = new int [nWidth*nHeight];
  444. pnGrowQueY = new int [nWidth*nHeight];
  445. // 图像数据的指针
  446. unsigned char *  pUnchInput =(unsigned char * )pDib->m_lpImage;
  447. // 定义堆栈的起点和终点
  448. // 当nStart=nEnd, 表示堆栈中只有一个点
  449. int nStart ;
  450. int nEnd   ;
  451. //初始化
  452. nStart = 0 ;
  453. nEnd   = 0 ;
  454. // 把种子点的坐标压入栈
  455. pnGrowQueX[nEnd] = nSeedX;
  456. pnGrowQueY[nEnd] = nSeedY;
  457. // 当前正在处理的象素
  458. int nCurrX ;
  459. int nCurrY ;
  460. // 循环控制变量
  461. int k ;
  462. // 图象的横纵坐标,用来对当前象素的4邻域进行遍历
  463. int xx;
  464. int yy;
  465. while (nStart<=nEnd)
  466. {
  467. // 当前种子点的坐标
  468. nCurrX = pnGrowQueX[nStart];
  469. nCurrY = pnGrowQueY[nStart];
  470. // 对当前点的4邻域进行遍历
  471. for (k=0; k<4; k++)
  472. {
  473. // 4邻域象素的坐标
  474. xx = nCurrX+nDx[k];
  475. yy = nCurrY+nDy[k];
  476. // 判断象素(xx,yy) 是否在图像内部
  477. // 判断象素(xx,yy) 是否已经处理过
  478. // pUnRegion[yy*nWidth+xx]==0 表示还没有处理
  479. // 生长条件:判断象素(xx,yy)和当前象素(nCurrX,nCurrY) 象素值差的绝对值
  480. if ( (xx < nWidth) && (xx>=0) && (yy<nHeight) && (yy>=0) 
  481.     && (pUnRegion[yy*nWidth+xx]==0) 
  482. && abs(pUnchInput[yy*nSaveWidth+xx] - pUnchInput[nCurrY*nSaveWidth+nCurrX])<nThreshold )
  483. {
  484. // 堆栈的尾部指针后移一位
  485. nEnd++;
  486. // 象素(xx,yy) 压入栈
  487. pnGrowQueX[nEnd] = xx;
  488. pnGrowQueY[nEnd] = yy;
  489. // 把象素(xx,yy)设置成逻辑1(255)
  490. // 同时也表明该象素处理过
  491. pUnRegion[yy*nWidth+xx] = 255 ;
  492. }
  493. }
  494. nStart++;
  495. }
  496. // 释放内存
  497. delete []pnGrowQueX;
  498. delete []pnGrowQueY;
  499.     pnGrowQueX = NULL ;
  500. pnGrowQueY = NULL ;
  501. }
  502. void DFT_2D(CDib * pDib,double * pTrRstRpart, double * pTrRstIpart)
  503. {
  504. double PI = 3.14159;
  505. //遍历图象的纵坐标
  506. int y;
  507. //遍历图象的横坐标
  508. int x;
  509. //频域的横坐标
  510. int m;
  511. //频域的纵坐标
  512. int n; 
  513. //图象的长宽大小
  514. CSize sizeImage = pDib->GetDimensions();
  515. int nWidth = sizeImage.cx ;
  516. int nHeight = sizeImage.cy ;
  517. //图像在计算机在存储中的实际大小
  518. CSize sizeImageSave = pDib->GetDibSaveDim();
  519. int nSaveWidth = sizeImageSave.cx;
  520. //图像数据的指针
  521. LPBYTE  pImageData = pDib->m_lpImage;
  522. //初始化结果数据
  523. for(n=0; n<nHeight ; n++ )
  524. for(m=0 ; m<nWidth ; m++ )
  525. {
  526. *( pTrRstRpart + n*nWidth + m ) =0;
  527. *( pTrRstIpart + n*nWidth + m ) =0;
  528. }
  529. double fCosTable;
  530. double fSinTable;
  531. int   nPxValue;
  532. fCosTable=0 ;
  533. nPxValue =0;
  534. double fTmpRstR;
  535. double fTmpRstI;
  536. for(n=0; n<nHeight ; n++ )
  537. for(m=0 ; m<nWidth ; m++ )
  538. {
  539. fTmpRstR=0;
  540. fTmpRstI=0;
  541. for(y=0; y<nHeight ; y++ )
  542. for(x=0 ; x<nWidth ; x++ )
  543. {
  544. fCosTable= cos( 2*PI*( ((double)m*x)/nWidth + ((double)n*y)/nHeight) ) ;
  545. fSinTable= sin( -2*PI*( ((double)m*x)/nWidth + ((double)n*y)/nHeight) ) ;
  546. nPxValue = *(pImageData+ y*nSaveWidth + x ) ;
  547. fTmpRstR+=fCosTable* nPxValue ;
  548. fTmpRstI+=fSinTable* nPxValue ;
  549. }
  550. *( pTrRstRpart + nWidth * n + m ) = fTmpRstR;
  551. *( pTrRstIpart + nWidth * n + m ) = fTmpRstI;
  552. }
  553. }
  554. void IDFT_2D(CDib * pDib,double * pTrRstRpart, double * pTrRstIpart)
  555. {
  556. double PI = 3.14159;
  557. //遍历图象的纵坐标
  558. int y;
  559. //遍历图象的横坐标
  560. int x;
  561. //频域的横坐标
  562. int m;
  563. //频域的纵坐标
  564. int n; 
  565. //图象的长宽大小
  566. CSize sizeImage = pDib->GetDimensions();
  567. int nWidth = sizeImage.cx ;
  568. int nHeight = sizeImage.cy ;
  569. //图像在计算机在存储中的实际大小
  570. CSize sizeImageSave = pDib->GetDibSaveDim();
  571. int nSaveWidth = sizeImageSave.cx;
  572. //图像数据的指针
  573. LPBYTE  pImageData = pDib->m_lpImage;
  574. double fCosTable;
  575. double fSinTable;
  576. fCosTable=0 ;
  577. fSinTable=0 ;
  578. double fTmpPxValue;
  579. double fRpartValue;
  580. double fIpartValue;
  581. fTmpPxValue=0;
  582. fRpartValue=0;
  583. fIpartValue=0;
  584. for(y=0; y<nHeight ; y++ )
  585. for(x=0 ; x<nWidth ; x++ )
  586. {
  587. fTmpPxValue=0;
  588. for(n=0; n<nHeight ; n++ )
  589. for(m=0 ; m<nWidth ; m++ )
  590. {
  591. fCosTable= cos( 2*PI*( ((double)m*x)/nWidth + ((double)n*y)/nHeight) ) ;
  592. fSinTable= sin( 2*PI*( ((double)m*x)/nWidth + ((double)n*y)/nHeight) ) ;
  593. fRpartValue=*(pTrRstRpart+ n*nHeight + m ) ;
  594. fIpartValue=*(pTrRstIpart+ n*nHeight + m ) ;
  595. fTmpPxValue+=fCosTable* fRpartValue-fSinTable*fIpartValue;
  596. }
  597. fTmpPxValue=fTmpPxValue/(nHeight*nWidth);
  598. *( pImageData + nSaveWidth * y + x) = (unsigned char) fTmpPxValue ;
  599. }
  600. }
  601. /*************************************************************************
  602.  *
  603.  * 函数名称:
  604.  *   SobelOperator()
  605.  *
  606.  * 输入参数:
  607.  *   CDib * pDib   - 指向CDib类的指针,含有原始图象信息
  608.  *   double * pdGrad - 指向梯度数据的指针,含有图像的梯度信息
  609.  *
  610.  * 返回值:
  611.  *   无
  612.  *
  613.  * 说明:
  614.  *   Sobe算子
  615.  *
  616.  *   并行边界分割
  617.  *
  618.  *************************************************************************
  619.  */
  620. void SobelOperator(CDib * pDib, double * pdGrad)
  621. {
  622. // 遍历图象的纵坐标
  623. int y;
  624. // 遍历图象的横坐标
  625. int x;
  626. // 图象的长宽大小
  627. CSize sizeImage = pDib->GetDimensions();
  628. int nWidth = sizeImage.cx ;
  629. int nHeight = sizeImage.cy ;
  630. // 图像在计算机在存储中的实际大小
  631. CSize sizeImageSave = pDib->GetDibSaveDim();
  632. // 图像在内存中每一行象素占用的实际空间
  633. int nSaveWidth = sizeImageSave.cx;
  634. // 图像数据的指针
  635. LPBYTE  lpImage = pDib->m_lpImage;
  636. // 初始化
  637. for(y=0; y<nHeight ; y++ )
  638. for(x=0 ; x<nWidth ; x++ )
  639. {
  640. *(pdGrad+y*nWidth+x)=0;
  641. }
  642. // 设置模板系数
  643. static int nWeight[2][3][3] ;
  644. nWeight[0][0][0] = -1 ;   
  645. nWeight[0][0][1] =  0 ;   
  646. nWeight[0][0][2] =  1 ;   
  647. nWeight[0][1][0] = -2 ;   
  648. nWeight[0][1][1] =  0 ;   
  649. nWeight[0][1][2] =  2 ;   
  650. nWeight[0][2][0] = -1 ;   
  651. nWeight[0][2][1] =  0 ;   
  652. nWeight[0][2][2] =  1 ;   
  653. nWeight[1][0][0] =  1 ;   
  654. nWeight[1][0][1] =  2 ;   
  655. nWeight[1][0][2] =  1 ;   
  656. nWeight[1][1][0] =  0 ;   
  657. nWeight[1][1][1] =  0 ;   
  658. nWeight[1][1][2] =  0 ;   
  659. nWeight[1][2][0] = -1 ;   
  660. nWeight[1][2][1] = -2 ;   
  661. nWeight[1][2][2] = -1 ;   
  662. //这个变量用来表示Laplacian算子象素值
  663. int nTmp[3][3];
  664. // 临时变量
  665. double dGrad   ;
  666. double dGradOne;
  667. double dGradTwo;
  668. // 模板循环控制变量
  669. int yy ;
  670. int xx ;
  671. // 下面开始利用Prewitt算子进行计算,为了保证计算所需要的
  672. // 的数据位于图像数据的内部,下面的两重循环的条件是
  673. // y<nHeight-1 而不是y<nHeight,相应的x方向也是x<nWidth-1
  674. // 而不是x<nWidth
  675. for(y=1; y<nHeight-1 ; y++ )
  676. for(x=1 ; x<nWidth-1 ; x++ )
  677. {
  678. dGrad    = 0 ; 
  679. dGradOne = 0 ;
  680. dGradTwo = 0 ;
  681. // Laplacian算子需要的各点象素值
  682. // 模板第一行
  683. nTmp[0][0] = lpImage[(y-1)*nSaveWidth + x - 1 ] ; 
  684. nTmp[0][1] = lpImage[(y-1)*nSaveWidth + x     ] ; 
  685. nTmp[0][2] = lpImage[(y-1)*nSaveWidth + x + 1 ] ; 
  686. // 模板第二行
  687. nTmp[1][0] = lpImage[y*nSaveWidth + x - 1 ] ; 
  688. nTmp[1][1] = lpImage[y*nSaveWidth + x     ] ; 
  689. nTmp[1][2] = lpImage[y*nSaveWidth + x + 1 ] ; 
  690. // 模板第三行
  691. nTmp[2][0] = lpImage[(y+1)*nSaveWidth + x - 1 ] ; 
  692. nTmp[2][1] = lpImage[(y+1)*nSaveWidth + x     ] ; 
  693. nTmp[2][2] = lpImage[(y+1)*nSaveWidth + x + 1 ] ; 
  694. // 计算梯度
  695. for(yy=0; yy<3; yy++)
  696. for(xx=0; xx<3; xx++)
  697. {
  698. dGradOne += nTmp[yy][xx] * nWeight[0][yy][xx] ;
  699. dGradTwo += nTmp[yy][xx] * nWeight[1][yy][xx] ;
  700. }
  701. dGrad = dGradOne*dGradOne + dGradTwo*dGradTwo  ;
  702. dGrad = sqrt(dGrad) ;
  703. // 梯度值写入内存
  704. *(pdGrad+y*nWidth+x)=dGrad;
  705. }
  706. }
  707. /*************************************************************************
  708.  *
  709.  * 函数名称:
  710.  *   PrewittOperator()
  711.  *
  712.  * 输入参数:
  713.  *   CDib * pDib   - 指向CDib类的指针,含有原始图象信息
  714.  *   double * pdGrad - 指向梯度数据的指针,含有图像的梯度信息
  715.  *
  716.  * 返回值:
  717.  *   无
  718.  *
  719.  * 说明:
  720.  *   Prewitt算子
  721.  *
  722.  *   并行边界分割
  723.  *
  724.  *************************************************************************
  725.  */
  726. void PrewittOperator(CDib * pDib, double * pdGrad)
  727. {
  728. // 遍历图象的纵坐标
  729. int y;
  730. // 遍历图象的横坐标
  731. int x;
  732. // 图象的长宽大小
  733. CSize sizeImage = pDib->GetDimensions();
  734. int nWidth = sizeImage.cx ;
  735. int nHeight = sizeImage.cy ;
  736. // 图像在计算机在存储中的实际大小
  737. CSize sizeImageSave = pDib->GetDibSaveDim();
  738. // 图像在内存中每一行象素占用的实际空间
  739. int nSaveWidth = sizeImageSave.cx;
  740. // 图像数据的指针
  741. LPBYTE  lpImage = pDib->m_lpImage;
  742. // 初始化
  743. for(y=0; y<nHeight ; y++ )
  744. for(x=0 ; x<nWidth ; x++ )
  745. {
  746. *(pdGrad+y*nWidth+x)=0;
  747. }
  748. // 设置模板系数
  749. static int nWeight[2][3][3] ;
  750. nWeight[0][0][0] = -1 ;   
  751. nWeight[0][0][1] =  0 ;   
  752. nWeight[0][0][2] =  1 ;   
  753. nWeight[0][1][0] = -1 ;   
  754. nWeight[0][1][1] =  0 ;   
  755. nWeight[0][1][2] =  1 ;   
  756. nWeight[0][2][0] = -1 ;   
  757. nWeight[0][2][1] =  0 ;   
  758. nWeight[0][2][2] =  1 ;   
  759. nWeight[1][0][0] =  1 ;   
  760. nWeight[1][0][1] =  1 ;   
  761. nWeight[1][0][2] =  1 ;   
  762. nWeight[1][1][0] =  0 ;   
  763. nWeight[1][1][1] =  0 ;   
  764. nWeight[1][1][2] =  0 ;   
  765. nWeight[1][2][0] = -1 ;   
  766. nWeight[1][2][1] = -1 ;   
  767. nWeight[1][2][2] = -1 ;   
  768. //这个变量用来表示Laplacian算子象素值
  769. int nTmp[3][3];
  770. // 临时变量
  771. double dGrad   ;
  772. double dGradOne;
  773. double dGradTwo;
  774. // 模板循环控制变量
  775. int yy ;
  776. int xx ;
  777. // 下面开始利用Prewitt算子进行计算,为了保证计算所需要的
  778. // 的数据位于图像数据的内部,下面的两重循环的条件是
  779. // y<nHeight-1 而不是y<nHeight,相应的x方向也是x<nWidth-1
  780. // 而不是x<nWidth
  781. for(y=1; y<nHeight-1 ; y++ )
  782. for(x=1 ; x<nWidth-1 ; x++ )
  783. {
  784. dGrad    = 0 ; 
  785. dGradOne = 0 ;
  786. dGradTwo = 0 ;
  787. // Laplacian算子需要的各点象素值
  788. // 模板第一行
  789. nTmp[0][0] = lpImage[(y-1)*nSaveWidth + x - 1 ] ; 
  790. nTmp[0][1] = lpImage[(y-1)*nSaveWidth + x     ] ; 
  791. nTmp[0][2] = lpImage[(y-1)*nSaveWidth + x + 1 ] ; 
  792. // 模板第二行
  793. nTmp[1][0] = lpImage[y*nSaveWidth + x - 1 ] ; 
  794. nTmp[1][1] = lpImage[y*nSaveWidth + x     ] ; 
  795. nTmp[1][2] = lpImage[y*nSaveWidth + x + 1 ] ; 
  796. // 模板第三行
  797. nTmp[2][0] = lpImage[(y+1)*nSaveWidth + x - 1 ] ; 
  798. nTmp[2][1] = lpImage[(y+1)*nSaveWidth + x     ] ; 
  799. nTmp[2][2] = lpImage[(y+1)*nSaveWidth + x + 1 ] ; 
  800. // 计算梯度
  801. for(yy=0; yy<3; yy++)
  802. for(xx=0; xx<3; xx++)
  803. {
  804. dGradOne += nTmp[yy][xx] * nWeight[0][yy][xx] ;
  805. dGradTwo += nTmp[yy][xx] * nWeight[1][yy][xx] ;
  806. }
  807. dGrad = dGradOne*dGradOne + dGradTwo*dGradTwo  ;
  808. dGrad = sqrt(dGrad) ;
  809. // 梯度值写入内存
  810. *(pdGrad+y*nWidth+x)=dGrad;
  811. }
  812. }
  813. /*************************************************************************
  814.  *
  815.  * 函数名称:
  816.  *   EdgeTrack()
  817.  *
  818.  * 输入参数:
  819.  *   CDib * pDib - 指向CDib类的指针,含有原始图象信息
  820.  *   unsigned char * pUnEdgeTrack - 指向边界跟踪结果的指针
  821.  *
  822.  * 返回值:
  823.  *   无
  824.  *
  825.  * 说明:
  826.  *   pUnEdgeTrack指针指向的数据区存储了边界跟踪的结果,其中1(逻辑)表示
  827.  *  对应象素为边界点,0表示为非边界点
  828.  *
  829.  *   串行边界分割
  830.  *
  831.  *************************************************************************
  832.  */
  833. void EdgeTrack(CDib * pDib, unsigned char * pUnEdgeTrack)
  834. {
  835. static int nDx[8]={-1,-1,-1, 0, 0, 1, 1, 1};
  836. static int nDy[8]={-1, 0, 1,-1, 1,-1, 0, 1};
  837. // 遍历图象的纵坐标
  838. int y;
  839. // 遍历图象的横坐标
  840. int x;
  841. // 图象的长宽大小
  842. CSize sizeImage = pDib->GetDimensions();
  843. int nWidth = sizeImage.cx ;
  844. int nHeight = sizeImage.cy ;
  845. // 指向梯度数据的指针
  846. double * pdGrad;
  847. // 按照图像的大小开辟内存空间,存储梯度计算的结果
  848. pdGrad=new double[nHeight*nWidth];
  849.     // 调用Roberts算子求梯度
  850. RobertsOperator(pDib, pdGrad);
  851. // 定义当前象素梯度值
  852. double dCurrGrad = 0;
  853. // 定义最大梯度值
  854. double dMaxGrad;
  855. // 设置初值
  856. dMaxGrad = 0;
  857. // 最大梯度值对应的象素点坐标
  858. int nPx;
  859. int nPy;
  860. nPx = 0;
  861. nPy = 0;
  862. // 求梯度最大值所在的象素点坐标
  863. for(y=0; y<nHeight; y++)
  864. {
  865. for(x=0; x<nWidth; x++)
  866. {
  867. dCurrGrad = pdGrad[y*nWidth + x] ;
  868. if( dMaxGrad< dCurrGrad )
  869. {
  870. dMaxGrad = dCurrGrad;
  871. nPx = x ;
  872. nPy = y ; 
  873. }
  874. }
  875. }
  876. // 初始化
  877. memset(pUnEdgeTrack,0,sizeof(unsigned char)*nWidth*nHeight);
  878. dCurrGrad = pdGrad[nPy*nWidth + nPx] ;
  879. // 从(nPx,nPy)点开始进行边界跟踪
  880. pUnEdgeTrack[nPy*nWidth + nPx] = 255 ;
  881. // 循环变量,遍历当前象素的8邻域
  882. int i ;
  883. int yy;
  884. int xx;
  885. int nDetX;
  886. int nDetY;
  887. while(dCurrGrad>10)
  888. {
  889. // 设置当前点为边界点
  890. pUnEdgeTrack[nPy*nWidth + nPx] = 255 ;
  891. dMaxGrad = 0 ;
  892. for(i=0; i<8; i++)
  893. {
  894. nDetX=nDx[i];
  895. nDetY=nDy[i];
  896. y = nPy + nDetY;
  897. x = nPx + nDetX;
  898. // 判断是否在图像内部
  899. if(x>=0 && x<nWidth && y>=0 && y<nHeight)
  900. {
  901. if( ( pdGrad[y*nWidth + x] > dMaxGrad)  && ( pUnEdgeTrack[y*nWidth + x] == 0) )
  902. {
  903. dMaxGrad = pdGrad[y*nWidth + x] ;
  904. yy = y;
  905. xx = x;
  906. }
  907. }
  908. }
  909. // 下一个边界点的梯度,横纵坐标
  910. dCurrGrad = dMaxGrad ;
  911. nPy = yy;
  912. nPx = xx;
  913. }
  914. //释放内存
  915. delete pdGrad;
  916. pdGrad = NULL;
  917. }
  918. /*************************************************************************
  919.  *
  920.  * 函数名称:
  921.  *   MakeGauss()
  922.  *
  923.  * 输入参数:
  924.  *   double sigma         - 高斯函数的标准差
  925.  *   double **pdKernel - 指向高斯数据数组的指针
  926.  *   int *pnWindowSize - 数据的长度
  927.  *
  928.  * 返回值:
  929.  *   无
  930.  *
  931.  * 说明:
  932.  *   这个函数可以生成一个一维的高斯函数的数字数据,理论上高斯数据的长度应
  933.  *   该是无限长的,但是为了计算的简单和速度,实际的高斯数据只能是有限长的
  934.  *   pnWindowSize就是数据长度
  935.  *   
  936.  *************************************************************************
  937.  */
  938. void MakeGauss(double sigma, double **pdKernel, int *pnWindowSize)
  939. {
  940. // 循环控制变量
  941. int i   ;
  942. // 数组的中心点
  943. int nCenter;
  944. // 数组的某一点到中心点的距离
  945. double  dDis  ; 
  946. double PI = 3.14159;
  947. // 中间变量
  948. double  dValue; 
  949. double  dSum  ;
  950. dSum = 0 ; 
  951. // 数组长度,根据概率论的知识,选取[-3*sigma, 3*sigma]以内的数据。
  952. // 这些数据会覆盖绝大部分的滤波系数
  953. *pnWindowSize = 1 + 2 * ceil(3 * sigma);
  954. // 中心
  955. nCenter = (*pnWindowSize) / 2;
  956. // 分配内存
  957. *pdKernel = new double[*pnWindowSize] ;
  958. for(i=0; i< (*pnWindowSize); i++)
  959. {
  960. dDis = (double)(i - nCenter);
  961. dValue = exp(-(1/2)*dDis*dDis/(sigma*sigma)) / (sqrt(2 * PI) * sigma );
  962. (*pdKernel)[i] = dValue ;
  963. dSum += dValue;
  964. }
  965. // 归一化
  966. for(i=0; i<(*pnWindowSize) ; i++)
  967. {
  968. (*pdKernel)[i] /= dSum;
  969. }
  970. }
  971. /*************************************************************************
  972.  *
  973.  * 函数名称:
  974.  *   GaussianSmooth()
  975.  *
  976.  * 输入参数:
  977.  *   unsigned char * pUnchImg - 指向图象数据的指针
  978.  *   int nWidth - 图象数据宽度
  979.  *   int nHeight - 图象数据高度
  980.  *   double dSigma - 高斯函数的标准差
  981.  *   unsigned char * pUnchSmthdImg - 指向经过平滑之后的图象数据
  982.  *
  983.  * 返回值:
  984.  *   无
  985.  *
  986.  * 说明:
  987.  *   为了抑止噪声,采用高斯滤波对图象进行滤波,滤波先对x方向进行,然后对
  988.  *   y方向进行。
  989.  *   
  990.  *************************************************************************
  991.  */
  992. void GaussianSmooth(unsigned char *pUnchImg, int nWidth, int nHeight, 
  993. double sigma, unsigned char * pUnchSmthdImg)
  994. {
  995. // 循环控制变量
  996.   int y;
  997. int x;
  998. int i;
  999. // 高斯滤波器的数组长度
  1000. int nWindowSize;
  1001. //  窗口长度的1/2
  1002. int nHalfLen;   
  1003. // 一维高斯数据滤波器
  1004. double *pdKernel ;
  1005. // 高斯系数与图象数据的点乘
  1006. double  dDotMul     ;
  1007. // 高斯滤波系数的总和
  1008. double  dWeightSum     ;          
  1009.   
  1010. // 中间变量
  1011. double * pdTmp ;
  1012. // 分配内存
  1013. pdTmp = new double[nWidth*nHeight];
  1014. // 产生一维高斯数据滤波器
  1015. // MakeGauss(sigma, &dKernel, &nWindowSize);
  1016. MakeGauss(sigma, &pdKernel, &nWindowSize) ;
  1017. // MakeGauss返回窗口的长度,利用此变量计算窗口的半长
  1018. nHalfLen = nWindowSize / 2;
  1019.   // x方向进行滤波
  1020. for(y=0; y<nHeight; y++)
  1021. {
  1022. for(x=0; x<nWidth; x++)
  1023. {
  1024. dDotMul = 0;
  1025. dWeightSum = 0;
  1026. for(i=(-nHalfLen); i<=nHalfLen; i++)
  1027. {
  1028. // 判断是否在图象内部
  1029. if( (i+x) >= 0  && (i+x) < nWidth )
  1030. {
  1031. dDotMul += (double)pUnchImg[y*nWidth + (i+x)] * pdKernel[nHalfLen+i];
  1032. dWeightSum += pdKernel[nHalfLen+i];
  1033. }
  1034. }
  1035. pdTmp[y*nWidth + x] = dDotMul/dWeightSum ;
  1036. }
  1037. }
  1038. // y方向进行滤波
  1039. for(x=0; x<nWidth; x++)
  1040. {
  1041. for(y=0; y<nHeight; y++)
  1042. {
  1043. dDotMul = 0;
  1044. dWeightSum = 0;
  1045. for(i=(-nHalfLen); i<=nHalfLen; i++)
  1046. {
  1047. // 判断是否在图象内部
  1048. if( (i+y) >= 0  && (i+y) < nHeight )
  1049. {
  1050. dDotMul += (double)pdTmp[(y+i)*nWidth + x] * pdKernel[nHalfLen+i];
  1051. dWeightSum += pdKernel[nHalfLen+i];
  1052. }
  1053. }
  1054. pUnchSmthdImg[y*nWidth + x] = (unsigned char)(int)dDotMul/dWeightSum ;
  1055. }
  1056. }
  1057. // 释放内存
  1058. delete []pdKernel;
  1059. pdKernel = NULL ;
  1060. delete []pdTmp;
  1061. pdTmp = NULL;
  1062. }
  1063. /*************************************************************************
  1064.  *
  1065.  * 函数名称:
  1066.  *   DirGrad()
  1067.  *
  1068.  * 输入参数:
  1069.  *   unsigned char *pUnchSmthdImg - 经过高斯滤波后的图象
  1070.  *   int nWidht - 图象宽度
  1071.  *   int nHeight       - 图象高度
  1072.  *   int *pnGradX                         - x方向的方向导数
  1073.  *   int *pnGradY                         - y方向的方向导数
  1074.  * 返回值:
  1075.  *   无
  1076.  *
  1077.  * 说明:
  1078.  *   这个函数计算方向倒数,采用的微分算子是(-1 0 1) 和 (-1 0 1)'(转置)
  1079.  *   计算的时候对边界象素采用了特殊处理
  1080.  *   
  1081.  *   
  1082.  *************************************************************************
  1083.  */
  1084. void DirGrad(unsigned char *pUnchSmthdImg, int nWidth, int nHeight,
  1085.  int *pnGradX , int *pnGradY)
  1086. {
  1087. // 循环控制变量
  1088. int y ;
  1089. int x ;
  1090. // 计算x方向的方向导数,在边界出进行了处理,防止要访问的象素出界
  1091. for(y=0; y<nHeight; y++)
  1092. {
  1093. for(x=0; x<nWidth; x++)
  1094. {
  1095. pnGradX[y*nWidth+x] = (int) ( pUnchSmthdImg[y*nWidth+min(nWidth-1,x+1)]  
  1096.           - pUnchSmthdImg[y*nWidth+max(0,x-1)]     );
  1097. }
  1098. }
  1099. // 计算y方向的方向导数,在边界出进行了处理,防止要访问的象素出界
  1100. for(x=0; x<nWidth; x++)
  1101. {
  1102. for(y=0; y<nHeight; y++)
  1103. {
  1104. pnGradY[y*nWidth+x] = (int) ( pUnchSmthdImg[min(nHeight-1,y+1)*nWidth + x]  
  1105. - pUnchSmthdImg[max(0,y-1)*nWidth+ x ]     );
  1106. }
  1107. }
  1108. }
  1109. /*************************************************************************
  1110.  *
  1111.  * 函数名称:
  1112.  *   GradMagnitude()
  1113.  *
  1114.  * 输入参数:
  1115.  *   int *pnGradX                         - x方向的方向导数
  1116.  *   int *pnGradY                         - y方向的方向导数
  1117.  *   int nWidht - 图象宽度
  1118.  *   int nHeight       - 图象高度
  1119.  *   int *pnMag                           - 梯度幅度   
  1120.  *
  1121.  * 返回值:
  1122.  *   无
  1123.  *
  1124.  * 说明:
  1125.  *   这个函数利用方向倒数计算梯度幅度,方向倒数是DirGrad函数计算的结果
  1126.  *   
  1127.  *************************************************************************
  1128.  */
  1129. void GradMagnitude(int *pnGradX, int *pnGradY, int nWidth, int nHeight, int *pnMag)
  1130. {
  1131. // 循环控制变量
  1132. int y ;
  1133. int x ;
  1134. // 中间变量
  1135. double dSqtOne;
  1136. double dSqtTwo;
  1137. for(y=0; y<nHeight; y++)
  1138. {
  1139. for(x=0; x<nWidth; x++)
  1140. {
  1141. dSqtOne = pnGradX[y*nWidth + x] * pnGradX[y*nWidth + x];
  1142. dSqtTwo = pnGradY[y*nWidth + x] * pnGradY[y*nWidth + x];
  1143. pnMag[y*nWidth + x] = (int)(sqrt(dSqtOne + dSqtTwo) + 0.5);
  1144. }
  1145. }
  1146. }
  1147. /*************************************************************************
  1148.  *
  1149.  * 函数名称:
  1150.  *   NonmaxSuppress()
  1151.  *
  1152.  * 输入参数:
  1153.  *   int *pnMag                - 梯度图
  1154.  *   int *pnGradX  - x方向的方向导数
  1155.  *   int *pnGradY              - y方向的方向导数
  1156.  *   int nWidth                - 图象数据宽度
  1157.  *   int nHeight               - 图象数据高度
  1158.  *   unsigned char *pUnchRst   - 经过NonmaxSuppress处理后的结果
  1159.  *
  1160.  * 返回值:
  1161.  *   无
  1162.  *
  1163.  * 说明:
  1164.  *   抑止梯度图中非局部极值点的象素。
  1165.  *   
  1166.  *************************************************************************
  1167.  */
  1168. void NonmaxSuppress(int *pnMag, int *pnGradX, int *pnGradY, int nWidth, 
  1169. int nHeight, unsigned char *pUnchRst)
  1170. {
  1171. // 循环控制变量
  1172. int y ;
  1173. int x ;
  1174. int nPos;
  1175. // x方向梯度分量
  1176. int gx  ;
  1177. int gy  ;
  1178. // 临时变量
  1179. int g1, g2, g3, g4 ;
  1180. double weight  ;
  1181. double dTmp1   ;
  1182. double dTmp2   ;
  1183. double dTmp    ;
  1184. // 设置图象边缘部分为不可能的边界点
  1185. for(x=0; x<nWidth; x++)
  1186. {
  1187. pUnchRst[x] = 0 ;
  1188. pUnchRst[nHeight-1+x] = 0;
  1189. }
  1190. for(y=0; y<nHeight; y++)
  1191. {
  1192. pUnchRst[y*nWidth] = 0 ;
  1193. pUnchRst[y*nWidth + nWidth-1] = 0;
  1194. }
  1195. for(y=1; y<nHeight-1; y++)
  1196. {
  1197. for(x=1; x<nWidth-1; x++)
  1198. {
  1199. nPos = y*nWidth + x ;
  1200. // 如果当前象素的梯度幅度为0,则不是边界点
  1201. if(pnMag[nPos] == 0 )
  1202. {
  1203. pUnchRst[nPos] = 0 ;
  1204. }
  1205. else
  1206. {
  1207. // 当前象素的梯度幅度
  1208. dTmp = pnMag[nPos] ;
  1209. // x,y方向导数
  1210. gx = pnGradX[nPos]  ;
  1211. gy = pnGradY[nPos]  ;
  1212. // 如果方向导数y分量比x分量大,说明导数的方向更加“趋向”于y分量。
  1213. if (abs(gy) > abs(gx)) 
  1214. {
  1215. // 计算插值的比例
  1216. weight = fabs(gx)/fabs(gy); 
  1217. g2 = pnMag[nPos-nWidth] ; 
  1218. g4 = pnMag[nPos+nWidth] ;
  1219. // 如果x,y两个方向的方向导数的符号相同
  1220. // C是当前象素,与g1-g4的位置关系为:
  1221. // g1 g2 
  1222. //  C         
  1223. //  g4 g3 
  1224. if (gx*gy > 0) 
  1225. g1 = pnMag[nPos-nWidth-1] ;
  1226. g3 = pnMag[nPos+nWidth+1] ;
  1227. // 如果x,y两个方向的方向导数的符号相反
  1228. // C是当前象素,与g1-g4的位置关系为:
  1229. //    g2 g1
  1230. //  C         
  1231. // g3 g4  
  1232. else 
  1233. g1 = pnMag[nPos-nWidth+1] ;
  1234. g3 = pnMag[nPos+nWidth-1] ;
  1235. }
  1236. // 如果方向导数x分量比y分量大,说明导数的方向更加“趋向”于x分量
  1237. // 这个判断语句包含了x分量和y分量相等的情况
  1238. else
  1239. {
  1240. // 计算插值的比例
  1241. weight = fabs(gy)/fabs(gx); 
  1242. g2 = pnMag[nPos+1] ; 
  1243. g4 = pnMag[nPos-1] ;
  1244. // 如果x,y两个方向的方向导数的符号相同
  1245. // C是当前象素,与g1-g4的位置关系为:
  1246. // g3   
  1247. // g4 C g2       
  1248. //       g1
  1249. if (gx*gy > 0) 
  1250. {
  1251. g1 = pnMag[nPos+nWidth+1] ;
  1252. g3 = pnMag[nPos-nWidth-1] ;
  1253. // 如果x,y两个方向的方向导数的符号相反
  1254. // C是当前象素,与g1-g4的位置关系为:
  1255. //      g1
  1256. // g4 C g2       
  1257. //  g3     
  1258. else 
  1259. g1 = pnMag[nPos-nWidth+1] ;
  1260. g3 = pnMag[nPos+nWidth-1] ;
  1261. }
  1262. }
  1263. // 下面利用g1-g4对梯度进行插值
  1264. {
  1265. dTmp1 = weight*g1 + (1-weight)*g2 ;
  1266. dTmp2 = weight*g3 + (1-weight)*g4 ;
  1267. // 当前象素的梯度是局部的最大值
  1268. // 该点可能是个边界点
  1269. if(dTmp>=dTmp1 && dTmp>=dTmp2)
  1270. {
  1271. pUnchRst[nPos] = 128 ;
  1272. }
  1273. else
  1274. {
  1275. // 不可能是边界点
  1276. pUnchRst[nPos] = 0 ;
  1277. }
  1278. }
  1279. } //else
  1280. } // for
  1281. }
  1282. /*************************************************************************
  1283.  *
  1284.  * 函数名称:
  1285.  *   TraceEdge()
  1286.  *
  1287.  * 输入参数:
  1288.  *   int    x - 跟踪起点的x坐标 
  1289.  *   int    y - 跟踪起点的y坐标
  1290.  *   int nLowThd - 判断一个点是否为边界点的低阈值
  1291.  *   unsigned char *pUnchEdge - 记录边界点的缓冲区
  1292.  *   int *pnMag               - 梯度幅度图
  1293.  *   int nWidth               - 图象数据宽度
  1294.  *
  1295.  * 返回值:
  1296.  *   无
  1297.  *
  1298.  * 说明:
  1299.  *   递归调用  
  1300.  *   从(x,y)坐标出发,进行边界点的跟踪,跟踪只考虑pUnchEdge中没有处理并且
  1301.  *   可能是边界点的那些象素(=128),象素值为0表明该点不可能是边界点,象素值
  1302.  *   为255表明该点已经被设置为边界点,不必再考虑
  1303.  *   
  1304.  *   
  1305.  *************************************************************************
  1306.  */
  1307. void TraceEdge (int y, int x, int nLowThd, unsigned char *pUnchEdge, int *pnMag, int nWidth) 
  1308. // 对8邻域象素进行查询
  1309. int xNb[8] = {1, 1, 0,-1,-1,-1, 0, 1} ;
  1310. int yNb[8] = {0, 1, 1, 1,0 ,-1,-1,-1} ;
  1311. int yy ;
  1312. int xx ;
  1313. int k  ;
  1314. for(k=0; k<8; k++)
  1315. {
  1316. yy = y + yNb[k] ;
  1317. xx = x + xNb[k] ;
  1318. // 如果该象素为可能的边界点,又没有处理过
  1319. // 并且梯度大于阈值
  1320. if(pUnchEdge[yy*nWidth+xx] == 128  && pnMag[yy*nWidth+xx]>=nLowThd)
  1321. {
  1322. // 把该点设置成为边界点
  1323. pUnchEdge[yy*nWidth+xx] = 255 ;
  1324. // 以该点为中心进行跟踪
  1325. TraceEdge(yy, xx, nLowThd, pUnchEdge, pnMag, nWidth);
  1326. }
  1327. }
  1328. /*************************************************************************
  1329.  *
  1330.  * 函数名称:
  1331.  *   EstimateThreshold()
  1332.  *
  1333.  * 输入参数:
  1334.  *   int *pnMag               - 梯度幅度图
  1335.  *  int nWidth               - 图象数据宽度
  1336.  *  int nHeight              - 图象数据高度
  1337.  *   int *pnThdHigh           - 高阈值
  1338.  *   int *pnThdLow            - 低阈值
  1339.  *  double dRatioLow         - 低阈值和高阈值之间的比例
  1340.  *  double dRatioHigh        - 高阈值占图象象素总数的比例
  1341.  *   unsigned char *pUnchEdge - 经过non-maximum处理后的数据
  1342.  *
  1343.  * 返回值:
  1344.  *   无
  1345.  *
  1346.  * 说明:
  1347.  *   经过non-maximum处理后的数据pUnchEdge,统计pnMag的直方图,确定阈值。
  1348.  *   本函数中只是统计pUnchEdge中可能为边界点的那些象素。然后利用直方图,
  1349.  *   根据dRatioHigh设置高阈值,存储到pnThdHigh。利用dRationLow和高阈值,
  1350.  *   设置低阈值,存储到*pnThdLow。dRatioHigh是一种比例:表明梯度小于
  1351.  *   *pnThdHigh的象素数目占象素总数目的比例。dRationLow表明*pnThdHigh
  1352.  *   和*pnThdLow的比例,这个比例在canny算法的原文里,作者给出了一个区间。
  1353.  *
  1354.  *************************************************************************
  1355.  */
  1356. void EstimateThreshold(int *pnMag, int nWidth, int nHeight, int *pnThdHigh,int *pnThdLow, 
  1357.  unsigned char * pUnchEdge, double dRatioHigh, double dRationLow) 
  1358. // 循环控制变量
  1359. int y;
  1360. int x;
  1361. int k;
  1362. // 该数组的大小和梯度值的范围有关,如果采用本程序的算法,那么梯度的范围不会超过pow(2,10)
  1363. int nHist[1024] ;
  1364. // 可能的边界数目
  1365. int nEdgeNb     ;
  1366. // 最大梯度值
  1367. int nMaxMag     ;
  1368. int nHighCount  ;
  1369. nMaxMag = 0     ; 
  1370. // 初始化
  1371. for(k=0; k<1024; k++) 
  1372. {
  1373. nHist[k] = 0; 
  1374. }
  1375. // 统计直方图,然后利用直方图计算阈值
  1376. for(y=0; y<nHeight; y++)
  1377. {
  1378. for(x=0; x<nWidth; x++)
  1379. {
  1380. // 只是统计那些可能是边界点,并且还没有处理过的象素
  1381. if(pUnchEdge[y*nWidth+x]==128)
  1382. {
  1383. nHist[ pnMag[y*nWidth+x] ]++;
  1384. }
  1385. }
  1386. }
  1387. nEdgeNb = nHist[0]  ;
  1388. nMaxMag = 0         ;
  1389. // 统计经过“非最大值抑止(non-maximum suppression)”后有多少象素
  1390. for(k=1; k<1024; k++)
  1391. {
  1392. if(nHist[k] != 0)
  1393. {
  1394. // 最大梯度值
  1395. nMaxMag = k;
  1396. }
  1397. // 梯度为0的点是不可能为边界点的
  1398. // 经过non-maximum suppression后有多少象素
  1399. nEdgeNb += nHist[k];
  1400. }
  1401. // 梯度比高阈值*pnThdHigh小的象素点总数目
  1402. nHighCount = (int)(dRatioHigh * nEdgeNb +0.5);
  1403. k = 1;
  1404. nEdgeNb = nHist[1];
  1405. // 计算高阈值
  1406. while( (k<(nMaxMag-1)) && (nEdgeNb < nHighCount) )
  1407. {
  1408. k++;
  1409. nEdgeNb += nHist[k];
  1410. }
  1411. // 设置高阈值
  1412. *pnThdHigh = k ;
  1413. // 设置低阈值
  1414. *pnThdLow  = (int)((*pnThdHigh) * dRationLow+ 0.5);
  1415. }
  1416. /*************************************************************************
  1417.  *
  1418.  * 函数名称:
  1419.  *   Hysteresis()
  1420.  *
  1421.  * 输入参数:
  1422.  *   int *pnMag               - 梯度幅度图
  1423.  *  int nWidth               - 图象数据宽度
  1424.  *  int nHeight              - 图象数据高度
  1425.  *  double dRatioLow         - 低阈值和高阈值之间的比例
  1426.  *  double dRatioHigh        - 高阈值占图象象素总数的比例
  1427.  *   unsigned char *pUnchEdge - 记录边界点的缓冲区
  1428.  *
  1429.  * 返回值:
  1430.  *   无
  1431.  *
  1432.  * 说明:
  1433.  *   本函数实现类似“磁滞现象”的一个功能,也就是,先调用EstimateThreshold
  1434.  *   函数对经过non-maximum处理后的数据pUnchSpr估计一个高阈值,然后判断
  1435.  *   pUnchSpr中可能的边界象素(=128)的梯度是不是大于高阈值nThdHigh,如果比
  1436.  *   该阈值大,该点将作为一个边界的起点,调用TraceEdge函数,把对应该边界
  1437.  *   的所有象素找出来。最后,当整个搜索完毕时,如果还有象素没有被标志成
  1438.  *   边界点,那么就一定不是边界点。
  1439.  *   
  1440.  *************************************************************************
  1441.  */
  1442. void Hysteresis(int *pnMag, int nWidth, int nHeight, double dRatioLow, 
  1443. double dRatioHigh, unsigned char *pUnchEdge)
  1444. {
  1445. // 循环控制变量
  1446. int y;
  1447. int x;
  1448. int nThdHigh ;
  1449. int nThdLow  ;
  1450. int nPos;
  1451. // 估计TraceEdge需要的低阈值,以及Hysteresis函数使用的高阈值
  1452. EstimateThreshold(pnMag, nWidth, nHeight, &nThdHigh, 
  1453.                &nThdLow, pUnchEdge,dRatioHigh, dRatioLow);
  1454.   // 这个循环用来寻找大于nThdHigh的点,这些点被用来当作边界点,然后用
  1455. // TraceEdge函数来跟踪该点对应的边界
  1456.    for(y=0; y<nHeight; y++)
  1457.  {
  1458.       for(x=0; x<nWidth; x++)
  1459. {
  1460. nPos = y*nWidth + x ; 
  1461. // 如果该象素是可能的边界点,并且梯度大于高阈值,该象素作为
  1462. // 一个边界的起点
  1463. if((pUnchEdge[nPos] == 128) && (pnMag[nPos] >= nThdHigh))
  1464. {
  1465. // 设置该点为边界点
  1466. pUnchEdge[nPos] = 255;
  1467. TraceEdge(y, x, nThdLow, pUnchEdge, pnMag, nWidth);
  1468. }
  1469.       }
  1470.    }
  1471.  // 那些还没有被设置为边界点的象素已经不可能成为边界点
  1472.    for(y=0; y<nHeight; y++)
  1473.  {
  1474.  for(x=0; x<nWidth; x++)
  1475.  {
  1476.  nPos = y*nWidth + x ; 
  1477.  if(pUnchEdge[nPos] != 255)
  1478.  {
  1479.  // 设置为非边界点
  1480.  pUnchEdge[nPos] = 0 ;
  1481.  }
  1482.  }
  1483.  }
  1484. }
  1485. /*************************************************************************
  1486.  *
  1487.  * 函数名称:
  1488.  *   Canny()
  1489.  *
  1490.  * 输入参数:
  1491.  *   unsigned char *pUnchImage- 图象数据
  1492.  *  int nWidth               - 图象数据宽度
  1493.  *  int nHeight              - 图象数据高度
  1494.  *   double sigma             - 高斯滤波的标准方差
  1495.  *  double dRatioLow         - 低阈值和高阈值之间的比例
  1496.  *  double dRatioHigh        - 高阈值占图象象素总数的比例
  1497.  *   unsigned char *pUnchEdge - canny算子计算后的分割图
  1498.  *
  1499.  * 返回值:
  1500.  *   无
  1501.  *
  1502.  * 说明:
  1503.  *   canny分割算子,计算的结果保存在pUnchEdge中,逻辑1(255)表示该点为
  1504.  *   边界点,逻辑0(0)表示该点为非边界点。该函数的参数sigma,dRatioLow
  1505.  *   dRatioHigh,是需要指定的。这些参数会影响分割后边界点数目的多少
  1506.  *************************************************************************
  1507.  */
  1508. void Canny(unsigned char *pUnchImage, int nWidth, int nHeight, double sigma,
  1509.  double dRatioLow, double dRatioHigh, unsigned char *pUnchEdge)
  1510. {
  1511. // 经过高斯滤波后的图象数据
  1512. unsigned char * pUnchSmooth ;
  1513.   
  1514. // 指向x方向导数的指针
  1515. int * pnGradX ; 
  1516. // 指向y方向导数的指针
  1517. int * pnGradY ;
  1518. // 梯度的幅度
  1519. int * pnGradMag ;
  1520. pUnchSmooth  = new unsigned char[nWidth*nHeight] ;
  1521. pnGradX      = new int [nWidth*nHeight]          ;
  1522. pnGradY      = new int [nWidth*nHeight]          ;
  1523. pnGradMag    = new int [nWidth*nHeight]          ;
  1524. // 对原图象进行滤波
  1525. GaussianSmooth(pUnchImage, nWidth, nHeight, sigma, pUnchSmooth) ;
  1526. // 计算方向导数
  1527. DirGrad(pUnchSmooth, nWidth, nHeight, pnGradX, pnGradY) ;
  1528. // 计算梯度的幅度
  1529. GradMagnitude(pnGradX, pnGradY, nWidth, nHeight, pnGradMag) ;
  1530. // 应用non-maximum 抑制
  1531. NonmaxSuppress(pnGradMag, pnGradX, pnGradY, nWidth, nHeight, pUnchEdge) ;
  1532. // 应用Hysteresis,找到所有的边界
  1533. Hysteresis(pnGradMag, nWidth, nHeight, dRatioLow, dRatioHigh, pUnchEdge);
  1534. // 释放内存
  1535. delete pnGradX      ;
  1536. pnGradX      = NULL ;
  1537. delete pnGradY      ;
  1538. pnGradY      = NULL ;
  1539. delete pnGradMag    ;
  1540. pnGradMag    = NULL ;
  1541. delete pUnchSmooth ;
  1542. pUnchSmooth  = NULL ;
  1543. }