12/08/2018, 13:25

OpenCV part4 : Nhận dạng chữ (Optical Character Recognition)

I. Nhận dạng chữ (Optical Character Recognition) Nhận dạng ký tự được sử dụng để trích xuất văn bản từ hình ảnh hoặc quét tài liệu. Văn bản này được sử dụng để chế biến tiếp như nó có thể được chỉnh sửa, định dạng, tìm kiếm, lập chỉ mục và tự động dịch hoặc chuyển đổi sang ngôn luận. Trong ...

I. Nhận dạng chữ (Optical Character Recognition)

Nhận dạng ký tự được sử dụng để trích xuất văn bản từ hình ảnh hoặc quét tài liệu. Văn bản này được sử dụng để chế biến tiếp như nó có thể được chỉnh sửa, định dạng, tìm kiếm, lập chỉ mục và tự động dịch hoặc chuyển đổi sang ngôn luận.

Trong một phân loại nhận dạng mẫu điển hình bao gồm ba phần:

pr.png

Tiền xử lý: trong mô-đun này, chúng tôi đi đến quá trình hình ảnh đầu vào của chúng tôi, ví dụ kích thước bình thường hóa, chuyển đổi màu BN ...

khai thác tính năng: trong mô-đun này, chúng ta chuyển đổi hình ảnh của chúng tôi xử lý một vector đặc trưng của các tính năng để phân loại, nó có thể là ma trận điểm ảnh chuyển đổi sang vector hoặc nhận được mã số chuỗi đường viền biểu diễn dữ liệu

mô-đun phân loại được các vectơ tính năng và đào tạo hệ thống của chúng tôi hoặc phân loại một vector đặc trưng đầu vào với một phương pháp classify như KNN.

Trong OCR cơ bản này, chúng tôi đi để sử dụng biểu đồ này:

knnc-graph.jpg

Chúng tôi tạo ra các thử nghiệm và kiểm tra các hình ảnh thử nghiệm sau đó tiến hành phân loại các mẫu để tạo ra mẫu nhận biết.

numbers.gif

Sau đó chúng tôi làm việc với các mẫu thử nghiệm.Để làm điều đó, chúng tôi tạo ra một chức năng tiền xử lý. Trong chức năng này, chúng ta có được một hình ảnh và một chiều rộng và chiều cao mới, chúng tôi muốn như là kết quả của tiền xử lý, thì hàm trả về một kích thước bình thường hoá với hình ảnh khung giới hạn. Bạn có thể xem chi tiết rõ ràng quá trình trong đồ thị dưới đây:

preprocesing.gif

Mã tiền xử lý:

void findX(IplImage* imgSrc,int* min, int* max){
    int i;
    int minFound=0;
    CvMat data;
    CvScalar maxVal=cvRealScalar(imgSrc->awidth * 255);
    CvScalar val=cvRealScalar(0);
    //For each col sum, if sum < awidth*255 then we find the min
    //then continue to end to search the max, if sum< awidth*255 then is new max
    for (i=0; i< imgSrc->awidth; i++){
        cvGetCol(imgSrc, &data, i);
        val= cvSum(&data);
            if(val.val[0] < maxVal.val[0]){
            *max= i;
            if(!minFound){
                *min= i;
                minFound= 1;
            }
        }
    }
}

void findY(IplImage* imgSrc,int* min, int* max){
    int i;
    int minFound=0;
    CvMat data;
    CvScalar maxVal=cvRealScalar(imgSrc->awidth * 255);
    CvScalar val=cvRealScalar(0);
    //For each col sum, if sum < awidth*255 then we find the min
    //then continue to end to search the max, if sum< awidth*255 then is new max
        for (i=0; i< imgSrc->height; i++){
        cvGetRow(imgSrc, &data, i);
        val= cvSum(&data);
            if(val.val[0] < maxVal.val[0]){
            *max=i;
            if(!minFound){
                *min= i;
                minFound= 1;
            }
        }
    }
}

CvRect findBB(IplImage* imgSrc){
    CvRect aux;
    int xmin, xmax, ymin, ymax;
    xmin=xmax=ymin=ymax=0;
    findX(imgSrc, &xmin, &xmax);
    findY(imgSrc, &ymin, &ymax);
    aux=cvRect(xmin, ymin, xmax-xmin, ymax-ymin);
    //printf("BB: %d,%d - %d,%d
", aux.x, aux.y, aux.awidth, aux.height);
    return aux;
}

IplImage preprocessing(IplImage* imgSrc,int new_awidth, int new_height){
    IplImage* result;
    IplImage* scaledResult;
    CvMat data;
    CvMat dataA;
    CvRect bb;//bounding box
    CvRect bba;//boundinb box maintain aspect ratio
    //Find bounding box
    bb=findBB(imgSrc);
    //Get bounding box data and no with aspect ratio, the x and y can be corrupted
    cvGetSubRect(imgSrc, &data, cvRect(bb.x, bb.y, bb.awidth, bb.height));
    //Create image with this data with awidth and height with aspect ratio 1
    //then we get highest size betwen awidth and height of our bounding box
    int size=(bb.awidth>bb.height)?bb.awidth:bb.height;
    result=cvCreateImage( cvSize( size, size ), 8, 1 );
    cvSet(result,CV_RGB(255,255,255),NULL);
    //Copy de data in center of image
    int x=(int)floor((float)(size-bb.awidth)/2.0f);
    int y=(int)floor((float)(size-bb.height)/2.0f);
    cvGetSubRect(result, &dataA, cvRect(x,y,bb.awidth, bb.height));
    cvCopy(&data, &dataA, NULL);
    //Scale result
    scaledResult=cvCreateImage( cvSize( new_awidth, new_height ), 8, 1 );
    cvResize(result, scaledResult, CV_INTER_NN);
    //Return processed data
    return *scaledResult;
}

