SegApi.cpp
上传用户:piaozanzhu
上传日期:2008-02-14
资源大小:212k
文件大小:42k
源码类别:

图形图像处理

开发平台:

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