0

I wanted to binarize low quality images and found that the existing solutions or programs which are implementations of global and local binarization techniques such as Sauvola’s method, NiBlack's method etc are not off much use.

I did find a few good papers regarding much better methods like the ones given in the papers: 1) http://www.ski.org/sites/default/files/publications/wacv11-display-reader.pdf#cite.adap-binar 2) https://www.jstage.jst.go.jp/article/elex/1/16/1_16_501/_pdf

But I haven't worked on image processing much before and so I wanted to know how I could proceed to implement it and what knowledge I need to implement these algorithms

Akshay Pai
  • 11
  • 3
  • you should first implement it in an easy-to-code way (to have a solution to compare to) and care about optimization later. From the paper you just have to know how to compute mean and variance of a subimage. – Micka Oct 20 '15 at 07:12
  • but what about the matrices mentioned ? how to i build that from the source images ? – Akshay Pai Oct 20 '15 at 07:16
  • arent you just interested in the binarization (2.1) ? – Micka Oct 20 '15 at 07:18
  • yes, but binarization requires thresholding, which intern requires the martix to fetch the pixels vales or something similar right ? – Akshay Pai Oct 20 '15 at 08:47
  • you mean the image and subimages? – Micka Oct 20 '15 at 08:51
  • Yes, one of the papers say I need to calculate the intensities of windows and so I am not sure how I can do that. I am guessing i have to extract the pixel values and store it into matrices and then run the thresholding functions on them. But I a not sure how i can run them – Akshay Pai Oct 20 '15 at 08:55
  • i cant access the 2nd paper. is the link broken? – Micka Oct 20 '15 at 09:57
  • posted an implementation of the first paper. – Micka Oct 20 '15 at 12:06
  • Can you give us your image? I have some algorithm and want test the result – Gogo Oct 09 '20 at 21:32

1 Answers1

1

I implemented the binarization of the first paper in like 10 minutes (less time than processing the 2nd image) - no guarantee that it's correct, better have a look at the formulas yourself:

int main()
{
    //cv::Mat input = cv::imread("../inputData/Lenna.png");
    cv::Mat input = cv::imread("../inputData/LongLineColor.jpg");

    cv::Mat gray;
    cv::cvtColor(input,gray,CV_BGR2GRAY);


    cv::Mat binaryImage = cv::Mat::zeros(gray.rows, gray.cols, CV_8UC1);

    // binarization:

    // TODO: adjust to your application:
    int smallWindowSize = 17; // suggested by the paper
    int bigWindowSize = 35; // suggested by the paper

    // TODO: adjust to your application
    double minTau =  10 ;

    // create roi relative to (0,0)
    cv::Rect roiTemplate1 = cv::Rect(-smallWindowSize/2,-smallWindowSize/2, smallWindowSize, smallWindowSize);
    cv::Rect roiTemplate2 = cv::Rect(-bigWindowSize/2,-bigWindowSize/2, bigWindowSize, bigWindowSize);

    cv::Rect imgROI = cv::Rect(0,0, gray.cols, gray.rows);

    for(int y=0; y<gray.rows; ++y)
    {
        std::cout << y << std::endl;
        for(int x=0; x<gray.cols; ++x)
        {
            double pixelThreshold = 255;

            // small roi
            cv::Rect cROIs = roiTemplate1 + cv::Point(x,y);
            // test whether ROI is inside the image. Reduce otherwise:
            cROIs = cROIs & imgROI;
            if(cROIs.width == 0 || cROIs.height == 0)
                continue;   // ignore this pixel

            // large roi
            cv::Rect cROIl =  roiTemplate2 + cv::Point(x,y);
            cROIl = cROIl & imgROI;
            if(cROIl.width == 0 || cROIl.height == 0)
                continue;   // ignore this pixel

            cv::Mat subSmall = gray(cROIs);
            cv::Mat subLarge = gray(cROIl);

            // evaluate subimages:
            // standard deviations
            double stdDevS =0;
            double stdDevL =0;
            // mean value
            double meanS =0;
            double minL =DBL_MAX;
            double meanL =0;

            // mean of small region
            for(int j=0; j<subSmall.rows; ++j)
                for(int i=0; i<subSmall.cols; ++i)
                {
                    meanS += subSmall.at<unsigned char>(j,i);
                }
            meanS = meanS/ (double)(subSmall.cols*subSmall.rows);


            // stddev of small region
            for(int j=0; j<subSmall.rows; ++j)
                for(int i=0; i<subSmall.cols; ++i)
                {
                    double diff = subSmall.at<unsigned char>(j,i) - meanS;
                    stdDevS += diff*diff;
                }
            stdDevS = sqrt(stdDevS/(double)(subSmall.cols*subSmall.rows));



            // mean and min of large region
            for(int j=0; j<subLarge.rows; ++j)
                for(int i=0; i<subLarge.cols; ++i)
                {
                    if(subLarge.at<unsigned char>(j,i)  < minL)
                    {
                        minL = subLarge.at<unsigned char>(j,i);
                        meanL += subLarge.at<unsigned char>(j,i);
                    }
                }
            meanL = meanL/ (double)(subLarge.cols*subLarge.rows);

            // stddef of large region
            for(int j=0; j<subLarge.rows; ++j)
                for(int i=0; i<subLarge.cols; ++i)
                {
                    double diff = subLarge.at<unsigned char>(j,i) - meanL;
                    stdDevL += diff*diff;
                }
            stdDevL = sqrt(stdDevL/(double)(subLarge.cols*subLarge.rows));

            // formula (2)
            double tau = ((meanS - minL) * (1-stdDevS/stdDevL))/2.0;
            // minimum
            if(tau < minTau) tau = minTau;

            // formula (1)
            double Threshold = meanS - tau;


            // for debugging:
            /*
            std::cout << "    meanS:" << meanS << std::endl;
            std::cout << "    std S:" << stdDevS << std::endl;
            std::cout << "    min L:" << minL << std::endl;
            std::cout << "    meanL:" << meanL << std::endl;
            std::cout << "    std L:" << stdDevL << std::endl;
            std::cout << "  threshold: " << Threshold << std::endl;
            */


            unsigned char pixelVal = gray.at<unsigned char>(y,x);
            if(pixelVal >= Threshold)
                binaryImage.at<unsigned char>(y,x) = 255;
            else
                binaryImage.at<unsigned char>(y,x) = 0;
        }
    }



    cv::imshow("input", input);
    cv::imshow("binary", binaryImage);
    //cv::imwrite("../outputData/binaryCustom.png", binaryImage);
    cv::waitKey(0);
    return 0;
}

giving me these results:

enter image description hereenter image description here

and

enter image description hereenter image description here

It is very slow but not optimized or encapsulated at all ;) And the results aren't sooo good imho. Probably you have to adjust the windowSizes to your application/task/objectSize

Micka
  • 19,585
  • 4
  • 56
  • 74
  • I tried this and for m use case, its definitely giving a better result but ti far from my requirement. Also it requires a lot of optimization as it is very slow. I might have to look for alternative solutions all together (i.e not following the binarization approach) . Thanks you for helping out. – Akshay Pai Oct 24 '15 at 09:30