Mastering OpenCV with Practical Computer Vision Projects / Опановуюємо OpenCV через практичні проекти комп'ютерного зору: Розділ 5. Розпізнавання номерних пластин з використанням SVM та нейронних мереж
| Английский оригинал | Перевод на украинский | |
|---|---|---|
Сожалеем, но текст оригинала доступен только зарегистрированным пользователям. |
//Отримуємо матрицю повороту Atronax 28.02.21 в 07:28 |
|
|
Маючи дану матрицю, можемо виконати над вхідним зображенням афінні перетворення (такі, що переводять паралельні лінії у паралельні) за допомогою функції warpAffine, для якої встановлюємо вхідне та вихідне зображення, матрицю перетворень, розміри результату (у цьому випадку такі ж) та використаний метод інтерполяції. За необхідності також можна визначити метод та значення границі. Atronax 28.02.21 в 07:38 |
||
|
//Створюємо та обертаємо зображення Atronax 28.02.21 в 07:38 |
||
|
Після цього відсікаємо фрагмент шляхом виклику функції getRectSubPix, яка копіює частину зображення заданої довжини та ширини з центром у певній точці. Якщо кадр , маємо замінити значення розмірів шляхом виклику C++ функції swap. Atronax 28.02.21 в 07:52 |
||
|
// Відсікаємо фрагмент зображення Atronax 28.02.21 в 08:05 |
||
|
Отримані результати не підходять для тренування та класифікації, адже мають різні розміри. До того ж, кожному зображенню відповідають власні мови освітлення, що накладає додаткові відмінності. Щоб виправити це, варто змінити розміри до однакових значень ширини та висоти, а також вирівняти світлову гістограму. Atronax 28.02.21 в 08:11 |
||
|
Mat resultResized; Atronax 03.03.21 в 15:51 |
||
|
Класифікація Atronax 03.03.21 в 15:51 |
||
|
Підготувавши та розділивши зображення на можливі сегменти, вирішимо, які з них вказують на номернимі знаки, а які ні. Для цього застосуємо метод опорних векторів (Support Vector Machine, SVM). Це алгоритм розпізнавання шаблонів, який відноситься до сімейства алгоритмів контрольованого навчання, призначених, у першу чергу, для бінарної класифікації. Цей спосіб заснований на використанні даних з відмітками. На першому етапі маємо натренувати алгоритм певною їх кількістю, причому кожен набір має бути описаний окремим класом. Atronax 03.03.21 в 16:03 |
||
|
SVM формує один чи більше гіперплощин, що використовуються для розрізнення кожного типу даних. Класичним прикладом є набір двовимірних точок; метод опорних векторів знаходить оптимальну лінію, яка розділяє його на два класи. Atronax 03.03.21 в 16:06 |
||
|
Спочатку варто натренувати класифікатор; завдання маємо завершити раніше за розробку головного застосунку, тому його називають ще офлайн-тренуванням. Це не легка робота, адже потребує достатньої кількості даних, причому великий набір не завжди дає кращі результати. Що ж до нашого випадку, маємо шукати інші шляхи через відсутність відкритої бази даних номерів. Мусимо зробити сотні знімків автомобілів, обробити їх та сегментувати. Для тренування нашої системи ми використали 75 зображень з номерними пластинами та 35 без них, розміри кожного складали 144x33 пікселів. Приклад можна бачити на наступному малюнку. Звісно, це не великий набір даних, але достатній для встановлених нами вимог. Для справжньої ж програми знадобиться значно більше даних. Atronax 03.03.21 в 16:21 |
||
|
Щоб зрозуміти суть машинного навчання, застосуємо алгоритм класифікатора до особливих зон зображення (майте на увазі, існують кращі способи для тренування SVM, серед яких розрізняють метод головних компонент, перетворення Фур'є, аналіз текстури і так далі). Atronax 03.03.21 в 16:32 |
||
|
Застосуємо клас DetectRegions, щоб підготувати зображення для тренування системи, а також встановимо значення savingRegions у положення true, щоб мати можливість їх зберігати. Скористаємось командами bash у файлі segmentAllFiles.sh, щоб здійснити процес над усіма кадрами. Він постачається разом з вихідним кодом книги. Atronax 03.03.21 в 16:39 |
||
|
Щоб спростити подальші дії, збережему дані підготовлених та оброблених зображень у файлі XML для прямого використання у функції SVM в подальшому. Код trainSVM.cpp створює його на основі тек розміщення і певної кількості файлів зображень. Atronax 03.03.21 в 16:41 |
||
|
Підказка Atronax 03.03.21 в 16:58 |
||
|
В іншій матриці Nx1 зберігаються класи, кожен з яких визначений певним плаваючим значенням. Atronax 03.03.21 в 17:07 |
||
|
FileStorage fs; Atronax 03.03.21 в 17:07 |
||
|
Далі маємо налаштувати параметри SVM, які визначають спосіб роботи алгоритму; для цього скористаємось структурою CvSVMParams. Таке відображення тренувальних даних підвищує їх подібність з метою формування лінійного розподілу набору даних. На цьому етапі завдяки ефективному використанню функції kernel підвищується розмірність даних. Ми ж застосуємо тип CvSVM::LINEAR, тобто відображення не проводиться. Atronax 03.03.21 в 17:20 |
||
|
//Встановлюємо параметри SVM Atronax 03.03.21 в 17:22 |
||
|
Класифікатор здатний прогнозувати можливе відсічене зображення шляхом використання функції класу SVM, яка повертає ідентифікатор i. У даному випадку клас кадрів зі знаками позначено одиницею, а клас без них нулем. Тоді до кожного знайденого фрагмента застосуємо метод опорних векторів, щоб розрізнити пластини та не пластини, а також зберегти коректні відповіді. Наступний код відноситься до головного застосунку, цей процес називають онлайн-обробкою: Atronax 03.03.21 в 17:38 |
||
|
vector<Plate> plates; Atronax 03.03.21 в 17:39 |
||
|
Розпізнавання пластин Atronax 03.03.21 в 17:39 |
||
|
Наступний крок обробки номерних знаків націлений виокремити символи шляхом оптичного розпізнавання (OCR). На кожному зображенні класу пластин виділяємо сегменти із символами та використовуємо алгоритм машинного навчання з назвою "штучна нейронна мережа" (Artificial Neural Network, ANN), щоб розпізнати їх. Також у цій частині ми навчимось оцінювати алгоритм класифікації. Atronax 04.03.21 в 09:31 |
||
|
Сегментація OCR Atronax 04.03.21 в 09:54 |
||
|
Mat img_threshold; Atronax 04.03.21 в 09:56 |
||
|
Ми використовуємо параметр CV_THRESH_BINARY_INV, щоб інвертувати результат порогової функції, у такому разі білі пікселі вхідного зображення стануть чорними та навпаки. Це важливо для розпізнавання контурів символів, адже алгоритм шукає лише білі пікселі. Atronax 04.03.21 в 10:00 |
||
|
Для кожного знайденого контура можемо здійснити необхідні перевірки та видалити фрагменти з некоректними розмірами чи відношенням сторін. У нашому випадку відповідне значення складає 45/77, і ми можемо прийняти до 35 відсотків похибки для перевернутих чи викривлених символів. Якщо площа більша за 80 відсотків, цей фрагмент вважаємо чорним блоком, а не символом. Щоб зарахувати відповідні фрагменти, можна скористатися функцією countNonZero, яка підраховує кількість пікселів зі значенням вище нуля. Atronax 04.03.21 в 10:42 |
||
|
bool OCR::verifySizes(Mat r) Atronax 04.03.21 в 10:46 |
||
|
Коли сегментований символ пройде перевірку, встановимо для нього ті ж розміри й позицію, які мають інші символи, та збережемо у векторі шляхом виклику відповідної функції допоміжного класу CharSegment. Він зберігає сегментоване зображення та позицію, необхідну для впорядкування, адже алгоритм пошуку контурів не здатен повертати їх у правильному порядку. Atronax 04.03.21 в 10:54 |
||
|
Виокремлення особливих точок Atronax 04.03.21 в 10:54 |
||
|
Наступний етап обробки сегментованих символів полягає у пошуку особливих зон для тренування та класифікації алгоритму штучної нейронної мережі. Atronax 04.03.21 в 11:03 |
||
|
Для кожного символа рахуємо кількість пікселів у ряду чи колонці з ненульовим значенням, за допомогою функції countNonZero, та зберігаємо результат у новій матриці mhist. Потім нормалізуємо її, вирахувавши максимальне значення серед елементів матриці завдяки функції minMaxLoc та розділивши всі елементи на нього викликом convertTo. Створимо функцію ProjectedHistogram, призначену для формування накопичувальних гістограм, роль вхідних даних у яких відіграватимуть бінарні зображення та необхідний тип - горизонтальний чи вертикальний: Atronax 04.03.21 в 12:28 |
||
|
Mat OCR::ProjectedHistogram(Mat img, int t) Atronax 04.03.21 в 12:28 |
||
|
Для інших особливих зон потрібні фрагменти низької роздільної здатності. Замість повного зображення символа використаємо невелику копію з розмірами, наприклад, 5x5 пікселів. Далі тренуємо систему символами, відповідні значення яких рівні 5x5, 10x10, 15x15 чи 20x20, а також оцінюємо, який з них повертає кращі результати. Потім створимо матрицю розмірності Mx1, у якій кожній колонці відповідатиме одна особлива зона. Atronax 04.03.21 в 12:32 |
||
|
Mat OCR::features(Mat in, int sizeData) Atronax 04.03.21 в 12:47 |
||
|
Класифікація OCR Atronax 04.03.21 в 12:48 |
||
|
На етапі класифікації використаємо алгоритм штучної нейронної мережі. Точніше кажучи, найпоширеніший серед них, багатошаровий перцептрон (Multi-Layer Perceptron, MLP) Румельхарта. MLP складається з мережі нейронів, що має рівні входу, виходу та один чи більше прихованих. Кожен з них має один чи більше нейронів, що пов'язані з попереднім та наступним рівнями. Atronax 04.03.21 в 13:03 |
||
|
Всі нейрони в MLP подібні, кожен з них має декілька входів (зв'язки з попередніми нейронами) та декілька виходів з однаковим значенням (зв'язки з наступними нейронами). Кожен нейрон вираховує вихідне значення як суму зважених входів плюс зміщення, та змінюється обраною функцією активації: Atronax 04.03.21 в 13:16 |
||
|
Існують три розповсюджені функції активації: тотожна, сигмоїда та гаусіан; найважливішою з них є друга. Її значення альфа та бета рівні одиниці: Atronax 04.03.21 в 14:11 |
||
|
Вага кожного рівня, синапси та нейрон вираховуються та навчаються шляхом тренування алгоритму штучної нейронної мережі. Щоб покращити здібності класифікатора, сформуємо матриці, як і у випадку методу опорних векторів, але відмітки в цьому разі дещо інші. Замість матриці розмірності Nx1, де N позначає кількість рядів тренувальних даних, а 1 - колонок, ми використовуємо числовий ідентифікатор. Маємо створити матрицю NxM, де N - це примірники, а M - класи (10 цифр та 20 літер у нашому випадку), та встановити одиницю у позиції (i, j) за умови, якщо ряд даних i має клас j. Atronax 04.03.21 в 14:20 |
||
|
Закодуємо функцію OCR::train, яка створить всі необхідні матриці та натренує систему на основі матриці тренувальних даних, класів та певної кількості прихованих нейронів у відповідних рівнях. Дані завантажуються з файлу XML, аналогічно до процесу SVM. Atronax 04.03.21 в 14:33 |
||
|
Щоб налаштувати клас штучної нейронної мережі, маємо визначити кількість нейронів на кожному рівні. У нашому випадку використовуємо лише один прихований рівень. Наступним кроком є визначення матриці з 1 ряду та 3 колонок, перша комірка якої позначає кількість особливих точок, друга - прихованих нейронів, а третя - класів. Atronax 04.03.21 в 14:36 |
||
|
Бібліотека OpenCV визначає клас CvANN_MLP для нейронної мережі. Функція create дозволить налаштувати екземпляр, визначивши кількість рівнів та нейронів, функцію активації, а також параметри альфа й бета: Atronax 04.03.21 в 14:40 |
||
|
void OCR::train(Mat TrainData, Mat classes, int nlayers) Atronax 04.03.21 в 14:41 |
||
|
// Створюємо матрицю з n тренувальними даними та m класами Atronax 04.03.21 в 14:44 |
||
|
Після тренування можемо класифікувати будь-яку особливу точку пластини за допомогою функції OCR::classify: Atronax 04.03.21 в 14:48 |
||
|
Клас CvANN_MLP використовує функцію прогнозування для класифікації вектора особливих точок. На відміну від схожого методу SVM, алгоритм штучної нейронної мережі повертає ряд, кожна комірка якого відповідає певному класу, а значення описують вірогідність приналежності вхідної особливої точки до нього. Atronax 04.03.21 в 15:11 |
||
|
Щоб завершити розпізнавання пластини, впорядковуємо символи та повертаємо стрічку шляхом виклику функції str() класу Plate, яку можемо зобразити на поверхні початкового кадра: Atronax 04.03.21 в 15:13 |
||
|
Оцінювання системи Atronax 04.03.21 в 15:14 |
||
|
Проєкт завершено, але під час тренування алгоритму машинного навчання, наприклад OCR, ми повинні знати, які особливі зони чи параметри краще використати та як зменшити похибки класифікації, пошуку та розпізнавання. Atronax 04.03.21 в 15:22 |
