Sunday 11 November 2018

c++ - Pre-processing before digit recognition with KNN classifier



Right now I'm trying to create digit recognition system using OpenCV. There are many articles and examples in WEB (and even on StackOverflow). I decided to use KNN classifier because this solution is the most popular in WEB. I found a database of handwritten digits with a training set of 60k examples and with error rate less than 5%.



I used this tutorial as an example of how to work with this database using OpenCV. I'm using exactly same technique and on test data (t10k-images.idx3-ubyte) I've got 4% error rate. But when I try to classify my own digits I've got much bigger error. For example:





And so on (I can upload all images if it's needed).




As you can see all digits have good quality and are easily-recognizable for human.



So I decided to do some pre-processing before classifying. From the table on MNIST database site I found that people are using deskewing, noise removal, blurring and pixel shift techniques. Unfortunately almost all links to the articles are broken. So I decided to do such pre-processing by myself, because I already know how to do that.



Right now, my algorithm is the following:




  1. Erode image (I think that my original digits are too
    rough).

  2. Remove small contours.

  3. Threshold and blur image.


  4. Center digit (instead of shifting).



I think that deskewing is not needed in my situation because all digits are normally rotated. And also I have no idea how to find a right rotation angle.
So after this I've got these images:




  • enter image description here is also 1

  • enter image description here is 3 (not 5 as it used to be)

  • enter image description here is 5 (not 8)


  • List item is 7 (profit!)



So, such pre-processing helped me a bit, but I need better results, because in my opinion such digits should be recognized without problems.



Can anyone give me any advice with pre-processing? Thanks for any help.



P.S. I can upload my source (c++) code.


Answer



I realized my mistake - it wasn't connected with pre-processing at all (thanks to @DavidBrown and @John). I used handwritten dataset of digits instead of printed (capitalized). I didn't find such database in the web so I decided to create it by myself. I have uploaded my database to the Google Drive.




And here's how you can use it (train and classify):



int digitSize = 16;
//returns list of files in specific directory
static vector getListFiles(const string& dirPath)
{
vector 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 images = getListFiles(digitPath);
for (int j=0; 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 {
trainData.at(counter*size+k) = mat.at(k);
}
responces.at(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);
}

No comments:

Post a Comment

php - file_get_contents shows unexpected output while reading a file

I want to output an inline jpg image as a base64 encoded string, however when I do this : $contents = file_get_contents($filename); print &q...