OCR распознавания простых цифр в OpenCV-Python - PullRequest
361 голосов
/ 23 февраля 2012

Я пытаюсь реализовать «распознавание цифр» в OpenCV-Python (cv2).Это только для учебных целей.Я хотел бы изучить возможности KNearest и SVM в OpenCV.

У меня есть 100 образцов (то есть изображений) каждой цифры.Я хотел бы потренироваться с ними.

Существует образец letter_recog.py, который поставляется с образцом OpenCV.Но я все еще не мог понять, как его использовать.Я не понимаю, что такое образцы, ответы и т. Д. Кроме того, сначала он загружает текстовый файл, который я сначала не понял.

Позже, немного поискав, я смог найти letter_recognition.данные в образцах cpp.Я использовал его и сделал код для cv2.KNearest в модели letter_recog.py (только для тестирования):

import numpy as np
import cv2

fn = 'letter-recognition.data'
a = np.loadtxt(fn, np.float32, delimiter=',', converters={ 0 : lambda ch : ord(ch)-ord('A') })
samples, responses = a[:,1:], a[:,0]

model = cv2.KNearest()
retval = model.train(samples,responses)
retval, results, neigh_resp, dists = model.find_nearest(samples, k = 10)
print results.ravel()

Это дало мне массив размером 20000, я не понимаю, что это такое.

Вопросы:

1) Что такое файл letter_recognition.data?Как создать этот файл из моего собственного набора данных?

2) Что обозначает results.reval()?

3) Как мы можем написать простой инструмент распознавания цифр, используя файл letter_recognition.data (либо KNearest, либо SVM)?

Ответы [ 3 ]

494 голосов
/ 08 марта 2012

Ну, я решил потренироваться сам над своим вопросом, чтобы решить вышеуказанную проблему. Я хотел реализовать простое распознавание текста с использованием функций KNearest или SVM в OpenCV. А ниже то, что я сделал и как. (это просто для того, чтобы научиться использовать KNearest для простых целей OCR).

1) Мой первый вопрос был о файле letter_recognition.data, который поставляется с образцами OpenCV. Я хотел знать, что находится внутри этого файла.

Содержит букву и 16 символов этой буквы.

И this SOF помогли мне найти его. Эти 16 особенностей объясняются в статье Letter Recognition Using Holland-Style Adaptive Classifiers. (Хотя я не понял некоторые функции в конце)

2) Поскольку я знал, что, не понимая всех этих особенностей, этот метод сделать сложно. Я попробовал некоторые другие документы, но все было немного сложно для новичка.

So I just decided to take all the pixel values as my features. (меня не беспокоили точность или производительность, я просто хотел, чтобы это работало, по крайней мере, с наименьшей точностью)

Я взял изображение ниже для моих тренировочных данных:

enter image description here

(я знаю, что объем обучающих данных меньше. Но, поскольку все буквы имеют одинаковый шрифт и размер, я решил попробовать это).

Чтобы подготовить данные для обучения, я сделал небольшой код в OpenCV. Он делает следующие вещи:

  1. Загружает изображение.
  2. Выбирает цифры (очевидно, путем нахождения контура и применения ограничений на область и высоту букв, чтобы избежать ложных обнаружений).
  3. Рисует ограничивающий прямоугольник вокруг одной буквы и ждет key press manually. На этот раз мы сами нажимаем цифровую клавишу , соответствующую букве в коробке.
  4. Как только соответствующая цифровая клавиша нажата, она изменяет размер этого поля до 10x10 и сохраняет 100 пиксельных значений в массиве (здесь образцы) и соответствующую вручную введенную цифру в другом массиве (здесь ответы).
  5. Затем сохраните оба массива в отдельных текстовых файлах.

В конце ручной классификации цифр все цифры в данных поезда (train.png) помечаются вручную, изображение будет выглядеть ниже:

enter image description here

Ниже приведен код, который я использовал для вышеуказанной цели (конечно, не очень чистый):

import sys

import numpy as np
import cv2

im = cv2.imread('pitrain.png')
im3 = im.copy()

gray = cv2.cvtColor(im,cv2.COLOR_BGR2GRAY)
blur = cv2.GaussianBlur(gray,(5,5),0)
thresh = cv2.adaptiveThreshold(blur,255,1,1,11,2)

