Що ж, я вирішив потренуватися на своєму питанні, щоб вирішити вищезгадану проблему. Що я хотів - це реалізувати спрощений OCR за допомогою функцій 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.
(Я не переймався точністю чи працездатністю, просто хотів, щоб це спрацювало хоча б з найменшою точністю)
Нижче я взяв зображення для своїх тренувальних даних:
(Я знаю, що кількість навчальних даних менша. Але, оскільки всі букви однакового шрифту та розміру, я вирішив спробувати це).
Щоб підготувати дані до навчання, я зробив невеликий код у OpenCV. Це робить наступні речі:
- Це завантажує зображення.
- Вибір цифр (очевидно шляхом контурного пошуку та застосування обмежень на площу та висоту літер, щоб уникнути помилкових виявлень).
- Намалюйте обмежуючий прямокутник навколо однієї літери і зачекайте
key press manually
. Цього разу ми самі натискаємо цифрову клавішу відповідно до букви у полі.
- Після натискання відповідної цифрової клавіші вона змінює розмір цього поля до 10x10 і зберігає значення 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 для підготовки даних
Для тестування ми робимо наступне:
- Завантажуємо зображення, яке використовується для тестування
- обробіть зображення, як раніше, і витягніть кожну цифру за допомогою контурних методів
- Намалюйте обмежувальне поле для цього, а потім змініть розмір до 10x10 і зберігайте його піксельні значення в масиві, як це було зроблено раніше.
- Тоді ми використовуємо функцію KNearest.find_nerely (), щоб знайти найближчий елемент до того, який ми дали. (Якщо пощастить, він визнає правильну цифру.)
Останні два етапи (навчання та тестування) я включив у єдиний код нижче:
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% точністю. Я припускаю, що це тому, що всі цифри однакового розміру і однакового розміру.
Але в будь-якому випадку це хороший початок для початківців (я сподіваюся, що так).