0

I have done a DNI text recognition project using Visual Studio, the OpenCV library and the KNN recognition method.

To simplify the project I cut the fields whose information I want to extract and I did the whole process to each of them, obtaining good results.

But I have noticed that the program confuses an M with N and also classifies O as 0.

How could I fix it?

I will show you the field where detection incorrect occurs.

CODE:

#include <iostream>
#include <vector>

#include <opencv2\opencv.hpp>
#include <opencv2\highgui/highgui.hpp>
#include <opencv2\core\core.hpp>
#include <opencv2\ml\ml.hpp>

#include "Preproceso.h"

// VARIABLES GLOBALES  
/////////////////////////////////////////////////////
const int AREA_MINIMA_DE_CONTORNO = 70;
const int ANCHO_IMAGEN_REDIM = 20;
const int ALTURA_IMAGEN_REDIM = 30;

class ContornoConDatos {
public:
// variables miembro 
///////////////////////////////////////////////////////////////////////////
std::vector<cv::Point> ptContorno;
cv::Rect rectanguloDelimitador;
float fltAreaDelContorno;
////////////////////////////////////////////////////////////////////////////
bool comprobarSiContornoValido() {
    if (fltAreaDelContorno < AREA_MINIMA_DE_CONTORNO) return false;
    return true;
}
////////////////////////////////////////////////////////////////////////////
static bool ordenarPosicionXRectDelim(const ContornoConDatos& ccdIzquierda, const ContornoConDatos& ccdDerecha) {
    return(ccdIzquierda.rectanguloDelimitador.x < ccdDerecha.rectanguloDelimitador.x);
}
};

//////////////////////////////////////////////////////////////////

void preprocess(cv::Mat &imagenOriginal, cv::Mat &imagenEscalaDeGrises, cv::Mat &imagenUmbralizada) {

imagenEscalaDeGrises = extraerValor(imagenOriginal);

cv::Mat imagenMaxContrasteEscalaDeGrises = maximizarContraste(imagenEscalaDeGrises);

cv::Mat imagenSuavizada;

cv::GaussianBlur(imagenMaxContrasteEscalaDeGrises, imagenSuavizada, TAMAÑO_FILTRO_SUAVE_GAUSSIANO, 0);

cv::adaptiveThreshold(imagenSuavizada, imagenUmbralizada, 255.0, CV_ADAPTIVE_THRESH_GAUSSIAN_C, CV_THRESH_BINARY_INV, TAMAÑO_BLOQUE_UMBRAL, ANCHO_UMBRAL);
}

///////////////////////////////////////////////////////////////////

cv::Mat extraerValor(cv::Mat &imagenOriginal) {
cv::Mat imagenHSV;
std::vector<cv::Mat> vectorDeImagenesHSV(2);
cv::Mat imagenValor;

cv::cvtColor(imagenOriginal, imagenHSV, CV_BGR2HSV);

cv::split(imagenHSV, vectorDeImagenesHSV);

imagenValor = vectorDeImagenesHSV[2];

return(imagenValor);
}

  //////////////////////////////////////////////////////////////
cv::Mat maximizarContraste(cv::Mat &imagenEscalaDeGrises) {
cv::Mat imagenTopHat;
cv::Mat imagenBlackHat;
cv::Mat imagenEscalaDeGrisesMasTopHat;
cv::Mat imagenEscalaDeGrisesMasTopHatMenosBlackHat;

cv::Mat structuringElement = cv::getStructuringElement(CV_SHAPE_RECT, cv::Size(3, 3));

cv::morphologyEx(imagenEscalaDeGrises, imagenTopHat, CV_MOP_TOPHAT, structuringElement);
cv::morphologyEx(imagenEscalaDeGrises, imagenBlackHat, CV_MOP_BLACKHAT, structuringElement);

imagenEscalaDeGrisesMasTopHat = imagenEscalaDeGrises + imagenTopHat;
imagenEscalaDeGrisesMasTopHatMenosBlackHat = imagenEscalaDeGrisesMasTopHat - imagenBlackHat;

return(imagenEscalaDeGrisesMasTopHatMenosBlackHat);
}

