How do I eliminate inner filling of letters?

asked 2015-06-18 03:29:46 -0500

updated 2015-06-18 09:49:05 -0500

Hi,

I am working on my first ever OpenCV project of 'reading text from image'. The sample image I'm using for testing is here -

image description

And the code,

cv::Mat image;
cv::Mat canny_output;
UIImageToMat(input, image, false);

    //Equalizing color
double alpha = 2.2; //1.0 to 3.0
double beta = 50.0; //1.0 to 100.0
Mat img = Mat::zeros( image.size(), image.type() );
image.convertTo(img, -1, alpha, beta);

    //Reduce noise and sharpen image
int kernel_length = 3;//blur reduces noise
blur(img, img, cv::Size(kernel_length, kernel_length), cv::Point(-1, -1));
GaussianBlur(img, img, cv::Size(kernel_length, kernel_length), 0, 0);
medianBlur(img, img, kernel_length);
    //sharpen
cv::addWeighted(img, 3.0, image, -0.5, 0, image);

    //Histogram Equalization
cvtColor(img, img, COLOR_BGR2GRAY);
adaptiveThreshold(~img, img, 255, ADAPTIVE_THRESH_GAUSSIAN_C, THRESH_BINARY, 255, -10);
equalizeHist( img, img );
cvtColor(img, img, COLOR_GRAY2BGR);

    //Local threshold
double maxVal, minVal;
cv::Mat output;
int top, bottom, left , right;
int borderType = cv::BORDER_CONSTANT;
cv::Scalar value;
top = (int) (4); bottom = (int) (4);
left = (int) (4); right = (int) (4);
output = img;
value = 0;
cv::copyMakeBorder(img,output,top,bottom,left,right,borderType,value);

for(int y = 4; y < output.rows - 4; y++) {
    for(int x = 4; x < output.cols - 4; x ++) {
            // apply local ROI
        cv::Mat ROI = output(cv::Rect(cv::Point(x-4,y-4),cv::Size(9,9)));
        cv::minMaxLoc(ROI,&minVal,&maxVal);    // extract max intensity values in the ROI
        if(img.at<uchar>(cv::Point(x-4,y-4)) >= 0.5*maxVal){    // apply local threshold w.r.t highest intensity level
            output.at<uchar>(cv::Point(x-4,y-4)) = 255;            // change pixel value in mask if true
        }else{
            output.at<uchar>(cv::Point(x-4,y-4)) = 0;
        }
    }
}

    //Canny edge
Canny(img, canny_output, 50, 200, 3);
cv::dilate(canny_output, canny_output, cv::Mat(), cv::Point(1, 1));

Mat structKernel = cv::getStructuringElement(MORPH_RECT, cv::Size(1, 1));
Mat closed;
cv::morphologyEx(canny_output, closed, MORPH_CLOSE, structKernel);
canny_output.convertTo(canny_output, CV_32F);
cv::divide(canny_output, closed, canny_output, 1, CV_32F);
cv::normalize(canny_output, canny_output, 0, 255, NORM_MINMAX);
canny_output.convertTo(canny_output, CV_8UC1);
cv::Size s = canny_output.size();
int rows = s.height;
int cols = s.width;
for (int i = 0; i < rows; i++) {
    for (int j = 0; j < cols; j++) {
        Mat block = canny_output.rowRange(i, (i+1)).colRange(j, (j+1));
        threshold(block, block, -1, 255, THRESH_BINARY_INV | THRESH_OTSU);
    }
}
adaptiveThreshold(canny_output, canny_output, 255, ADAPTIVE_THRESH_MEAN_C, THRESH_BINARY,75,10);
bitwise_not(canny_output, canny_output);

int divider = 1;
if (canny_output.rows > canny_output.cols) {
    divider = (canny_output.rows / canny_output.cols) * 4.3;
} else {
    divider = (canny_output.cols / canny_output.rows) * 4.3;
}

    //Remove unwanted horizontal, vertical lines
Mat horizontal = canny_output.clone();
int horizontalsize = horizontal.cols / divider;
Mat horizontalStructure = getStructuringElement(MORPH_RECT, cv::Size(horizontalsize,1));
erode(horizontal, horizontal, horizontalStructure, cv::Point(-1, -1));
dilate(horizontal, horizontal, horizontalStructure, cv::Point(-1, -1));
cv::bitwise_xor(canny_output, horizontal, canny_output);

Mat vertical = canny_output.clone();
int verticalsize = vertical.rows / divider;
Mat verticalStructure = getStructuringElement(MORPH_RECT, cv::Size( 1,verticalsize));
erode(vertical, vertical, verticalStructure, cv::Point(-1, -1));
dilate(vertical, vertical, verticalStructure, cv::Point(-1, -1));
cv::bitwise_xor(canny_output, vertical, canny_output);
cv::bitwise_not(canny_output, canny_output);
    // Step 1
Mat ...
(more)
edit retag flag offensive close merge delete

Comments

What are you trying to achieve here? I don't see any code to read this Text here!

Balaji R gravatar imageBalaji R ( 2015-06-18 04:46:37 -0500 )edit

Reading part is done using tesseract. @Balaji Renganathan

Vivekananth T gravatar imageVivekananth T ( 2015-06-18 05:14:56 -0500 )edit
1

Is this some sort of example image for your approach? Because i can simply apply adaptive threshold on this image with parameters BlockSize 29 & Min GreyThreshold 16. Here is your desired results.

Balaji R gravatar imageBalaji R ( 2015-06-18 05:34:55 -0500 )edit

Yes this is a test image. Is there any way to dynamically calculate the block size and threshold? That is the same result I want. @Balaji Renganathan

Vivekananth T gravatar imageVivekananth T ( 2015-06-18 05:51:53 -0500 )edit
1

@Vivekananth T use the normal threshold function with the CV_THRESH_OTSU parameter.

theodore gravatar imagetheodore ( 2015-06-18 09:45:09 -0500 )edit
1

The filling is due to the fact you are using contours with outer contours only. You will need to use contours with the hierarchy option and then remove all contours that have a parent contour.

StevenPuttemans gravatar imageStevenPuttemans ( 2015-06-18 09:50:08 -0500 )edit

@theodore instead of adaptive threshold? @StevenPuttemans How do I find that? Could you help with suggestion/example?

Vivekananth T gravatar imageVivekananth T ( 2015-06-18 12:02:36 -0500 )edit
theodore gravatar imagetheodore ( 2015-06-18 15:22:03 -0500 )edit
StevenPuttemans gravatar imageStevenPuttemans ( 2015-06-19 02:53:49 -0500 )edit

@theodore Changing as you suggested, fixed the problem. Thank you! @StevenPuttemans Thank you for the suggestion. I tried various combos, the problem is, the image's background has multiple colors, so I stopped with thresholding and passed it to OCR.

Vivekananth T gravatar imageVivekananth T ( 2015-06-22 03:31:25 -0500 )edit