Akshay Pai
Akshay Pai

Reputation: 11

image binarization using newer Algorithms in opencv

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

Upvotes: 0

Views: 1849

Answers (1)

Micka
Micka

Reputation: 20130

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

Upvotes: 1

Related Questions