OpenCV-Python में सरल डिजिट रिकग्निशन OCR


380

मैं ओपनसीवी-पायथन (cv2) में "डिजिट रिकग्निशन ओसीआर" को लागू करने की कोशिश कर रहा हूं। यह सिर्फ सीखने के उद्देश्य के लिए है। मैं OpenCV में KNearest और SVM दोनों विशेषताओं को सीखना चाहूंगा।

मेरे पास प्रत्येक अंक के 100 नमूने (यानी चित्र) हैं। मैं उनके साथ ट्रेनिंग करना चाहूंगा।

एक नमूना है letter_recog.pyजो OpenCV नमूने के साथ आता है। लेकिन मैं अभी भी यह पता नहीं लगा सका कि इसका उपयोग कैसे करना है। मुझे समझ में नहीं आ रहा है कि नमूने, प्रतिक्रियाएं आदि क्या हैं। इसके अलावा, यह पहली बार एक txt फ़ाइल को लोड करता है, जिसे मैं पहले नहीं समझता था।

बाद में थोड़ा खोज करने पर, मैं cpp नमूनों में एक पत्र_लेखांक .डेटा पा सकता था। मैंने इसका उपयोग किया और पत्र के लिए cv2.KNearest के लिए एक कोड बनाया_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) का उपयोग करके एक साधारण अंक पहचान उपकरण कैसे लिख सकते हैं?

जवाबों:


527

ठीक है, मैंने उपरोक्त समस्या को हल करने के लिए अपने प्रश्न पर खुद को कसरत करने का निर्णय लिया। मैं जो चाहता था वह OpenCV में KNearest या SVM सुविधाओं का उपयोग करके एक सरल ओसीआर लागू करना है। और नीचे मैंने क्या और कैसे किया। (यह केवल साधारण ओसीआर उद्देश्यों के लिए KNearest का उपयोग करना सीखने के लिए है)।

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. (मैं सटीकता या प्रदर्शन के बारे में चिंतित नहीं था, मैं सिर्फ यह काम करना चाहता था, कम से कम सटीकता के साथ)

मैंने अपने प्रशिक्षण डेटा के लिए छवि नीचे दी है:

यहां छवि विवरण दर्ज करें

(मुझे पता है कि प्रशिक्षण डेटा की मात्रा कम है। लेकिन, चूंकि सभी पत्र एक ही फ़ॉन्ट और आकार के हैं, इसलिए मैंने इस पर प्रयास करने का फैसला किया है)।

प्रशिक्षण के लिए डेटा तैयार करने के लिए, मैंने OpenCV में एक छोटा कोड बनाया। यह निम्नलिखित बातें करता है:

  1. यह छवि को लोड करता है।
  2. अंकों का चयन करता है (स्पष्ट रूप से समोच्च खोजने और झूठे प्रतिबंधों से बचने के लिए पत्र और क्षेत्र की ऊंचाई पर बाधाओं को लागू करके)।
  3. बाउंडिंग आयत को एक अक्षर के चारों ओर खींचता है और प्रतीक्षा करता है key press manually। इस बार हम बॉक्स में अक्षर के अनुरूप अंक कुंजी दबाते हैं
  4. एक बार संबंधित अंक कुंजी दबाए जाने के बाद, यह इस बॉक्स को 10x10 पर आकार देता है और एक सरणी (यहां, नमूने) में 100 पिक्सेल मान बचाता है और इसी प्रकार मैन्युअल रूप से किसी अन्य सरणी (यहां, प्रतिक्रियाएं) में अंक दर्ज करता है।
  5. फिर अलग-अलग txt फ़ाइलों में दोनों सरणियों को सहेजें।

अंकों के मैनुअल वर्गीकरण के अंत में, ट्रेन डेटा (train.png) में सभी अंकों को स्वयं द्वारा लेबल किया जाता है, छवि नीचे दी गई होगी:

यहां छवि विवरण दर्ज करें

नीचे दिए गए कोड का उपयोग मैंने उपरोक्त उद्देश्य के लिए किया है (बेशक, इतना साफ नहीं):

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)

अब हम प्रशिक्षण और परीक्षण भाग में प्रवेश करते हैं।

परीक्षण भाग के लिए मैंने नीचे दी गई छवि का उपयोग किया, जिसमें उसी प्रकार के अक्षर हैं जिन्हें मैं प्रशिक्षित करता था।

यहां छवि विवरण दर्ज करें

प्रशिक्षण के लिए हम निम्नानुसार हैं :

  1. पहले से हमारे द्वारा सहेजे गए txt फ़ाइलों को लोड करें
  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)

और यह काम किया, नीचे मुझे जो परिणाम मिला है:

यहां छवि विवरण दर्ज करें


यहां इसने 100% सटीकता के साथ काम किया। मुझे लगता है कि यह इसलिए है क्योंकि सभी अंक समान और समान आकार के हैं।

लेकिन किसी भी तरह, यह शुरुआती लोगों के लिए जाने के लिए एक अच्छी शुरुआत है (मुझे आशा है कि)।


67
+1 लंबी पोस्ट, लेकिन बहुत शैक्षिक। यह opencv टैग जानकारी पर
karlphillip

