影象驗證碼識別(四)——灰度化和二值化
阿新 • • 發佈:2019-01-22
一、灰度化
灰度化應用很廣,而且也比較簡單。灰度圖就是將白與黑中間的顏色等分為若干等級,絕大多數位256階。在RGB模型種,黑色(R=G=B=0)與白色(R=G=B=255),那麼256階的灰度劃分就是R=G=B=i,其中i取0到255.
從前面可以知道,OpenCV讀取圖片之後影象的顏色資料矩陣預設是3通道的,也就是RGB模型,所以每個pixel都有3個分量,分別代表r,g和b的值。因此將三個分量值都改為同一個灰度值,圖片就實現灰度化。
灰度化的方法一般有以下幾種:
1. 分量法
在rgb三個分量種按照需求選取一個分量作為灰度值
2. 最大值
選取rgb的最大值作為該pixel的灰度值
3. 平均值
g[i,j] = (r[i,j] + g[i,j] + b[i,j]) / 3,取rgb的平均值作為灰度值
4. 加權變換
由於人眼對綠色的敏感最高,對藍色敏感最低,因此,按下式對RGB三分量進行加權平均能得到較合理的灰度影象。
g[i,j] = 0.3*r[i,j] + 0.59*g[i,j] + 0.11*b[i,j]
而OpenCV提供了灰度化的API,這裡就直接呼叫了,採用的是加權變換的方法.
- void Image::toGray()
- {
- Mat iGray;
- if (getChannel() == 3)
- {
-
std::cout << "width : "
- std::cout << "height: " << getHeight() << std::endl;
- cvtColor(m_Mat,iGray,COLOR_BGR2GRAY);
- m_Mat = iGray;
-
std::cout << "The file has been converted to gray map successfully" << std::endl;
- std::cout << "***********************************" << std::endl;
- }
- else
- {
- std::cout << "the image file is not RGB file!" << std::endl;
- }
- }
二、二值化
二值化故名思議,就是整個影象所有畫素只有兩個值可以選擇,一個是黑(灰度為0),一個是白(灰度為255)。二值化的好處就是將圖片上的有用資訊和無用資訊區分開來,比如二值化之後的驗證碼圖片,驗證碼畫素為黑色,背景和干擾點為白色,這樣後面對驗證碼畫素處理的時候就會很方便。 常見的二值化方法為固定閥值和自適應閥值,固定閥值就是制定一個固定的數值作為分界點,大於這個閥值的畫素就設為255,小於該閥值就設為0,這種方法簡單粗暴,但是效果不一定好.另外就是自適應閥值,每次根據圖片的灰度情況找合適的閥值。自適應閥值的方法有很多,這裡採用了一種類似K均值的方法,就是先選擇一個值作為閥值,統計大於這個閥值的所有畫素的灰度平均值和小於這個閥值的所有畫素的灰度平均值,再求這兩個值的平均值作為新的閥值。重複上面的計算,直到每次更新閥值後,大於該閥值和小於該閥值的畫素數目不變為止。 程式碼如下:
- //k-mean method to find a threshold by itself
- void Image::Binarization()
- {
- int i,j,nWidth,nHeight;
- int nBack_count,nData_count;
- int nBack_sum,nData_sum;
- uchar ucThre,ucThre_new;
- int nValue;
- nWidth = getWidth();
- nHeight = getHeight();
- //initial the threshold
- ucThre = 0;
- ucThre_new = 127;
- std::cout << "Initial Threshold is :" << (int)ucThre_new << std::endl;
- std::cout << "***********************************" << std::endl;
- while(ucThre != ucThre_new) {
- nBack_sum = nData_sum = 0;
- nBack_count = nData_count = 0;
- for (j = 0; j < nHeight; ++j)
- {
- for (i = 0; i < nWidth; ++i)
- {
- nValue = getPixel(i,j);
- if (nValue > ucThre_new)
- {
- nBack_sum += nValue;
- nBack_count++;
- }
- else {
- nData_sum += nValue;
- nData_count++;
- }
- }
- }// end for i
- nBack_sum = nBack_sum / nBack_count;
- nData_sum = nData_sum / nData_count;
- ucThre = ucThre_new;
- ucThre_new = (nBack_sum + nData_sum) / 2;
- }// end while
- std::cout << "After Binarization threshold is :" << (int)ucThre_new << std::endl;
- int nBlack = 0;
- int nWhite = 0;
- for (j = 0; j < nHeight; ++j)
- {
- for (i = 0; i < nWidth; ++i)
- {
- nValue = getPixel(i,j);
- if (nValue > ucThre_new)
- {
- setPixel(i,j,(uchar)WHITE);
- nWhite++;
- }
- else {
- setPixel(i,j,BLACK);
- nBlack++;
- }
- }
- }
- //backgroud is black,swap black and white
- if (nBlack > nWhite)
- {
- for (j = 0; j < nHeight; ++j)
- for (i = 0; i < nWidth; ++i)
- {
- nValue = getPixel(i,j);
- if ( !nValue )
- setPixel(i,j,(uchar)WHITE);
- else
- setPixel(i,j,BLACK);
- }
- }
- std::cout << "Binarization finished!" << std::endl;
- }
這裡最後在二值化之後有一個反色的操作,在專案種規定了白色是背景,黑色是資料,但是有時候如果驗證碼圖片的色彩比較重,最後會導致背景為黑色,資料為白色,所以這裡進行了一個判斷,由於絕大多數情況下,驗證碼的大部分畫素還是充當背景的,所以如果最後統計的黑色畫素點數目大於白色的,就將所有畫素的二值取反。