本文基於分裂合併分割算法,提出了兩種新的分割算法:基於八叉樹的分裂合併算法和基於自適應包圍盒的分裂合併算法。下面將對這兩種算法進行描述。node
區域生長法的原理是根據種子像素點(體數據中爲體素點)向其周圍擴散,對區域周圍的每個像素/體素進行分析。區域生長法須要用額外的內存區域來保存待生長的像素/體素點,而且算法的時間複雜度較大。區域生長的過程當中,須要以像素/體素爲單位一圈一圈地向外擴張。對於體數據來講,區域生長法花費較多的運算時間。算法
分裂合併的基本思想是:先將整幅圖像或體數據依據某種規則分裂出不少個形狀規則的子區域,子區域內的像素或體素特徵具備一致性,而後合併特徵類似的子區域,從而實現分割的目的[2]。體數據分裂後的各子區域是一個立方體的體素集合,集合內全部體素屬於同一區域組織。分裂的同時須要對子區域生成連通圖,即將體數據中分裂區域之間的鄰接關係以圖的形式表達出來。合併的工做與區域生長法類似,先選擇一個區域生長的種子點,肯定該種子點所在的正方體分裂區域;而後以該區域爲中心,對其全部相鄰的子區域進行判斷;若是相鄰區域與該區域是同一組織則進行合併。該算法實際是以立方體節點爲最小單位對種子點進行生長,所以其運算效率要優於區域生長法。在區域分裂時須要生成區域鄰接圖,經過鄰接圖表示各子區域間的相鄰關係。在區域合併時,須要經過鄰接圖來查找等待合併的子區域。優化
分裂的子區域結構須要包含如下幾種數據:spa
(1)空間包圍盒。用來記錄子區域的大小和位置;指針
(2)內部數據特徵值。能夠記錄所包含體素的灰度特徵,分佈狀況等;code
(3)相鄰節點。每一個非邊界節點至少有6個相鄰節點(每一個立方體面一個)。在區域合併時須要判斷該節點的全部相鄰節點。blog
分裂子區域結構爲以下代碼中的結構體BoxNode。該結構中保存着其數據區域、特徵值、與之相鄰的子區域。遞歸
struct BoxNode // 節點包圍盒結構體,每個節點表示一個分裂後的子區域隊列 {ip Point ptStart; // 節點的開始位置 Point ptEnd; // 節點的結束位置 WORD voxelValue; // 體素值 long nMergeRagion; // 該節點的合併區域,未合併時值爲-1 std::set<BoxNode*> setNeighbours; // 相鄰節點指針集合 void AddNeighbour(BoxNode* nodePtr) // 添加相鄰節點 {setNeighbours.insert(nodePtr); } void RemoveNeighbour(BoxNode* nodePtr) // 移除相鄰節點 {setNeighbours.erase(nodePtr);} }; |
對於二維圖像來講,能夠採用四叉樹的方式對其進行分裂:首先選取區域一致性準則(如像素灰度值),而後根據這一準則將圖像等分紅四個區域,並分別判斷這些區域是否知足一致性準則;若是不知足一致性,則繼續分裂[24]。本課題所研究的是對三維體數據的分割,所以對其分裂時將基於二維的四叉樹方法擴展爲針對三維的八叉樹。
八叉樹是每一個非葉節點有且僅有8個子節點的一種樹形結構體,它是表現一個被立方體封裝的三維物體的理想結構[5]。對於有着物體密集的數據集合,八叉樹可以快速的進行數據管理、可視化裁剪、光線跟蹤等三維空間操做。八叉樹的根節點包含一個立方體,它封閉着所有體數據。每一個節點的子節點是8個相同大小的立方體,它們將父節點等分爲八份。如圖5-6所示。
a.初始節點 b.第一次分裂 c.第二次分裂
圖5- 6八叉樹模型
用Volume表示整個體數據區域,P表示區域特徵一致性測度的謂詞邏輯,從最高層開始,將Volume分裂成8個相同的正方體子區域Volumei,對於任一個區域Volumei若是P(Volumei)爲false就將繼續對Volumei進行8等分,直至P(Volumei)爲true或Volumei爲單個體素爲止。對於一個2n 2n 2n的體數據,最多能夠分解至n層,第n層數據區域爲單個體素。對於本文實驗所採用的醫學圖像來講,圖像分辨率爲512 512。在對XOY平面上進行分裂時最多能夠分解至第8層。假如斷層數目爲M,那麼處理時將Z軸方向上的斷層個數設爲2k,使得 2k<= M< 2(k+1)。在八叉樹分裂的同時須要生成區域鄰接圖,即每次分裂後,新生成的8個節點是上下左右相鄰的,而後判斷每一個新生成節點與原相鄰節點是否相鄰。
圖5- 7基於八叉樹的分裂算法流程圖 |
基於八叉樹分裂算法的步驟以下所述:
a.生成一個包含整個體數據的節點,該節點的開始位置爲原點:
ptStart(0, 0, 0);結束位置爲體數據的最大值ptEnd(xmax, ymax, zmax);
b. 對步驟a生成的節點進行特徵一致性檢測,若是不一致則該節點須要進行分裂處理,跳到步驟c,不然,跳到結束分裂;
c.將節點進行八叉樹分裂處理,即由該節點分裂出八個新的子節點;
d.創建新生成的八個子節點的相鄰關係,並更新新節點與舊節點的相鄰關係;
e.對新生成的八個子節點分別作特徵一致性檢測,若是不一致,則對該節點進行步驟c的操做;
f.分裂完成。
基於八叉樹分裂算法採用分治遞歸的策略,可以快速有效的對體數據進行分裂處理。因爲該算法將一個問題拆分紅8個子問題,因此能夠採用並行的多CPU運算對其進行優化。但基於八叉樹分裂算法會產生過分分裂的狀況,假如體數據中一個特徵一致性區域正好位於某個待分裂節點的中心位置,那麼使用八叉樹會將該區域分裂成八份。
爲了解決區域的過分分裂的問題,本文提出一種基於自適應包圍盒的分裂算法,能有效解決這一過分分裂問題。該算法的基本思想是一次性的遍歷體數據中每一個體素,找出全部具備特徵值一致性的立方體區域,而後生成這些立方體區域的鄰接圖。
圖5- 8基於自適應包圍盒的分裂算法流程圖
自適應包圍盒的分裂算法的實現步驟以下所述:
a. 首先建立一個分裂區域子節點集合setNodes,和一個與體數據相同大小的數據區域NodeArray。NodeArray用於存放每一個體素所在子節點的地址;還須要將NodeArray的數據內容設置爲空;
b.依次遍歷體數據中的每個體素點Voxel(xi, yi,zi);當完成遍歷後,跳轉到步驟g。
c.判斷Voxel(xi,yi,zi)是否已經被擴展過;判斷方法是看其對應NodeArray(xi,yi,zi)中的值是否爲空;若是是擴展過的,則返回步驟b;
d.建立一個子區域節點Nodej,該節點的起始位置爲Voxel(xi,yi,zi);
e.分別對X、Y、Z三個座標軸的正方向進行擴展,並判斷新擴展的體素與Voxel(xi,yi,zi)是否具備特徵一致性;當某一方向出現不一致的體素時,則中止該方向的擴展;
f.步驟e完成後會獲得節點Nodej的結束位置Voxel(xi+m, yi+n,zi+k);先將Nodej所包含的體素Voxel(xs,ys,zs)所對應NodeArray(xs,ys,zs)的值設置爲Nodej的地址,再將其添加到子節點集合setNodes中;
g.在遍歷完體數據中全部體素後,經過遍歷NodeArray中的數據,生成子區域節點的相鄰關係。
自適應包圍盒的分裂算法可以只經過兩次遍歷體數據,就能夠對體數據進行分裂;而且分裂後的區域不會有像八叉樹那種過分分裂的狀況。
該階段與區域生長算法很類似:首先選擇一個種子點,得到該種子點所在的子區域Vmerge。根據區域鄰接圖,對任意與Vmerge相鄰的子區域Vi,若P(Vmerge U Vi)==true,則將其合併並將Vi的相鄰子區域設爲Vmerge相鄰的子區域。直至沒有知足合併條件的相鄰區域爲止。
a.經過種子點的座標位置找到種子點所在的子區域節點;而後建立一個待合併的隊列,並將剛纔找到的子區域節點放到該隊列中;再將該子區域節點放到合併區域集合中;
b.從待合併隊列中取出一個子區域節點;若是待合併隊列爲空則表示合併完成,跳轉到步驟d;
c.依次對步驟b中取出的節點的相鄰節點作一致性檢測,若是檢測經過,則將該相鄰節點分別放到待合併隊列和合並區域集合中;
d.完成合並操做。
代碼實現:
1 /**************************************************************** 2 File name : VolumeDataSplitMerge.CPP 3 Author : 葉峯 4 Version : 1.0a 5 Create Date : 2011/12/01 6 Description : 7 Others : 8 *****************************************************************/ 9 10 // -------------------------------------------------------------------------------------- 11 12 #include <Windows.h> 13 #include <set> 14 15 // -------------------------------------------------------------------------------------- 16 17 struct Point // 體數據中的體素點座標 18 { 19 long x; 20 long y; 21 long z; 22 Point() 23 { 24 x = 0; y = 0; z = 0; 25 } 26 Point(long _x, long _y, long _z) 27 { 28 x = _x; y = _y; z = _z; 29 } 30 }; 31 32 struct BoxNode // 節點包圍盒結構體 33 { 34 Point ptStart; // 節點的開始位置 35 Point ptEnd; // 節點的結束位置 36 WORD voxelValue; // 體素值 37 bool isMergedTest; // 用於判斷該節點是否被合併 38 std::set<BoxNode*> setNeighbours; // 相鄰節點指針集合 39 void AddNeighbour(BoxNode* nodePtr) // 添加相鄰節點 40 { 41 setNeighbours.insert(nodePtr); 42 } 43 void RemoveNeighbour(BoxNode* nodePtr) // 移除相鄰節點 44 { 45 setNeighbours.erase(nodePtr); 46 } 47 }; 48 49 // -------------------------------------------------------------------------------------- 50 51 WORD* g_pVolumeData; // 體數據 52 long g_dwX, g_dwY, g_dwZ; // 體數據的大小 53 WORD g_nThreshold; 54 std::set<BoxNode*> g_setSplitNodes; // 分裂出的全部區域節點的集合 55 56 // -------------------------------------------------------------------------------------- 57 58 // 判斷一個體素座標點是否在一包圍盒節點中 59 inline bool IsPointInBox(const Point& point, const BoxNode& box) 60 { 61 return (point.x >= box.ptStart.x && point.x <= box.ptEnd.x && 62 point.y >= box.ptStart.y && point.y <= box.ptEnd.y && 63 point.z >= box.ptStart.z && point.z <= box.ptEnd.z); 64 } 65 66 // 得到一個座標點的體素值 67 inline WORD GetVoxelValue(const Point& point) 68 { 69 return g_pVolumeData[point.z*g_dwY*g_dwX + point.y*g_dwX + point.x]; 70 } 71 inline WORD GetVoxelValue(long x, long y, long z) 72 { 73 return g_pVolumeData[z*g_dwY*g_dwX + y*g_dwX + x]; 74 } 75 76 // 判斷兩個體素值是否類似 77 inline bool IsSimilar(WORD value0, WORD value1) 78 { 79 if (value0 > value1) 80 return (value0 - value1 <= g_nThreshold); 81 else 82 return (value1 - value0 <= g_nThreshold); 83 } 84 85 // 判斷兩個節點包圍盒是否相鄰 86 inline bool IsNodeNeighbour(const BoxNode* nodePtr0, const BoxNode* nodePtr1) 87 { 88 if (nodePtr0->ptStart.x - 1 == nodePtr1->ptEnd.x) 89 { 90 if (nodePtr0->ptStart.y <= nodePtr1->ptEnd.y && 91 nodePtr0->ptEnd.y <= nodePtr1->ptStart.y && 92 nodePtr0->ptStart.z <= nodePtr1->ptEnd.z && 93 nodePtr0->ptEnd.z <= nodePtr1->ptStart.z) 94 { 95 return true; 96 } 97 } 98 else if (nodePtr0->ptEnd.x + 1 == nodePtr1->ptStart.x) 99 { 100 if (nodePtr0->ptStart.y <= nodePtr1->ptEnd.y && 101 nodePtr0->ptEnd.y <= nodePtr1->ptStart.y && 102 nodePtr0->ptStart.z <= nodePtr1->ptEnd.z && 103 nodePtr0->ptEnd.z <= nodePtr1->ptStart.z) 104 { 105 return true; 106 } 107 } 108 else if (nodePtr0->ptStart.y - 1 == nodePtr1->ptEnd.y) 109 { 110 if (nodePtr0->ptStart.x <= nodePtr1->ptEnd.x && 111 nodePtr0->ptEnd.x <= nodePtr1->ptStart.x && 112 nodePtr0->ptStart.z <= nodePtr1->ptEnd.z && 113 nodePtr0->ptEnd.z <= nodePtr1->ptStart.z) 114 { 115 return true; 116 } 117 } 118 else if (nodePtr0->ptEnd.y + 1 == nodePtr1->ptStart.y) 119 { 120 if (nodePtr0->ptStart.x <= nodePtr1->ptEnd.x && 121 nodePtr0->ptEnd.x <= nodePtr1->ptStart.x && 122 nodePtr0->ptStart.z <= nodePtr1->ptEnd.z && 123 nodePtr0->ptEnd.z <= nodePtr1->ptStart.z) 124 { 125 return true; 126 } 127 } 128 else if (nodePtr0->ptStart.z - 1 == nodePtr1->ptEnd.z) 129 { 130 if (nodePtr0->ptStart.y <= nodePtr1->ptEnd.y && 131 nodePtr0->ptEnd.y <= nodePtr1->ptStart.y && 132 nodePtr0->ptStart.x <= nodePtr1->ptEnd.x && 133 nodePtr0->ptEnd.x <= nodePtr1->ptStart.x) 134 { 135 return true; 136 } 137 } 138 else if (nodePtr0->ptEnd.z + 1 == nodePtr1->ptStart.z) 139 { 140 if (nodePtr0->ptStart.y <= nodePtr1->ptEnd.y && 141 nodePtr0->ptEnd.y <= nodePtr1->ptStart.y && 142 nodePtr0->ptStart.x <= nodePtr1->ptEnd.x && 143 nodePtr0->ptEnd.x <= nodePtr1->ptStart.x) 144 { 145 return true; 146 } 147 } 148 149 return false; 150 } 151 152 // -------------------------------------------------------------------------------------- 153 // 更新8叉樹節點的相鄰關係 154 void UpdateOctreeNodeNeighbour(BoxNode* oldNodePtr, BoxNode* newNodePtr) 155 { 156 BoxNode* nerghbourNodePtr; 157 std::set<BoxNode*>::iterator itor = oldNodePtr->setNeighbours.begin(); 158 while (itor != oldNodePtr->setNeighbours.end()) 159 { 160 nerghbourNodePtr = *itor; 161 nerghbourNodePtr->RemoveNeighbour(oldNodePtr); 162 if (IsNodeNeighbour(nerghbourNodePtr, newNodePtr)) 163 { 164 nerghbourNodePtr->AddNeighbour(newNodePtr); 165 newNodePtr->AddNeighbour(nerghbourNodePtr); 166 } 167 itor++; 168 } 169 } 170 171 // 使用8叉樹法分裂體數據 172 void OctreeSplitVolumeData(BoxNode* nodePtr) 173 { 174 if (nodePtr == NULL) // 當nodePtr爲空指針時表示對整個體數據進行分裂 175 { 176 nodePtr = new BoxNode(); 177 nodePtr->ptStart = Point(0, 0, 0); 178 nodePtr->ptEnd = Point(g_dwX - 1, g_dwY - 1, g_dwZ - 1); 179 nodePtr->voxelValue = GetVoxelValue(nodePtr->ptStart); 180 nodePtr->isMergedTest = false; 181 } 182 183 bool shouldSplit = false; 184 // 判斷是否須要分裂該節點 185 WORD voxelValue; 186 for (long z = nodePtr->ptStart.z; z <= nodePtr->ptEnd.z; z++) 187 { 188 for (long y = nodePtr->ptStart.y; y <= nodePtr->ptEnd.y; y++) 189 { 190 for (long x = nodePtr->ptStart.x; x <= nodePtr->ptEnd.x; x++) 191 { 192 voxelValue = GetVoxelValue(x, y, z); 193 if (!IsSimilar(voxelValue, nodePtr->voxelValue)) 194 { 195 shouldSplit = true; 196 break; 197 } 198 } 199 if (shouldSplit) 200 break; 201 } 202 if (shouldSplit) 203 break; 204 } 205 206 if (!shouldSplit) 207 { 208 return; 209 } 210 211 Point ptSplit; // 分裂點 212 ptSplit.x = (nodePtr->ptStart.x + nodePtr->ptEnd.x)/2; 213 ptSplit.y = (nodePtr->ptStart.y + nodePtr->ptEnd.y)/2; 214 ptSplit.z = (nodePtr->ptStart.z + nodePtr->ptEnd.z)/2; 215 216 // 8分原節點 217 BoxNode* nodeSplitPtr[8]; 218 for (long i = 0; i < 8; i++) 219 { 220 nodeSplitPtr[i] = new BoxNode(); 221 nodeSplitPtr[i]->ptStart = nodePtr->ptStart; 222 nodeSplitPtr[i]->ptEnd = ptSplit; 223 224 if (i & 1) // X軸向變化 225 { 226 nodeSplitPtr[i]->ptStart.x = ptSplit.x + 1; 227 nodeSplitPtr[i]->ptEnd.x = nodePtr->ptEnd.x; 228 if (nodeSplitPtr[i]->ptStart.x > nodeSplitPtr[i]->ptEnd.x) // 非法的分裂點 229 { 230 delete nodeSplitPtr[i]; 231 nodeSplitPtr[i] = NULL; 232 continue; 233 } 234 } 235 if (i & 2) // Y軸向變化 236 { 237 nodeSplitPtr[i]->ptStart.y = ptSplit.y + 1; 238 nodeSplitPtr[i]->ptEnd.y = nodePtr->ptEnd.y; 239 if (nodeSplitPtr[i]->ptStart.y > nodeSplitPtr[i]->ptEnd.y) // 非法的分裂點 240 { 241 delete nodeSplitPtr[i]; 242 nodeSplitPtr[i] = NULL; 243 continue; 244 } 245 } 246 if (i & 4) // Z軸向變化 247 { 248 nodeSplitPtr[i]->ptStart.z = ptSplit.z + 1; 249 nodeSplitPtr[i]->ptEnd.z = nodePtr->ptEnd.z; 250 if (nodeSplitPtr[i]->ptStart.z > nodeSplitPtr[i]->ptEnd.z) // 非法的分裂點 251 { 252 delete nodeSplitPtr[i]; 253 nodeSplitPtr[i] = NULL; 254 continue; 255 } 256 } 257 258 nodeSplitPtr[i]->voxelValue = nodePtr->voxelValue; 259 nodeSplitPtr[i]->isMergedTest = false; 260 UpdateOctreeNodeNeighbour(nodePtr, nodeSplitPtr[i]); 261 } 262 263 for (long i = 0; i < 8; i++) 264 { 265 if (nodeSplitPtr[i]) 266 { 267 for (long j = 0; j < 8; j++) 268 { 269 if (i != j && nodeSplitPtr[j]) 270 { 271 // 添加相鄰關係 272 nodeSplitPtr[i]->AddNeighbour(nodeSplitPtr[j]); 273 } 274 } 275 g_setSplitNodes.insert(nodeSplitPtr[i]); 276 } 277 } 278 279 // 刪除舊的節點 280 delete nodePtr; 281 g_setSplitNodes.erase(nodePtr); 282 283 // 遞歸處理分裂的節點 284 for (long i = 0; i < 8; i++) 285 { 286 if (nodeSplitPtr[i]) 287 { 288 OctreeSplitVolumeData(nodeSplitPtr[i]); 289 } 290 } 291 } 292 293 // -------------------------------------------------------------------------------------- 294 void UpdateVoxelNodePtr(BoxNode* nodePtr, BoxNode** listVoxelNodePtr) 295 { 296 for (long z = nodePtr->ptStart.z; z <= nodePtr->ptEnd.z; z++) 297 for (long y = nodePtr->ptStart.y; y <= nodePtr->ptEnd.y; y++) 298 for (long x = nodePtr->ptStart.x; x <= nodePtr->ptEnd.x; x++) 299 listVoxelNodePtr[z*g_dwY*g_dwX + y*g_dwX + x] = nodePtr; 300 } 301 302 // 自適應包圍盒法分裂體數據 303 void AdaptableSplitVolumeData() 304 { 305 // 記錄每個體素點所在的節點包圍盒 306 BoxNode** listVoxelNodePtr = (BoxNode**)malloc(sizeof(BoxNode*)*g_dwX*g_dwY*g_dwZ); 307 memset(listVoxelNodePtr, 0, sizeof(BoxNode*)*g_dwX*g_dwY*g_dwZ); 308 309 for (long z = 0; z < g_dwZ; z++) 310 { 311 for (long y = 0; y < g_dwY; y++) 312 { 313 for (long x = 0; x < g_dwX; x++) 314 { 315 if (listVoxelNodePtr[z*g_dwY*g_dwX + y*g_dwX + x] != NULL) 316 { 317 continue; 318 } 319 320 BoxNode* nodePtr = new BoxNode(); 321 nodePtr->ptStart = Point(x, y, z); 322 nodePtr->ptEnd = nodePtr->ptStart; 323 nodePtr->voxelValue = GetVoxelValue(nodePtr->ptStart); 324 nodePtr->isMergedTest = false; 325 326 // 擴張該節點 327 bool xExtend = true; 328 bool yExtend = true; 329 bool zExtend = true; 330 long extend; 331 WORD voxelValue; 332 while (xExtend || yExtend || zExtend) 333 { 334 if (xExtend) // X軸方向擴張 335 { 336 extend = nodePtr->ptEnd.x + 1; 337 if (extend == g_dwX) 338 { 339 xExtend = false; 340 } 341 else 342 { 343 for (long _z = nodePtr->ptStart.z; _z <= nodePtr->ptEnd.z; _z++) 344 { 345 for (long _y = nodePtr->ptStart.y; _y <= nodePtr->ptEnd.y; _y++) 346 { 347 voxelValue = GetVoxelValue(extend, _y, _z); 348 if (!IsSimilar(voxelValue, nodePtr->voxelValue)) 349 { 350 xExtend = false; 351 break; 352 } 353 } 354 if (!xExtend) 355 { 356 break; 357 } 358 } 359 360 if (xExtend) 361 { 362 nodePtr->ptEnd.x++; 363 } 364 } 365 } 366 367 if (yExtend) // Y軸方向擴張 368 { 369 extend = nodePtr->ptEnd.y + 1; 370 if (extend == g_dwY) 371 { 372 yExtend = false; 373 } 374 else 375 { 376 for (long _z = nodePtr->ptStart.z; _z <= nodePtr->ptEnd.z; _z++) 377 { 378 for (long _x = nodePtr->ptStart.x; _x <= nodePtr->ptEnd.x; _x++) 379 { 380 voxelValue = GetVoxelValue(_x, extend, _z); 381 if (!IsSimilar(voxelValue, nodePtr->voxelValue)) 382 { 383 yExtend = false; 384 break; 385 } 386 } 387 if (!yExtend) 388 { 389 break; 390 } 391 } 392 393 if (yExtend) 394 { 395 nodePtr->ptEnd.y++; 396 } 397 } 398 } 399 } 400 401 if (zExtend) // Z軸方向擴張 402 { 403 extend = nodePtr->ptEnd.z + 1; 404 if (extend == g_dwZ) 405 { 406 zExtend = false; 407 } 408 else 409 { 410 for (long _x = nodePtr->ptStart.x; _x <= nodePtr->ptEnd.x; _x++) 411 { 412 for (long _y = nodePtr->ptStart.y; _y <= nodePtr->ptEnd.y; _y++) 413 { 414 voxelValue = GetVoxelValue(_x, _y, extend); 415 if (!IsSimilar(voxelValue, nodePtr->voxelValue)) 416 { 417 zExtend = false; 418 break; 419 } 420 } 421 if (!zExtend) 422 { 423 break; 424 } 425 } 426 427 if (zExtend) 428 { 429 nodePtr->ptEnd.z++; 430 } 431 } 432 } 433 434 UpdateVoxelNodePtr(nodePtr, listVoxelNodePtr); 435 g_setSplitNodes.insert(nodePtr); 436 } 437 } 438 } 439 440 // 生成節點的相鄰關係 441 BoxNode* nodePtr; 442 BoxNode* nodeNext; 443 for (long z = 0; z < g_dwZ - 1; z++) 444 { 445 for (long y = 0; y < g_dwY - 1; y++) 446 { 447 for (long x = 0; x < g_dwX - 1; x++) 448 { 449 nodePtr = listVoxelNodePtr[z*g_dwY*g_dwX + y*g_dwX + x]; 450 nodeNext = listVoxelNodePtr[z*g_dwY*g_dwX + y*g_dwX + x + 1]; 451 if (nodePtr != nodeNext) 452 { 453 nodePtr->AddNeighbour(nodeNext); 454 nodeNext->AddNeighbour(nodePtr); 455 } 456 nodeNext = listVoxelNodePtr[z*g_dwY*g_dwX + (y + 1)*g_dwX + x]; 457 if (nodePtr != nodeNext) 458 { 459 nodePtr->AddNeighbour(nodeNext); 460 nodeNext->AddNeighbour(nodePtr); 461 } 462 nodeNext = listVoxelNodePtr[(z + 1)*g_dwY*g_dwX + y*g_dwX + x]; 463 if (nodePtr != nodeNext) 464 { 465 nodePtr->AddNeighbour(nodeNext); 466 nodeNext->AddNeighbour(nodePtr); 467 } 468 } 469 } 470 } 471 472 free(listVoxelNodePtr); 473 } 474 475 // -------------------------------------------------------------------------------------- 476 477 // 合併體數據 478 void MergeSplitVolumeData(IN const Point& ptStart, // 輸入起始點座標 479 OUT std::set<BoxNode*>& setMerge // 輸出合併的節點集合 480 ) 481 { 482 setMerge.clear(); 483 484 // 由起始點座標查找起始節點 485 BoxNode* pStartNode = NULL; 486 std::set<BoxNode*>::iterator itor = g_setSplitNodes.begin(); 487 while(itor != g_setSplitNodes.end()) 488 { 489 if (IsPointInBox(ptStart, *(*itor))) 490 { 491 pStartNode = *itor; 492 break; 493 } 494 itor++; 495 } 496 if (!pStartNode) 497 { 498 return; 499 } 500 501 std::set<BoxNode*> setExtending; // 正在擴張中的節點集合 502 std::set<BoxNode*> setExtendNext; // 下一步須要擴張的節點集合 503 setExtending.insert(pStartNode); 504 505 WORD voxelValue = pStartNode->voxelValue; 506 BoxNode* pCurrentNode; 507 508 while(setExtending.size() > 0) 509 { 510 setExtendNext.clear(); 511 std::set<BoxNode*>::iterator itor = setExtending.begin(); 512 while (itor != setExtending.end()) 513 { 514 pCurrentNode = *itor; 515 pCurrentNode->isMergedTest = true; 516 if (IsSimilar(voxelValue, (*itor)->voxelValue)) 517 { 518 setMerge.insert(*itor); 519 520 std::set<BoxNode*>::iterator itor2 = pCurrentNode->setNeighbours.begin(); 521 while(itor2 != pCurrentNode->setNeighbours.end()) 522 { 523 if (!(*itor2)->isMergedTest) 524 { 525 setExtendNext.insert((*itor2)); 526 } 527 } 528 } 529 itor++; 530 } 531 532 setExtending = setExtendNext; 533 } 534 }