OpenCV - C ++: создание OCR с использованием KNN - PullRequest
0 голосов
/ 27 июня 2019

Я пытаюсь построить 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

Тестовое изображение: Test image

Выход: Output

UPDATE Я сделал несколько изменений в коде, чтобы увидеть, что эффективно обнаружено, и вот что получилось: new output

        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);

Это текущее поведение моей программы.

Без сортировки: правильные контуры, правильное обнаружение, неправильный порядок вывода

С сортировкой: неправильные контуры (поэтому неправильное обнаружение), правильный порядок

...