OpenCV-Python (cv2)에서 “Digit Recognition OCR”을 구현하려고합니다. 그것은 단지 학습 목적입니다. OpenCV의 KNearest 및 SVM 기능을 모두 배우고 싶습니다.
각 숫자의 샘플 100 개 (예 : 이미지)가 있습니다. 그들과 함께 훈련하고 싶습니다.
샘플이 letter_recog.py
OpenCV의 샘플이 제공됩니다. 그러나 나는 여전히 그것을 사용하는 방법을 알 수 없었습니다. 나는 샘플, 응답 등이 무엇인지 이해하지 못합니다. 또한 처음에는 txt 파일을로드하지만 처음에는 이해하지 못했습니다.
나중에 조금 검색하면 cpp 샘플에서 letter_recognition.data를 찾을 수 있습니다. 나는 그것을 사용하고 letter_recog.py 모델에서 cv2.KNearest 코드를 만들었습니다 (테스트 용).
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)을 사용하여 간단한 숫자 인식 도구를 작성하는 방법은 무엇입니까?
답변
글쎄, 나는 위의 문제를 해결하기 위해 내 질문에 스스로 운동하기로 결정했다. 내가 원하는 것은 OpenCV에서 KNearest 또는 SVM 기능을 사용하여 간단한 OCR을 구현하는 것입니다. 아래는 내가 한 일과 방법입니다. (단순한 OCR 목적으로 KNearest를 사용하는 방법을 배우기위한 것입니다).
1) 첫 번째 질문은 OpenCV 샘플과 함께 제공되는 letter_recognition.data 파일에 관한 것입니다. 그 파일 안에 무엇이 있는지 알고 싶었습니다.
그것은 그 편지의 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에서 작은 코드를 만들었습니다. 다음과 같은 일을합니다.
- 이미지를로드합니다.
- 숫자를 선택합니다 (잘못 감지되지 않도록 문자의 면적과 높이에 대한 윤곽 찾기 및 구속 조건 적용).
- 한 글자 주위에 경계 사각형을 그리고를 기다립니다
key press manually
. 이번에 는 문자 입력 상자에 해당하는 숫자 키 를 누릅니다. - 해당 숫자 키를 누르면이 상자의 크기가 10×10으로 조정되고 배열 (여기서는 샘플)에 100 개의 픽셀 값과 다른 배열 (여기서는 응답)에 수동으로 입력 한 숫자가 저장됩니다.
- 그런 다음 두 배열을 별도의 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)
이제 교육 및 테스트 부분에 들어갑니다.
테스트 부분에서 아래 이미지를 사용했는데 훈련하는 데 사용한 것과 동일한 유형의 문자가 있습니다.
훈련을 위해 다음과 같이합니다 .
- 앞서 저장 한 txt 파일을 불러옵니다.
- 우리가 사용하는 분류기의 인스턴스를 만듭니다 (여기서는 KNearest입니다)
- 그런 다음 KNearest.train 함수를 사용하여 데이터를 학습시킵니다.
테스트 목적으로 다음과 같이합니다.
- 테스트에 사용 된 이미지를로드합니다
- 이미지를 이전과 같이 처리하고 윤곽 방법을 사용하여 각 숫자를 추출하십시오.
- 이에 대한 경계 상자를 그린 다음 10×10으로 크기를 조정하고 이전과 같이 픽셀 값을 배열에 저장하십시오.
- 그런 다음 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 % 정확도로 작동했습니다. 나는 이것이 모든 숫자가 같은 종류와 같은 크기이기 때문에 가정합니다.
그러나 어쨌든 이것은 초보자에게 좋은 출발입니다 (그렇기를 바랍니다).
답변
C ++ 코드에 관심이있는 사람들은 아래 코드를 참조하십시오. 좋은 설명을 해준 Abid Rahman 에게 감사드립니다 .
절차는 위와 동일하지만 형상 찾기는 첫 번째 계층 레벨 윤곽 만 사용하므로 알고리즘은 각 숫자에 대해 외부 윤곽 만 사용합니다.
샘플 및 라벨 데이터 생성을위한 코드
//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로 감지되고 점에 대해서는 훈련되지 않았습니다. 또한 첫 번째 계층 레벨의 모든 윤곽을 샘플 입력으로 고려하고 있으므로 사용자는 면적을 계산하여 피할 수 있습니다.
답변
기계 학습의 최신 기술에 관심이 있다면 딥 러닝을 살펴 봐야합니다. GPU를 지원하는 CUDA가 있거나 Amazon Web Services에서 GPU를 사용해야합니다.
Google Udacity는 Tensor Flow를 사용하여 이에 대한 훌륭한 자습서를 제공합니다 . 이 튜토리얼에서는 직접 작성한 숫자로 자신의 분류기를 훈련시키는 방법을 알려줍니다. Convolutional Networks를 사용한 테스트 세트에서 97 % 이상의 정확도를 얻었습니다.