#################      Now finding Contours         ###################

contours,hierarchy = cv2.findContours(thresh,cv2.RETR_LIST,cv2.CHAIN_APPROX_SIMPLE)

samples =  np.empty((0,100))
responses = []
keys = [i for i in range(48,58)]

for cnt in contours:
    if cv2.contourArea(cnt)>50:
        [x,y,w,h] = cv2.boundingRect(cnt)

        if  h>28:
            cv2.rectangle(im,(x,y),(x+w,y+h),(0,0,255),2)
            roi = thresh[y:y+h,x:x+w]
            roismall = cv2.resize(roi,(10,10))
            cv2.imshow('norm',im)
            key = cv2.waitKey(0)

            if key == 27:  # (escape to quit)
                sys.exit()
            elif key in keys:
                responses.append(int(chr(key)))
                sample = roismall.reshape((1,100))
                samples = np.append(samples,sample,0)

responses = np.array(responses,np.float32)
responses = responses.reshape((responses.size,1))
print "training complete"

np.savetxt('generalsamples.data',samples)
np.savetxt('generalresponses.data',responses)

Теперь мы приступаем к обучению и тестированию.

Для тестирования части я использовал изображение ниже, которое имеет тот же тип букв, которые я использовал для обучения.

enter image description here

Для обучения мы делаем следующее :

  1. Загрузить текстовые файлы, которые мы уже сохранили ранее
  2. создайте экземпляр используемого классификатора (здесь это KNearest)
  3. Затем мы используем функцию KNearest.train для обучения данных

Для целей тестирования мы делаем следующее:

  1. Загружаем изображение, используемое для тестирования
  2. обработать изображение как ранее и извлечь каждую цифру, используя методы контура
  3. Нарисуйте для него ограничивающую рамку, затем измените размер до 10x10 и сохраните значения его пикселей в массиве, как было сделано ранее.
  4. Затем мы используем функцию KNearest.find_nearest (), чтобы найти ближайший элемент к тому, который мы дали. (Если повезет, он распознает правильную цифру.)

Последние два шага (обучение и тестирование) я включил в один код ниже:

import cv2
import numpy as np

#######   training part    ############### 
samples = np.loadtxt('generalsamples.data',np.float32)
responses = np.loadtxt('generalresponses.data',np.float32)
responses = responses.reshape((responses.size,1))

model = cv2.KNearest()
model.train(samples,responses)

############################# testing part  #########################

im = cv2.imread('pi.png')
out = np.zeros(im.shape,np.uint8)
gray = cv2.cvtColor(im,cv2.COLOR_BGR2GRAY)
thresh = cv2.adaptiveThreshold(gray,255,1,1,11,2)

contours,hierarchy = cv2.findContours(thresh,cv2.RETR_LIST,cv2.CHAIN_APPROX_SIMPLE)

for cnt in contours:
    if cv2.contourArea(cnt)>50:
        [x,y,w,h] = cv2.boundingRect(cnt)
        if  h>28:
            cv2.rectangle(im,(x,y),(x+w,y+h),(0,255,0),2)
            roi = thresh[y:y+h,x:x+w]
            roismall = cv2.resize(roi,(10,10))
            roismall = roismall.reshape((1,100))
            roismall = np.float32(roismall)
            retval, results, neigh_resp, dists = model.find_nearest(roismall, k = 1)
            string = str(int((results[0][0])))
            cv2.putText(out,string,(x,y+h),0,1,(0,255,0))

cv2.imshow('im',im)
cv2.imshow('out',out)
cv2.waitKey(0)

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

enter image description here


Здесь он работал со 100% точностью. Я предполагаю, что это потому, что все цифры имеют одинаковый вид и одинаковый размер.

Но в любом случае, это хорошее начало для начинающих (надеюсь, что так).

48 голосов
/ 03 января 2014

Для тех, кто интересуется кодом C ++, можете обратиться к приведенному ниже коду. Спасибо Абид Рахман за хорошее объяснение.


Процедура та же, что и выше, но при поиске контура используется только контур первого уровня иерархии, поэтому алгоритм использует только внешний контур для каждой цифры.

