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 }
整体代码:在我的文件中: