Як покращити розпізнавання цифр моделі, підготовленої на MNIST?


12

Я працюю над роздрукованим вручну багатозначним розпізнаванням Java, використовуючи OpenCVбібліотеку для попередньої обробки та сегментації, і Kerasмодель, навчену на MNIST (з точністю 0,98) для розпізнавання.

Розпізнавання, здається, працює досить добре, крім однієї речі. Мережа досить часто не впізнає їх (номер "один"). Я не можу зрозуміти, чи це відбувається через попередню обробку / неправильну реалізацію сегментації, або якщо мережа, що навчається на стандартному MNIST, просто не бачила номер один, який схожий на мої тестові випадки.

Ось як виглядають проблемні цифри після попередньої обробки та сегментації:

введіть тут опис зображеннястає введіть тут опис зображенняі класифікується як 4.

введіть тут опис зображеннястає введіть тут опис зображенняі класифікується як 7.

введіть тут опис зображеннястає введіть тут опис зображенняі класифікується як 4. І так далі...

Це щось, що можна було б виправити, покращивши процес сегментації? А точніше, покращуючи навчальний набір?

Редагувати: Покращення навчального набору (збільшення даних) безумовно допоможе, що я вже тестую, питання правильної попередньої обробки ще залишається.

Моя попередня обробка складається з зміни розміру, перетворення в масштаб сірого, бінаризації, інверсії та розширення. Ось код:

Mat resized = new Mat();
Imgproc.resize(image, resized, new Size(), 8, 8, Imgproc.INTER_CUBIC);

Mat grayscale = new Mat();
Imgproc.cvtColor(resized, grayscale, Imgproc.COLOR_BGR2GRAY);

Mat binImg = new Mat(grayscale.size(), CvType.CV_8U);
Imgproc.threshold(grayscale, binImg, 0, 255, Imgproc.THRESH_OTSU);

Mat inverted = new Mat();
Core.bitwise_not(binImg, inverted);

Mat dilated = new Mat(inverted.size(), CvType.CV_8U);
int dilation_size = 5;
Mat kernel = Imgproc.getStructuringElement(Imgproc.CV_SHAPE_CROSS, new Size(dilation_size, dilation_size));
Imgproc.dilate(inverted, dilated, kernel, new Point(-1,-1), 1);

Попередньо оброблене зображення потім сегментується на окремі цифри наступним чином:

List<Mat> digits = new ArrayList<>();
List<MatOfPoint> contours = new ArrayList<>();
Imgproc.findContours(preprocessed.clone(), contours, new Mat(), Imgproc.RETR_EXTERNAL, Imgproc.CHAIN_APPROX_SIMPLE);

// code to sort contours
// code to check that contour is a valid char

List rects = new ArrayList<>();

for (MatOfPoint contour : contours) {
     Rect boundingBox = Imgproc.boundingRect(contour);
     Rect rectCrop = new Rect(boundingBox.x, boundingBox.y, boundingBox.width, boundingBox.height);

     rects.add(rectCrop);
}

for (int i = 0; i < rects.size(); i++) {
    Rect x = (Rect) rects.get(i);
    Mat digit = new Mat(preprocessed, x);

    int border = 50;
    Mat result = digit.clone();
    Core.copyMakeBorder(result, result, border, border, border, border, Core.BORDER_CONSTANT, new Scalar(0, 0, 0));

    Imgproc.resize(result, result, new Size(28, 28));
    digits.add(result);
}

1
Ви використовуєте маску або оригінальні пікселі сірого масштабу як маску для класифікації?
Micka

@Micka Я використовую попередньо оброблену (бінарнізовану, перевернуту, розширену) версію. Оні, які відповідають навчальному набору MNIST. У моїй публікації є приклади числа "1" після попередньої обробки.
youngpanda

Відповіді:


5

Я вважаю, що ваша проблема - процес розширення. Я розумію, що ви хочете нормалізувати розміри зображень, але ви не повинні порушувати пропорції, вам слід змінити розмір до максимально бажаного на одну вісь (ту, яка дозволяє найбільшу зміну масштабу, не дозволяючи іншому розміру осі перевищувати максимальний розмір) і заповнити з фоновим кольором решта зображення. Це не те, що "стандартний MNIST просто не бачив номер один, який виглядає як ваші тестові випадки", ви робите свої зображення схожими на різні навчені числа (ті, які розпізнаються)

Перекриття джерела та оброблених зображень

Якщо ви дотримувались правильної пропорції ваших зображень (джерела та після обробки), ви можете бачити, що ви не просто змінили розмір зображення, але "спотворили" його. Це може бути результатом або неоднорідного розширення або неправильного зміни розміру


Я вважаю, що @SiR має деяку вагу. Спробуйте не змінювати співвідношення сторін числових літералів.
ZdaR

Вибачте, я не дуже дотримуюся. Як ви вважаєте, проблема мого розширення чи зміни розміру? Я змінюю розмір зображення лише на початку цим рядком Imgproc.resize(image, resized, new Size(), 8, 8, Imgproc.INTER_CUBIC);. Тут пропорція залишається такою ж, де я порушую пропорції?
youngpanda

@SiR у відповідь на ваші зміни вище: так, я не просто змінюю розмір зображення, я застосовую різні операції, одна з них - це дилатація, яка є морфологічною, що спричиняє незначне "спотворення", оскільки спричиняє яскраві області в межах або ви маєте на увазі зміни розміру в самому кінці, де я
створюю

@youngpanda, ви можете знайти тут обговорення stackoverflow.com/questions/28525436/… цікаво. Це може дати вам зрозуміти , чому ваш підхід не приносить хороших результатів
SiR

@SiR дякую за посилання, я знайомий з LeNet, але приємно читати ще раз
youngpanda

5

Тут уже розміщено декілька відповідей, але жоден з них не відповідає на ваше фактичне запитання щодо попередньої обробки зображень .

У свою чергу, я не бачу жодних суттєвих проблем з вашою реалізацією, доки це навчальний проект, добре зроблений.

Але можна помітити одне, що ви можете пропустити. Існують основні операції з математичної морфології: ерозія та дилатація (використовуються вами). І є складні операції: різні комбінації основних (наприклад, відкривання та закриття). Посилання на Вікіпедію - не найкраща довідка з резюме, але ви можете почати з неї, щоб отримати ідею.

Зазвичай краще використовувати відкривання замість ерозії та закриття замість розширення, оскільки в цьому випадку оригінальне двійкове зображення змінюється набагато менше (але бажаний ефект очищення гострих країв або заповнення зазорів досягається). Тож у вашому випадку слід перевірити закриття (розширення зображення з подальшим стиранням того ж ядра). У випадку, якщо надмірне зображення 8 * 8 сильно модифікується, коли ви розширюєте навіть ядро ​​1 * 1 (на 1 піксель більше 16% зображення (що менше на великих зображеннях).

Щоб візуалізувати ідею, перегляньте наступні фото (із підручників OpenCV: 1 , 2 ):

розширення: оригінальний символ і розширений

закриття: оригінальний символ і закритий

Сподіваюся, це допомагає.


Дякую за вклад! Насправді це не навчальний проект, тож у чому тоді буде проблема? .. Моє зображення досить велике, коли я застосовую розширення, 8x8 - це не розмір зображення, це коефіцієнт зміни висоти та ширини. Але це все ще може бути варіантом удосконалення, щоб спробувати різні математичні операції. Я не знав про відкриття та закриття, спробую це! Дякую.
youngpanda

Моя помилка, неправильно прочитати розмір виклику, як це було з 8 * 8 як новий розмір. Якщо ви хочете використовувати OCR в реальному світі, ви розглядаєте варіант передачі, вивчаючи свою оригінальну мережу на даних, типових для вашої області використання. Принаймні перевірте, чи покращує це точність, як правило, це слід робити.
f4f

Інша річ, яку потрібно перевірити - це порядок попередньої обробки: siyscale-> binary-> inverse-> resize. Змінення розміру - це дорога операція, і я не бачу потреби в застосуванні її до кольорового зображення. І сегментація символів може здійснюватися без контуру виявлення (з чимось менш затратним), якщо у вас є певний формат введення, але це може бути важко реалізувати.
f4f

Якщо у мене був інший набір даних крім MNIST, я міг би спробувати перенести навчання :) Я спробую змінити порядок попередньої обробки та повернутися до вас. Дякую! Я ще не знайшов легшого варіанту, ніж виявлення контуру для своєї проблеми ...
youngpanda

1
Добре. Ви можете самостійно збирати набір даних із тих зображень, якими будете користуватися OCR, - це звичайна практика.
f4f

4

Отже, вам потрібен комплексний підхід, оскільки кожен крок вашого каскаду обчислень спирається на попередні результати. У своєму алгоритмі є наступні функції:

  1. Попередня обробка зображень

Як було сказано раніше, якщо ви застосовуєте розмір, ви втрачаєте інформацію про пропорції зображення. Вам потрібно виконати одну і ту ж переробку зображень цифр, щоб отримати ті самі результати, які були передбачені в тренувальному процесі.

Краще, якщо ви просто обрізаєте зображення за допомогою зображень фіксованого розміру. У цьому варіанті вам не знадобиться пошук контурів та зміна розміру цифр перед навчальним процесом. Тоді ви можете внести невеликі зміни в алгоритм обрізання для кращого розпізнавання: просто знайдіть контур і поставте свою цифру, не змінюючи розмір у центрі відповідної рамки зображення для розпізнавання.