12
किसी की दिलचस्पी होने पर, मैंने इस कोड से कुछ घंटियाँ और सीटी के साथ एक उचित OO इंजन बनाया: github.com/goncalopp/simple-ocr-opencv
goncalopp

10
ध्यान दें कि जब आपके पास अच्छी तरह से परिभाषित सही फ़ॉन्ट है, तो एसवीएम और केएनएन का उपयोग करने की कोई आवश्यकता नहीं है। उदाहरण के लिए, अंक 0, 4, 6, 9 एक समूह बनाते हैं, अंक 1, 2, 3, 5, 7 एक और, और 8 अन्य रूप। यह समूह यूलर नंबर द्वारा दिया गया है। तब "0" का कोई अंतिम बिंदु नहीं होता है, "4" में दो होते हैं, और "6" और "9" सेंट्रोइड स्थिति द्वारा प्रतिष्ठित होते हैं। "3" केवल एक ही है, दूसरे समूह में, 3 समापन बिंदुओं के साथ। "1" और "7" कंकाल की लंबाई से प्रतिष्ठित हैं। जब अंक के साथ उत्तल पतवार पर विचार करते हैं, तो "5" और "2" में दो छेद होते हैं और उन्हें सबसे बड़े छेद के केंद्र द्वारा प्रतिष्ठित किया जा सकता है।
mmgp

4
समस्या मिल गई .. धन्यवाद। यह एक महान ट्यूटोरियल था। मैं एक छोटी सी गलती कर रहा था। अगर मेरे और @rash में किसी और को भी इसी मुद्दे का सामना करना पड़ता है, तो ऐसा इसलिए है क्योंकि आप गलत कुंजी दबा रहे हैं। बॉक्स में प्रत्येक संख्या के लिए, आपको यह दर्ज करना होगा कि वह इस पर प्रशिक्षित न हो। उम्मीद है की वो मदद करदे।
shalki

19
एक तारकीय ट्यूटोरियल। धन्यवाद! OpenCV के नवीनतम (3.1) वर्जन के साथ इसे प्राप्त करने के लिए कुछ बदलावों की आवश्यकता है: कंट्रोल्स, पदानुक्रम = cv2.findContours (थ्र, cv2.RETR_LIST, cv2 .CHAIN_APPROX_SIMPLE) => _, contours, hierarchy = cv2 =v2 (thre, cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE), मॉडल = cv2.KNearest () => मॉडल = cv2.ml.KNearest__reate (), model.train (नमूने, प्रतिक्रियाएं) => model.train (नमूने, cv2.ml) .ROW_SAMPLE, प्रतिक्रियाएं), प्रतिधारण, परिणाम, Apt_resp, dists = model.find_nearest (roismall, k = 1) => retval, परिणाम, SAM_resp, dists = model_find_nearest (रोमस्मॉल, k = 1)
जोहान्स ब्रोडवॉल

53

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 के रूप में पाया जाता है और हमने डॉट के लिए प्रशिक्षित नहीं किया है। इसके अलावा, मैं पहले पदानुक्रम स्तर के प्रत्येक समोच्च को नमूना इनपुट के रूप में मान रहा हूं, उपयोगकर्ता क्षेत्र की गणना करके इससे बच सकता है।

परिणाम


1
मैं इस कोड को चलाने के लिए थक गया। मैं नमूना और लेबल डेटा बनाने में सक्षम था। लेकिन जब मैं परीक्षण-प्रशिक्षण फ़ाइल चलाता *** stack smashing detected ***:हूं , तो यह एक त्रुटि के साथ चलता है और इसलिए मुझे एक अंतिम उचित छवि नहीं मिल रही है क्योंकि आप ऊपर (हरे रंग में अंक) प्राप्त कर रहे हैं
स्केम

1
मैं char name[4];आपके कोड में परिवर्तन करता हूं char name[7];और मुझे स्टैक संबंधित त्रुटि नहीं मिली, लेकिन फिर भी मुझे सही परिणाम नहीं मिल रहे हैं। मैं यहाँ की तरह एक छवि हो रही है < i.imgur.com/qRkV2B4.jpg >
एसकेएम

@skm सुनिश्चित करें कि आपको छवि में अंकों की संख्या के समान समोच्च मिल रहे हैं, कंसोल पर परिणाम प्रिंट करके भी देखें।
हारिस

1
हैलो, क्या हम एक प्रशिक्षित नेट को उपयोग करने के लिए लोड कर सकते हैं?
योद

14

यदि आप मशीन लर्निंग में कला के क्षेत्र में रुचि रखते हैं, तो आपको डीप लर्निंग में देखना चाहिए। आपके पास CUDA का समर्थन करने वाला GPU होना चाहिए या वैकल्पिक रूप से Amazon Web Services पर GPU का उपयोग करना चाहिए।

Google Udacity में Tensor Flow का उपयोग करके इस पर एक अच्छा ट्यूटोरियल है । यह ट्यूटोरियल आपको सिखाएगा कि अपने स्वयं के क्लासिफायर को हाथ से लिखे अंकों पर कैसे प्रशिक्षित किया जाए। मुझे संवैधानिक नेटवर्क का उपयोग करके परीक्षण सेट पर 97% से अधिक की सटीकता प्राप्त हुई।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.