//////////////////////////////////////////////////////
int main()
{
cv::Ptr<cv::ml::KNearest> kNearest(cv::ml::KNearest::create());

// read in training classifications ///////////////////////////////////////////////////
cv::Mat matrizClasificacionCaracteres;
cv::FileStorage archivoClasificacion("classifications.xml", cv::FileStorage::READ);
if (archivoClasificacion.isOpened() == false) {
    std::cout << "error, unable to open training classifications file, exiting program\n\n";
    return(0);
}
archivoClasificacion["classifications"] >> matrizClasificacionCaracteres;
archivoClasificacion.release();

// read in training images ////////////////////////////////////////////////////////////
cv::Mat matrizPixelesImagenReferencia;
cv::FileStorage archivoImagenes("images.xml", cv::FileStorage::READ);
if (archivoImagenes.isOpened() == false) {
    std::cout << "error, unable to open training images file, exiting program\n\n";
    return(0);
}
archivoImagenes["images"] >> matrizPixelesImagenReferencia;
archivoImagenes.release();

////////////////////////////////////////////////////////////////
//entrenamiento

//cv::Ptr<cv::ml::KNearest> kNearest(cv::ml::KNearest::create());

kNearest->setDefaultK(1);

kNearest->train(matrizPixelesImagenReferencia, cv::ml::ROW_SAMPLE, matrizClasificacionCaracteres);


//Leer la imagen base, de la cual obtengo solamente las dimensiones para asignarselas a mi verdadera imagen, con la que voy a trabajar
cv::Mat imagenBase = cv::imread("ImagenBase.png");
if (imagenBase.empty()) {
    std::cout << "No se puede leer la imagen del archivo \n\n";
    return (0);
}
////////////////////////////////////////////////////////////////

//Leer la imagen con la que voy a trabajar, a la cual le realizo el cambio de dimension
cv::Mat imagenOriginal = cv::imread("DNI-Jessica.jpg");

//cv::Mat imagenOriginal = cv::imread("DNI.png");
if (imagenOriginal.empty()) {
    std::cout << "No se puede leer la imagen del archivo \n\n";
    return (0);
}

// Redimensionar imagenOriginal al tamaño de imagenBase para que todas las imagenes de entrada tengan las mismas dimensiones
cv::resize(imagenOriginal, imagenOriginal, imagenBase.size());
cv::imshow("ImagenOriginal", imagenOriginal);


//Sección de interés 2: SEGUNDO APELLIDO///////////////////////////////
cv::Rect dimensionesROI2(255, 128, 300, 30);
//cv::Rect dimensionesROI2(250, 125, 300, 30);
//Recortar la imagen original para obtener ROI2
cv::Mat ROI2 = imagenOriginal(dimensionesROI2);

//PRE-PROCESADO DE LA IMAGEN/////////////////////////////////////////////

cv::Mat imagenEscalaDeGrises2;
cv::Mat imagenSuavizada2;
cv::Mat imagenUmbralizada2;


preprocess(ROI2, imagenSuavizada2, imagenUmbralizada2);

cv::threshold(imagenUmbralizada2, imagenUmbralizada2, 0.0, 255.0, CV_THRESH_BINARY | CV_THRESH_OTSU);

cv::Mat imagenDeTrabajo2 = imagenUmbralizada2.clone();

cv::imshow("Imagen pre-procesada 2", imagenUmbralizada2);

////////////////////////////////////////////////////////

std::vector<ContornoConDatos> todosLosContornos2;
std::vector<ContornoConDatos> contornosValidos2;

std::vector<std::vector<cv::Point> > ptContornos2;
std::vector<cv::Vec4i> v4iJerarquia2;

cv::findContours(imagenDeTrabajo2, ptContornos2, v4iJerarquia2, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE);

for (int i = 0; i < ptContornos2.size(); i++)
{
    ContornoConDatos contornoConDatos2;
    contornoConDatos2.ptContorno = ptContornos2[i];
    contornoConDatos2.rectanguloDelimitador = cv::boundingRect(contornoConDatos2.ptContorno);
    contornoConDatos2.fltAreaDelContorno = cv::contourArea(contornoConDatos2.ptContorno);
    todosLosContornos2.push_back(contornoConDatos2);
}

for (int i = 0; i < todosLosContornos2.size(); i++)
{
    if (todosLosContornos2[i].comprobarSiContornoValido())
    {
        contornosValidos2.push_back(todosLosContornos2[i]);
    }
}

std::sort(contornosValidos2.begin(), contornosValidos2.end(), ContornoConDatos::ordenarPosicionXRectDelim);
std::string strCadenaFinal2;

for (int i = 0; i < contornosValidos2.size(); i++)
{
    cv::rectangle(ROI2, contornosValidos2[i].rectanguloDelimitador, cv::Scalar(0, 255, 0), 2);

    cv::Mat imagenROI2 = imagenUmbralizada2(contornosValidos2[i].rectanguloDelimitador);

    cv::Mat imagenROIRedimensionada2;

    cv::resize(imagenROI2, imagenROIRedimensionada2, cv::Size(ANCHO_IMAGEN_REDIM, ALTURA_IMAGEN_REDIM));

    cv::Mat matrizROIDatosTipoReal2;

    imagenROIRedimensionada2.convertTo(matrizROIDatosTipoReal2, CV_32FC1);

    cv::Mat matrizROIConRealesSimples2 = matrizROIDatosTipoReal2.reshape(1, 1);

    cv::Mat vectorCaracterActual2(0, 0, CV_32F);

    kNearest->findNearest(matrizROIConRealesSimples2, 1, vectorCaracterActual2);

    float fltCaracterActual2 = (float)vectorCaracterActual2.at<float>(0, 0);
    strCadenaFinal2 = strCadenaFinal2 + char(int(fltCaracterActual2));
}

std::cout << "\n" << "SEGUNDO APELLIDO = " << strCadenaFinal2 << "\n";

cv::imshow("SEGUNDO APELLIDO", ROI2);

Preproceso.h CODE:

#include<opencv2/core/core.hpp>
#include<opencv2/highgui/highgui.hpp>
#include<opencv2/imgproc/imgproc.hpp>

// global variables 
//////////////////////////////////////////////
const cv::Size TAMAÑO_FILTRO_SUAVE_GAUSSIANO = cv::Size(5, 5);
const int TAMAÑO_BLOQUE_UMBRAL = 19;
const int ANCHO_UMBRAL = 9;

// function prototypes 
////////////////////////////////////////////////////////////////////////////

void preprocess(cv::Mat &imagenOriginal, cv::Mat &imagenEscalaDeGrises, 
cv::Mat &imagenUmbralizada);

cv::Mat extraerValor(cv::Mat &imagenOriginal);

cv::Mat maximizarContraste(cv::Mat &imagenEscalaDeGrises); 

This is the DNI image enter image description here

And this, the base image enter image description here

And to finish, here are the xml files that I use as a database in the program:

https://github.com/MicrocontrollersAndMore/OpenCV_3_KNN_Character_Recognition_Cpp/blob/master/classifications.xml

https://github.com/MicrocontrollersAndMore/OpenCV_3_KNN_Character_Recognition_Cpp/blob/master/images.xml

  • 1
    Can you provide an example Image? It's hard to improve your code without examples. Also please post your "preproceso.h". Is the purpose of this project to learn something or just tp get the job done? If it is about the job I recommemd the tesseract ocr library – TruckerCat Jul 17 '17 at 10:01
  • Sorry, I have already added all the files needed to compile the program – Jessica Garre Morales Jul 17 '17 at 11:48
  • 1
    From what I can tell, the fields you are pulling from make it pretty obvious whether or not you're expecting a string of letters or numbers. That should make it fairly easy to whitelist or blacklist certain characters (such as 0 when expecting only letters). – DavidBittner Jul 17 '17 at 11:54
  • Thanks, I wont be able to look at it today. But tomorrow I will check if I can help you – TruckerCat Jul 17 '17 at 13:28
  • Yes DavidBittner, a solution would be to establish in the fields of the name and surname that only letters will appear, but my knowledge of OCR and programming in general is rather scarce and then I can not find a way to do it. But thanks! – Jessica Garre Morales Jul 17 '17 at 15:30
  • Okey R_Valdez, many thanks! – Jessica Garre Morales Jul 17 '17 at 15:31

0 Answers0