UnCodebase類中有一個GetPicValidByValue( int dgGrayValue) 函數,可以得到前景的有效區域,常有人問我前景/背景的分界值dgGrayValue是如何確定的(常用的是灰度128)。這個值的獲取是有數學算法,叫最大類間方差法,即圖像的前後景的平方差爲最大時的值就是我們關心的分界值,對付如 這樣較複雜的背景非常管用,下面是具體的C#代碼。
/// <summary>
/// 得到灰度圖像前景背景的臨界值 最大類間方差法,yuanbao,2007.08
/// </summary>
/// <returns>前景背景的臨界值</returns>
public int GetDgGrayValue()
{
int[] pixelNum = new int[256]; //圖象直方圖,共256個點
int n, n1, n2;
int total; //total爲總和,累計值
double m1, m2, sum, csum, fmax, sb; //sb爲類間方差,fmax存儲最大方差值
int k, t, q;
int threshValue = 1; // 閾值
int step = 1;
//生成直方圖
for (int i =0; i < bmpobj.Width ; i++)
{
for (int j = 0; j < bmpobj.Height; j++)
{
//返回各個點的顏色,以RGB表示
pixelNum[bmpobj.GetPixel(i,j).R]++; //相應的直方圖加1
}
}
//直方圖平滑化
for (k = 0; k <= 255; k++)
{
total = 0;
for (t = -2; t <= 2; t++) //與附近2個灰度做平滑化,t值應取較小的值
{
q = k + t;
if (q < 0) //越界處理
q = 0;
if (q > 255)
q = 255;
total = total + pixelNum[q]; //total爲總和,累計值
}
pixelNum[k] = (int)((float)total / 5.0 + 0.5); //平滑化,左邊2個+中間1個+右邊2個灰度,共5個,所以總和除以5,後面加0.5是用修正值
}
//求閾值
sum = csum = 0.0;
n = 0;
//計算總的圖象的點數和質量矩,爲後面的計算做準備
for (k = 0; k <= 255; k++)
{
sum += (double)k * (double)pixelNum[k]; //x*f(x)質量矩,也就是每個灰度的值乘以其點數(歸一化後爲概率),sum爲其總和
n += pixelNum[k]; //n爲圖象總的點數,歸一化後就是累積概率
}
fmax = -1.0; //類間方差sb不可能爲負,所以fmax初始值爲-1不影響計算的進行
n1 = 0;
for (k = 0; k < 256; k++) //對每個灰度(從0到255)計算一次分割後的類間方差sb
{
n1 += pixelNum[k]; //n1爲在當前閾值遍前景圖象的點數
if (n1 == 0) { continue; } //沒有分出前景後景
n2 = n - n1; //n2爲背景圖象的點數
if (n2 == 0) { break; } //n2爲0表示全部都是後景圖象,與n1=0情況類似,之後的遍歷不可能使前景點數增加,所以此時可以退出循環
csum += (double)k * pixelNum[k]; //前景的“灰度的值*其點數”的總和
m1 = csum / n1; //m1爲前景的平均灰度
m2 = (sum - csum) / n2; //m2爲背景的平均灰度
sb = (double)n1 * (double)n2 * (m1 - m2) * (m1 - m2); //sb爲類間方差
if (sb > fmax) //如果算出的類間方差大於前一次算出的類間方差
{
fmax = sb; //fmax始終爲最大類間方差(otsu)
threshValue = k; //取最大類間方差時對應的灰度的k就是最佳閾值
}
}
return threshValue;
}
/// 得到灰度圖像前景背景的臨界值 最大類間方差法,yuanbao,2007.08
/// </summary>
/// <returns>前景背景的臨界值</returns>
public int GetDgGrayValue()
{
int[] pixelNum = new int[256]; //圖象直方圖,共256個點
int n, n1, n2;
int total; //total爲總和,累計值
double m1, m2, sum, csum, fmax, sb; //sb爲類間方差,fmax存儲最大方差值
int k, t, q;
int threshValue = 1; // 閾值
int step = 1;
//生成直方圖
for (int i =0; i < bmpobj.Width ; i++)
{
for (int j = 0; j < bmpobj.Height; j++)
{
//返回各個點的顏色,以RGB表示
pixelNum[bmpobj.GetPixel(i,j).R]++; //相應的直方圖加1
}
}
//直方圖平滑化
for (k = 0; k <= 255; k++)
{
total = 0;
for (t = -2; t <= 2; t++) //與附近2個灰度做平滑化,t值應取較小的值
{
q = k + t;
if (q < 0) //越界處理
q = 0;
if (q > 255)
q = 255;
total = total + pixelNum[q]; //total爲總和,累計值
}
pixelNum[k] = (int)((float)total / 5.0 + 0.5); //平滑化,左邊2個+中間1個+右邊2個灰度,共5個,所以總和除以5,後面加0.5是用修正值
}
//求閾值
sum = csum = 0.0;
n = 0;
//計算總的圖象的點數和質量矩,爲後面的計算做準備
for (k = 0; k <= 255; k++)
{
sum += (double)k * (double)pixelNum[k]; //x*f(x)質量矩,也就是每個灰度的值乘以其點數(歸一化後爲概率),sum爲其總和
n += pixelNum[k]; //n爲圖象總的點數,歸一化後就是累積概率
}
fmax = -1.0; //類間方差sb不可能爲負,所以fmax初始值爲-1不影響計算的進行
n1 = 0;
for (k = 0; k < 256; k++) //對每個灰度(從0到255)計算一次分割後的類間方差sb
{
n1 += pixelNum[k]; //n1爲在當前閾值遍前景圖象的點數
if (n1 == 0) { continue; } //沒有分出前景後景
n2 = n - n1; //n2爲背景圖象的點數
if (n2 == 0) { break; } //n2爲0表示全部都是後景圖象,與n1=0情況類似,之後的遍歷不可能使前景點數增加,所以此時可以退出循環
csum += (double)k * pixelNum[k]; //前景的“灰度的值*其點數”的總和
m1 = csum / n1; //m1爲前景的平均灰度
m2 = (sum - csum) / n2; //m2爲背景的平均灰度
sb = (double)n1 * (double)n2 * (m1 - m2) * (m1 - m2); //sb爲類間方差
if (sb > fmax) //如果算出的類間方差大於前一次算出的類間方差
{
fmax = sb; //fmax始終爲最大類間方差(otsu)
threshValue = k; //取最大類間方差時對應的灰度的k就是最佳閾值
}
}
return threshValue;
}
現在網上的大多數的驗證碼都是加了干擾的,一般分爲干擾點和干擾線,如下圖。標用1、2、3的分別爲點、線、字符。
去幹擾,一般是逐點分析,這三種情況下,每一點及周邊8個點的情況都不一樣(分別爲1點,3點,8點),這是一種干擾信息的粒度比字符的粒度小的典型情況。現在就可以動手編寫去雜代碼了。
2.2 根據周邊有效點數去噪函數
/// <summary>
/// 去掉雜點(適合雜點/雜線粗爲1)
/// </summary>
/// <param name="dgGrayValue">背前景灰色界限</param>
/// <returns></returns>
public void ClearNoise(int dgGrayValue, int MaxNearPoints)
{
Color piexl;
int nearDots = 0;
int XSpan, YSpan, tmpX, tmpY;
//逐點判斷
for (int i = 0; i < bmpobj.Width; i++)
for (int j = 0; j < bmpobj.Height; j++)
{
piexl = bmpobj.GetPixel(i, j);
if (piexl.R < dgGrayValue)
{
nearDots = 0;
//判斷周圍8個點是否全爲空
if (i == 0 || i == bmpobj.Width - 1 || j == 0 || j == bmpobj.Height - 1) //邊框全去掉
{
bmpobj.SetPixel(i, j, Color.FromArgb(255, 255, 255));
}
else
{
if (bmpobj.GetPixel(i - 1, j - 1).R < dgGrayValue) nearDots++;
if (bmpobj.GetPixel(i, j - 1).R < dgGrayValue) nearDots++;
if (bmpobj.GetPixel(i + 1, j - 1).R < dgGrayValue) nearDots++;
if (bmpobj.GetPixel(i - 1, j).R < dgGrayValue) nearDots++;
if (bmpobj.GetPixel(i + 1, j).R < dgGrayValue) nearDots++;
if (bmpobj.GetPixel(i - 1, j + 1).R < dgGrayValue) nearDots++;
if (bmpobj.GetPixel(i, j + 1).R < dgGrayValue) nearDots++;
if (bmpobj.GetPixel(i + 1, j + 1).R < dgGrayValue) nearDots++;
}
if (nearDots < MaxNearPoints)
bmpobj.SetPixel(i, j, Color.FromArgb(255, 255, 255)); //去掉單點 && 粗細小3鄰邊點
}
else //背景
bmpobj.SetPixel(i, j, Color.FromArgb(255, 255, 255));
}
}
/// 去掉雜點(適合雜點/雜線粗爲1)
/// </summary>
/// <param name="dgGrayValue">背前景灰色界限</param>
/// <returns></returns>
public void ClearNoise(int dgGrayValue, int MaxNearPoints)
{
Color piexl;
int nearDots = 0;
int XSpan, YSpan, tmpX, tmpY;
//逐點判斷
for (int i = 0; i < bmpobj.Width; i++)
for (int j = 0; j < bmpobj.Height; j++)
{
piexl = bmpobj.GetPixel(i, j);
if (piexl.R < dgGrayValue)
{
nearDots = 0;
//判斷周圍8個點是否全爲空
if (i == 0 || i == bmpobj.Width - 1 || j == 0 || j == bmpobj.Height - 1) //邊框全去掉
{
bmpobj.SetPixel(i, j, Color.FromArgb(255, 255, 255));
}
else
{
if (bmpobj.GetPixel(i - 1, j - 1).R < dgGrayValue) nearDots++;
if (bmpobj.GetPixel(i, j - 1).R < dgGrayValue) nearDots++;
if (bmpobj.GetPixel(i + 1, j - 1).R < dgGrayValue) nearDots++;
if (bmpobj.GetPixel(i - 1, j).R < dgGrayValue) nearDots++;
if (bmpobj.GetPixel(i + 1, j).R < dgGrayValue) nearDots++;
if (bmpobj.GetPixel(i - 1, j + 1).R < dgGrayValue) nearDots++;
if (bmpobj.GetPixel(i, j + 1).R < dgGrayValue) nearDots++;
if (bmpobj.GetPixel(i + 1, j + 1).R < dgGrayValue) nearDots++;
}
if (nearDots < MaxNearPoints)
bmpobj.SetPixel(i, j, Color.FromArgb(255, 255, 255)); //去掉單點 && 粗細小3鄰邊點
}
else //背景
bmpobj.SetPixel(i, j, Color.FromArgb(255, 255, 255));
}
}
圖像預處理中有多種濾波算法,其原理與方法分別爲
1) 中值濾波
它通過從圖像中的某個採樣窗口取出奇數個數據進行排序得到的結果。顧名思義,所謂中值就是窗口中奇數個數據按大小順序排列後處於中心位置的那個數。中值濾波以窗口的中值作爲處理結果。
實現起來很簡單
1:先對窗口排序
2:用排序後的中值取代要處理的數據即可
注意事項:
1:注意圖像邊緣數據的處理
2:對於不同的目的選用不同的窗體,一般有3×3,5×5等等
/// <summary>
/// 3×3中值濾波除雜,yuanbao,2007.10
/// </summary>
/// <param name="dgGrayValue"></param>
public void ClearNoise(int dgGrayValue)
{
int x, y;
byte[] p = new byte[9]; //最小處理窗口3*3
byte s;
//byte[] lpTemp=new BYTE[nByteWidth*nHeight];
int i, j;
//--!!!!!!!!!!!!!!下面開始窗口爲3×3中值濾波!!!!!!!!!!!!!!!!
for (y = 1; y < bmpobj.Height - 1; y++) //--第一行和最後一行無法取窗口
{
for (x = 1; x < bmpobj.Width - 1; x++)
{
//取9個點的值
p[0] = bmpobj.GetPixel(x - 1, y - 1).R;
p[1] = bmpobj.GetPixel(x, y - 1).R;
p[2] = bmpobj.GetPixel(x + 1, y - 1).R;
p[3] = bmpobj.GetPixel(x - 1, y).R;
p[4] = bmpobj.GetPixel(x, y).R;
p[5] = bmpobj.GetPixel(x + 1, y).R;
p[6] = bmpobj.GetPixel(x - 1, y + 1).R;
p[7] = bmpobj.GetPixel(x, y + 1).R;
p[8] = bmpobj.GetPixel(x + 1, y + 1).R;
//計算中值
for (j = 0; j < 5; j++)
{
for (i = j + 1; i < 9; i++)
{
if (p[j] > p[i])
{
s = p[j];
p[j] = p[i];
p[i] = s;
}
}
}
// if (bmpobj.GetPixel(x, y).R < dgGrayValue)
bmpobj.SetPixel(x, y, Color.FromArgb(p[4], p[4], p[4])); //給有效值付中值
}
}
}
/// 3×3中值濾波除雜,yuanbao,2007.10
/// </summary>
/// <param name="dgGrayValue"></param>
public void ClearNoise(int dgGrayValue)
{
int x, y;
byte[] p = new byte[9]; //最小處理窗口3*3
byte s;
//byte[] lpTemp=new BYTE[nByteWidth*nHeight];
int i, j;
//--!!!!!!!!!!!!!!下面開始窗口爲3×3中值濾波!!!!!!!!!!!!!!!!
for (y = 1; y < bmpobj.Height - 1; y++) //--第一行和最後一行無法取窗口
{
for (x = 1; x < bmpobj.Width - 1; x++)
{
//取9個點的值
p[0] = bmpobj.GetPixel(x - 1, y - 1).R;
p[1] = bmpobj.GetPixel(x, y - 1).R;
p[2] = bmpobj.GetPixel(x + 1, y - 1).R;
p[3] = bmpobj.GetPixel(x - 1, y).R;
p[4] = bmpobj.GetPixel(x, y).R;
p[5] = bmpobj.GetPixel(x + 1, y).R;
p[6] = bmpobj.GetPixel(x - 1, y + 1).R;
p[7] = bmpobj.GetPixel(x, y + 1).R;
p[8] = bmpobj.GetPixel(x + 1, y + 1).R;
//計算中值
for (j = 0; j < 5; j++)
{
for (i = j + 1; i < 9; i++)
{
if (p[j] > p[i])
{
s = p[j];
p[j] = p[i];
p[i] = s;
}
}
}
// if (bmpobj.GetPixel(x, y).R < dgGrayValue)
bmpobj.SetPixel(x, y, Color.FromArgb(p[4], p[4], p[4])); //給有效值付中值
}
}
}
|
中值濾波的窗口還可以有多種形狀,上面程序選擇的是矩形(容易計算),其實窗口還可以是菱形,圓形,十字形等等,不同的窗口形狀有不同的濾波效果,對有緩慢且有較長輪廓線的物體適合用矩形或者原型窗口,對於有尖頂角物體的圖像適合採用十字形窗口。
中值濾波可以進行線性組合,不同窗口形狀的濾波器可以線性組合
改進中值濾波方法:
對一些內容複雜的圖像,可以使用複合型中值濾波。如, 中值濾波線性組合、高階中值濾波組合、加權中值濾波以及迭代中值濾波等。
中值濾波的線性組合是將幾種窗口尺寸大小和形狀不同的中值濾波器複合使用,只要各窗口都與中心對稱,濾波輸出可保持幾個方向上的邊緣跳變,而且跳變幅度可調節。
高階中值濾波組合可以使輸入圖像中任意方向的細線條保持不變。
爲了在一定的條件下儘可能去除噪聲,又有效保持圖像細節,可以對中值濾波器參數進行修正, 如加權中值濾波, 也就是對輸入窗口進行加權。
也可以是對中值濾波器的使用方法進行變化, 保證濾波的效果, 還可以和其他濾波器聯合使用。
2).均值濾波(模糊算法)
均值濾波是典型的線性濾波算法,它是指在圖像上對待處理的像素給一個模板,該模板包括了其周圍的臨近像素。將模板中的全體像素的均值來代替原來的像素值的方法。
3)維納(Wiener)濾波
維納(Wiener)濾波是對退化圖像進行恢復處理的另一種常用算法,是一種有約束的恢復處理方法,其採用的維納濾波器是一種最小均方誤差濾波器,其數學形式比較複雜:
F(u,v)=[(1/H(u,v))*(|H(u,v)|2)/(|H(u,v)|2+s*[Sn(u,v)/Sf(u,v)])]*G(u,v)
& nbsp; 當s爲1時,上式就是普通的維納濾波;如果s爲變量,則爲參數維納濾波,如果沒有噪聲干擾,即Sn (u,v)=0時,上式實際就是前面的逆濾波。從其數學形式可以看出:維納濾波比逆濾波在對噪聲的處理方面要強一些。以上只是理論上的數學形式,在進行實際處理時,往往不知道噪聲函數Sn(u,v)和Sf(u,v)的分佈情況,因此在實際應用時多用下式進行近似處理:
F(u,v)=[(1/H(u,v))* (|H(u,v)|2)/(|H(u,v)|2+K)]*G(u,v)
其中K是一個預先設定的常數。