Також слід звернути більше уваги на алгоритм бінаризації. У мене був досвід вивчення впливу порогових значень бінаризації на помилку навчання: можу сказати, що це дуже важливий фактор. Ви можете спробувати інші алгоритми бінаризації, щоб перевірити цю ідею. Наприклад, ви можете використовувати цю бібліотеку для тестування альтернативних алгоритмів бінаризації.

  1. Алгоритм навчання

Для підвищення якості розпізнавання ви використовуєте перехресну перевірку в навчальному процесі. Це допоможе вам уникнути проблеми надмірності вашого навчальних даних. Наприклад, ви можете прочитати цю статтю, де пояснено, як її використовувати з Керасом.

Іноді більш високі показники точності вимірювання нічого не говорять про реальну якість розпізнавання, тому що навчений АНН не знайшов шаблон у навчальних даних. Це може бути пов'язано з навчальним процесом або вхідним набором даних, як пояснено вище, або це може спричинити вибір архітектури ANN.

  1. ANN архітектура

Це велика проблема. Як визначити кращу архітектуру ANN для вирішення завдання? Немає загальних способів зробити це. Але є кілька способів наблизитися до ідеалу. Наприклад, ви можете прочитати цю книгу . Це допомагає зробити краще бачення своєї проблеми. Також ви можете знайти тут деякі евристичні формули відповідають числу прихованих шарів / елементів для ИНСА. Також тут ви знайдете невеликий огляд цього.

Я сподіваюся, що це допоможе.


1. Якщо я вас правильно зрозумів, я не можу вирізати фіксований розмір, це зображення багатоцифрового числа, і всі випадки відрізняються за розміром / місцем тощо. Або ви мали на увазі щось інше? Так, я спробував різні методи бінаризації та налаштував параметри, якщо це саме ви маєте на увазі. 2. Насправді розпізнавання на MNIST - це чудово, там не відбувається переозброєння, точність, яку я згадав, - це точність тесту. Ні мережа, ні її навчання не є проблемою. 3. Дякую за всі посилання, я дуже задоволений своєю архітектурою, хоча, звичайно, завжди є можливість для вдосконалення.
youngpanda

Так, ви це отримаєте. Але у вас завжди є можливість зробити свій набір даних більш уніфікованим. У вашому випадку просто краще обрізати зображення цифр за контурами, як ви вже робите. Але після цього буде краще просто розгорнути свої цифри зображення до уніфікованого розміру відповідно до максимального розміру цифрового зображення за шкалою x і y. Ви можете скористатися центром області цифр контуру для цього. Це дасть ваші більш чисті вхідні дані для вашого алгоритму навчання.
Єгор Замотаєв

Ви маєте на увазі, що я повинен пропустити дилатацію? Врешті-решт я вже центрую зображення, коли застосовую рамку (50 пікс на кожній стороні). Після цього я змінюю розмір кожної цифри до 28x28, оскільки це розмір, який нам потрібен для MNIST. Ви маєте на увазі, що я можу змінити розмір до 28x28 по-різному?
Янгпанда

1
Так, розширення небажане. Ваші контури можуть мати різні співвідношення по висоті та ширині, тому вам потрібен вдосконалення вашого алгоритму тут. Принаймні, ви повинні робити розміри зображень з однаковими співвідношеннями. Оскільки у вас розміри 28х28 вхідних зображень, ви повинні підготувати зображення з однаковим співвідношенням 1: 1 за шкалами x та y. Ви повинні отримати не 50 піксельних меж для кожної сторони зображення, а межі X, Y px, які відповідають умові: contourSizeX + borderSizeX == contourSizeY + borderSizeY. Це все.
Єгор Замотаєв

Я вже пробував без розширення (забув згадати у пості). Це не змінило жодних результатів ... Мій номер кордону був експериментальним. В ідеалі мені знадобляться мої цифри для розміщення поля 20x20 (нормалізованого розміру як такого в наборі даних), а після цього
змініть

1

Після деяких досліджень та експериментів я прийшов до висновку, що сама проблема попередньої обробки зображень не є проблемою (я змінив деякі запропоновані параметри, наприклад, розмір та форму розширення, але вони не мали вирішального значення для результатів). Однак допомогли 2 наступні речі:

  1. Як зауважив @ f4f, мені потрібно було зібрати власний набір даних із реальними даними. Це вже надзвичайно допомогло.

  2. Я вніс важливі зміни в попередній обробці сегментації. Отримавши окремі контури, я спочатку нормалізую розміри зображень, щоб вони вмістилися у 20x20піксельну коробку (як вони є MNIST). Після цього я розміщую поле посередині 28x28зображення, використовуючи центр маси (що для двійкових зображень є середнім значенням для обох вимірів).

Звичайно, є ще важкі випадки сегментації, такі як перекриття чи з'єднані цифри, але вищезазначені зміни дали відповідь на моє початкове запитання та покращили мою класифікаційну ефективність.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.