1. 直方圖雙峰法(mode 法)
Prewitt 等人於六十年代中期提出的直方圖雙峰法(也稱 mode 法) 是典型的全局單閾值分割方法。該方法的基本思想是:假設圖像中有明顯的目標和背景,則其灰度直方圖呈雙峰分布,當灰度級直方圖具有雙峰特性時,選取兩峰之間的谷對應的灰度級作為閾值。如果背景的灰度值在整個圖像中可以合理地看作為恆定,而且所有物體與背景都具有幾乎相同的對比度,那么,選擇一個正確的、固定的全局閾值會有較好的效果。例如圖4.1所示:
圖4.1原始灰度圖像
圖4.2灰度直方圖
選定閾值M為100
算法實現:找到第一個峰值和第二個峰值, 再找到第一和第二個峰值之間的谷值,谷值就是那個閥值了。
2. 固定閾值分割
就是設定一個固定的值, 像素灰度大於就該像素編程0或者255或者其他的,小於的又等於什么的。
1 for (int i = 0; i < nWidth; ++i) 2 { 3 for (int j = 0; j < nHigh; ++j) 4 { 5 if (Image[i][j] >= 閾值) 6 { 7 Image[i][j] = 255; 8 } 9 else 10 { 11 Image[i][j] = 0; 12 } 13 } 14 }
這個閾值選什么值呢, 1中的雙峰法就是一個閾值產生的方法。
3. 半閾值分割
1 for (j = 0; j < height; j++) 2 { 3 for (i = 0; i < wide; i++) 4 { 5 lpSrc = p_data + wide*j + i; 6 lpDst = temp + wide*j + i; 7 8 if ((*lpSrc - 閾值) < 30) 9 *lpDst = *lpSrc; 10 else 11 *lpDst = 255; 12 } 13 }
不知道為什么這么做, 為什么這樣就叫做半閾值?
4. 迭代閾值圖像分割
http://topic.csdn.net/u/20080402/10/d3cb6789-fa60-4758-b232-7a89926f07b9.html
迭代法是基於逼近的思想,其步驟如下:
1. 求出圖象的最大灰度值和最小灰度值,分別記為ZMAX和ZMIN,令初始閾值T0=(ZMAX+ZMIN)/2;
2. 根據閾值TK將圖象分割為前景和背景,分別求出兩者的平均灰度值ZO和ZB
3. 求出新閾值TK+1=(ZO+ZB)/2;
4. 若TK==TK+1,則所得即為閾值;否則轉2,迭代計算。
我想問下,ZO和ZB怎么求??
1. 統計圖像灰度直方圖
2. 找到最大灰度值ZMAX和最小灰度值ZMIN,並計算T0 =(ZMAX+ZMIN)/2
3. 計算小於T0的所有灰度的均值ZO和大於T0的所有灰度的均值ZB(用直方圖求就可以)。
例如,你的直方圖從10到250有值,則T0 = 260/2 = 130.
1 ZO = Sum(nHist[i] * i) / Sum(nHist[i]); 10 <= i <= 130 2 BO = Sum(nHist[i] * i) / Sum(nHist[i]); 131 <= i <= 250
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////
1 ZO = .0, ZB = .0; 2 int nB = 0, nO = 0; 3 BYTE bytVal = 0; 4 5 while (還有圖像數據沒讀完) 6 { 7 bytVal = ReadNextPixel(); 8 if (bytVal > T0) 9 { 10 ZB += bytVal; 11 ++nB; 12 } 13 else 14 { 15 ZO += bytVal; 16 ++nO; 17 } 18 } 19 ZO /= nO; 20 ZB /= nB;
//////////////////////////////////////////////////////////////////////////////////////////////////////////
偽代碼1
A. 找到灰度圖中最大灰度nZmax和最小灰度nZmin(代碼略)
B. 求T0。
1 T0 = (nZmax + nZmin) / 2;
C. 迭代了求出閾值
1 int i; 2 while (true) 3 { 4 // 計算下一個迭代閥值 5 for (i = 0; i < T0 + 1; i++) 6 { 7 Temp0 += tongji[i] * i; 8 Temp1 += tongji[i]; 9 } 10 for (i = T0 + 1; i < 256; i++) 11 { 12 Temp2 += tongji[i] * i; 13 Temp3 += tongji[i]; 14 } 15 // (大於T0的灰度均值 + 小於T0的灰度均值) / 2 16 T2 = (Temp0 / Temp1 + Temp2 / Temp3) / 2; 17 // 看迭代結果是否已收斂 18 if (T0 == T2) 19 break; 20 else 21 T0 = T2; 22 }
D. 根據上一步求到的T2閾值進行圖像分割
1 // 對各像素進行灰度轉換 2 // 對各像素進行灰度轉換 3 for (j = 0; j < height; j ++) 4 { 5 for (i = 0; i < wide; i ++) 6 { 7 // 讀取像素 8 unsigned char temp = *((unsigned char *)p_data + wide * j + i); 9 // 判斷像素灰度值是否超出范圍 10 if (temp < T0) 11 temp = 0; 12 else 13 temp = 255; 14 // 回寫處理完的像素 15 *((unsigned char *)p_data + wide * j + i) = temp; 16 } 17 }
//////////////////////////////////////////////////////////////////////////////////////////////////////////
偽代碼2
C. 找到灰度圖中最大灰度iMaxGrayValue和最小灰度iMinGrayValue (代碼略)
D.求iNewThreshold。
iNewThreshold = (iMaxGrayValue + iMinGrayValue) / 2;
C. 迭代了求出閾值
1 //迭代求最佳閾值 2 iNewThreshold = (iMinGrayValue + iMaxGrayValue)/2; 3 iThreshold = 0; 4 for(iIterationTimes = 0; iThreshold != iNewThreshold && iIterationTimes < 100;iIterationTimes ++) 5 { 6 iThreshold = iNewThreshold; 7 lP1 =0; 8 lP2 =0; 9 lS1 = 0; 10 lS2 = 0; 11 //求兩個區域的灰度平均值 12 for (i = iMinGrayValue;i < iThreshold;i++) 13 { 14 lP1 += lHistogram[i]*i; 15 lS1 += lHistogram[i]; 16 } 17 iMean1GrayValue = (unsigned char)(lP1 / lS1); 18 for (i = iThreshold+1;i < iMaxGrayValue;i++) 19 { 20 lP2 += lHistogram[i]*i; 21 lS2 += lHistogram[i]; 22 } 23 iMean2GrayValue = (unsigned char)(lP2 / lS2); 24 iNewThreshold = (iMean1GrayValue + iMean2GrayValue)/2; 25 }
// 這里限制的迭代次數不大於100,考慮到效率吧。
D. 根據上一步求到的iNewThreshold閾值進行圖像分割
1 //根據閾值將圖像二值化 2 for (i = 0;i < lHeight ;i++) 3 { 4 for(j = 0;j < lWidth ;j++) 5 { 6 // 指向源圖像倒數第j行,第i個象素的指針 7 lpSrc = (char *)lpDIBBits + lLineBytes *i + j; 8 9 // 指向目標圖像倒數第j行,第i個象素的指針 10 lpDst = (char *)lpNewDIBBits + lLineBytes *i + j; 11 12 pixel = (unsigned char)*lpSrc; 13 14 if(pixel <= iThreshold) 15 { 16 *lpDst = (unsigned char)0; 17 } 18 else 19 { 20 *lpDst = (unsigned char)255; 21 } 22 } 23 }
5. 自適應閾值圖像分割
在許多情況下,物體和背景的對比度在圖象中不是各處一樣的,這時很難用統一的一個閾值將物體與背景分開。這時可以根據圖象的局部特征分別采用不同的閾值進行分割。實際處理時,需要按照具體問題將圖象分成若干子區域分別選擇閾值,或者動態地根據一定的鄰域范圍選擇每點處的閾值,進行圖象分割。
1). 大津法(OTSU)
最大類間方差法是由日本學者大津於1979年提出的,是一種自適應的閾值確定的方法,又叫大津
法,簡稱OTSU。它是按圖像的灰度特性,將圖像分成背景和目標2部分。背景和目標之間的類間方差
越大,說明構成圖像的2部分的差別越大,當部分目標錯分為背景或部分背景錯分為目標都會導致2部
分差別變小。因此,使類間方差最大的分割意味着錯分概率最小。
對於圖像I(x,y),前景(即目標)和背景的分割閾值記作T, 屬於前景的像素點數占整幅圖像的比例記為ω0,其平均灰度μ0;背景像素點數占整幅圖像的比例為ω1,其平均灰度為μ1。圖像的總平均灰度記為μ,類間方差記為g。
假設圖像的背景較暗,並且圖像的大小為M×N,
圖像中像素的灰度值小於閾值T的像素個數記作N0,像素灰度大於閾值T的像素個數記作N1,則有:
1 ω0 = N0/ M×N (1) 2 ω1 = N1/ M×N (2) 3 N0 + N1 = M×N (3) 4 ω0 + ω1 = 1 (4) 5 μ= ω0 * μ0 + ω1 * μ1 (5) 6 g = ω0 (μ0 -μ) ^ 2 + ω1 (μ1 - μ)^2 (6)
將式(5)代入式(6),得到等價公式:
1 g = ω0 ω1 (μ0 - μ1) ^ 2 (7)
采用遍歷的方法得到使類間方差最大的閾值T,即為所求。
Otus算法使用的是聚類的思想,即把圖像的灰度數按灰度級分成2個部分,使2個部分的之間的灰度值差異最大,每個部分之內的灰度差異最小的,找到這樣的一個灰度級t划分。通過方差的計算實現,即方差最小的值對應的t即是理想的划分。
http://hi.baidu.com/cwynamespace/blog/item/896ed529955c61f998250a47.html
偽代碼1)
1 FLOAT result; 2 3 int cnt0; 4 int cnt1; 5 FLOAT max=0.0; 6 for (thre = 1; thre < 255; thre++) 7 { 8 cnt0=0; 9 cnt1=0; 10 pixeltotalC0=0.0; 11 pixeltotalC1=0.0; 12 // 計算背景與目標的像素數各是多少 13 // 計算背景與目標的像素值總和各是多少 14 for (i=0; i<lHeight; i++) 15 { 16 for (j=0; j<lWidth; j++) 17 { 18 if (ImageSrc[i][j] <= thre) 19 { 20 cnt0++; 21 pixeltotalC0 += ImageSrc[i][j]; 22 } 23 else 24 { 25 cnt1++; 26 pixeltotalC1 += ImageSrc[i][j]; 27 } 28 } 29 } 30 cnt0=cnt0; 31 cnt1=cnt1; 32 33 rateC0 = 1.0 * cnt0 / (lHeight * lWidth); // 計算背景的面積比例 34 rateC1 = 1 - rateC0; // 計算目標的面積比例 35 36 // 計算背景平均灰度 37 if (cnt0 != 0) 38 { 39 pixelaverC0 = pixeltotalC0 / cnt0; 40 } 41 else 42 { 43 pixelaverC0 = 0; 44 } 45 46 // 計算目標平均灰度 47 if (cnt1 !=0) 48 { 49 pixelaverC1 = pixeltotalC1 / cnt1; 50 } 51 else 52 { 53 pixelaverC1 = 0; 54 } 55 56 // 計算類間方差 57 result = rateC0 * rateC1 * (pixelaverC0 - pixelaverC1) * (pixelaverC0 - pixelaverC1); 58 59 // 找到最大的類間方差, 就找到最佳的閾值了 60 if(result > max) 61 { 62 max = result; 63 threbest = thre; 64 } 65 } 66 67 // 進行二值化 68 for (i=0; i<lHeight; i++) 69 { 70 for (j=0; j<lWidth; j++) 71 { 72 if (ImageSrc[i][j] >= threbest) 73 { 74 ImageDst[i][j] = (unsigned char)255; 75 } 76 else 77 { 78 ImageDst[i][j] = (unsigned char)0; 79 } 80 } 81 }
明顯這段代碼的效率會低一點,它是怎對每一個灰度值在圖像中的所有點進行計算。
看下面代碼,效率會高一點。
偽代碼2)
http://fcwhx007.bokewu.com/blog173376.htm
1 /* 2 OTSU 算法可以說是自適應計算單閾值(用來轉換灰度圖像為二值圖像)的簡單高效方法。下面的代碼最早由 Ryan Dibble提供,此后經過多人Joerg.Schulenburg, R.Z.Liu 等修改,補正。 3 算法對輸入的灰度圖像的直方圖進行分析,將直方圖分成兩個部分,使得兩部分之間的距離最大。划分點就是求得的閾值。 4 parameter: *image --- buffer for image 5 rows, cols --- size of image 6 x0, y0, dx, dy --- region of vector used for computing threshold 7 vvv --- debug option, is 0, no debug information outputed 8 */ 9 /*======================================================================*/ 10 /* OTSU global thresholding routine */ 11 /* takes a 2D unsigned char array pointer, number of rows, and */ 12 /* number of cols in the array. returns the value of the threshold */ 13 /*======================================================================*/ 14 // 這段代碼可以針對圖像的區域 15 int otsu (unsigned char *image, int rows, int cols, int x0, int y0, int dx, int dy) 16 { 17 unsigned char *np; // 圖像指針 18 int thresholdValue=1; // 閾值 19 int ihist[256]; // 圖像直方圖,個點 20 21 int i, j, k; // various counters 22 int n, n1, n2, gmin, gmax; 23 double m1, m2, sum, csum, fmax, sb; 24 25 // 對直方圖置零... 26 memset(ihist, 0, sizeof(ihist)); 27 28 gmin=255; gmax=0; 29 // 生成直方圖 30 // 求出最大像素值和最小像素值 31 // 求出圖像中各個灰度值的個數存於數組ihist中 32 for (i = y0 + 1; i < y0 + dy - 1; i++) 33 { 34 np = &image[i*cols+x0+1]; 35 for (j = x0 + 1; j < x0 + dx - 1; j++) 36 { 37 ihist[*np]++; 38 if(*np > gmax) gmax=*np; 39 if(*np < gmin) gmin=*np; 40 np++; /* next pixel */ 41 } 42 } 43 44 // set up everything 45 sum = csum = 0.0; 46 n = 0; 47 48 // 不知道這個有什么用? 49 for (k = 0; k <= 255; k++) 50 { 51 // 圖像的總灰度值 52 sum += (double) k * (double) ihist[k]; /* x*f(x) 質量矩*/ 53 // 總像素點數? 不就是等於寬*高嗎 54 n += ihist[k]; /* f(x) 質量*/ 55 } 56 57 if (!n) 58 { 59 // if n has no value, there is problems... 60 fprintf (stderr, "NOT NORMAL thresholdValue = 160/n"; 61 return (160); 62 } 63 64 // do the otsu global thresholding method 65 fmax = -1.0; 66 n1 = 0; 67 for (k = 0; k < 255; k++) 68 { 69 n1 += ihist[k]; 70 if (!n1) 71 { 72 continue; 73 } 74 n2 = n - n1; 75 if (n2 == 0) 76 { 77 break; 78 } 79 csum += (double) k *ihist[k]; 80 m1 = csum / n1; 81 m2 = (sum - csum) / n2; 82 sb = (double) n1 *(double) n2 *(m1 - m2) * (m1 - m2); 83 /* bbg: note: can be optimized. */ 84 if (sb > fmax) 85 { 86 fmax = sb; 87 thresholdValue = k; 88 } 89 } 90 // at this point we have our thresholding value 91 return(thresholdValue); 92 }
http://hi.baidu.com/flyingmooding/blog/item/a434e134e3139bd7a2cc2b63.html
2). 均值法
思想很簡單,就是把圖像分成m*n塊子圖,求取每一塊子圖的灰度均值(就是所有像素灰度值之和除以像素點的數量),這個均值就是閾值了。
這種方法明顯不比大津法好,因為均值法和大津法都是從圖像整體來考慮閾值的,但是大津法找了一個類間方差最大值來求出最佳閾值的;這兩種方法子圖越多應該分割效果會好一點,但效率可能會變慢。
6. 最佳閾值
閾值的選擇需要根據具體問題來確定,一般通過實驗來確定。對於給定的圖象,可以通過分析直方圖的方法確定最佳的閾值,例如當直方圖明顯呈現雙峰情況時,可以選擇兩個峰值的中點作為最佳閾值。
所謂最佳閾值就是根據一定的方法(例如雙峰法),找出圖像中目標與背景的分割最佳閾值就是了。方法多種多樣,對不同的圖片可以有不同的方法(因為不同的圖片有不同的特點)。方法是多種多樣的,答案是豐富多彩的。
轉自:http://blog.csdn.net/bagboy_taobao_com/article/details/5645425
附:OpenCv中實現了三種跟圖像分割相關的算法(http://www.cnblogs.com/xrwang/archive/2010/02/28/ImageSegmentation.html)