opencv字元切割與識別(訓練分類器)
阿新 • • 發佈:2021-01-24
技術標籤:影象處理opencv
訓練(分類器)
//opencv2.4.9 + vs2012 + 64位
#include <windows.h>
#include <iostream>
#include <fstream>
#include <opencv2/opencv.hpp>
using namespace cv;
using namespace std;
char* WcharToChar(const wchar_t* wp)
{
char *m_char;
int len = WideCharToMultiByte(CP_ACP, 0, wp, wcslen(wp), NULL, 0, NULL, NULL);
m_char = new char[len + 1];
WideCharToMultiByte(CP_ACP, 0, wp, wcslen(wp), m_char, len, NULL, NULL);
m_char[len] = '\0';
return m_char;
}
wchar_t* CharToWchar(const char* c)
{
wchar_t *m_wchar;
int len = MultiByteToWideChar(CP_ACP, 0, c, strlen(c), NULL, 0) ;
m_wchar = new wchar_t[len + 1];
MultiByteToWideChar(CP_ACP, 0, c, strlen(c), m_wchar, len);
m_wchar[len] = '\0';
return m_wchar;
}
wchar_t* StringToWchar(const string& s)
{
const char* p = s.c_str();
return CharToWchar(p);
}
void train(const string perfileReadPath, const string fileform)
{
const int sample_mun_perclass = 40;//訓練字元每類數量
const int class_mun = 10 + 26;//訓練字元類數 0-9 A-Z 除了I、O
const int image_cols = 8;
const int image_rows = 16;
string fileReadName, fileReadPath;
char temp[256];
float trainingData[class_mun*sample_mun_perclass][image_rows*image_cols] = { { 0 } };//每一行一個訓練樣本
float labels[class_mun*sample_mun_perclass][class_mun] = { { 0 } };//訓練樣本標籤
for (int i = 0; i <= class_mun - 1; i++)//不同類
{
//讀取每個類資料夾下所有影象
int j = 0;//每一類讀取影象個數計數
if (i <= 9)//0-9
{
sprintf(temp, "%d", i);
//printf("%d\n", i);
}
else//A-Z
{
sprintf(temp, "%c", i + 55);
//printf("%c\n", i+55);
}
fileReadPath = perfileReadPath + "/" + temp + "/" + fileform;
cout << "資料夾" << temp << endl;
HANDLE hFile;
LPCTSTR lpFileName = StringToWchar(fileReadPath);//指定搜尋目錄和檔案型別,如搜尋d盤的音訊檔案可以是"D:\\*.mp3"
WIN32_FIND_DATA pNextInfo; //搜尋得到的檔案資訊將儲存在pNextInfo中;
hFile = FindFirstFile(lpFileName, &pNextInfo);//請注意是 &pNextInfo , 不是 pNextInfo;
if (hFile == INVALID_HANDLE_VALUE)
{
continue;//搜尋失敗
}
//do-while迴圈讀取
do
{
if (pNextInfo.cFileName[0] == '.')//過濾.和..
continue;
j++;//讀取一張圖
//wcout<<pNextInfo.cFileName<<endl;
printf("%s\n", WcharToChar(pNextInfo.cFileName));
//對讀入的圖片進行處理
Mat srcImage = imread(perfileReadPath + "/" + temp + "/" + WcharToChar(pNextInfo.cFileName), CV_LOAD_IMAGE_GRAYSCALE);
Mat resizeImage;
Mat trainImage;
Mat result;
resize(srcImage, resizeImage, Size(image_cols, image_rows), (0, 0), (0, 0), CV_INTER_AREA);//使用象素關係重取樣。當影象縮小時候,該方法可以避免波紋出現
threshold(resizeImage, trainImage, 0, 255, CV_THRESH_BINARY | CV_THRESH_OTSU);
for (int k = 0; k<image_rows*image_cols; ++k)
{
trainingData[i*sample_mun_perclass + (j - 1)][k] = (float)trainImage.data[k];
//trainingData[i*sample_mun_perclass+(j-1)][k] = (float)trainImage.at<unsigned char>((int)k/8,(int)k%8);//(float)train_image.data[k];
//cout<<trainingData[i*sample_mun_perclass+(j-1)][k] <<" "<< (float)trainImage.at<unsigned char>(k/8,k%8)<<endl;
}
} while (FindNextFile(hFile, &pNextInfo) && j<sample_mun_perclass);//如果設定讀入的圖片數量,則以設定的為準,如果圖片不夠,則讀取資料夾下所有圖片
}
// Set up training data Mat
Mat trainingDataMat(class_mun*sample_mun_perclass, image_rows*image_cols, CV_32FC1, trainingData);
cout << "trainingDataMat——OK!" << endl;
// Set up label data
for (int i = 0; i <= class_mun - 1; ++i)
{
for (int j = 0; j <= sample_mun_perclass - 1; ++j)
{
for (int k = 0; k < class_mun; ++k)
{
if (k == i)
labels[i*sample_mun_perclass + j][k] = 1;
else
labels[i*sample_mun_perclass + j][k] = 0;
}
}
}
Mat labelsMat(class_mun*sample_mun_perclass, class_mun, CV_32FC1, labels);
cout << "labelsMat:" << endl;
ofstream outfile("out.txt");
outfile << labelsMat;
//cout<<labelsMat<<endl;
cout << "labelsMat——OK!" << endl;
//訓練程式碼
cout << "training start...." << endl;
CvANN_MLP bp;
// Set up BPNetwork's parameters
CvANN_MLP_TrainParams params;
params.train_method = CvANN_MLP_TrainParams::BACKPROP;
params.bp_dw_scale = 0.001;
params.bp_moment_scale = 0.1;
params.term_crit = cvTermCriteria(CV_TERMCRIT_ITER | CV_TERMCRIT_EPS, 10000, 0.0001); //設定結束條件
//Setup the BPNetwork
Mat layerSizes = (Mat_<int>(1, 5) << image_rows*image_cols, 128, 128, 128, class_mun);
bp.create(layerSizes, CvANN_MLP::SIGMOID_SYM, 1.0, 1.0);//CvANN_MLP::SIGMOID_SYM
//CvANN_MLP::GAUSSIAN
//CvANN_MLP::IDENTITY
cout << "正在訓練..." << endl;
bp.train(trainingDataMat, labelsMat, Mat(), Mat(), params);
bp.save("bpcharModel.xml"); //save classifier
cout << "訓練結束,生成bpModel1.xml檔案" << endl;
}
void test(string xmlfile,string imgfile)
{
CvANN_MLP bp;
char bufxml[1024];
strcpy(bufxml, xmlfile.c_str());
bp.load(bufxml); //載入xml
const int image_cols = 8;
const int image_rows = 16;
char temp[256];
//測試神經網路
cout << "測試:" << endl;
Mat test_image = imread(imgfile, CV_LOAD_IMAGE_GRAYSCALE);
Mat test_temp;
resize(test_image, test_temp, Size(image_cols, image_rows), (0, 0), (0, 0), CV_INTER_AREA);//使用象素關係重取樣。當影象縮小時候,該方法可以避免波紋出現
threshold(test_temp, test_temp, 0, 255, CV_THRESH_BINARY | CV_THRESH_OTSU);
Mat_<float>sampleMat(1, image_rows*image_cols);
for (int i = 0; i<image_rows*image_cols; ++i)
{
sampleMat.at<float>(0, i) = (float)test_temp.at<uchar>(i / 8, i % 8);
}
Mat responseMat;
bp.predict(sampleMat, responseMat);
Point maxLoc;
double maxVal = 0;
minMaxLoc(responseMat, NULL, &maxVal, NULL, &maxLoc);
if (maxLoc.x <= 9)//0-9
{
sprintf(temp, "%d", maxLoc.x);
//printf("%d\n", i);
}
else//A-Z
{
sprintf(temp, "%c", maxLoc.x + 55);
//printf("%c\n", i+55);
}
cout << "識別結果:" << temp << " 相似度:" << maxVal * 100 << "%" << endl;
imshow("test_image", test_image);
waitKey(0);
}
int main()
{
train("C:\\Users\\GuoJawee\\Desktop\\資料集_\\訓練集1", "*.jpg");
system("pause");
//test("bpcharModel.xml","1.bmp");
}
字元識別/切割
#include <iostream>
#include <stdlib.h>
#include <stdio.h>
#include <opencv2/opencv.hpp>
#include <map>
#include <time.h>
using namespace cv;
using namespace std;
//膨脹的size(3,3)
int dilateValue = 1;
//面積、寬高比
double min_area = 200;
double min_width_div_height = 0.08;
double max_width_div_height = 2.0;
CvANN_MLP bp;
//載入分類模型
void loadXML(string xmlfile)
{
char buf[1024];
strcpy(buf, xmlfile.c_str());
bp.load(buf);
}
//單個字元分類
char charClassify(Mat roi)
{
const int image_cols = 8;
const int image_rows = 16;
char temp[256];
resize(roi, roi, Size(image_cols, image_rows), (0, 0), (0, 0), CV_INTER_AREA);//使用象素關係重取樣。當影象縮小時候,該方法可以避免波紋出現
cvtColor(roi, roi, CV_RGB2GRAY, 0); //必須轉換
threshold(roi, roi, 0, 255, CV_THRESH_BINARY | CV_THRESH_OTSU);
Mat_<float>sampleMat(1, image_rows*image_cols);
for (int i = 0; i < image_rows*image_cols; ++i)
{
sampleMat.at<float>(0, i) = (float)roi.at<uchar>(i / 8, i % 8);
}
Mat responseMat;
bp.predict(sampleMat, responseMat);
Point maxLoc;
double maxVal = 0;
minMaxLoc(responseMat, NULL, &maxVal, NULL, &maxLoc);
if (maxLoc.x <= 9)//0-9
{
sprintf(temp, "%d", maxLoc.x);
//printf("%d\n", i);
}
else//A-Z
{
sprintf(temp, "%c", maxLoc.x + 55);
//printf("%c\n", i+55);
}
return temp[0];
}
//字元識別
string charRecognition(string imgfile, bool showflag)
{
Mat srcImg = imread(imgfile);
Mat greyImg, dilateImg;
cvtColor(srcImg, dilateImg, CV_BGR2GRAY);
threshold(dilateImg, dilateImg, 0, 255, CV_THRESH_OTSU);//自適應二值化
dilateImg = 255 - dilateImg;//顏色反轉
Mat element = getStructuringElement(MORPH_RECT, Size(dilateValue, dilateValue)); //第一個引數MORPH_RECT表示矩形的卷積核,當然還可以選擇橢圓形的、交叉型的
dilate(dilateImg, dilateImg, element);
Mat dstImg = srcImg.clone();
vector<vector<Point>> contours;
vector<Vec4i> hierarcy;
findContours(dilateImg, contours, hierarcy, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_NONE);
vector<Rect> boundRect(contours.size());
vector<RotatedRect> box(contours.size());
Point2f rect[4];
map<int, char> map_; //左上角座標 字元
for (int i = 0; i < contours.size(); i++)
{
box[i] = minAreaRect(Mat(contours[i]));
boundRect[i] = boundingRect(Mat(contours[i]));
box[i].points(rect);
double areaValue = (double)boundRect[i].width * (double)boundRect[i].height;
double width_div_height = (double)boundRect[i].width / (double)boundRect[i].height;
if (areaValue < min_area || min_width_div_height > width_div_height || width_div_height > max_width_div_height)
{
continue;
}
//cout << "面積 = " << areaValue << " 寬高比 = " << width_div_height << endl;
Mat roi = dstImg(Rect(Point(boundRect[i].x, boundRect[i].y), Point(boundRect[i].x + boundRect[i].width, boundRect[i].y + boundRect[i].height)));
char ch = charClassify(roi);
map_.emplace(boundRect[i].x, ch);
if (showflag == true)
{
rectangle(dstImg,
Point(boundRect[i].x, boundRect[i].y),
Point(boundRect[i].x + boundRect[i].width, boundRect[i].y + boundRect[i].height),
Scalar(0, 0, 255), 2, 8);
string tempstr(1, ch); //ch轉str
putText(dstImg, tempstr, Point(boundRect[i].x, boundRect[i].y), FONT_HERSHEY_COMPLEX, 1, cv::Scalar(0, 255, 0), 1, 8, 0);
}
}
string resstr = "";
for (auto node : map_)
{
string tmpstr(1, node.second);
resstr += tmpstr;
}
if (showflag == true)
{
cv::imshow("結果圖", dstImg);
cv::imwrite("res" + imgfile, dstImg);
}
return resstr;
}
int main()
{
loadXML("./bpcharModel.xml"); //載入模型
clock_t start = clock();
string resstr = charRecognition("4.png", false); //影象路徑 是否顯示
cout << "識別字符結果 = " << resstr << endl << endl;
clock_t ends = clock();
cout << "程式執行時間 = " << (double)(ends - start) / CLOCKS_PER_SEC << endl;
waitKey(0);
system("pause");
return 0;
}