Распознавание символов с помощью KNN и OpenCV 3
У меня есть вопрос о KNN и OpenCV, я надеюсь, что кто-то с большим опытом в этом может пролить свет... Я пытаюсь сделать распознавание символов на изображениях, поступающих через веб-камеру. У меня есть термометр с 4 входами, и я хотел бы провести некоторые измерения и записать отображаемые температуры с отметкой времени. В моей настройке у меня есть прямоугольник, нарисованный на изображении, поступающем с веб-камеры, я помещаю его туда, чтобы всегда размещать измеритель в одном и том же месте и избегать ненужной работы по поиску на дисплее.
Что я сделал: - Я использовал веб-камеру, чтобы сохранить последние цифры каждого из 4 измерений, при этом обдувая датчики горячим воздухом. Я собрал в общей сложности 200 образцов цифр и скопировал их в одно изображение - это формирует мои тренировочные данные. - Я повторил это изображение с прямоугольником и прочитал с клавиатуры правильный ответ для соответствующей цифры. Я сделал это для всех 200 номеров и собрал 200 пар.
Теперь код строится нормально, но я продолжаю получать сообщение об ошибке, когда findnearest вызывается в функции RecognizeDigit. В черном окне консоли, которое появляется при запуске программы, я получаю сообщение об ошибке, утверждающее, что либо столбец test_sample, либо его тип неверен. Я давно не работал над openCV, и я просто не понимаю, в чем дело. Насколько я понимаю, и пример данных, и ответ имеют форму, состоящую из 1 строки и n столбцов. В моем случае test_samples, переданные Mat RecignizeDigit( Mat), имеют размер 80 x 80, это означает, что - после изменения формы (1, 1) я получу 200 выборок * 6400 пикселей в моем Mat. Это правильно, я проверил с отладкой, пошагово переходя к точке, где все идет плохо. Ответы, пришедшие с клавиатуры, также изменяются, и во время отладки я вижу, что он имеет 1 строку и 200 столбцов, как и ожидалось. Если я передам 80x80 test_sample моей функции и изменим его форму, я получу 1 x 6400 Mat в результате. В моей голове, KNN должен просто перебрать свои доступные образцы и найти ближайших соседей и дать мне лучший ответ.
Я что-то не так делаю? Пожалуйста, смотрите мой код прилагается..
#include <opencv2/opencv.hpp>
using namespace cv;
using namespace ml;
using namespace std;
void CreateSampleData(void);
void TrainSampleData(void);
Mat RecognizeDigit(Mat matSample);
Ptr<TrainData> trainingData;
Ptr<KNearest> kclassifier = KNearest::create();
int main()
{
// CreateSampleData();
TrainSampleData();
Mat sample;
Mat gray, blur, thresh, kernel;
int BSrows = 0, BScols = 0;
Mat webcam; // source image refreshed from webcam
Mat livecam;
Mat webcamroi;
Mat drawing = Mat::zeros(thresh.size(), CV_8UC3);
Mat BigSample = Mat::zeros(Size(600, 470), CV_8UC1);
Mat BigSampleROI = Mat::zeros(Size(30, 47), CV_8UC1);
Mat toconv, convres;
// this is the input stream from the webcam that has to be processed
VideoCapture cap;
if (!cap.open(0)) return 0;
for (;;)
{
while (!cap.read(webcam));
cap.read(webcam);
livecam = webcam;
// image processing, thresholding, converting, cutting...
cvtColor(webcam, webcam, COLOR_BGR2GRAY);
GaussianBlur(webcam, webcam, Size(13, 13), 0, 0);
adaptiveThreshold(webcam, webcam, 255, 1, 1, 13, 2);
kernel = getStructuringElement(MORPH_CROSS, Size(5, 5));
morphologyEx(webcam, webcam, MORPH_CLOSE, kernel);
kernel = getStructuringElement(MORPH_ELLIPSE, Size(5, 5));
morphologyEx(webcam, webcam, MORPH_CLOSE, kernel);
rectangle(livecam, Point(295, 0), Point(490,250), Scalar(0, 0, 255), 2, LINE_8, 0);
cvNamedWindow("Use this window to tune the position of the camera", WINDOW_AUTOSIZE);
imshow("Use this window to tune the position of the camera", livecam);
webcamroi = webcam(Rect(Point(295, 0), Point(490, 250)));
drawing = webcamroi;
rectangle(drawing, Point(68, 25), Point(96, 70), Scalar(255, 0, 0), 2, LINE_8, 0);
rectangle(drawing, Point(100, 25), Point(128, 70), Scalar(255, 0, 0), 2, LINE_8, 0);
rectangle(drawing, Point(132, 25), Point(160, 70), Scalar(255, 0, 0), 2, LINE_8, 0);
rectangle(drawing, Point(68, 78), Point(96, 123), Scalar(255, 0, 0), 2, LINE_8, 0);
rectangle(drawing, Point(100, 78), Point(128, 123), Scalar(255, 0, 0), 2, LINE_8, 0);
rectangle(drawing, Point(132, 78), Point(160, 123), Scalar(255, 0, 0), 2, LINE_8, 0);
rectangle(drawing, Point(68, 132), Point(96, 176), Scalar(255, 0, 0), 2, LINE_8, 0);
rectangle(drawing, Point(100, 132), Point(128, 176), Scalar(255, 0, 0), 2, LINE_8, 0);
rectangle(drawing, Point(132, 132), Point(160, 176), Scalar(255, 0, 0), 2, LINE_8, 0);
rectangle(drawing, Point(68, 185), Point(96, 229), Scalar(255, 0, 0), 2, LINE_8, 0);
rectangle(drawing, Point(100, 185), Point(128, 229), Scalar(255, 0, 0), 2, LINE_8, 0);
rectangle(drawing, Point(132, 185), Point(160, 229), Scalar(255, 0, 0), 2, LINE_8, 0);
toconv = drawing(Rect(68, 25, 30, 47));
convres = RecognizeDigit(toconv);
#ifdef CREATE_TRAINING_IMAGE
// this collects samples of known size onto a learning image
if (BScols < 10)
{
if (BSrows < 20)
{
BigSampleROI = drawing(Rect(132, ((BSrows%4 *53) + 28), 30, 47));
BigSampleROI.copyTo(BigSample(Rect((BSrows * 30), (BScols * 47), 30, 47)));
waitKey(300);
cvNamedWindow("what gets saved", WINDOW_AUTOSIZE); // Create a window for display.
imshow("what gets saved", BigSample);
BSrows++;
}
else
{
BSrows = 0;
BScols++;
}
}
else
{
// BScols = 0;
// BSrows = 0;
}
imshow("Processed Display Window", drawing);
#endif
if (waitKey(10) == 27)
{
imwrite("BigSampleImage.bmp", BigSample);
break;
}
}
waitKey(0); // Wait for a keystroke in the window
return 0;
}
void CreateSampleData(void)
{
Mat src = imread("C:\\Users\\****\\Documents\\Visual Studio 2015\\Projects\\MeterRecognizer\\MeterRecognizer\\TrainData.bmp", 0);
//
vector< vector <Point> > contours;
vector< Vec4i > hierarchy;
Mat image;
Mat sample;
Mat sample_array, response_array;
for (int BSrows = 0; BSrows < 10; BSrows++)
{
for (int BScols = 0; BScols < 20; BScols++)
{
rectangle(src, Point((BScols * 30), (BSrows * 47)), Point(((BScols * 30) + 30), ((BSrows * 47) + 47)), Scalar(0, 0, 255), 1, 8, 0);
image = src(Rect((BScols * 30), (BSrows * 47), 30, 47));
cvNamedWindow("Processed image", WINDOW_AUTOSIZE); // Create a window for display.
imshow("Processed image", src); // processed image
Mat tmp1, tmp2;
resize(image, tmp1, Size(80, 80), 0, 0, INTER_LINEAR);
tmp1.convertTo(tmp2, CV_32FC1);
cvNamedWindow("Character to be stored", WINDOW_AUTOSIZE); // Create a window for display.
imshow("Character to be stored", image); // processed image
sample_array.push_back(tmp2); // Store sample data
int c = waitKey(0); // Read corresponding label for contour from keyoard
c -= 0x30; // Convert ascii to intiger value
response_array.push_back(c); // Store label to a mat
}
}
// Store the data to file
Mat response, tmp;
tmp = response_array.reshape(1, 1); //make continuous
tmp.convertTo(response, CV_32FC1); // Convert to float
tmp = sample_array.reshape(1, 1);
tmp.convertTo(sample, CV_32FC1); // Convert to float
FileStorage Data("C:\\Users\\****\\Documents\\Visual Studio 2015\\Projects\\MeterRecognizer\\MeterRecognizer\\TrainingData.yml", FileStorage::WRITE); // Store the sample data in a file
Data << "data" << sample;
Data.release();
FileStorage Label("C:\\Users\\****\\Documents\\Visual Studio 2015\\Projects\\MeterRecognizer\\MeterRecognizer\\LabelData.yml", FileStorage::WRITE); // Store the label data in a file
Label << "label" << response;
Label.release();
printf("Training and Label data created successfully.\n");
imshow("src", src);
waitKey();
}
void TrainSampleData(void)
{
// Read stored sample and label for training
Mat sample, response;
FileStorage Data("C:\\Users\\Gautinfo\\Documents\\Visual Studio 2015\\Projects\\MeterRecognizer\\MeterRecognizer\\TrainingData.yml",FileStorage::READ); // Read traing data to a Mat
Data["data"] >> sample;
Data.release();
FileStorage Label("C:\\Users\\Gautinfo\\Documents\\Visual Studio 2015\\Projects\\MeterRecognizer\\MeterRecognizer\\LabelData.yml",FileStorage::READ); // Read label data to a Mat
Label["label"] >> response;
Label.release();
// KNN algorithm related variables and procedures
int number_of_train_elements = 200;
int number_of_sample_elements = 200;
// Mat matTrainFeatures(0, number_of_train_elements, CV_32F);
// Mat matTrainLabels(0, number_of_train_elements, CV_32F);
sample.convertTo(sample, CV_32FC1);
response.convertTo(response, CV_32FC1);
Mat matTrainLabels = Mat::zeros(response.size(), CV_32FC1);
Mat matTrainFeatures = Mat::zeros(sample.size(), CV_32FC1);
trainingData = TrainData::create(matTrainFeatures, SampleTypes::ROW_SAMPLE, matTrainLabels);
kclassifier->setIsClassifier(true);
kclassifier->setAlgorithmType(KNearest::Types::BRUTE_FORCE);
kclassifier->setDefaultK(1);
kclassifier->train(trainingData);
printf("Training completed.\n");
waitKey();
}
Mat RecognizeDigit( Mat matSample)
{
Mat matResults;
Mat tmp1, tmp2;
resize(matSample, tmp1, Size(80, 80), 0, 0, INTER_LINEAR);
tmp2 = tmp1.reshape(1, 1); //make continuous
tmp2.convertTo(tmp2, CV_32FC1);
cvNamedWindow("What KNN gets", WINDOW_AUTOSIZE); // Create a
window for display.
imshow("What KNN gets", tmp2);
if(tmp2.type() == CV_32FC1)
{
printf("Type is OK.\n");
printf("Column number: %i\n", tmp2.cols);
}
waitKey(0);
kclassifier->findNearest(tmp2, kclassifier->getDefaultK(), matResults);
return matResults;
}