15

现在我正在尝试使用 OpenCV 创建数字识别系统。WEB 中有很多文章和示例(甚至在StackOverflow上)。我决定使用KNN 分类器,因为这个解决方案在 WEB 中是最流行的。我找到了一个手写数字数据库,其中包含 60k 个示例的训练集,错误率低于 5%。

我以本教程为例,说明如何使用 OpenCV 处理该数据库。我使用完全相同的技术,在测试数据(t10k-images.idx3-ubyte)上我有 4% 的错误率。但是当我尝试对自己的数字进行分类时,我遇到了更大的错误。例如:

  • 在此处输入图像描述被识别为7
  • 在此处输入图像描述并被在此处输入图像描述识别为5
  • 在此处输入图像描述并被在此处输入图像描述识别为1
  • 在此处输入图像描述被识别为8

依此类推(如果需要,我可以上传所有图像)。

如您所见,所有数字都具有良好的质量,并且易于人类识别。

所以我决定在分类之前做一些预处理。从MNIST 数据库站点上的表格中,我发现人们正在使用去歪斜去噪模糊像素移位技术。不幸的是,几乎所有文章的链接都被破坏了。所以我决定自己做这样的预处理,因为我已经知道怎么做。

现在,我的算法如下:

  1. 侵蚀图像(我认为我的原始数字太
    粗糙了)。
  2. 去除小轮廓。
  3. 阈值和模糊图像。
  4. 中心数字(而不是移位)。

我认为在我的情况下不需要纠偏,因为所有数字通常都是旋转的。而且我也不知道如何找到合适的旋转角度。所以在这之后我得到了这些图像:

  • 在此处输入图像描述也是1
  • 在此处输入图像描述3(不是以前的5
  • 在此处输入图像描述5(不是8
  • 项目清单7(利润!)

所以,这样的预处理对我有点帮助,但我需要更好的结果,因为在我看来,这样的数字应该可以毫无问题地被识别。

任何人都可以给我任何关于预处理的建议吗?谢谢你的帮助。

PS我可以上传我的源代码(c++)。

4

3 回答 3

4

我意识到我的错误 - 它根本与预处理无关(感谢@DavidBrown@John)。我使用手写的数字数据集而不是打印的(大写)。我在网上没有找到这样的数据库,所以我决定自己创建它。我已将我的数据库上传到Google Drive

以下是你如何使用它(训练和分类):

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);
}
于 2013-05-12T16:43:25.433 回答
1

5 & 6 , 1 & 7, 9 & 8 被认为是相同的,因为类的中心点太相似了。那这个呢 ?

  • 将连接组件标记方法应用于数字以获得数字的真实边界并在这些边界上裁剪图像。因此,您将在更正确的区域上工作,并且中心点被标准化。
  • 然后将数字水平分成两部分。(例如除以“8”后会有两个圆圈)

因此,“9”和“8”以及“5”和“6”更容易识别。上部相同,但下部不同。

于 2013-05-09T04:46:47.653 回答
0

我不能给你比你自己的答案更好的答案,但我想提供一个建议。您可以通过以下方式改进您的数字识别系统:

  • 在白色和黑色补丁上应用骨架化过程

  • 之后,应用距离变换。

通过这种方式,当数字不完全居中或它们不完全相同时,从形态学上讲,您可以改进分类器的结果。

于 2017-02-13T16:01:47.653 回答