Pengenalan Digit Sederhana OCR di OpenCV-Python

380

Saya mencoba menerapkan "Digit Recognition OCR" di OpenCV-Python (cv2). Itu hanya untuk tujuan belajar. Saya ingin mempelajari fitur KNearest dan SVM di OpenCV.

Saya memiliki 100 sampel (yaitu gambar) setiap digit. Saya ingin berlatih bersama mereka.

Ada sampel letter_recog.pyyang disertakan dengan sampel OpenCV. Tapi saya masih tidak tahu bagaimana cara menggunakannya. Saya tidak mengerti apa contohnya, tanggapan dll. Juga, itu memuat file txt pada awalnya, yang saya tidak mengerti dulu.

Kemudian saat mencari sedikit, saya bisa menemukan letter_recognition.data dalam sampel cpp. Saya menggunakannya dan membuat kode untuk cv2.KNearest dalam model letter_recog.py (hanya untuk pengujian):

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

Ini memberi saya array ukuran 20000, saya tidak mengerti apa itu.

Pertanyaan:

1) Apa itu file letter_recognition.data? Bagaimana cara membuat file itu dari kumpulan data saya sendiri?

2) Apa yang results.reval()dilambangkan?

3) Bagaimana kita dapat menulis alat pengenalan digit sederhana menggunakan file letter_recognition.data (baik KNearest atau SVM)?

Abid Rahman K
sumber

Jawaban:

528

Yah, saya memutuskan untuk melatih diri pada pertanyaan saya untuk menyelesaikan masalah di atas. Yang saya inginkan adalah mengimplementasikan OCR sederhana menggunakan fitur KNearest atau SVM di OpenCV. Dan di bawah ini adalah apa yang saya lakukan dan bagaimana caranya. (itu hanya untuk mempelajari cara menggunakan KNearest untuk tujuan OCR sederhana).

1) Pertanyaan pertama saya adalah tentang file letter_recognition.data yang datang dengan sampel OpenCV. Saya ingin tahu apa yang ada di dalam file itu.

Ini berisi surat, bersama dengan 16 fitur surat itu.

Dan this SOFmembantu saya menemukannya. 16 fitur ini dijelaskan di koran Letter Recognition Using Holland-Style Adaptive Classifiers. (Meskipun saya tidak mengerti beberapa fitur pada akhirnya)

2) Karena saya tahu, tanpa memahami semua fitur itu, sulit untuk melakukan metode itu. Saya mencoba beberapa makalah lain, tetapi semuanya agak sulit bagi pemula.

So I just decided to take all the pixel values as my features. (Saya tidak khawatir tentang akurasi atau kinerja, saya hanya ingin itu berfungsi, setidaknya dengan akurasi paling sedikit)

Saya mengambil gambar di bawah ini untuk data pelatihan saya:

masukkan deskripsi gambar di sini

(Saya tahu jumlah data pelatihan kurang. Tapi, karena semua huruf memiliki ukuran dan huruf yang sama, saya memutuskan untuk mencoba ini).

Untuk menyiapkan data untuk pelatihan, saya membuat kode kecil di OpenCV. Ia melakukan hal-hal berikut:

  1. Itu memuat gambar.
  2. Pilih digit (jelas dengan menemukan kontur dan menerapkan batasan pada area dan ketinggian huruf untuk menghindari deteksi palsu).
  3. Gambarlah persegi panjang pembatas di sekitar satu huruf dan tunggu key press manually. Kali ini kami menekan tombol angka sesuai dengan huruf dalam kotak.
  4. Setelah tombol angka yang sesuai ditekan, ia mengubah ukuran kotak ini menjadi 10x10 dan menyimpan nilai 100 piksel dalam array (di sini, sampel) dan angka yang dimasukkan secara manual dalam array lain (di sini, tanggapan).
  5. Kemudian simpan kedua array dalam file txt terpisah.

Di akhir klasifikasi manual angka, semua digit dalam data kereta (train.png) dilabeli secara manual oleh kami sendiri, gambar akan terlihat seperti di bawah ini:

masukkan deskripsi gambar di sini

Di bawah ini adalah kode yang saya gunakan untuk tujuan di atas (tentu saja, tidak begitu bersih):

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)

Sekarang kita masuk ke bagian pelatihan dan pengujian.

Untuk bagian pengujian saya menggunakan gambar di bawah ini, yang memiliki jenis huruf yang sama yang saya gunakan untuk melatih.

masukkan deskripsi gambar di sini

Untuk pelatihan kami lakukan sebagai berikut :

  1. Muat file txt yang sudah kami simpan sebelumnya
  2. buat instance classifier yang kami gunakan (di sini, ini KNearest)
  3. Kemudian kita menggunakan fungsi KNearest.train untuk melatih data

Untuk tujuan pengujian, kami lakukan sebagai berikut:

  1. Kami memuat gambar yang digunakan untuk pengujian
  2. memproses gambar seperti sebelumnya dan mengekstraksi setiap digit menggunakan metode kontur
  3. Gambar kotak pembatas untuknya, lalu ubah ukuran menjadi 10x10, dan simpan nilai pikselnya dalam array seperti yang dilakukan sebelumnya.
  4. Kemudian kami menggunakan fungsi KNearest.find_nearest () untuk menemukan item terdekat dengan yang kami berikan. (Jika beruntung, ia mengenali angka yang benar.)

