种子点生长算法下——三维种子点生长
上一篇文章“二維種子點生長研究”介紹了二維種子點生長,對于平面圖像,二維種子點生長其實很容易理解,其典型應(yīng)用是“油漆桶”。例如在一個具有多片聯(lián)通區(qū)域的二維圖上,我們能用油漆桶尋找所需要的單片聯(lián)通區(qū)域。
至于三維種子點生長,其目的與二維種子點生長一樣,都是要找出圖像中的聯(lián)通區(qū)域。三維圖像由于其多了一維,所以三維的聯(lián)通區(qū)域就不太好用二維圖片來查看了。關(guān)于三位圖片的介紹可以參考第一篇“圖像數(shù)據(jù)的組織方式”。不過借助一些三維可視化軟件,如ParaView,我們也可以觀察三維圖像的內(nèi)容。
下面的圖片是顯示在ParaView下打開一張三維圖片Lobster.raw的結(jié)果,我們通過調(diào)節(jié)不同體素值的透明度,就能凸顯出三維圖像中所需要關(guān)心的內(nèi)容。
| 圖像預(yù)覽 | |||
| 像素透明度曲線 |
從上圖看,由于三維圖像本身是個長方體,各個像素有自己的顏色,所以只能看到外表面的樣子。順便提一下,在三維圖像中,我們一般不使用“像素”這個詞,而是使用“體素”來形容組成圖像的這些單元。我們可以使用ParaView體繪制屬性中調(diào)節(jié)透明度的選項,把一部分值較小的體素設(shè)為透明,就能看到這個三維圖像中,值較大的體素就像一個個小磚頭一樣堆成了一個龍蝦的形狀。也就是說,如果我們在這些體素中找一個種子點,利用三維圖像種子點生長算法,就能找到所有組成這個龍蝦的體素。
上一篇文章已經(jīng)介紹了二維種子點生長算法的相關(guān)知識,介紹了三種種子點生長算法—泛洪法、掃描線法和區(qū)段法,并在最后分析了他們的性能。本文的核心部分是三維種子點點生長的討論,也就是把上一篇文章的算法擴展到三維,那么本文也按照相同的結(jié)構(gòu)來說明這三種算法是如何擴展到三維的。
一、泛洪法
泛洪法的基本思想上篇文章詳細介紹過,在三維圖像中,需要擴展的主要是鄰域的范圍。鄰域從二維擴展到三維后,就不再是四鄰域和八鄰域而是六鄰域和二十六鄰域。示意圖如下:
其中綠色的點表示為當前點(藍色點)的6鄰域,綠色+紅色的點為這個像素的26鄰域。
對于三維點P,P的6鄰域可以表示為:
S=Adj6(P)={P0(P.X-1,P.Y,P.Z),P1(P.X+1,P.Y,P.Z),P2(P.X,P.Y-1,P.Z),P3(P.X,P.Y+1P.Z),P4(P.X,P.Y,P.Z-1),P5(P.X,P.Y,P.Z+1)}。
P的26鄰域可以表示為:
S=Adj26(P)={P0(P.X-1,P.Y,P.Z),P1(P.X+1,P.Y,P.Z),P2(P.X,P.Y-1,P.Z),P3(P.X,P.Y+1,P.Z),
P4(P.X-1,P.Y-1,P.Z),P5(P.X+1,P.Y+1,P.Z),P6(P.X+1,P.Y-1,P.Z),P7(P.X-1,P.Y+1,P.Z),
P8(P.X-1,P.Y-1,P.Z-1),P9(P.X+1,P.Y+1,P.Z-1),P10(P.X+1,P.Y-1,P.Z-1),P11(P.X-1,P.Y+1,P.Z-1)
P12(P.X,P.Y-1,P.Z-1),P13(P.X,P.Y+1,P.Z-1),P14(P.X+1,P.Y,P.Z-1),P15(P.X-1,P.Y,P.Z-1),
P16(P.X,P.Y,P.Z-1),P17(P.X,P.Y,P.Z+1),P18(P.X+1,P.Y-1,P.Z+1),P19(P.X-1,P.Y+1,P.Z+1),
P20(P.X-1,P.Y-1,P.Z+1),P21(P.X+1,P.Y+1,P.Z+1),P22(P.X,P.Y-1,P.Z+1),P23(P.X,P.Y+1,P.Z+1)
P24(P.X-1,P.Y,P.Z+1),P25(P.X+1,P.Y,P.Z+1)}。
那么實際上,三維泛洪法采用和二維泛洪法一樣的邏輯。
FloodFill(seed,bitmap,includePredicate,process)set all postions of flagMap to falseset container Q to empty.push seed into Qset seed position in flagMap to trueprocess(seed)while Q is not emptypop a point P from Qforeach point T in Adj(P)if includePredicate(T) is trueset position of T in flagMap to truepush T into Qprocess(T)end注意在三維的情況下,相應(yīng)的點類、圖像類、位圖標記表類等基礎(chǔ)數(shù)據(jù)結(jié)構(gòu)都需要分別增加一維。相應(yīng)類的c#定義如下:
public struct Int16Triple{public int X;public int Y;public int Z;public Int16Triple(int x, int y,int z){X = x;Y = y;Z = z;}}public class BitMap3d{public const byte WHITE = 255;public const byte BLACK = 0;public byte[] data;public int width;public int height;public int depth;public BitMap3d(int width, int height,int depth, byte v){this.width = width;this.height = height;this.depth = depth;data = new byte[width * height * depth];for (int i = 0; i < width * height*depth; i++)data[i] = v;}public BitMap3d(byte[] data, int width, int height,int depth){this.data = data;this.width = width;this.height = height;this.depth = depth;}public void SetPixel(int x, int y,int z,byte v){data[x + y * width+z*width*height] = v;}public byte GetPixel(int x, int y,int z){return data[x + y * width+z*width*height];}public void ReadRaw(string path){FileStream fs = new FileStream(path, FileMode.Open, FileAccess.Read);fs.Read(data, 0, width*height*depth);fs.Close();}public void SaveRaw(string path){FileStream fs = new FileStream(path, FileMode.OpenOrCreate, FileAccess.Write);fs.Write(data, 0, data.Length);fs.Close();}}public class FlagMap3d{public int width;public int height;public int depth;BitArray flags;public FlagMap3d(int width, int height,int depth){this.width = width;this.height = height;this.depth = depth;flags = new BitArray(width * height*depth, false);}public void SetFlagOn(int x, int y, int z,bool v){flags[x + y * width+z*width*height] = v;}public bool GetFlagOn(int x, int y,int z){return flags[x + y * width+z*width*height];} }相應(yīng)實現(xiàn)的c#代碼如下:
class FloodFill3d{protected BitMap3d bmp;protected FlagMap3d flagsMap;protected Container<Int16Triple> container;protected int count = 0;public byte min;public byte max;protected bool IncludePredicate(Int16Triple p){byte v = bmp.GetPixel(p.X, p.Y, p.Z);return v > min && v < max;}protected void Process(Int16Triple p){count++;return;}protected virtual void ExcuteFloodFill(BitMap3d data, Int16Triple seed){this.bmp = data;data.ResetVisitCount();flagsMap = new FlagMap3d(data.width, data.height,data.depth);Int16Triple[] adjPoints6 = new Int16Triple[6];flagsMap.SetFlagOn(seed.X, seed.Y, seed.Z,true);container.Push(seed);Process(seed);while (!container.Empty()){Int16Triple p = container.Pop();InitAdj6(ref adjPoints6, ref p);for (int adjIndex = 0; adjIndex < 6; adjIndex++){Int16Triple t = adjPoints6[adjIndex];if (t.X < data.width && t.X >= 0 && t.Y < data.height && t.Y >= 0&&t.Z<data.depth&&t.Z>=0){if (!flagsMap.GetFlagOn(t.X, t.Y,t.Z) && IncludePredicate(t)){flagsMap.SetFlagOn(t.X, t.Y,t.Z, true);container.Push(t);Process(t);}}}}return;}protected void InitAdj6(ref Int16Triple[] adjPoints6, ref Int16Triple p){adjPoints6[0].X = p.X - 1;adjPoints6[0].Y = p.Y;adjPoints6[0].Z = p.Z;adjPoints6[1].X = p.X + 1;adjPoints6[1].Y = p.Y;adjPoints6[1].Z = p.Z;adjPoints6[2].X = p.X;adjPoints6[2].Y = p.Y - 1;adjPoints6[2].Z = p.Z;adjPoints6[3].X = p.X;adjPoints6[3].Y = p.Y + 1;adjPoints6[3].Z = p.Z;adjPoints6[4].X = p.X;adjPoints6[4].Y = p.Y;adjPoints6[4].Z = p.Z-1;adjPoints6[5].X = p.X;adjPoints6[5].Y = p.Y;adjPoints6[5].Z = p.Z+1;}}二、掃描線法
二維的掃描線法,是從一個點彈出堆棧后,對其左右掃描出兩個端點,然后再對端點范圍的兩側(cè)即Y-1和Y+1方向進行CheckRange。算法拓展到三維,只需增加檢查Z-1和Z+1方向即可。也就是“兩側(cè)”變成了“四側(cè)”,即:
掃描這四條線等價于6向泛洪法,假如掃描下面八條線,就相當于26向泛洪法
掃描線法拓展到三維,基本操作FindXleft,FindXright,CheckRange分別修改至相應(yīng)的三維即可。其作用沒有任何變化。
下面是三維掃描線線的c#代碼:
class ScanlineFill3d{protected int count = 0;protected Container<Int16Triple> container;//這個容器可以是Queue和Stack中任意一種,這里抽象成一個Containerprotected BitMap3d bmp;public FlagMap3d flagsMap;protected virtual void ExcuteScanlineFill(BitMap3d data, Int16Triple seed){this.bmp = data;data.ResetVisitCount();flagsMap = new FlagMap3d(data.width, data.height, data.depth);flagsMap.SetFlagOn(seed.X, seed.Y, seed.Z, true);container.Push(seed);Process(seed);while (!container.Empty()){Int16Triple p = container.Pop();int xleft = FindXLeft(p);int xright = FindXRight(p);if (p.Y - 1 >= 0)CheckRange(xleft, xright, p.Y - 1, p.Z);if (p.Y + 1 < data.height)CheckRange(xleft, xright, p.Y + 1, p.Z);if (p.Z - 1 >= 0)CheckRange(xleft, xright, p.Y, p.Z - 1);if (p.Z + 1 < data.depth)CheckRange(xleft, xright, p.Y, p.Z + 1);}}//該函數(shù)為掃描線法主體protected void CheckRange(int xleft, int xright, int y, int z){for (int i = xleft; i <= xright; ){if ((!flagsMap.GetFlagOn(i, y, z)) && IncludePredicate(i, y, z)){int rb = i + 1;while (rb <= xright && (!flagsMap.GetFlagOn(rb, y, z)) && IncludePredicate(rb, y, z)){rb++;}rb--;Int16Triple t = new Int16Triple(rb, y, z);flagsMap.SetFlagOn(rb, y, z, true);container.Push(t);Process(t);i = rb + 1;}else{i++;}}}//CheckRange操作protected int FindXLeft(Int16Triple p){int xleft = p.X - 1;while (true){if (xleft == -1 || flagsMap.GetFlagOn(xleft, p.Y, p.Z)){break;}else{byte value = bmp.GetPixel(xleft, p.Y, p.Z);if (IncludePredicate(xleft, p.Y, p.Z)){Int16Triple t = new Int16Triple(xleft, p.Y, p.Z);flagsMap.SetFlagOn(xleft, p.Y, p.Z, true);Process(t);xleft--;}else{break;}}}return xleft + 1;}//FindXLeft操作protected int FindXRight(Int16Triple p){int xright = p.X + 1;while (true){if (xright == bmp.width || flagsMap.GetFlagOn(xright, p.Y, p.Z)){break;}else{byte value = bmp.GetPixel(xright, p.Y, p.Z);if (IncludePredicate(xright, p.Y, p.Z)){Int16Triple t = new Int16Triple(xright, p.Y, p.Z);flagsMap.SetFlagOn(xright, p.Y, p.Z, true);Process(t);xright++;}else{break;}}}return xright - 1;}//FindXRight操作public byte min;public byte max;protected bool IncludePredicate(int x,int y,int z){byte v = bmp.GetPixel(x, y,z);return v > min && v < max;}protected void Process(Int16Triple p){count++;}}三、區(qū)段法
區(qū)段法擴展到三維也遵循和掃描線法一樣的原則,相應(yīng)的基本操作FindXleft,FindXright,CheckRange需要分別修改到三維的情況。同時在主函數(shù)邏輯中,監(jiān)測相應(yīng)的相鄰區(qū)段時要注意ParentDirection所指示的父區(qū)段的特殊性。
下面是三維區(qū)段法的C#代碼:
enum ParentDirections{Y0 = 1, Y2 = 2,Z0=3,Z2=4, Non = 5}enum ExtendTypes{LeftRequired = 1, RightRequired = 2, AllRez = 3, UnRez = 4}struct Span{public int XLeft;public int XRight;public int Y;public int Z;public ExtendTypes Extended;public ParentDirections ParentDirection;}class SpanFill3d{protected int count = 0;protected BitMap3d bmp;public FlagMap3d flagsMap;protected Container<Span> container;//以Span為單位的Queue或Stack容器protected virtual void ExcuteSpanFill(BitMap3d data, Int16Triple seed){this.bmp = data;data.ResetVisitCount();flagsMap = new FlagMap3d(data.width, data.height,data.depth);Process(seed);flagsMap.SetFlagOn(seed.X, seed.Y,seed.Z, true);Span seedspan = new Span();seedspan.XLeft = seed.X;seedspan.XRight = seed.X;seedspan.Y = seed.Y;seedspan.Z = seed.Z;seedspan.ParentDirection = ParentDirections.Non;seedspan.Extended = ExtendTypes.UnRez;container.Push(seedspan);while (!container.Empty()){Span span = container.Pop();#region AllRezif (span.Extended == ExtendTypes.AllRez){if (span.ParentDirection == ParentDirections.Y2){if (span.Y - 1 >= 0)//[spx-spy,y-1,z]CheckRange(span.XLeft, span.XRight, span.Y - 1, span.Z, ParentDirections.Y2);if (span.Z - 1 >= 0)//[spx-spy,y,z-1]CheckRange(span.XLeft, span.XRight, span.Y, span.Z - 1, ParentDirections.Z2);if (span.Z + 1 < data.depth)//[spx-spy,y,z+1]CheckRange(span.XLeft, span.XRight, span.Y, span.Z + 1, ParentDirections.Z0);continue;}if (span.ParentDirection == ParentDirections.Y0){if (span.Y + 1 < bmp.height)//[spx-spy,y+1,z]CheckRange(span.XLeft, span.XRight, span.Y + 1, span.Z, ParentDirections.Y0);if (span.Z - 1 > 0)//[spx-spy,y,z-1]CheckRange(span.XLeft, span.XRight, span.Y, span.Z - 1, ParentDirections.Z2);if (span.Z + 1 < data.depth)//[spx-spy,y,z+1]CheckRange(span.XLeft, span.XRight, span.Y, span.Z + 1, ParentDirections.Z0);continue;}if (span.ParentDirection == ParentDirections.Z2){if (span.Y - 1 >= 0)//[spx-spy,y-1,z]CheckRange(span.XLeft, span.XRight, span.Y - 1, span.Z, ParentDirections.Y2);if (span.Y + 1 < bmp.height)//[spx-spy,y+1,z]CheckRange(span.XLeft, span.XRight, span.Y + 1, span.Z, ParentDirections.Y0);if (span.Z - 1 >= 0)//[spx-spy,y,z-1]CheckRange(span.XLeft, span.XRight, span.Y, span.Z - 1, ParentDirections.Z2);continue;}if (span.ParentDirection == ParentDirections.Z0){if (span.Y - 1 >= 0)CheckRange(span.XLeft, span.XRight, span.Y - 1, span.Z, ParentDirections.Y2);if (span.Y + 1 < bmp.height)CheckRange(span.XLeft, span.XRight, span.Y + 1, span.Z, ParentDirections.Y0);if (span.Z + 1 < data.depth)CheckRange(span.XLeft, span.XRight, span.Y, span.Z + 1, ParentDirections.Z0);continue;}throw new Exception();}#endregion#region UnRezif (span.Extended == ExtendTypes.UnRez){int xl = FindXLeft(span.XLeft, span.Y,span.Z);int xr = FindXRight(span.XRight, span.Y,span.Z);if (span.ParentDirection == ParentDirections.Y2){if (span.Y - 1 >= 0)CheckRange(xl, xr, span.Y - 1, span.Z, ParentDirections.Y2);if (span.Y + 1 < bmp.height){if (xl != span.XLeft)CheckRange(xl, span.XLeft, span.Y + 1, span.Z, ParentDirections.Y0);if (span.XRight != xr)CheckRange(span.XRight, xr, span.Y + 1, span.Z, ParentDirections.Y0);}if (span.Z - 1 >= 0)CheckRange(xl, xr, span.Y, span.Z - 1, ParentDirections.Z2);if (span.Z + 1 < bmp.depth)CheckRange(xl, xr, span.Y, span.Z + 1, ParentDirections.Z0);continue;}if (span.ParentDirection == ParentDirections.Y0){if (span.Y + 1 < bmp.height)CheckRange(xl, xr, span.Y + 1, span.Z, ParentDirections.Y0);if (span.Y - 1 >= 0){if (xl != span.XLeft)CheckRange(xl, span.XLeft, span.Y - 1, span.Z, ParentDirections.Y2);if (span.XRight != xr)CheckRange(span.XRight, xr, span.Y - 1, span.Z, ParentDirections.Y2);}if (span.Z - 1 >= 0)CheckRange(xl, xr, span.Y, span.Z - 1, ParentDirections.Z2);if (span.Z + 1 < bmp.depth)CheckRange(xl, xr, span.Y, span.Z + 1, ParentDirections.Z0);continue;}if (span.ParentDirection == ParentDirections.Z2){if (span.Y - 1 >= 0)CheckRange(xl, xr, span.Y - 1, span.Z, ParentDirections.Y2);if (span.Y + 1 < bmp.height)CheckRange(xl, xr, span.Y + 1, span.Z, ParentDirections.Y0);if (span.Z - 1 >= 0)CheckRange(xl, xr, span.Y, span.Z - 1, ParentDirections.Z2);if (span.Z + 1 < bmp.depth){if (xl != span.XLeft)CheckRange(xl, span.XLeft, span.Y, span.Z+1, ParentDirections.Z0);if (span.XRight != xr)CheckRange(span.XRight, xr, span.Y, span.Z+1, ParentDirections.Z0);}continue;}if (span.ParentDirection == ParentDirections.Z0){if (span.Y - 1 >= 0)CheckRange(xl, xr, span.Y - 1, span.Z, ParentDirections.Y2);if (span.Y + 1 < bmp.height)CheckRange(xl, xr, span.Y + 1, span.Z, ParentDirections.Y0);if (span.Z - 1 >= 0){if (xl != span.XLeft)CheckRange(xl, span.XLeft, span.Y, span.Z -1, ParentDirections.Z2);if (span.XRight != xr)CheckRange(span.XRight, xr, span.Y, span.Z - 1, ParentDirections.Z2);}if (span.Z + 1 < bmp.depth)CheckRange(xl, xr, span.Y, span.Z + 1, ParentDirections.Z0);continue;}if (span.ParentDirection == ParentDirections.Non){if (span.Y + 1 < bmp.height)CheckRange(xl, xr, span.Y + 1, span.Z, ParentDirections.Y0);if (span.Y - 1 >= 0)CheckRange(xl, xr, span.Y - 1, span.Z, ParentDirections.Y2);if (span.Z - 1 >= 0)CheckRange(xl, xr, span.Y, span.Z - 1, ParentDirections.Z2);if (span.Z + 1 < bmp.depth)CheckRange(xl, xr, span.Y, span.Z + 1, ParentDirections.Z0);continue;}throw new Exception();}#endregion#region LeftRequiredif (span.Extended == ExtendTypes.LeftRequired){int xl = FindXLeft(span.XLeft, span.Y,span.Z);if (span.ParentDirection == ParentDirections.Y2){if (span.Y - 1 >= 0)CheckRange(xl, span.XRight, span.Y - 1, span.Z, ParentDirections.Y2);if (span.Y + 1 < bmp.height && xl != span.XLeft)CheckRange(xl, span.XLeft, span.Y + 1, span.Z, ParentDirections.Y0);if (span.Z - 1 >= 0)CheckRange(xl, span.XRight, span.Y , span.Z-1, ParentDirections.Z2);if (span.Z + 1 < bmp.depth)CheckRange(xl, span.XRight, span.Y , span.Z+1, ParentDirections.Z0);continue;}if (span.ParentDirection == ParentDirections.Y0){if (span.Y - 1 >= 0 && xl != span.XLeft)CheckRange(xl, span.XLeft, span.Y - 1, span.Z, ParentDirections.Y2);if (span.Y + 1 < bmp.height)CheckRange(xl, span.XRight, span.Y + 1, span.Z, ParentDirections.Y0);if (span.Z - 1 >= 0)CheckRange(xl, span.XRight, span.Y, span.Z - 1, ParentDirections.Z2);if (span.Z + 1 < bmp.depth)CheckRange(xl, span.XRight, span.Y, span.Z + 1, ParentDirections.Z0);continue;}if (span.ParentDirection == ParentDirections.Z2){if (span.Y - 1 >= 0 )CheckRange(xl, span.XRight, span.Y - 1, span.Z, ParentDirections.Y2); if (span.Y + 1 < bmp.height)CheckRange(xl, span.XRight, span.Y + 1, span.Z, ParentDirections.Y0);if (span.Z - 1 >= 0)CheckRange(xl, span.XRight, span.Y, span.Z - 1, ParentDirections.Z2);if (span.Z + 1 < bmp.depth && xl != span.XLeft)CheckRange(xl, span.XLeft, span.Y, span.Z + 1, ParentDirections.Z0);continue;}if (span.ParentDirection == ParentDirections.Z0){if (span.Y - 1 >= 0)CheckRange(xl, span.XRight, span.Y - 1, span.Z, ParentDirections.Y2);if (span.Y + 1 < bmp.height)CheckRange(xl, span.XRight, span.Y + 1, span.Z, ParentDirections.Y0);if (span.Z - 1 >= 0 && xl != span.XLeft)CheckRange(xl, span.XLeft, span.Y, span.Z - 1, ParentDirections.Z2);if (span.Z + 1 < bmp.depth )CheckRange(xl, span.XRight, span.Y, span.Z + 1, ParentDirections.Z0);continue;}throw new Exception();}#endregion#region RightRequiredif (span.Extended == ExtendTypes.RightRequired){int xr = FindXRight(span.XRight, span.Y,span.Z);if (span.ParentDirection == ParentDirections.Y2){if (span.Y - 1 >= 0)CheckRange(span.XLeft, xr, span.Y - 1,span.Z, ParentDirections.Y2);if (span.Y + 1 < bmp.height && span.XRight != xr)CheckRange(span.XRight, xr, span.Y + 1,span.Z, ParentDirections.Y0);if (span.Z - 1 >= 0)CheckRange(span.XLeft, xr, span.Y, span.Z-1, ParentDirections.Z2);if (span.Z + 1 < bmp.depth)CheckRange(span.XLeft, xr, span.Y, span.Z+1, ParentDirections.Z0); continue;}if (span.ParentDirection == ParentDirections.Y0){if (span.Y + 1 < bmp.height)CheckRange(span.XLeft, xr, span.Y + 1, span.Z, ParentDirections.Y0);if (span.Y - 1 >= 0 && span.XRight != xr)CheckRange(span.XRight, xr, span.Y - 1, span.Z, ParentDirections.Y2);if (span.Z - 1 >= 0)CheckRange(span.XLeft, xr, span.Y, span.Z - 1, ParentDirections.Z2);if (span.Z + 1 < bmp.depth)CheckRange(span.XLeft, xr, span.Y, span.Z + 1, ParentDirections.Z0); continue;}if (span.ParentDirection == ParentDirections.Z2){if (span.Y - 1 >= 0)CheckRange(span.XLeft, xr, span.Y - 1, span.Z, ParentDirections.Y2);if (span.Y + 1 < bmp.height)CheckRange(span.XLeft, xr, span.Y + 1, span.Z, ParentDirections.Y0);if (span.Z - 1 >= 0)CheckRange(span.XLeft, xr, span.Y, span.Z - 1, ParentDirections.Z2);if (span.Z + 1 < bmp.depth && span.XRight != xr)CheckRange(span.XRight, xr, span.Y, span.Z + 1, ParentDirections.Z0);continue;}if (span.ParentDirection == ParentDirections.Z0){if (span.Y - 1 >= 0)CheckRange(span.XLeft, xr, span.Y - 1, span.Z, ParentDirections.Y2);if (span.Y + 1 < bmp.height)CheckRange(span.XLeft, xr, span.Y + 1, span.Z, ParentDirections.Y0);if (span.Z - 1 >= 0 && span.XRight != xr)CheckRange(span.XRight, xr, span.Y, span.Z - 1, ParentDirections.Z2);if (span.Z + 1 < bmp.depth)CheckRange(span.XLeft, xr, span.Y, span.Z + 1, ParentDirections.Z0); continue;}throw new Exception();}#endregion}}protected void CheckRange(int xleft, int xright, int y, int z,ParentDirections ptype){for (int i = xleft; i <= xright; ){if ((!flagsMap.GetFlagOn(i, y,z)) && IncludePredicate(i, y,z)){int lb = i;int rb = i + 1;while (rb <= xright && (!flagsMap.GetFlagOn(rb, y,z)) && IncludePredicate(rb, y,z)){rb++;}rb--;Span span = new Span();span.XLeft = lb;span.XRight = rb;span.Y = y;span.Z = z;if (lb == xleft && rb == xright){span.Extended = ExtendTypes.UnRez;}else if (rb == xright){span.Extended = ExtendTypes.RightRequired;}else if (lb == xleft){span.Extended = ExtendTypes.LeftRequired;}else{span.Extended = ExtendTypes.AllRez;}span.ParentDirection = ptype;for (int j = lb; j <= rb; j++){flagsMap.SetFlagOn(j, y,z, true);Process(new Int16Triple(j, y,z));}container.Push(span);i = rb + 1;}else{i++;}}}//區(qū)段法的CheckRange 注意與掃描線的CheckRange的不同protected int FindXRight(int x, int y,int z){int xright = x + 1;while (true){if (xright == bmp.width || flagsMap.GetFlagOn(xright, y,z)){break;}else{if (IncludePredicate(xright, y,z)){Int16Triple t = new Int16Triple(xright, y,z);flagsMap.SetFlagOn(xright, y,z ,true);Process(t);xright++;}else{break;}}}return xright - 1;}protected int FindXLeft(int x, int y,int z){int xleft = x - 1;while (true){if (xleft == -1 || flagsMap.GetFlagOn(xleft, y, z)){break;}else{if (IncludePredicate(xleft, y,z)){Int16Triple t = new Int16Triple(xleft, y,z);flagsMap.SetFlagOn(xleft, y,z, true);Process(t);xleft--;}else{break;}}}return xleft + 1;}public byte min;public byte max;protected bool IncludePredicate(int x, int y, int z){byte v = bmp.GetPixel(x, y, z);return v > min && v < max;}protected void Process(Int16Triple p){count++;}}?四、算法測試與實驗
算法測試采用來自http://www.volvis.org的5組體數(shù)據(jù),注意這里的閾值范圍是值一個體素值的范圍(min,max),在此范圍內(nèi)的體素才納入?yún)^(qū)域。也就是說本文的includePredicate不再是跟上一篇一樣是判斷像素灰度為255的納入?yún)^(qū)域,而是判斷是否在這個(min,max)的范圍內(nèi)。
| 圖像預(yù)覽 | 數(shù)據(jù)名稱 | 參數(shù)說明 |
| Lobster |
| |
| engine |
| |
| backpack |
| |
| phantom |
| |
| cube | ?
|
測試結(jié)果如下:
| 測試數(shù)據(jù) | 泛洪法(棧式) | 泛洪法(隊列式) | 掃描線法(棧式) | 掃描線法(隊列式) | 區(qū)段法(棧式) | 區(qū)段法(隊列式) | 區(qū)域點數(shù) |
| lobster | 86ms | 76ms | 56ms | 64ms | 40ms | 47ms | 277367 |
| engine | 329ms | 345ms | 216ms | 254ms | 137ms | 183ms | 1154807 |
| backpack | 596ms | 638ms | 426ms | 470ms | 319ms | 351ms | 19115450 |
| phantom | 13468ms | 14118ms | 6520ms | 8279ms | 3701ms | 4066ms | 39759257 |
| cube | 2120ms | 2259ms | 1292ms | 1404ms | 691ms | 692ms | 800000 |
對于其中的backpack數(shù)據(jù)的單元訪問比例統(tǒng)計如下:
| 算法 | GetPixel/總點數(shù) | GetFlagOn/總點數(shù) | SetFlagOn/總點數(shù) | Push和Pop/總點數(shù) | 總點數(shù) | 時間花費 |
| 泛洪法(棧式) | 1.945581 | 5.997103 | 1.0 | 1.0 | 1915450 | 596ms |
| 泛洪法(隊列式) | 1.945581 | 5.997103 | 1.0 | 1.0 | 1915450 | 638ms |
| 掃描線法(棧式) | 4.165088 | 5.337406 | 1.0 | 0.2668 | 1915450 | 426ms |
| 掃描線法(隊列式) | 2.715424 | 5.774523 | 1.0 | 0.7589 | 1915450 | 470ms |
| 區(qū)段法(棧式) | 1.911485 | 3.526728 | 1.0 | 0.2147 | 1915450 | 319ms |
| 區(qū)段法(隊列式) | 1.931963 | 3.754333 | 1.0 | 0.3628 | 1915450 | 351ms |
通過測試可以看出,相比于二維種子點生長,由于多了一維,所以利用X軸數(shù)據(jù)連續(xù)特性所能達到增加效率的效果明顯減弱。既可以從時間結(jié)果上看,也可以從單元訪問比例上看,都能得到這一結(jié)論。不過從整體上看,效率上仍然能夠得出如下結(jié)論:棧式算法略塊于隊列式算法;區(qū)段法快于掃描線法,掃描線法快于泛洪法。
總結(jié)
以上是生活随笔為你收集整理的种子点生长算法下——三维种子点生长的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: C++中静态成员数据初始化问题
- 下一篇: linux安装VScode