Я пытаюсь построить OCR с KNN в OpenCV. Я сделал несколько попыток, но результат не тот, который я ожидал, и я думаю, что я сделал несколько ошибок в части обнаружения. Первая проблема - это нераспознание букв, всегда читаемых как цифры (при их обнаружении). Более того, иногда даже цифры не распознаются, в этом случае они часто пропускаются. Ниже приведен мой код до сих пор.
Обучение
void trainText() {
Mat thr, gray, con;
Mat src = imread("training_chars.png");
cvtColor(src, gray, CV_BGR2GRAY);
threshold(gray,thr,125,255,THRESH_BINARY_INV);
imshow("text", thr);
waitKey();
thr.copyTo(con);
vector<vector<Point>> contours;
vector<Vec4i> hierarchy;
Mat sample;
Mat response_array;
findContours( con, contours, hierarchy,CV_RETR_CCOMP, CV_CHAIN_APPROX_SIMPLE );
for(int i = 0; i<contours.size(); i=hierarchy[i][0]) {
Rect r= boundingRect(contours[i]); //Find bounding rect for each contour
rectangle(src,Point(r.x,r.y), Point(r.x+r.width,r.y+r.height), Scalar(0,0,255),2,8,0);
Mat ROI = thr(r); //Crop the image
Mat tmp1, tmp2;
resize(ROI,tmp1, Size(20,30), 0,0,INTER_LINEAR );
tmp1.convertTo(tmp2,CV_32FC1); //convert to float
imshow("src",src);
int c=waitKey(0); // Read corresponding label for contour from keyoard
c-=0x30; // Convert ascii to integer value
response_array.push_back(c); // Store label to a mat
rectangle(src,Point(r.x,r.y), Point(r.x+r.width,r.y+r.height), Scalar(0,255,0),2,8,0);
sample.push_back(tmp2.reshape(1,1)); // Store sample data
}
Mat response,tmp;
tmp=response_array.reshape(1,1); //make continuous
tmp.convertTo(response,CV_32FC1); // Convert to float
FileStorage Data("TrainingData.xml",FileStorage::WRITE); // Store the sample data in a file
Data << "data" << sample;
Data.release();
FileStorage Label("LabelData.xml",FileStorage::WRITE); // Store the label data in a file
Label << "label" << response;
Label.release();
cout<<"Training and Label data created successfully....!! "<<endl;
imshow("src",src);
waitKey(0);
}
Тестирование
string getText (Mat image) {
Mat thr1,gray1,con1;
Mat src1 = image.clone();
cvtColor(src1,gray1,CV_BGR2GRAY);
threshold(gray1,thr1,125,255,THRESH_BINARY_INV);
thr1.copyTo(con1);
Mat sample1;
Mat response1,tmp1;
FileStorage Data1("TrainingData.xml",FileStorage::READ);
Data1["data"] >> sample1;
Data1.release();
FileStorage Label1("LabelData.xml",FileStorage::READ); // Read label data to a Mat
Label1["label"] >> response1;
Label1.release();
Ptr<ml::KNearest> knn(ml::KNearest::create());
knn->train(sample1, ml::ROW_SAMPLE,response1); // Train with sample and responses
cout<< "Training compleated.....!!" <<endl;
vector<vector<Point>> contours1; // Vector for storing contour
vector<Vec4i> hierarchy1;
//Create input sample by contour finding and cropping
findContours(con1, contours1, hierarchy1, CV_RETR_CCOMP, CV_CHAIN_APPROX_SIMPLE);
string result;
for(int i = 0; i<contours1.size(); i=hierarchy1[i][0]) {
Rect r= boundingRect(contours1[i]);
rectangle(image, r, Scalar(0,255,0), 2);
Mat ROI = thr1(r);
Mat tmp1, tmp2;
resize(ROI,tmp1, Size(20,30), 0,0,INTER_LINEAR );
tmp1.convertTo(tmp2,CV_32FC1);
Mat bestLabels;
float p = knn -> findNearest(tmp2.reshape(1,1),4, bestLabels);
result = result + " " + char(int(p));
}
imshow("Im with bbox", image);
waitKey();
return result;
}
Изображение, используемое для обучения:
![Image used for training](https://i.stack.imgur.com/awhpO.png)
Тестовое изображение:
![Test image](https://i.stack.imgur.com/u7Nx1.png)
Выход:
![Output](https://i.stack.imgur.com/5zNya.png)
UPDATE
Я сделал несколько изменений в коде, чтобы увидеть, что эффективно обнаружено, и вот что получилось:
![new output](https://i.stack.imgur.com/a70QY.png)
cout << p << endl;
if(int(p) < 32 || int(p) == 127)
p = 45;
result = result + char(int(p));
Как видите, почти каждая цифра соответствует не отображаемому символу. Я сделал замену с «-», чтобы сразу увидеть, какой символ был распознан, а какой нет.
«B» определяется как «2», «C» - как «1», а единственное правильное обнаружение - «3».
ОБНОВЛЕНИЕ 2
Я выяснил, в чем была одна из проблем, в частности, эта строка в обучающей части:
c-=0x30;
Теперь обнаружение работает нормально, но есть другая проблема. Порядок это не правильный. Я пытаюсь отсортировать контуры, но что-то подобное приводит к неправильным контурам, возможно, из-за иерархии в последнем цикле for в тестовой части:
bool sorting (vector<Point> &a, vector<Point> &b) {
Rect ra = boundingRect(a);
Rect rb = boundingRect(b);
return (ra.x < rb.x);
}
[...]
sort(contours1.begin(), contours1.end(), sorting);
Это текущее поведение моей программы.
Без сортировки: правильные контуры, правильное обнаружение, неправильный порядок вывода
![](https://i.stack.imgur.com/pJBVz.png)
С сортировкой: неправильные контуры (поэтому неправильное обнаружение), правильный порядок
![](https://i.stack.imgur.com/72c3t.png)