2

I was researching on how to calculate an Optimal Threshold for ImageJ and found this explanation of the Otsu Thresholding, which I thought made perfect sense for me to use.

I've struggled with implementing it though, and after some thought I found a mistake with the way the weight and mean were calculated, and it now finds the optimal threshold of 77, which for the coin image looks good to me since it almost completely separates the background from the coins (and you'd be able to automatically count the coins, or measure them for size, ectr)

new coin image with optimal threshold

it also seems to work pretty well with this image, even though it has varying intensities of light: rice image with varying intensities

I'm quite happy with my solution found, but if you have any feedback or can find something else, that'd be great! This homework was tough but I learned a lot from it :)

public float calculateMeanFG(int[] histogram, int t) {
    float sumI = 0;
    int total = 0;

    //cumulate the histogram for < 256
    for (int i = t; i < 256; i++) {
        sumI += histogram[i] * i;
        total = i;
    }

    return sumI / total;
}

public float calculateMeanBG(int[] histogram, int t) {
    float sumI = 0;

    //cumulate the histogram for < t
    for (int i = 0; i < t; i++) {
        sumI += histogram[i] * i;
    }
    return sumI;
}


public float calculateWeightFG(int[] histogram, int t, int total) {
    int sum = 0;
    for (int i = t; i < 256; i++) {
        sum += histogram[i];

    }

    return sum / total;
}


public int[] getHistogram(ImageProcessor ip, int height, int width) {
    byte[] outP = ((byte[]) ip.getPixels()).clone();
    int[][] inDataArr = new int[width][height];
    int[] histogram = new int[256];

    int idx = 0;
    for (int y = 0; y < height; y++) {
        for (int x = 0; x < width; x++) {
            // fill in values
            inDataArr[x][y] = outP[idx];
            if (inDataArr[x][y] < 0) {
                inDataArr[x][y] += 256;
            } // if
            histogram[inDataArr[x][y]] += 1; // count grayscale occurrences
            idx++;
        } // for x
    } // for y

    return histogram;
}

public int[][] convergeOptThresh(int[][] imgArr, int width, int height) {

    int BG_VAL = 0;
    int FG_VAL = 255;

    int[] histogram = getHistogram(ip, height, width);

    // total number of pixels
    int total = imgArr.length;
    // cumulative hist
    float sum = 0;
    for (int i = 0; i < 256; i++)
        sum += i * histogram[i];

    float sumBG = 0; // sum background
    float weightBG = 0;
    float weightFG = 0;

    float varMax = 0;
    int threshold = 0;

        for (int t = 0; t < 256; t++) {
            weightBG = calculateMeanBG(histogram, t);
            weightBG /= total;

            weightFG = calculateWeightFG(histogram, t, total);
            if ((int)weightFG == 0)
                break;

            sumBG += (float) (t * histogram[t]);

            float meanBG = sumBG / t;
            float meanFG = calculateMeanFG(histogram, t);

            // calculate between class variance
            float varBetween = weightBG * weightFG * (meanBG - meanFG) * (meanBG - meanFG);

            // check if new max found
            if (varBetween > varMax) {
                varMax = varBetween;
                threshold = t;
            }

    }

    IJ.log("optimal threshold: " + threshold);

    int[][] retArr = new int[width][height];

    for (int x = 0; x < width; x++) {
        for (int y = 0; y < height; y++) {
            if (imgArr[x][y] <= threshold) {
                retArr[x][y] = BG_VAL;
            } else {
                retArr[x][y] = FG_VAL;
            }
        }
    }

    return retArr;
}
gizmo
  • 81
  • 8
  • @gpasch the threshold calculated in the image is 42, which removes all detail of the original coin image – gizmo Jan 31 '19 at 08:52
  • @gpasch I think i have found a solution- my mistake was in calculating the mean and the weight the wrong way, i'll add it in a bit – gizmo Jan 31 '19 at 08:54

1 Answers1

1

not sure if this is what you meant? sorry- still new to SO >.<

public float calculateMeanFG(int[] histogram, int t) {
    float sumI = 0;
    int total = 0;

    //cumulate the histogram for < 256
    for (int i = t; i < 256; i++) {
        sumI += histogram[i] * i;
        total = i;
    }

    return sumI / total;
}

public float calculateMeanBG(int[] histogram, int t) {
    float sumI = 0;

    //cumulate the histogram for < t
    for (int i = 0; i < t; i++) {
        sumI += histogram[i] * i;
    }
    return sumI;
}


public float calculateWeightFG(int[] histogram, int t, int total) {
    int sum = 0;
    for (int i = t; i < 256; i++) {
        sum += histogram[i];

    }

    return sum / total;
}


public int[] getHistogram(ImageProcessor ip, int height, int width) {
    byte[] outP = ((byte[]) ip.getPixels()).clone();
    int[][] inDataArr = new int[width][height];
    int[] histogram = new int[256];

    int idx = 0;
    for (int y = 0; y < height; y++) {
        for (int x = 0; x < width; x++) {
            // fill in values
            inDataArr[x][y] = outP[idx];
            if (inDataArr[x][y] < 0) {
                inDataArr[x][y] += 256;
            } // if
            histogram[inDataArr[x][y]] += 1; // count grayscale occurrences
            idx++;
        } // for x
    } // for y

    return histogram;
}

public int[][] convergeOptThresh(int[][] imgArr, int width, int height) {

    int BG_VAL = 0;
    int FG_VAL = 255;

    int[] histogram = getHistogram(ip, height, width);

    // total number of pixels
    int total = imgArr.length;
    // cumulative hist
    float sum = 0;
    for (int i = 0; i < 256; i++)
        sum += i * histogram[i];

    float sumBG = 0; // sum background
    float weightBG = 0;
    float weightFG = 0;

    float varMax = 0;
    int threshold = 0;

        for (int t = 0; t < 256; t++) {
            weightBG = calculateMeanBG(histogram, t);
            weightBG /= total;

            weightFG = calculateWeightFG(histogram, t, total);
            if ((int)weightFG == 0)
                break;

            sumBG += (float) (t * histogram[t]);

            float meanBG = sumBG / t;
            float meanFG = calculateMeanFG(histogram, t);

            // calculate between class variance
            float varBetween = weightBG * weightFG * (meanBG - meanFG) * (meanBG - meanFG);

            // check if new max found
            if (varBetween > varMax) {
                varMax = varBetween;
                threshold = t;
            }

    }

    IJ.log("optimal threshold: " + threshold);

    int[][] retArr = new int[width][height];

    for (int x = 0; x < width; x++) {
        for (int y = 0; y < height; y++) {
            if (imgArr[x][y] <= threshold) {
                retArr[x][y] = BG_VAL;
            } else {
                retArr[x][y] = FG_VAL;
            }
        }
    }

    return retArr;
}
gizmo
  • 81
  • 8
  • Please use the edit link on your question to add additional information. The Post Answer button should be used only for complete answers to the question. - [From Review](/review/low-quality-posts/22086073) – Arc676 Jan 31 '19 at 20:26
  • sorry- i kind of answered my question in the original ask.. didn't really know how to mark the question as solved any other way, I've tried to fix it? is this better? @arc676 – gizmo Feb 01 '19 at 08:39
  • @gizmo If you have found a solution to your problem, you can answer your own question. To mark the problem as solved, click the check mark under the vote buttons on your answer. To help other users who might have the same problem and find this page, it's recommended that your answer contain all the information relevant to the solution and your question just contains the problem statement, especially as there are currently no other answers. – Arc676 Feb 02 '19 at 16:55