Saya menyertakan dua langkah terakhir (pelatihan dan pengujian) dalam satu kode di bawah ini:

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)

Dan itu berhasil, di bawah ini adalah hasil yang saya dapatkan:

masukkan deskripsi gambar di sini


Di sini ia bekerja dengan akurasi 100%. Saya berasumsi ini karena semua digit memiliki jenis dan ukuran yang sama.

Tapi bagaimanapun juga, ini adalah awal yang baik untuk pemula (saya harap begitu).

Abid Rahman K
sumber
67
+1 Pos panjang, tetapi sangat mendidik. Ini harus pergi ke info tag
pembuka
12
kalau-kalau ada yang tertarik, saya membuat mesin OO yang tepat dari kode ini, bersama dengan beberapa lonceng dan peluit: github.com/goncalopp/simple-ocr-opencv
goncalopp
10
Perhatikan bahwa tidak perlu menggunakan SVM dan KNN ketika Anda memiliki font sempurna yang terdefinisi dengan baik. Misalnya, angka 0, 4, 6, 9 membentuk satu kelompok, angka 1, 2, 3, 5, 7 membentuk yang lain, dan 8 lainnya. Grup ini diberikan oleh nomor euler. Kemudian "0" tidak memiliki titik akhir, "4" memiliki dua, dan "6" dan "9" dibedakan berdasarkan posisi centroid. "3" adalah satu-satunya, di grup lain, dengan 3 titik akhir. "1" dan "7" dibedakan oleh panjang kerangka. Ketika mempertimbangkan lambung cembung bersama dengan angka, "5" dan "2" memiliki dua lubang dan mereka dapat dibedakan dengan centroid lubang terbesar.
mmgp
4
Punya masalah .. Terima kasih. Itu adalah tutorial yang bagus. Saya melakukan kesalahan kecil. Jika ada orang lain yang menghadapi masalah yang sama dalam hal ini seperti saya dan @rash maka itu karena Anda menekan tombol yang salah. Untuk setiap angka dalam kotak, Anda harus memasukkan no itu sehingga mendapat pelatihan di atasnya. Semoga itu bisa membantu.
shalki
19
Tutorial bintang. Terima kasih! Ada beberapa perubahan yang diperlukan untuk menjalankan ini dengan verscon terbaru (3.1) dari OpenCV: kontur, hierarki = cv2.findContours (thresh, cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE) => _, kontur, hierarki = cv2.findContours (thresh, cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE), model = cv2.KNearest () => model = cv2.ml.KNearest_create (), model.train (sampel, tanggapan) => model.train (sampel, cv2.ml .ROW_SAMPLE, tanggapan), retval, hasil, neigh_resp, dists = model.find_nearest (roismall, k = 1) => retval, hasil, neigh_resp, dists = model.find_nearest (roismall, k = 1)
Johannes Brodwall
53

Bagi mereka yang tertarik dengan kode C ++ dapat merujuk kode di bawah ini. Terima kasih Abid Rahman untuk penjelasannya yang bagus.


Prosedurnya sama seperti di atas tetapi, penemuan kontur hanya menggunakan kontur level hierarki pertama, sehingga algoritma hanya menggunakan kontur luar untuk setiap digit.

Kode untuk membuat sampel dan Label data

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

Kode untuk pelatihan dan pengujian

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

Hasil

Dalam hasilnya titik di baris pertama terdeteksi sebagai 8 dan kami belum dilatih untuk titik. Saya juga mempertimbangkan setiap kontur di tingkat hierarki pertama sebagai input sampel, pengguna dapat menghindarinya dengan menghitung area.

Hasil

Haris
sumber
1
Saya lelah menjalankan kode ini. Saya dapat membuat data sampel dan label. Tetapi ketika saya menjalankan file tes-pelatihan, itu berjalan dengan kesalahan *** stack smashing detected ***:dan karenanya saya tidak mendapatkan gambar yang tepat akhir karena Anda mendapatkan di atas (angka dalam warna hijau)
skm
1
Saya mengubah char name[4];kode Anda ke char name[7];dan saya tidak mendapatkan kesalahan terkait stack tetapi saya masih tidak mendapatkan hasil yang benar. Saya mendapatkan gambar seperti di sini < i.imgur.com/qRkV2B4.jpg >
skm
@skm Pastikan Anda mendapatkan jumlah kontur yang sama dengan jumlah digit pada gambar, juga coba dengan mencetak hasilnya di konsol.
Haris
1
Halo, bisakah kita memuat jaring yang terlatih untuk digunakan?
Yode
14

Jika Anda tertarik pada keadaan seni dalam Pembelajaran Mesin, Anda harus melihat ke Deep Learning. Anda harus memiliki CUDA yang mendukung GPU atau sebagai alternatif menggunakan GPU di Amazon Web Services.

Google Udacity memiliki tutorial yang bagus tentang ini menggunakan Tensor Flow . Tutorial ini akan mengajarkan Anda cara melatih penggolong Anda sendiri dengan digit tulisan tangan. Saya mendapat akurasi lebih dari 97% pada set tes menggunakan Convolutional Networks.

Yonatan Simson
sumber