使用 KNN 分类器进行数字识别前的预处理
Posted
技术标签:
【中文标题】使用 KNN 分类器进行数字识别前的预处理【英文标题】:Pre-processing before digit recognition with KNN classifier 【发布时间】:2013-04-30 09:34:35 【问题描述】:现在我正在尝试使用 OpenCV 创建数字识别系统。 WEB上有很多文章和例子(甚至在***)。我决定使用KNN classifier,因为这个解决方案在 WEB 中是最流行的。我找到了一个database of handwritten digits,它的训练集包含 60k 个示例,错误率低于 5%。
我以this tutorial 为例,说明如何使用 OpenCV 处理此数据库。我正在使用完全相同的技术,并且在测试数据 (t10k-images.idx3-ubyte
) 上我的错误率是 4%。但是当我尝试对自己的数字进行分类时,我遇到了更大的错误。例如:
等等(如果需要,我可以上传所有图片)。
如您所见,所有数字都具有良好的质量,并且很容易被人类识别。
所以我决定在分类之前做一些预处理。从MNIST database site 的表格中,我发现人们正在使用 deskewing、noise removal、blurring 和 pixel shift技巧。不幸的是,几乎所有文章的链接都被破坏了。所以我决定自己做这样的预处理,因为我已经知道该怎么做了。
现在,我的算法如下:
-
腐蚀图像(我觉得我原来的数字太
粗糙)。
去除小轮廓。
阈值和模糊图像。
中心数字(而不是移位)。
我认为在我的情况下不需要纠偏,因为所有数字通常都会旋转。而且我也不知道如何找到合适的旋转角度。 所以在这之后我得到了这些图片:
也是 1 是 3(不是以前的 5) 是 5(不是 8) 是 7(利润!)所以,这样的预处理对我有点帮助,但我需要更好的结果,因为在我看来,这样的数字应该可以毫无问题地识别出来。
谁能给我关于预处理的任何建议?感谢您的帮助。
附:我可以上传我的源代码 (c++)。
【问题讨论】:
好吧,您的训练数据是手写数字,但这些是打印数字。也许用打印的数字训练? @DavidBrown 我想过,但是我在哪里可以找到这么大(60k)的数据库?自己创作? @ArtemStorozhuk,使用你电脑上安装的字体作为训练集。 @John 哇,我不知道我可以在我的电脑上找到它们!一直想着谷歌。你以为我一个人就能练出这么强大的db?手写数字与普通印刷数字有那么大的区别吗? @ArtemStorozhuk,您的结果有力地证明了它们完全不同。 【参考方案1】:我不能给你比你自己的答案更好的答案,但我想提供一个建议。您可以通过以下方式改进您的数字识别系统:
在白色和黑色补丁上应用骨架化过程。
之后,应用距离变换。
从形态学上讲,当数字不完全居中或不完全相同时,通过这种方式可以改善分类器的结果。
【讨论】:
【参考方案2】:我意识到我的错误 - 它根本与预处理无关(感谢 @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);
【讨论】:
【参考方案3】:5 & 6 , 1 & 7, 9 & 8 被认为是相同的,因为类的中心点太相似了。那这个呢 ?
将连接组件标记方法应用于数字以获得数字的真实边界并在这些边界上裁剪图像。因此,您将在更正确的区域上工作,并且中心点被归一化。 然后将数字水平分成两部分。 (例如除以“8”后会有两个圆圈)因此,“9”和“8”以及“5”和“6”更容易识别。上半部分相同,下半部分不同。
【讨论】:
以上是关于使用 KNN 分类器进行数字识别前的预处理的主要内容,如果未能解决你的问题,请参考以下文章