Предварительная обработка перед распознаванием цифр с помощью классификатора KNN

Сейчас я пытаюсь создать систему распознавания цифр, используя OpenCV. В WEB (и даже на qaru.site/info/2933/...) есть много статей и примеров. Я решил использовать классификатор KNN, потому что это решение является самым популярным в WEB. Я нашел базу данных рукописных цифр с обучающим набором из 60 тыс. Примеров и с частотой ошибок менее 5%.

Я использовал этот учебник в качестве примера того, как работать с этой базой данных с помощью OpenCV. Я использую точно такую ​​же технику и тестовые данные (t10k-images.idx3-ubyte). У меня 4% ошибка. Но когда я пытаюсь классифицировать свои собственные цифры, у меня гораздо большая ошибка. Например:

  • enter image description here распознается как 7
  • enter image description here и enter image description here распознаются как 5
  • enter image description here и enter image description here распознаются как 1
  • enter image description here распознается как 8

И так далее (я могу загрузить все изображения, если это необходимо).

Как вы можете видеть, все цифры имеют хорошее качество и легко узнаваемы для человека.

Поэтому я решил сделать предварительную обработку перед классификацией. Из таблицы на сайт базы данных MNIST Я обнаружил, что люди используют методы округления, шумоподавления, размытия и смещения пикселей. К сожалению, почти все ссылки на статьи нарушены. Поэтому я решил сделать такую ​​предварительную обработку самостоятельно, потому что я уже знаю, как это сделать.

Сейчас мой алгоритм выглядит следующим образом:

  • Erode image (Я думаю, что мои оригинальные цифры тоже шероховатая).
  • Удалить небольшие контуры.
  • Порог и размытие изображения.
  • Центральная цифра (вместо переключения).

Я думаю, что в моей ситуации нет необходимости в работе, потому что все цифры обычно вращаются. А также я понятия не имею, как найти правильный угол поворота. Поэтому после этого у меня есть следующие изображения:

  • enter image description here также 1
  • enter image description here 3 (не 5, как это было раньше)
  • enter image description here 5 (не 8)
  • List item 7 (прибыль!)

Итак, такая предварительная обработка мне немного помогла, но мне нужны лучшие результаты, потому что, на мой взгляд, такие цифры должны быть распознаны без проблем.

Может ли кто-нибудь дать мне какие-либо советы по предварительной обработке? Спасибо за любую помощь.

P.S. Я могу загрузить исходный код (С++).

Ответ 1

Я осознал свою ошибку - она ​​не была связана с предварительной обработкой вообще (благодаря @DavidBrown и @John). Я использовал рукописный набор цифр вместо напечатанных (заглавных). Я не нашел такую ​​базу данных в сети, поэтому решил создать ее самостоятельно. Я загрузил свою базу данных в Google Диск.

И вот как вы можете его использовать (тренировать и классифицировать):

int digitSize = 16;
//returns list of files in specific directory
static vector<string> getListFiles(const string& dirPath)
{
    vector<string> result;
    DIR *dir;
    struct dirent *ent;
    if ((dir = opendir(dirPath.c_str())) != NULL)
    {
        while ((ent = readdir (dir)) != NULL)
        {
            if (strcmp(ent->d_name, ".") != 0 && strcmp(ent->d_name, "..") != 0 )
            {
                result.push_back(ent->d_name);
            }
        }
        closedir(dir);
    }
    return result;
}

void DigitClassifier::train(const string& imagesPath)
{
    int num = 510;
    int size = digitSize * digitSize;
    Mat trainData = Mat(Size(size, num), CV_32FC1);
    Mat responces = Mat(Size(1, num), CV_32FC1);

    int counter = 0;
    for (int i=1; i<=9; i++)
    {
        char digit[2];
        sprintf(digit, "%d/", i);
        string digitPath(digit);
        digitPath = imagesPath + digitPath;
        vector<string> images = getListFiles(digitPath);
        for (int j=0; j<images.size(); j++)
        {
            Mat mat = imread(digitPath+images[j], 0);
            resize(mat, mat, Size(digitSize, digitSize));
            mat.convertTo(mat, CV_32FC1);
            mat = mat.reshape(1,1);
            for (int k=0; k<size; k++)
            {
                trainData.at<float>(counter*size+k) = mat.at<float>(k);
            }
            responces.at<float>(counter) = i;
            counter++;
        }
    }
    knn.train(trainData, responces);
}

int DigitClassifier::classify(const Mat& img) const
{
    Mat tmp = img.clone();

    resize(tmp, tmp, Size(digitSize, digitSize));

    tmp.convertTo(tmp, CV_32FC1);

    return knn.find_nearest(tmp.reshape(1, 1), 5);
}

Ответ 2

5 и 6, 1 и 7, 9 и 8 признаны одинаковыми, потому что центральные точки классов слишком схожи. Как насчет этого?

  • Применить метод маркировки подключенных компонентов к цифрам для получения реальных границ цифр и обрезать изображения над этими границами. Таким образом, вы будете работать над более правильной областью, и центральные точки будут нормализованы.
  • Затем разделите цифры на две части по горизонтали. (Например, у вас будет два круга после деления "8" )

В результате "9" и "8" являются более узнаваемыми, а также "5" и "6". Верхние части будут такими же, но нижние части отличаются.

Ответ 3

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

  • Нанесите на белый и черный патч процесс скелетонирования.

  • После этого примените преобразование расстояния .

Таким образом, вы можете улучшить результаты классификатора, когда цифры не точно центрированы, или они не являются точно такими же, морфологически говоря.