How do I eliminate inner filling of letters?
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 -
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 ...
What are you trying to achieve here? I don't see any code to read this Text here!
Reading part is done using tesseract. @Balaji Renganathan
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.
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 use the normal threshold function with the
CV_THRESH_OTSU
parameter.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.
@theodore instead of adaptive threshold? @StevenPuttemans How do I find that? Could you help with suggestion/example?
@Vivekananth T yup
Check this blog post