天天看點

簡單的bmp驗證碼識别 (c++)

  菜鳥學習筆記供新手學習參考,一個有着很多限制的小程式,大神路過多多指點。

  不廢話,正題如下:

  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驗證碼識别

繼續閱讀