0

My self implemented otsu returns a good quality of binarize image but if the image has "more black" pixels it returns a full black image and if the image has "more white" pixels it returns a full white image.

Im using drawable and if I use camera it returns the output with the same idea (full black or full white). How can I correctly binarize the image even if the image has more black or more white?

(the image that returns with full black or full white can be binarize by sauvola as I try to compare and check the output. I didn't put the sauvola output to avoid long post but I can post it if needed.)

Here are some of the output on Otsu:

Good Output: #1 #2

Bad Output: #3 #4 (the image below is from camera capture) #5

Otsu Binarization Code

Bitmap BWimg = Bitmap.createBitmap(gImg.getWidth(), gImg.getHeight(), gImg.getConfig());

    int width = gImg.getWidth();
    int height = gImg.getHeight();
    int A, R, G, B, colorPixel;

    // histo-thresh

    double Wcv = 0;
    int[] Bx = new int[256];
    int[] By = new int[256];
    int[] Fx = new int[256];
    int[] Fy = new int[256];
    double Bw = 0, Bm = 0, Bv = 0, Bp = 0;
    double Fw = 0, Fm = 0, Fv = 0, Fp = 0;
    int c = 0, ImgPix = 0;

    // pixel check for histogram

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

            colorPixel = gImg.getPixel(x, y);

            A = Color.alpha(colorPixel);
            R = Color.red(colorPixel);
            G = Color.green(colorPixel);
            B = Color.blue(colorPixel);

            int gray = (int) (0.2989 * R + 0.5870 * G + 0.1140 * B);
            if (gray > 128) { // white - foreground
                Fx[gray] = gray;
                Fy[gray] = Fy[gray] + 1;
                Fw = Fw + 1;
                Fp = Fp + 1;
            }
            else { // black - background
                Bx[gray] = gray;
                By[gray] = By[gray] + 1;
                Bw = Bw + 1;
                Bp = Bp + 1;
            }
            ImgPix = ImgPix + 1;
        }
    }

    //BG hist
    Bw = Bw / ImgPix; //BG weight

    int i;
    for (i = 0; i < Bx.length; i++) { //BG mean
        Bm = Bm + (Bx[i] * By[i]);
        Bm = Bm / Bp;
    }
    for (i = 0; i < Bx.length; i++) { //BG variance
        Bv = Bv + (Math.pow((Bx[i] - Bm), 2) * By[i]); // (Bx[i]-Bm) * (Bx[i]-Bm)
    }
    Bv = Bv / Bp;


    //FG hist
    Fw = Fw / ImgPix; //BG weight

    for (i = 0; i < Bx.length; i++) { //BG mean
        Fm = Fm + (Fx[i] * Fy[i]);
    }
    Fm = Fm / Fp;

    for (i = 0; i < Bx.length; i++) { //BG variance
        Fv = Fv + (Math.pow((Fx[i] - Fm), 2) * Fy[i]); // (Fx[i]-Fm) * (Fx[i]-Fm)
    }
    Fv = Fv / Fp;

    // within class variance
    Wcv = (Bw * Bv) + (Fw * Fv);

    //int gray2 = 0;

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

            colorPixel = gImg.getPixel(x, y);

            A = Color.alpha(colorPixel);
            R = Color.red(colorPixel);
            G = Color.green(colorPixel);
            B = Color.blue(colorPixel);

            //int gray2 = (int) ((0.2989 * R) + (0.5870 * G) + (0.1140 * B));
            int gray2 = (R + G + B);
            if (gray2 > Wcv) {
                gray2 = 255;
            }
            else {
                gray2 = 0;
            }

            BWimg.setPixel(x, y, Color.argb(A, gray2, gray2, gray2));
        }
    }

    return BWimg;
Ray
  • 50
  • 11
  • I've noticed that your past 3 questions are variations on a theme, really asking about the techniques for image thresholding. Nothing here is specifically Android. And as you've described yourself as a 'programming newbie' I would lean toward going through existing tutorials/courses to give you a framework of understanding what you are doing specifically in image processing (a very deep subject where you may be coming upon the limits of the algorithm). Use your favorite search engine and responses here: https://stackoverflow.com/questions/7432277/how-do-i-get-started-with-image-processing – Morrison Chang Apr 11 '18 at 22:38
  • Im asking about how can I fix my output since I self-implemented it as my project. This is the last for otsu since i can return a good binarize image but not every time. And since i can't something that can fix it (except for some open source that I need to change whole self implemented code) pdf and labbook really helped me to make my own code but my only problem is the output. Recreating tesseract is my project and yes, im going to use opencv for some process but for others, it's self-implemented (Otsu-almost, Sauvola-done, and maybe Blobs) – Ray Apr 11 '18 at 22:48
  • Assuming you are basing this code from: http://www.labbookpages.co.uk/software/imgProc/otsuThreshold.html where is the conditional for the foreground weight and background weight (the `continue` and `break`)? Also where in your code is the new maximum found? – Morrison Chang Apr 12 '18 at 02:47
  • the first part with "// pixel check for histogram" comment and width and height for loop is where i check if the "gray" > 128 or not. if yes then it saves to Foreground else Background. after that it will solve BG weight, mean, variance then FG weight, mean, variance. then finally within class variance. after that, width-height for loop for pixels and if gray2 > Wcv(threshold-within class variance) setPixel to 255 else 0. and yes, im using within class variance, not between class variance. – Ray Apr 12 '18 at 03:01
  • 1
    `where i check if the "gray" > 128 or not` - why? the image should already be in grayscale format (pixel by pixel), you are calculating the new white/black threshold via Otsu. I recommend taking a working version from whichever site you've based the code on and running it through their test images and your test images. See if you can figure out what isn't matching like do you have the same histogram for the same image. Good luck. – Morrison Chang Apr 12 '18 at 03:23
  • it's just to check if the pixel is greater than 128 or not so i can separate black and white as BG-FG since its already in grayscale. I just dont know why do i get a good result and sometimes not if black has more values than white and vice versa(as seen in the "Bad Output" sample on my question. ill still try to implement the code from my sources and ill try to convert it from java to android. and thanks for trying to help me. – Ray Apr 12 '18 at 04:13

1 Answers1

1

I got my answer last tuesday and forgot to post it. And I got it running.

@Morrison Chang thanks for the little push and clarifications about my codes

    Bitmap BWimg = Bitmap.createBitmap(gImg.getWidth(), gImg.getHeight(), gImg.getConfig());

    int width = gImg.getWidth();
    int height = gImg.getHeight();
    int A, R, G, B, colorPixel;

    double Wcv = 0, th = 0;
    int[] tPXL = new int[256];
    int[][] pxl = new int[width][height];
    double Bw, Bm, Bv, Fw, Fm, Fv;
    int np, ImgPix = 0, fth = 0;

    // pixel check for histogram //
    for (int x = 0; x < width; x++) {
        for (int y = 0; y < height; y++) {

            colorPixel = gImg.getPixel(x, y);

            A = Color.alpha(colorPixel);
            R = Color.red(colorPixel);
            G = Color.green(colorPixel);
            B = Color.blue(colorPixel);

            int gray = (int) ( (0.2126 * R) + (0.7152 * G) + (0.0722 * B) ); // (int) ( (0.299 * R) + (0.587 * G) + (0.114 * B) );
            pxl[x][y] = gray;
            tPXL[gray] = tPXL[gray] + 1;
            ImgPix = ImgPix + 1;
        }
    }

    // ----- histo-variance ----- //
    for (int t = 0; t < 256; t++){
        Bw = 0; Bm = 0; Bv = 0;
        Fw = 0; Fm = 0; Fv = 0;
        np = 0;

        if (t == 0){ // all white/foreground as t0 ----- //
             Fw = 1;

            for (int d = 0; d < 256; d++) { //mean
                Fm = Fm + (d * tPXL[d]);
            }
            Fm = Fm / ImgPix;

            for (int e = 0; e < 256; e++) { //variance
                Fv = Fv + (Math.pow((e - Fm), 2) * tPXL[e]);
            }
            Fv = Fv / ImgPix;

        }

        else { // main thresholding
            for (int d = 0; d < (t-1); d++){ // BG weight & mean + BG pixel
                Bw = Bw + tPXL[d];
                Bm = Bm + (d * tPXL[d]);
                np = np + tPXL[d];
            }
            Bw = Bw / ImgPix;
            Bm = Bm / np;

            for (int e = 0; e < (t-1); e++) { //BG variance
                Bv = Bv + (Math.pow((e - Bm), 2) * tPXL[e]);
            }
            Bv = Bv / np;

            for (int j = t; j < 256; j++) { // FG weight & mean + BG pixel
                Fw = Fw + tPXL[j];
                Fm = Fm + (j * tPXL[j]);
                np = ImgPix - np;
            }
            Fw = Fw / ImgPix;
            Fm = Fm / np;

            for (int k = t; k < 256; k++) { //FG variance
                Fv = Fv + (Math.pow((k - Fm), 2) * tPXL[k]);
            }
            Fv = Fv / np;

        }

        // within class variance
        Wcv = (Bw * Bv) + (Fw * Fv);

        if (t == 0){
            th = Wcv;
        }
        else if (Wcv < th){
            th = Wcv;
            fth = t;
        }
    }

    // set binarize pixel
    for (int x = 0; x < width; x++) {
        for (int y = 0; y < height; y++) {

            int fnpx = pxl[x][y];
            colorPixel = gImg.getPixel(x, y);

            A = Color.alpha(colorPixel);

            if (fnpx > fth) { //R > fth
                fnpx = 255;
                BWimg.setPixel(x, y, Color.argb(A, fnpx, fnpx, fnpx));
            }

            else {
                fnpx = 0;
                BWimg.setPixel(x, y, Color.argb(A, fnpx, fnpx, fnpx));
            }
        }
    }

    return BWimg;
Ray
  • 50
  • 11