4

I have an image as shown in the attached figure. Sometimes, the digit's black color intensity is not much difference from their neighbour pixels and I have problem to extract these digits (for example, setting a threshold is not efficient as black color's intensity is close to gray color's intensity because of reflection or not well focus during the image capture). I like to make it more difference between black and background gray color so I can extract the digit without much noise. What I do is I increase the difference using addWeighted function from OpenCV. color is the original RGB image. Does it make sense with my processing or is there any more efficient approach?

Mat blur_img = new Mat(color.size(), color.type());
org.opencv.core.Size size = new Size(9,9);
Imgproc.GaussianBlur(color, blur_img, size, 2);
Mat sharpened = new Mat(color.size(), CvType.CV_32FC3);
Core.addWeighted(color, 1.5, blur_img, -0.5, 0, sharpened);
batuman
  • 7,066
  • 26
  • 107
  • 229
  • Looks fine to me. You can also consider to use [Laplacian Filter](http://stackoverflow.com/questions/11878281/image-sharpening-using-laplacian-filter). – herohuyongtao Mar 13 '14 at 15:53
  • I feel like not very reliable, some captured images it works well. But for some, it doesn't do really. – batuman Mar 13 '14 at 16:00

3 Answers3

5

you need to do local thresholding(bernsen,sauvola,local otsu etc), opencv also happens to have adaptiveThreshold function. Here's an example. Just make sure to play around with the parameters.

adaptiveThreshold

adaptiveThreshold

bernsen

bernsen

code

#include <opencv2/opencv.hpp>
using namespace cv;

Mat thresh_bernsen(Mat& gray,int ksize,int contrast_limit)
{
    Mat ret = Mat::zeros(gray.size(),gray.type());
    for(int i=0;i<gray.cols;i++ )
    {
        for(int j=0;j<gray.rows;j++ )
        {
            double mn=999,mx=0;
            int ti=0,tj=0;
            int tlx=i-ksize/2;
            int tly=j-ksize/2;
            int brx=i+ksize/2;
            int bry=j+ksize/2;
            if(tlx<0) tlx=0;
            if(tly<0) tly=0;
            if(brx>=gray.cols) brx=gray.cols-1;
            if(bry>=gray.rows) bry=gray.rows-1;

            minMaxIdx(gray(Rect(Point(tlx,tly),Point(brx,bry))),&mn,&mx,0,0);
            /* this does the above
            for(int ik=-ksize/2;ik<=ksize/2;ik++)
            {
                for(int jk=-ksize/2;jk<=ksize/2;jk++)
                {
                    ti=i+ik;
                    tj=j+jk;
                    if(ti>0 && ti<gray.cols && tj>0 && tj<gray.rows)
                    {
                        uchar pix = gray.at<uchar>(tj,ti);
                        if(pix<mn) mn=pix;
                        if(pix>mx) mx=pix;
                    }
                }
            }*/
            int median = 0.5 * (mn+mx);
            if(median<contrast_limit)
            {
                ret.at<uchar>(j,i)=0;
            }else
            {
                uchar pix = gray.at<uchar>(j,i);
                ret.at<uchar>(j,i) = pix>median?255:0;
            }
        }
    }
    return ret;
}
int main()
{
    Mat gray = imread("c:/data/number.jpg",0);
    gray=255-gray;
    Mat adaptthresh,bernsen;
    adaptiveThreshold(gray,adaptthresh,255,ADAPTIVE_THRESH_GAUSSIAN_C,THRESH_BINARY,41,1);
    bernsen=thresh_bernsen(gray,25,40);
    imshow("gray",gray);
    imshow("adaptthresh",adaptthresh);
    imshow("bernsen",bernsen);
    waitKey(0);
}
Zaw Lin
  • 5,629
  • 1
  • 23
  • 41
  • bernsen has better result, but it is very slow especially for my mobile application. – batuman Mar 14 '14 at 06:59
  • ya..the implementation is in need of some speed up :(. maybe there's a neon implementation somewhere... failing that,you can try imagej. it implements a bunch of local thresholding methods. maybe one of them is fast enough and good enough for you. you can also try tweaking the parameters of adaptivethreshold, like maybe increase the blocksize or median blur the image before adaptivethreshold. or you can downsize the image before passing to bernsen(need to reduce ksize for smaller images) to make it faster. – Zaw Lin Mar 14 '14 at 18:47
3

Simple thresholding doesn’t account for lighting variations across the image and adaptive thresholding doesn’t take advantage of connected regions.

The current leader in extraction of segments like this is MSER. It goes through all possible thresholds and finds connected most stable regions (across all thresholds). Don’t re-invent a bicycle, use the proven best features and open source such as openCV MSER. There are more links on stack overflow.

Community
  • 1
  • 1
Vlad
  • 4,425
  • 1
  • 30
  • 39
  • But isn't the code in OpenCV for MSER for detecting keypoints? Could you perhaps also provide the reference articles/code to show how to use it to perform segmentation in this case because the ones you gave are for detecting keypoints and not segmentation per se and it is not obvious how the modifications can be made. I think it'll be interesting to take a look at that. Thanks. – lightalchemist Mar 14 '14 at 07:20
  • This for detecting regions like CC (connected components) but more stable. Follow the links above to find code, these are the best I could find. MSER is widely used now for text detection. Also see mser_sample.cpp in c folder of samples src. – Vlad Mar 14 '14 at 13:59
  • mser probably cant handle uneven lighting...vlfeat also has a mser implementation and i think they give different results(vlfeat seems better)..might as well try both – Zaw Lin Mar 14 '14 at 18:25
1

EDIT: Following the comments by Vlad, I found the following link on OpenCV's 3.0 dev branch: http://docs.opencv.org/trunk/modules/objdetect/doc/erfilter.html. That appears to be the technique for detecting text using a modification of the original MSER algorithm for detecting keypoint mentioned by Vlad. Seems like OpenCV may have it soon. The details for this technique is described in 2 papers found in the link above. Thanks Vlad.

If you filter your image using Non-local means denoising before you threshold it using the algorithms mentioned by Zaw, you can reduce some of the noise:

fastNlMeansDenoising(gray, gray, 3, 5, 15);

Adaptive threshold as described by Zaw above (using same parameters):

adaptive_threshold

Bernsen threshold as described by Zaw (using same parameters): bern_threshold

Adjusting the parameters for thresholding and denoising might give you better results.

lightalchemist
  • 10,031
  • 4
  • 47
  • 55