區域生長算法
2014年9月19日 17:01:44
大道理一擺:
(以下說明轉載,感覺寫的很好)
歷史:區域生長是一種古老的圖像分割方法,最早的區域生長圖像分割方法是由Levine等人提出的。該方法一般有兩種方式,一種是先給定圖像中要分割的目標物體內的一個小塊或者說種子區域(seed point),再在種子區域基礎上不斷將其周圍的像素點以一定的規則加入其中,達到最終將代表該物體的所有像素點結合成一個區域的目的;另一種是先將圖像分割成很多的一致性較強,如區域內像素灰度值相同的小區域,再按一定的規則將小區域融合成大區域,達到分割圖像的目的,典型的區域生長法如T. C. Pong等人提出的基於小面(facet)模型的區域生長法,區域生長法固有的缺點是往往會造成過度分割,即將圖像分割成過多的區域 。
區域生長是一種串行區域分割的圖像分割方法,其優點是基本思想相對簡單,通常能將具有相同特征的聯通區域分割出來,並能提供很好的邊界信息和分割結果。在沒有先驗知識可以利用時,可以取得最佳的性能,可以用來分割比較復雜的圖象,如自然景物。但是,區域生長法是一種迭代的方法,空間和時間開銷都比較大,噪聲和灰度不均一可能會導致空洞和過分割,並在對圖像中的陰影效果處理上往往不是很好。
區域生長的基本思想是將具有相似性質的像素集合起來構成區域。具體先對每個需要分割的區域找一個種子像素作為生長的起點,然后將種子像素周圍鄰域中與種子像素具有相同或相似性質的像素(根據某種事先確定的生長或相似准則來判定)合並到種子像素所在的區域中。將這些新像素當做新的種子像素繼續進行上面的過程,直到再沒有滿足條件的像素可被包括進來,這樣,一個區域就長成了。
區域生長是指從某個像素出發,按照一定的准則,逐步加入鄰近像素,當滿足一定的條件時,區域生長終止。區域生長的好壞決定於1.初始點(種子點)的選取。2.生長准則。3.終止條件。區域生長是從某個或者某些像素點出發,最后得到整個區域,進而實現目標的提取。
簡單來說下三個法則,對出需要分割的圖像:1、選取圖像中的一點為種子點(種子點的選取需要具體情況具體分析)。2、在種子點處進行8鄰域或4鄰域擴展,判定准則是:如果考慮的像素與種子像素灰度值差的絕對值小於某個門限T,則將該像素包括進種子像素所在的區域。3、當不再有像素滿足加入這個區域的准則時,區域生長停止。
區域生長實現的步驟如下:
1. 對圖像順序掃描!找到第1個還沒有歸屬的像素, 設該像素為(x0, y0);
2. 以(x0, y0)為中心, 考慮(x0, y0)的8鄰域像素(x, y),如果(x,, y)滿足生長准則, 將(x, y)與(x0, y0)合並(在同一區域內), 同時將(x, y)壓入堆棧;
3. 從堆棧中取出一個像素, 把它當作(x0, y0)返回到步驟2;
4. 當堆棧為空時!返回到步驟1;
5. 重復步驟1 - 4直到圖像中的每個點都有歸屬時。生長結束。
進一步解釋:注意“沒有歸屬”四個字,從種子點出發,在其8領域內查找滿足生長准則的點歸並到種子點所在區域內,在程序中就是通過 push 到 stack 堆棧中來實現,每一個被遍歷的元素都會被標記,凡是被標記的元素在下次檢測的時候都不會被考慮,因為它已經有了“歸屬”,區域生長的目的也就是將“歸屬”於種子點區域的部分分割出來而已。隨着迭代次數的增加,每次在 top 一個元素並 pop 后加入到 stack 中的元素慢慢變少,直到停止生長為止!
關於區域生長的代碼如下:
(一)遍歷圖像尋找種子點
1 public void AreaGrow() 2 { 3 GetBinaryzation2(200);//二值化 4 areaNum = 0; 5 6 for (int i = 0; i < height; i++) 7 { 8 for (int j = 0; j < width; j++) 9 { 10 if (grayValue[i, j] != 0)//只要區域點的灰度不是0就不能成為種子點 -- 需要修改 11 { 12 continue; 13 } 14 CAllAreaInfo call = new CAllAreaInfo(); 15 AreaGrowSmall(i, j, ref call, true);//fasle表示這個區域不符合要求 16 if ((call.PixelNumber >= circleMin && call.PixelNumber <= circleMax) || call.PixelNumber >= hookMin) 17 { 18 //保存信息 19 _call.Add(call); 20 } 21 else 22 { 23 AreaGrowSmall(i, j, ref call, false); 24 areaNum -= 2; 25 26 } 27 } 28 } 29 }
(二)根據種子點進行區域生長(利於單個區域計算)
1 public void AreaGrowSmall(int seedx, int seedy, ref CAllAreaInfo tempCall, bool isArea) 2 { 3 //8領域 x--水平 y--垂直 4 int[] dx = new int[] { -1, 0, 1, -1, 1, -1, 0, 1 }; 5 int[] dy = new int[] { -1, -1, -1, 0, 0, 1, 1, 1 }; 6 7 //4鄰域 8 //int[] dx = new int[] { -1, 0, 0, 1 }; 9 //int[] dy = new int[] { 0, -1, 1, 0 }; 10 11 //閾值 與種子灰度值相差為3 12 int standardValue = 3;//需要修改 13 14 //堆棧 15 Stack<int> SeedX = new Stack<int>(); 16 Stack<int> SeedY = new Stack<int>(); 17 18 //當前正在處理的像素點 19 int currentPixel_x, currentPixel_y; 20 21 //標記領域的循環變量 22 int k = 0; 23 24 //int areaNum = 0;//區域標號 25 26 //----求重心 27 int seedxSum = 0; 28 int seedySum = 0; 29 30 //-------- 31 int max_x = 0, min_x = 0; 32 int max_y = 0, min_y = 0; 33 34 //處理鄰域 35 int xx, yy; 36 37 //標記種子 38 grayValue[seedx, seedy] = 1; 39 40 areaNum++;//區域標號 41 42 //種子進棧 43 SeedX.Push(seedx); 44 SeedY.Push(seedy); 45 //---- 46 //計算最大矩形 47 max_x = min_x = seedx; 48 max_y = min_y = seedy; 49 //---- 50 51 //加上種子坐標 52 seedxSum = seedx; 53 seedySum = seedy; 54 55 //SeedPoint.Add(tempPoint); 56 int temp = 1; 57 58 while (SeedX.Count > 0 || SeedY.Count > 0)//兩個棧均為空時 退出 59 { 60 currentPixel_x = SeedX.Pop(); 61 currentPixel_y = SeedY.Pop(); 62 63 for (k = 0; k < 8; k++) 64 //for (k = 0; k < 4; k++) 65 { 66 67 xx = currentPixel_x + dx[k]; 68 yy = currentPixel_y + dy[k]; 69 70 //判斷點是否在圖像區域內 71 if ((xx < height && xx >= 0) && (yy < width && yy >= 0)) 72 { 73 //判斷是否檢查過了 74 if (grayValue[xx, yy] != 1&&isArea)//沒檢查 75 { 76 //要生長的區域為黑色區域則grayValue[xx, yy]=0,而 grayValue[seedx, seedy]=1 77 if (Math.Abs(grayValue[xx, yy] - grayValue[seedx, seedy]) <= standardValue) 78 { 79 grayValue[xx, yy] = 1; 80 SeedX.Push(xx); 81 SeedY.Push(yy); 82 temp++; 83 //重心 84 seedxSum += xx; 85 seedySum += yy; 86 { 87 if (xx > max_x) 88 max_x = xx; 89 if (xx < min_x) 90 min_x = xx; 91 if (yy > max_y) 92 max_y = yy; 93 if (yy < min_y) 94 min_y = yy; 95 } 96 } 97 else 98 { 99 //邊緣(輪廓) 100 grayValue[xx, yy] = 255; 101 } 102 } 103 if (grayValue[xx, yy]==1 && isArea == false) 104 { 105 if (Math.Abs(grayValue[xx, yy] - grayValue[seedx, seedy]) <= standardValue) 106 { 107 grayValue[xx, yy] = 255; 108 SeedX.Push(xx); 109 SeedY.Push(yy); 110 temp++; 111 //重心 112 seedxSum += xx; 113 seedySum += yy; 114 115 { 116 if (xx > max_x) 117 max_x = xx; 118 if (xx < min_x) 119 min_x = xx; 120 if (yy > max_y) 121 max_y = yy; 122 if (yy < min_y) 123 min_y = yy; 124 } 125 } 126 } 127 } 128 } 129 }//while 130 if (isArea == false) 131 { 132 grayValue[seedx, seedy] = 255; 133 } 134 //保存信息 135 tempCall.AreaNum = areaNum; 136 Point p = new Point(seedy, seedx); 137 tempCall.SeedPoint = p; 138 tempCall.PixelNumber = temp; 139 //重心 140 Point cp = new Point(seedySum / temp, seedxSum / temp); 141 tempCall.CenterPoint = cp; 142 }
(三)二值化計算
1 public void GetBinaryzation2(int StandValue) 2 { 3 unsafe 4 { 5 int _width = workBitmap.Width; 6 int _height = workBitmap.Height;//hang 7 8 //灰度化 9 BitmapData data = workBitmap.LockBits(new Rectangle(0, 0, _width, _height), ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb); 10 byte* p = (byte*)data.Scan0; 11 //byte[,] grayValueArray = new byte[_height, _width]; 12 int offset = data.Stride - _width * 3; 13 {//灰度化 14 for (int i = 0; i < _height; i++) 15 { 16 for (int j = 0; j < _width; j++) 17 { 18 int tempValue = (int)(p[2] * 0.299 + p[1] * 0.587 + p[0] * 0.114); 19 grayValue[i, j] = tempValue > StandValue ? (byte)255 : (byte)0; 20 p += 3; 21 } 22 p += offset; 23 } 24 } 25 workBitmap.UnlockBits(data); 26 } 27 }
整體代碼:在我的文件中: