Javascript Bradley adaptive thresholding implementation

1.3k Views Asked by At

I have been trying to implement Bradley Adaptive thresholding. I know there is a python code in one of the stack overflow questions. But i am struggling to implement the same in JS by following that. Can anyone please help me? So far my code is:

function computeAdaptiveThreshold (imagetest,imageWidth,imageHeight,callback) 
{

    var   size = imageWidth*imageHeight*4;
    var  s = imageWidth/8;
    var s2=s>>1;
    var t=0.15;
    var it=1.0-t;
    var i,j,diff,x1,y1,x2,y2,ind1,ind2,ind3;
    var sum=0;
    var ind=0;
    var integralImg = [];

    var canvas = document.createElement('canvas');
    var bin = canvas.getContext('2d').createImageData(imageWidth, imageHeight);

    for(i=0;i<imageWidth;i++)
    {
        sum = 0;

        for(j=0;j<imageHeight;j++)
        {
            index = i *imageHeight + j;
            sum += imagetest.data[index];

            if(i== 0)
            {
                integralImg[index] = sum;
            }
            else
            {
                //index = (i-1) * height + j;
                integralImg[index] = integralImg[(i-1) * imageHeight + j] + sum;
            }
        }
    }

    x1=0;

    for(i=1;i<imageWidth;++i)
    {
        sum=0;
        ind=i;
        ind3=ind-s2;
        if(i>s)
        {
            x1=i-s;
        }
        diff=i-x1;
        for(j=0;j<imageHeight;++j)
        {
            sum+=imagetest.data[ind];// & 0xFF;
            integralImg[ind] = integralImg[(ind-1)]+sum;
            ind+=imageWidth;
            if(i<s2)continue;
            if(j<s2)continue;
            y1=(j<s ? 0 : j-s);
            ind1=y1*imageWidth;
            ind2=j*imageWidth;

            if (((imagetest.data[ind3])*(diff * (j - y1))) < ((integralImg[(ind2 + i)] - integralImg[(ind1 + i)] - integralImg[(ind2 + x1)] + integralImg[(ind1 + x1)])*it)) {
                bin.data[ind3] = 0;
            } else {
                bin.data[ind3] = 255;
            }
            ind3 += imageWidth;
        }
    }


    y1 = 0;
    for( j = 0; j < imageHeight; ++j )
    {
        i = 0;
        y2 =imageHeight- 1;
        if( j <imageHeight- s2 )
        {
            i = imageWidth - s2;
            y2 = j + s2;
        }

        ind = j * imageWidth + i;
        if( j > s2 ) y1 = j - s2;
        ind1 = y1 * imageWidth;
        ind2 = y2 * imageWidth;
        diff = y2 - y1;
        for( ; i < imageWidth; ++i, ++ind )
        {

            x1 = ( i < s2 ? 0 : i - s2);
            x2 = i + s2;

            // check the border
            if (x2 >= imageWidth) x2 = imageWidth - 1;

            if (((imagetest.data[ind])*((x2 - x1) * diff)) < ((integralImg[(ind2 + x2)] - integralImg[(ind1 + x2)] - integralImg[(ind2 + x1)] + integralImg[(ind1 + x1)])*it)) {
                bin.data[ind] = 0;
            } else {
                bin.data[ind] = 255;
            }
        }
    }

    callback(bin);`

I am getting very bad images. I should say i cannot call it as a image.

1

There are 1 best solutions below

2
On

I think your first effort should be to refactor your code : it will be much easier to handle the index.
Then you'll see that you have issues with your indexes : an image -even a gray one- is an RGBA Array, meaning 4 bytes = 32 bits per pixel.
You could handle this by doing a conversion RGBA-> b&W image, then thresholding, then doing b&w -> RGBA back.
...Or handle the RGBA components as you go. Notice that here you only want to output black or white, so you can create an Int32 view on the array, and write at once R,G,B,A for each pixels.

So some code (working here : http://jsfiddle.net/gamealchemist/3zuopz19/8/ ) :

function computeAdaptiveThreshold(sourceImageData, ratio, callback) {
    var integral = buildIntegral_Gray(sourceImageData);

    var width = sourceImageData.width;
    var height = sourceImageData.height;
    var s = width >> 4; // in fact it's s/2, but since we never use s...

    var sourceData = sourceImageData.data;
    var result = createImageData(width, height);
    var resultData = result.data;
    var resultData32 = new Uint32Array(resultData.buffer);

    var x = 0,
        y = 0,
        lineIndex = 0;

    for (y = 0; y < height; y++, lineIndex += width) {
        for (x = 0; x < width; x++) {

            var value = sourceData[(lineIndex + x) << 2];
            var x1 = Math.max(x - s, 0);
            var y1 = Math.max(y - s, 0);
            var x2 = Math.min(x + s, width - 1);
            var y2 = Math.min(y + s, height - 1);
            var area = (x2 - x1 + 1) * (y2 - y1 + 1);
            var localIntegral = getIntegralAt(integral, width, x1, y1, x2, y2);
            if (value * area > localIntegral * ratio) {
                resultData32[lineIndex + x] = 0xFFFFFFFF;
            } else {
                resultData32[lineIndex + x] = 0xFF000000;
            }
        }
    }
    return result;
}

function createImageData(width, height) {
    var canvas = document.createElement('canvas');
    return canvas.getContext('2d').createImageData(width, height);
}

function buildIntegral_Gray(sourceImageData) {
    var sourceData = sourceImageData.data;
    var width = sourceImageData.width;
    var height = sourceImageData.height;
    // should it be Int64 Array ??
    // Sure for big images 
    var integral = new Int32Array(width * height)
    // ... for loop
    var x = 0,
        y = 0,
        lineIndex = 0,
        sum = 0;
    for (x = 0; x < width; x++) {
        sum += sourceData[x << 2];
        integral[x] = sum;
    }

    for (y = 1, lineIndex = width; y < height; y++, lineIndex += width) {
        sum = 0;
        for (x = 0; x < width; x++) {
            sum += sourceData[(lineIndex + x) << 2];
            integral[lineIndex + x] = integral[lineIndex - width + x] + sum;
        }
    }
    return integral;
}

function getIntegralAt(integral, width, x1, y1, x2, y2) {
    var result = integral[x2 + y2 * width];
    if (y1 > 0) {
        result -= integral[x2 + (y1 - 1) * width];
        if (x1 > 0) {
            result += integral[(x1 - 1) + (y1 - 1) * width];
        }
    }
    if (x1 > 0) {
        result -= integral[(x1 - 1) + (y2) * width];
    }
    return result;
}