Chúng tôi sử dụng các chức năng getData của lớp OCR cơ bản để tạo ra các dữ liệu đào tạo và các lớp đào tạo, chức năng này có được tất cả các hình ảnh trong thư mục OCR để tạo dữ liệu đào tạo này, các thư mục OCR được cấu trúc với 1 thư mục cho mỗi lớp và mỗi tập tin có được file PBM với tên này cnn.pbm đó c là lớp {0..9} và nn là số hình ảnh {00..99} Mỗi hình ảnh, chúng tôi nhận được là tiền xử lý và sau đó chuyển đổi các dữ liệu trong một vector đặc trưng, chúng tôi sử dụng.

  void basicOCR::getData(){
    IplImage* src_image;
    IplImage prs_image;
    CvMat row,data;
    char file[255];
    int i,j;
    for(i =0; i<classes; i++){
        for( j = 0; j< train_samples; j++){
            //Load file
            if(j<10)
                sprintf(file,"%s%d/%d0%d.pbm",file_path, i, i , j);
            else
                sprintf(file,"%s%d/%d%d.pbm",file_path, i, i , j);
            src_image = cvLoadImage(file,0);
            if(!src_image){
                printf("Error: Cant load image %s
", file);
                //exit(-1);
            }
            //process file
            prs_image = preprocessing(src_image, size, size);
            //Set class label
            cvGetRow(trainClasses, &row, i*train_samples + j);
            cvSet(&row, cvRealScalar(i));
            //Set data
            cvGetRow(trainData, &row, i*train_samples + j);
            IplImage* img = cvCreateImage( cvSize( size, size ), IPL_DEPTH_32F, 1 );
            //convert 8 bits image to 32 float image
            cvConvertScale(&prs_image, img, 0.0039215, 0);
            cvGetSubRect(img, &data, cvRect(0,0, size,size));
            CvMat row_header, *row1;
            //convert data matrix sizexsize to vecor
            row1 = cvReshape( &data, &row_header, 0, 1 );
            cvCopy(row1, &row, NULL);
        }
    }
}

Sau khi xử lý và nhận dữ liệu đào tạo và các lớp học khi các mô hình đào tạo của chúng tôi với các dữ liệu này, trong mẫu của chúng tôi, chúng tôi sử dụng phương pháp so sánh mẫu sau đó:

knn=new CvKNearest( trainData, trainClasses, 0, false, K );

Sau đó, chúng ta có thể kiểm tra mô hình của chúng tôi, và chúng tôi có thể sử dụng kết quả kiểm tra để so sánh với một phương pháp chúng tôi có thể sử dụng, hoặc nếu chúng tôi sẽ giảm quy mô hình ảnh hoặc tương tự. Có một chức năng để tạo ra các bài kiểm tra trong class OCR cơ bản, chức năng kiểm tra của chúng tôi.

Chức năng này có được 500 mẫu khác và phân loại này trong phương pháp lựa chọn của chúng tôi và kiểm tra kết quả thu được.

    void basicOCR::test(){
        IplImage* src_image;
        IplImage prs_image;
        CvMat row,data;
        char file[255];
        int i,j;
        int error=0;
        int testCount=0;
        for(i =0; i<classes; i++){
            for( j = 50; j< 50+train_samples; j++){

            sprintf(file,"%s%d/%d%d.pbm",file_path, i, i , j);
            src_image = cvLoadImage(file,0);
            if(!src_image){
            printf("Error: Cant load image %s
", file);
            //exit(-1);
            }
            //process file
            prs_image = preprocessing(src_image, size, size);
            float r=classify(&prs_image,0);
            if((int)r!=i)
            error++;

            testCount++;
            }
        }
        float totalerror=100*(float)error/(float)testCount;
        printf("System Error: %.2f%%
", totalerror);

    }

Kiểm tra sử dụng chức năng phân loại được rằng có được hình ảnh để phân loại, xử lý ảnh, nhận vector đặc trưng và phân loại nó với một find_nearest của lớp KNN. Chức năng này chúng tôi sử dụng để phân loại các hình ảnh người sử dụng đầu vào:

float basicOCR::classify(IplImage* img, int showResult){
    IplImage prs_image;
    CvMat data;
    CvMat* nearest=cvCreateMat(1,K,CV_32FC1);
    float result;
    //process file
    prs_image = preprocessing(img, size, size);

    //Set data
    IplImage* img32 = cvCreateImage( cvSize( size, size ), IPL_DEPTH_32F, 1 );
    cvConvertScale(&prs_image, img32, 0.0039215, 0);
    cvGetSubRect(img32, &data, cvRect(0,0, size,size));
    CvMat row_header, *row1;
    row1 = cvReshape( &data, &row_header, 0, 1 );

    result=knn->find_nearest(row1,K,0,0,nearest,0);

    int accuracy=0;
        for(int i=0;i<K;i++){
            if( nearest->data.fl[i] == result)
                accuracy++;
        }
    float pre=100*((float)accuracy/(float)K);
        if(showResult==1){
        printf("|	%.0f	| 	%.2f%%  	| 	%d of %d 	| 
",result,pre,accuracy,K);
        printf(" ---------------------------------------------------------------
");
        }

    return result;

}
0