Код для создания образца и метки данных

//Process image to extract contour
Mat thr,gray,con;
Mat src=imread("digit.png",1);
cvtColor(src,gray,CV_BGR2GRAY);
threshold(gray,thr,200,255,THRESH_BINARY_INV); //Threshold to find contour
thr.copyTo(con);

// Create sample and label data
vector< vector <Point> > contours; // Vector for storing contour
vector< Vec4i > hierarchy;
Mat sample;
Mat response_array;  
findContours( con, contours, hierarchy,CV_RETR_CCOMP, CV_CHAIN_APPROX_SIMPLE ); //Find contour

for( int i = 0; i< contours.size(); i=hierarchy[i][0] ) // iterate through first hierarchy level contours
{
    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(10,10), 0,0,INTER_LINEAR ); //resize to 10X10
    tmp1.convertTo(tmp2,CV_32FC1); //convert to float
    sample.push_back(tmp2.reshape(1,1)); // Store  sample data
    imshow("src",src);
    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
    rectangle(src,Point(r.x,r.y), Point(r.x+r.width,r.y+r.height), Scalar(0,255,0),2,8,0);    
}

// Store the data to file
Mat response,tmp;
tmp=response_array.reshape(1,1); //make continuous
tmp.convertTo(response,CV_32FC1); // Convert  to float

FileStorage Data("TrainingData.yml",FileStorage::WRITE); // Store the sample data in a file
Data << "data" << sample;
Data.release();

FileStorage Label("LabelData.yml",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();

Код для обучения и тестирования

Mat thr,gray,con;
Mat src=imread("dig.png",1);
cvtColor(src,gray,CV_BGR2GRAY);
threshold(gray,thr,200,255,THRESH_BINARY_INV); // Threshold to create input
thr.copyTo(con);


// Read stored sample and label for training
Mat sample;
Mat response,tmp;
FileStorage Data("TrainingData.yml",FileStorage::READ); // Read traing data to a Mat
Data["data"] >> sample;
Data.release();

FileStorage Label("LabelData.yml",FileStorage::READ); // Read label data to a Mat
Label["label"] >> response;
Label.release();


KNearest knn;
knn.train(sample,response); // Train with sample and responses
cout<<"Training compleated.....!!"<<endl;

vector< vector <Point> > contours; // Vector for storing contour
vector< Vec4i > hierarchy;

//Create input sample by contour finding and cropping
findContours( con, contours, hierarchy,CV_RETR_CCOMP, CV_CHAIN_APPROX_SIMPLE );
Mat dst(src.rows,src.cols,CV_8UC3,Scalar::all(0));

for( int i = 0; i< contours.size(); i=hierarchy[i][0] ) // iterate through each contour for first hierarchy level .
{
    Rect r= boundingRect(contours[i]);
    Mat ROI = thr(r);
    Mat tmp1, tmp2;
    resize(ROI,tmp1, Size(10,10), 0,0,INTER_LINEAR );
    tmp1.convertTo(tmp2,CV_32FC1);
    float p=knn.find_nearest(tmp2.reshape(1,1), 1);
    char name[4];
    sprintf(name,"%d",(int)p);
    putText( dst,name,Point(r.x,r.y+r.height) ,0,1, Scalar(0, 255, 0), 2, 8 );
}

imshow("src",src);
imshow("dst",dst);
imwrite("dest.jpg",dst);
waitKey();

Результат

В результате точка в первой строке определяется как 8, и мы не обучены для точки. Также я рассматриваю каждый контур на первом уровне иерархии в качестве примера ввода, пользователь может избежать его, вычисляя площадь.

Results

11 голосов
/ 16 мая 2016

Если вас интересует современное состояние машинного обучения, вам стоит заняться углубленным изучением.У вас должен быть CUDA, поддерживающий GPU, или вы можете использовать GPU в Amazon Web Services.

У Google Udacity есть хороший учебник по этому вопросу с использованием Tensor Flow .Из этого туториала вы узнаете, как обучить свой собственный классификатор написанным от руки цифрам.Я получил точность более 97% в тестовом наборе с использованием Convolutional Networks.

...