菜鳥學習筆記供新手學習參考,一個有著很多限制的小程序,大神路過多多指點。
不廢話,正題如下:
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、識別圖片, 啟動識別模塊,無法識別直接跳過識別下一個圖片(假如以刷票為目的,保證一定成功率就行了)。


