菜鳥學習筆記供新手學習參考,一個有着很多限制的小程式,大神路過多多指點。
不廢話,正題如下:
bmp驗證碼識别的流程大概分為:
1、擷取bmp圖檔中識别需要用到的資料,包括像素高度、像素寬度以及位圖資料。
2、對擷取的資料進行必要的處理,包括灰階化、二值化、去噪等。
3、對處理後的資料(一個二值化的數組)進行掃描,分割出各個字元(即确定多對數組下标)。
4、建立字元模闆庫,将分割出的字元與模闆一一對比,找出對應的字元。
下面具體講解:
1、擷取資料。
bmp檔案結構與讀取遍地都是,細節不贅述。
需要注意:1)簡單識别用到的也就是biWidth(19-22位元組),biHeight(23-26位元組),以及位圖資料(>54位元組)。
2)位圖資料是倒着存放,讀取或處理的時候要進行處理。
位圖資料每行會自動填充位元組為4的倍數,讀取或處理時最好跳過相應數目的位元組。
3)每個像素有3個分量RGB,對應3個位元組。
4)關于調色闆:由于現在基本都是24位真彩圖,是以這個基本可以不考慮,是以才是讀取54位元組以後的為位圖資料。
調色闆個人了解: 類似超市寄存物品,每個格子就是一種顔色,通過你的編号(即位圖資料,注
意這裡每個像素不是3位元組,而是因多少位的圖檔而異),拿到你想要的顔色。
// 提供方法:
// 1、取得圖檔資訊。
// 2、取得圖檔資料。
class readBmp
{
public:
bool getWidth(long &); //得到位圖的寬
bool getHeight(long &); //得到位圖的高
bool getBit(int &); //得到位圖位數
bool getData(bit_t *&); //讀取檔案的顔色資訊,即像素部分,即rgb資訊
readBmp(const char *path);
~readBmp();
private:
int bitWidth;
int bitHeight;
int bitBit;
bool bitReadSuccess; //記錄構造函數裡bmp檔案是否成功讀取
void distroy();
bit_t *tmpData; //從檔案讀出的資料位址
bool isInforExisted(void); //判斷bmp檔案是否成功讀取
};
/*
在構造函數裡讀取所需的檔案資訊,包括像素高度、寬度、和位圖資料
自己申請的空間在析構函數釋放
*/
readBmp::readBmp(const char *path)
{
ifstream bmpFile(path, ios::in | ios::binary); //建立一個讀取檔案流對象, 不自動生成不存在檔案,二進制方式打開
bitReadSuccess = bmpFile.good(); //判斷檔案是否成功打開
if(true == bitReadSuccess)
{
bmpFile.seekg(18, ios::beg); //bmp檔案結構中WIDTH 和 HEIGHT為long類型,在19-26位元組
bmpFile.read((char *)(&bitWidth), 4); //由于read方法參數類型限制,要将第一個參數如左處理
//64位系統g++ long 為8位元組,不能用sizeof(long)擷取位元組數,否則檔案指針偏移量過大,讀取過多
bmpFile.read((char *)(&bitHeight), 4);
bmpFile.seekg(2, ios::cur);
bmpFile.read((char *)(&bitBit), sizeof (bitBit));
if (bitBit != 24) //暫時隻考慮24位圖檔的讀取,2、4、8位的可以以後擴充
{
cout << "非24位圖檔,請選擇合适的圖檔重試!" << endl;
}
else
{
long count = 0;
bmpFile.seekg(22, ios::cur);
tmpData = new bit_t[bitWidth * bitHeight * 3];
long skipWidth = (4 - bitWidth*3%4)%4; //計算每行讀取時要skip的位元組數
for(int i = bitHeight - 1; i >= 0; i--)
{
for( int j = 0; j < bitWidth * 3; j++)
bmpFile.get(tmpData[count++]);
bmpFile.seekg(skipWidth, ios::cur); //跳過計算過的位元組數
}
}
bmpFile.close();
}
}
bool readBmp::getWidth(long &width) //擷取像素寬度
{
if (isInforExisted() == true)
width = bitWidth;
else
return false;
return true;
}
bool readBmp::getHeight(long &height) //擷取像素高度
{
if (isInforExisted() == true)
height = bitHeight;
else
return false;
return true;
}
bool readBmp::getBit(int &bit) //擷取位圖位數
{
if (isInforExisted() == true)
bit = bitBit;
else
return false;
return true;
}
bool readBmp::isInforExisted() //判斷檔案資訊是否讀入
{
if (false == bitReadSuccess)
cout << "圖檔檔案資訊讀取失敗,請重新讀取後重試!" << endl;
return bitReadSuccess;
}
bool readBmp::getData(bit_t *&data)
{
if (isInforExisted() == true)
data = tmpData;
else
return false;
return true;
}
readBmp::~readBmp()
{
delete []tmpData;
}
2、資料處理。
位圖資料讀取完成應該得到一個一維數組,總共biWidth*biHeight*3個位元組。(本人用char類型存儲)
1)灰階化。
像素資訊RGB 3個分量一般是不同的,共同表達一中顔色資訊。當R=G=B時表達的是一種灰色。是以将圖像灰階化隻需要将
3個分量的值變為相等的即可,可以采用權重法、最大值法等,具體可百度“灰階化”。
注意:采用何種方法灰階化對後面的二值化影響很大,可分别采用測試效果,由于本人處理的圖檔比較特殊,用的最大值法。
前面得到的一維數組在這一步可以轉化為一個二維數組 arr[biHeight][biWidth] (3個分量可合并為1個)。
2)二值化。
其實就是根據算法或經驗從位圖資料中得到一個值(網稱門檻值),周遊灰階化後得到的二維數組,利用這個值将數組二值化。
我用的是全域疊代法,效果一般,求門檻值可自選算法或自選值。
3)去噪。
也就是去除圖檔中的幹擾點、雜點。我用的是很屌絲的與周圍的點比較異同的方法(多調用幾遍效果也還不錯)。大神自尋算法。
//一些圖檔的處理方法
//建立此對象時同時進行灰階化
class sortBmp
{
public:
sortBmp(int width, int height, char *indata);
~sortBmp();
int getThreshold(); //計算門檻值
bool binarize(); //二值化
bool show(); //輸出資料資訊
bool noiseSort(); //去噪
int **getData();
private:
bool isGrey;
bool isBinary;
int bitWidth;
int bitHeight;
int **bmpData;
int maxGreyValue, minGreyValue;
int Threshold;
int greyValueExisted[256], greyValueCount[256], greyValueExistedCount;
bool isGreyValueExisted(int);
bool addGreyValueCount(int);
bool addGreyValueMember(int);
bool greyValueExistedSort();
double sum_data(int, int, int *, int *);
double sum_data(int, int, int *);
bool deleteNoisePoint();
};
sortBmp::sortBmp(const int width, const int height, char *indata):bitWidth(width), bitHeight(height)
{
int i, j, k, key;
int tmp[3];
//動态申請一個二維數組
bmpData = new int*[height];
for(k = 0; k < height; k++)
{
bmpData[k] = new int[width];
}
//key記錄以為數組的下标
key = -1;
//注意第一維的下标,以此實作資料的倒置
for (i = height - 1; i >= 0; i--)
for (j = 0; j < width; j++)
{
// bmpData[i][j] = ((indata[++key] & 0xFF)*0.3 + (indata[++key] & 0xFF)*0.59 + (indata[++key] & 0xFF)*0.11); //jia quan
// bmpData[i][j] = ((indata[++key] & 0xFF) + (indata[++key] & 0xFF) + (indata[++key] & 0xFF))/3; //ping jun
tmp[0] = indata[++key] & 0xFF;
tmp[1] = indata[++key] & 0xFF;
tmp[2] = indata[++key] & 0xFF; //與0xff與運算char轉換為int
bmpData[i][j] = (tmp[0]>tmp[1]?tmp[0]:tmp[1])>tmp[2]?(tmp[0]>tmp[1]?tmp[0]:tmp[1]):tmp[2];
}
isGrey = true;
isBinary = false;
//測試輸出
// cout << endl << endl;
// for(int i = 0; i < width*height*3; i++)
// cout << (indata[i] & 0xFF) << '\t';
// cout << endl;
}
sortBmp::~sortBmp()
{
for (int i = 0; i < bitHeight; i++)
delete []bmpData[i];
delete []bmpData;
// delete []bmpData;
// cout << "對象析構。" << endl;
}
int sortBmp::getThreshold() //擷取門檻值的算法。 本質是漸進到一個合适的值
{
int threshold[2], tmpThreshold; //用來儲存初始門檻值、緩存門檻值和最終的門檻值
maxGreyValue = **bmpData;
minGreyValue = **bmpData;
for (int i = 0; i < 256; i++)
greyValueCount[i] = 0;
greyValueExistedCount = 0;
for (int i = 0; i < bitHeight; i++)
for (int j = 0; j < bitWidth; j++)
{
//擷取最大最小灰階值
//測試語句
// cout << bmpData[i][j] << ' ' ;
if (maxGreyValue < bmpData[i][j])
maxGreyValue = bmpData[i][j];
if (minGreyValue > bmpData[i][j])
minGreyValue = bmpData[i][j];
//如果灰階值已錄入,就增加統計的數目,如果未錄入,就将其錄入
if (isGreyValueExisted(bmpData[i][j]) == true)
addGreyValueCount(bmpData[i][j]);
else
addGreyValueMember(bmpData[i][j]); //此函數要在greyValueExisted裡添加資料,修改greyValueCount和greyValueExistedCount的值
}
greyValueExistedSort();
//準備資料完畢。
threshold[0] = 0;
threshold[1] = (maxGreyValue + minGreyValue)/2;
while(threshold[1] != threshold[0])
{
tmpThreshold =
0.4 * (
sum_data(greyValueExisted[0], threshold[1], greyValueExisted, greyValueCount)
/sum_data(greyValueExisted[0], threshold[1], greyValueCount)
+
sum_data(threshold[1]+1, greyValueExisted[greyValueExistedCount-1], greyValueExisted, greyValueCount)
/sum_data(threshold[1]+1, greyValueExisted[greyValueExistedCount-1], greyValueCount)
);
threshold[0] = threshold[1];
threshold[1] = tmpThreshold;
}
Threshold = threshold[1];
return threshold[1];
}
double sortBmp::sum_data(int start_value, int end_value, int *value_data, int *count_data)
{
int sum_4_arg = 0;
int i = 0;
while (greyValueExisted[i] < start_value)
i++;
while (greyValueExisted[i] <= end_value && i < greyValueExistedCount)
{
sum_4_arg += value_data[i]*count_data[i];
i++;
}
return sum_4_arg;
}
double sortBmp::sum_data(int start_value, int end_value, int *count_data)
{
int sum_3_arg = 0;
int i = 0;
while (greyValueExisted[i] < start_value)
i++;
while (greyValueExisted[i] <= end_value && i < greyValueExistedCount)
{
sum_3_arg += count_data[i];
i++;
}
return sum_3_arg;
}
bool sortBmp::isGreyValueExisted(int data)
{
if (greyValueExistedCount == 0)
return false;
for (int i = 0; i < greyValueExistedCount; i++)
if (data == greyValueExisted[i])
return true;
return false;
}
bool sortBmp::addGreyValueMember(int data)
{
greyValueExisted[greyValueExistedCount] = data;
greyValueCount[greyValueExistedCount]++;
greyValueExistedCount++;
return true;
}
bool sortBmp::addGreyValueCount(int data)
{
for (int i = 0; i < greyValueExistedCount; i++)
if (greyValueExisted[i] == data)
greyValueCount[i]++;
return true;
}
bool sortBmp::greyValueExistedSort()
{
int tmp_existed, tmp_count;
for (int i = 0; i < greyValueExistedCount - 1; i++)
for (int j = i+1; j < greyValueExistedCount; j++)
{
if (greyValueExisted[i] > greyValueExisted[j])
{
tmp_existed = greyValueExisted[i];
greyValueExisted[i] = greyValueExisted[j];
greyValueExisted[j] = tmp_existed;
tmp_count = greyValueCount[i];
greyValueCount[i] = greyValueCount[j];
greyValueCount[j] = tmp_count;
}
}
return true;
}
//以上都是為算法服務 -。-
bool sortBmp::binarize()
{
for (int i = 0; i < bitHeight; i++)
for (int j = 0; j < bitWidth; j++)
if (bmpData[i][j] > Threshold)
bmpData[i][j] = 255;
else
bmpData[i][j] = 0;
return true;
}
bool sortBmp::show()
{
for(int i = 0; i < bitWidth; i++)
cout << '-';
cout << endl;
for (int i = 0; i < bitHeight; i++)
{
for (int j = 0; j < bitWidth; j++)
if (bmpData[i][j] == 0)
cout << '*';
else
cout << ' ';
cout << '|' << endl;
}
for(int i = 0; i < bitWidth; i++)
cout << '-';
cout << endl;
/* for (int i = 0; i < bitHeight; i++)
{
for (int j = bitWidth+1; j < bitWidth; j++)
if (bmpData[i][j] == 0)
cout << '*';
else
cout << ' ';
}
*/
}
bool sortBmp::deleteNoisePoint() //在這裡去噪,道理簡單,寫起來真難受
{
for (int i = 1; i < bitHeight-1; i++)
for (int j = 1; j < bitWidth-1; j++)
if (((bmpData[i-1][j-1] != bmpData[i][j]) + (bmpData[i-1][j] != bmpData[i][j]) + (bmpData[i-1][j+1] != bmpData[i][j]) + (bmpData[i][j-1] != bmpData[i][j]) + (bmpData[i][j+1] != bmpData[i][j]) + (bmpData[i+1][j-1] != bmpData[i][j]) + (bmpData[i+1][j] != bmpData[i][j]) + (bmpData[i+1][j+1] != bmpData[i][j])) >= 7)//用6會損失很多資料點,選擇後續處理3個噪點在一起的情況
bmpData[i][j] = 255;//~bmpData[i][j];
//處理4個角
/* if ((bmpData[0][0] != bmpData[0][1]) + (bmpData[0][0] != bmpData[1][0]) + (bmpData[0][0] != bmpData[1][1]) >= 2)
bmpData[0][0] = ~bmpData[0][0];
if ((bmpData[0][bitWidth-1] != bmpData[0][bitWidth-2]) + (bmpData[0][bitWidth-1] != bmpData[1][bitWidth-2]) + (bmpData[0][bitWidth-1] != bmpData[1][bitWidth-1]) >= 2)
bmpData[0][bitWidth-1] = ~bmpData[0][0];
if ((bmpData[bitHeight-1][0] != bmpData[bitHeight-2][0]) + (bmpData[bitHeight-1][0] != bmpData[bitHeight-2][1]) + (bmpData[bitHeight-1][0] != bmpData[bitHeight-1][1]) >= 2)
bmpData[0][0] = ~bmpData[0][0];
if ((bmpData[bitHeight-1][bitWidth-1] != bmpData[bitHeight-2][bitWidth-1]) + (bmpData[bitHeight-1][bitWidth-1] != bmpData[bitHeight-2][bitWidth-2]) + (bmpData[bitHeight-1][bitWidth-1] != bmpData[bitHeight-1][bitWidth-2]) >= 2)
bmpData[0][0] = ~bmpData[0][0];
*/
bmpData[0][0] = bmpData[0][bitWidth-1] = bmpData[bitHeight-1][0] = bmpData[bitHeight-1][bitWidth-1] = 255;
//處理除角的邊界
for (int i = 1; i < bitWidth-2; i++)
if ((bmpData[0][i] != bmpData[0][i-1]) + (bmpData[0][i] != bmpData[0][i+1]) + (bmpData[0][i] != bmpData[1][i]) + (bmpData[0][i] != bmpData[1][i-1]) + (bmpData[0][i] != bmpData[1][i+1]) >= 4)
bmpData[0][i] = 255;//~bmpData[0][i];
for (int i = 1; i < bitHeight-2; i++)
if ((bmpData[i][0] != bmpData[i-1][0]) + (bmpData[i][0] != bmpData[i+1][0]) + (bmpData[i][0] != bmpData[i][1]) + (bmpData[i][0] != bmpData[i-1][1]) + (bmpData[i][0] != bmpData[i+1][1]) >= 4)
bmpData[i][0] = 255;//~bmpData[i][0];
for (int i = 1; i < bitWidth-2; i++)
if ((bmpData[bitHeight-1][i] != bmpData[bitHeight-1][i-1]) + (bmpData[bitHeight-1][i] != bmpData[bitHeight-1][i+1]) + (bmpData[bitHeight-1][i] != bmpData[bitHeight-2][i]) + (bmpData[bitHeight-1][i] != bmpData[bitHeight-2][i-1]) + (bmpData[bitHeight-1][i] != bmpData[bitHeight-2][i+1]) >= 4)
bmpData[bitHeight-1][i] = 255;//~bmpData[bitHeight-1][i];
for (int i = 1; i < bitHeight-2; i++)
if ((bmpData[i][bitWidth-1] != bmpData[i-1][bitWidth-1]) + (bmpData[i][bitWidth-1] != bmpData[i+1][bitWidth-1]) + (bmpData[i][bitWidth-1] != bmpData[i][bitWidth-2]) + (bmpData[i][bitWidth-1] != bmpData[i-1][bitWidth-2]) + (bmpData[i][bitWidth-1] != bmpData[i+1][bitWidth-2]) >= 4)
bmpData[i][bitWidth-1] = 255;//~bmpData[i][bitWidth-1];
return true;
}
bool sortBmp::noiseSort() //多調用即便去噪更徹底
{
deleteNoisePoint();
deleteNoisePoint();
deleteNoisePoint();
return true;
}
int** sortBmp::getData()
{
return bmpData;
}
3、字元分割。
其實從資料處理開始可選行就很強了,流程是一樣的,方法卻有很多,可以根據自己的能力和想要的效果自己選取合适的算法。
由于我要處理的圖檔隻有4個字元且字元都是分開的,是以我也就周遊這個二維數組,得到8對邊界, 每個字元兩對。
4、模闆比對。
這個算法可選行也很強,由于要求不高我還是用的最簡單的方法。
将所有字元高、寬中的最大值最為二維數組的兩個範圍,這個限定大小的二維數組加上一個char類型資料就是一個模闆。
比如所有的字元最寬的1個寬為12,最高的一個高為14,那麼模闆就是arr[14][12]、在加上其對應字元。
遇到一個字元先判斷模闆檔案中是否存在,存在就輸出對應資訊。
如果不存在,将字元存入這個數組,然後将這個二維數組和對應的字元(自己輸入)存入檔案。
class recognizeBmp
{
public:
recognizeBmp(int, int, int **);
void showCharacter();
void showResult();
private:
int wide_range[8];
int height_range[8];
int bitHeight;
int bitWidth;
int **bmpData;
void getCharacter(void);
void getRanges(void);
void get_inform(void);
bool recognize(int (*)[16], char &, int, int);
void addCharacter(int (*)[16], char);
bool compare_char_arr(int (*)[16], int (*)[16], int, int);
};
recognizeBmp::recognizeBmp(int height, int width, int**tmpData):bitWidth(width), bitHeight(height), bmpData(tmpData)
{
getRanges();
}
void recognizeBmp::getRanges(void) //取得邊界範圍
{
int i = 0;
int h = 0;
int w = 0;
long sum = 255;
long tmp_sum = 255;
for (w = 0; w < bitWidth; w++) //處理4對左右邊界
{
tmp_sum = sum;
sum = 255;
h = 0;
while (h < bitHeight)
{
sum &= bmpData[h++][w];
}
if (sum != tmp_sum)
wide_range[i++] = w;
}
sum = 255;
for (h = 0; h < bitHeight; h++)
{
sum &= bmpData[h][bitWidth-1];
}
if (sum == 0)
wide_range[7] = bitWidth-1;
for (i = 1; i <= 7; i+=2) //處理4對上下邊界
if (wide_range[i] != bitWidth-1)
wide_range[i]--;
for (int count = 0; count < 4; count++)
{
tmp_sum = 255;
sum = 255;
for (h = 0; h < bitHeight; h++)
{
tmp_sum = sum; //從上找上邊界
sum = 255;
for (w = wide_range[2*count]; w < wide_range[2*count+1]; w++)
{
sum &= bmpData[h][w];
}
if (tmp_sum != sum)
{
height_range[2*count] = h;
break;
}
}
tmp_sum = 255;
sum = 255;
for (h = bitHeight-1; h >= 0; h--)
{
tmp_sum = sum; //從下找下邊界
sum = 255;
for (w = wide_range[2*count]; w < wide_range[2*count+1]; w++)
{
sum &= bmpData[h][w];
}
if (tmp_sum != sum)
{
height_range[2*count+1] = h;
break;
}
}
}
for (int count = 0; count < 4; count++) //假如有字元邊界在最底部的話
{
sum = 255;
for (i = wide_range[2*count]; i < wide_range[2*count+1]; i++)
{
sum &= bmpData[bitHeight-1][w];
}
if (sum != 255)
height_range[2*count +1] = bitHeight-1;
}
}
void recognizeBmp::showCharacter(void) //輸出字元
{
for (int count = 0; count < 4; count++)
{
for (int i = height_range[count*2]; i <= height_range[count*2+1]; i++)
{
for (int j = wide_range[count*2]; j <= wide_range[count*2+1]; j++)
{
if (bmpData[i][j] == 255)
cout << ' ';
else
cout << '*';
}
cout << endl;
}
cout << endl;
}
}
/*
void recognizeBmp::normalize(void)
{
int sum = 0;
int tmp_char[10][10] = {0};
for (int c = 0; c < 4; c++)
{
for (int i = 0; i < 10; i++)
for (int j = 0; j < 10; j++)
{
sum = 0;
for (int h = height_range[2*c]+i*(height_range[2*c+1]-height_range[2*c]+1)/10;
h < height_range[2*c]+(i+1)*(height_range[2*c+1]-height_range[2*c]+1)/10
&& h <= height_range[2*c+1]; h++)
for (int w = wide_range[2*c]+j*(wide_range[2*c+1]-wide_range[2*c]+1)/10;
w < wide_range[2*c]+(j+1)*(wide_range[2*c+1]-wide_range[2*c]+1)/10
&& w <= wide_range[2*c+1]; w++)
if (bmpData[h][w] == 0)
sum++;
// if (sum > (i*(height_range[2*c+1]-height_range[2*c]+1)/10 +1 )*(j*(wide_range[2*c+1]-wide_range[2*c]+1)/10 +1 )*0.4 && sum != 0)
if (sum >= 2)
tmp_char[i][j] = 1;
}
//測試語句
for (int i = 0; i < 10; i++)
{
for (int j = 0; j < 10; j++)
cout << tmp_char[i][j] << ' ';
cout << endl;
}
cout << endl;
}
}
*/ //歸一化失敗,另尋它法
void recognizeBmp::get_inform(void)
{
int tmp_char[16][16] = {0}; //字元最大有16
int i = 0;
int j = 0;
int width = 0;
int height = 0;
char character;
for (int c = 0; c < 4; c++) //4個字元4次識别或者錄入
{
height = height_range[2*c+1] - height_range[2*c] + 1;
width = wide_range[2*c+1] - wide_range[2*c] + 1;
i = 0;
for (int h = height_range[2*c]; h <= height_range[2*c+1]; h++) //隻輸入字元範圍的資料
{
j = 0;
for (int w = wide_range[2*c]; w <= wide_range[2*c+1]; w++)
{
tmp_char[i][j] = bmpData[h][w];
j++;
}
i++;
}
if (true == recognize(tmp_char, character, height, width)) //檢驗是否可識别
cout << "第" << c+1 << "個字元是:" << character << endl;
else
{
cout << "字元資訊不存在,請添加以便下次使用" << endl << "字元:" << endl; //不可識别的話就錄入
for (int h = height_range[2*c]; h <= height_range[2*c+1]; h++) //輸出讓使用者判斷是什麼字元
{
for (int w = wide_range[2*c]; w <= wide_range[2*c+1]; w++)
if (bmpData[h][w] == 0)
cout << '*';
else
cout << ' ';
cout << endl;
}
cout << endl << "第" << c+1 << "個字元是:" << endl;
cin >> character;
addCharacter(tmp_char, character);
}
}
}
void recognizeBmp::showResult(void)
{
get_inform();
cout << endl << endl;
}
bool recognizeBmp::recognize(int (*tmp_char)[16], char &character, int height, int width)
{
int test_char[16][16] = {0};
if ((double)height/width >= 1.5) //對特殊字元的處理,提高準确率,不過貌似沒什麼效果,不知到問題在哪
{
width *= 2;
}
ifstream infile("../data/characters", ios::binary | ios::in);
if (infile.eof())
return false;
while (!infile.eof())
{
infile.read((char *)test_char, 16*16*4);
character = infile.get();
if (true == compare_char_arr(tmp_char, test_char, height, width)) //字元與模闆比較
return true;
}
return false;
infile.close();
}
void recognizeBmp::addCharacter(int (*tmp_char)[16], char character) //存入新的模闆
{
ofstream outfile("../data/characters", ios::binary | ios::app);
outfile.write((char *)tmp_char, 16*16*4);
outfile.put(character);
outfile.close();
}
bool recognizeBmp::compare_char_arr(int (*tmp_char)[16], int (*test_char)[16], int height, int width)
{
int count = 0;
for (int i = 0; i < height; i++)
for (int j = 0; j < width; j++)
if (tmp_char[i][j] == test_char[i][j])
count++;
if(count >= height*width*7/8) // 7/8是多次測試得到的合适的值
return true;
else
return false;
}
基本就這麼多了,隻是給毫無頭緒的朋友們提供一個思路,具體細節可以自己實作。
這個程式其實是未完成的,是以有很多缺陷和限制。
本來的思路:
1、先掃描一定數量的圖檔,啟動學習子產品,将未識别的字元存入模闆檔案,最終得到一個模闆檔案。
2、識别圖檔, 啟動識别子產品,無法識别直接跳過識别下一個圖檔(假如以刷票為目的,保證一定成功率就行了)。
最後,我的環境是linux g++。
完整代碼已上傳,傳送門:bmp驗證碼識别