egegunes egegunes - 1 month ago 7
C Question

Otsu thresholding

I'm trying to calculate a threshold value using Otsu's method. Sample image is 8-bit, grayscale, bmp file.

Histogram for that image generated by following:

/* INITIALIZE ARRAYS */
for(int i = 0; i < 255; i++) occurrence[i] = 0;
for(int i = 0; i < 255; i++) histogram[i] = 0;

/* START AT BEGINNING OF RASTER DATA */
fseek(input_img, (54 + 4 * color_number), SEEK_SET);

/* READ RASTER DATA */
for(int r = 0; r <= original_img.rows - 1; r++) {
for(int c = 0; c <= original_img.cols -1; c++) {
fread(p_char, sizeof(char), 1, input_img);
pixel_value = *p_char;
/* COUNT OCCURRENCES OF PIXEL VALUE */
occurrence[pixel_value] = occurrence[pixel_value] + 1;
total_pixels++;
}
}

for(int i = 0; i <= 255; i++) {
/* TAKES NUMBER OF OCCURRENCES OF A PARTICULAR PIXEL
* AND DIVIDES BY THE TOTAL NUMBER OF PIXELS YIELDING
* A RATIO */
histogram[i] = (float) occurrence[i] / (float) total_pixels;
}


Histogram then passed to function
otsu_method
in main:

threshold_value = otsu_method(histogram, total_pixels);


Function
otsu_method
:

int otsu_method(float *histogram, long int total_pixels) {
double omega[256], myu[256];
double max_sigma, sigma[256];
int threshold;

omega[0] = histogram[0];
myu[0] = 0.0;

for(int i = 1; i < 256; i++) {
omega[i] = omega[i - 1] + histogram[i];
myu[i] = myu[i - 1] + i * histogram[i];
}

threshold = 0;
max_sigma = 0.0;

for(int i = 0; i < 255; i++) {
if(omega[i] != 0.0 && omega[i] != 1.0)
sigma[i] = pow(myu[255] * omega[i], 2) / (omega[i] * (1.0 - omega[i]));
else
sigma[i] = 0.0;
if(sigma[i] > max_sigma) {
max_sigma = sigma[i];
threshold = i;
}
}

printf("Threshold value: %d\n", threshold);

return threshold;
}


And binarize image based on threshold value:

void threshold_image(FILE* input_file, FILE* output_file, unsigned long vector_size, int threshold_value) {
unsigned char* p_char;
unsigned char dummy;
struct_img binary_img;
unsigned char* binary_data;

dummy = '0';
p_char = &dummy;

binary_img.data = malloc(vector_size * sizeof(char));
if(binary_img.data == NULL) printf("Failed to malloc binary_img.data\n");

binary_data = binary_img.data;

/* CONVERT PIXEL TO BLACK AND WHITE BASED ON THRESHOLD VALUE */
for(int i = 0; i < vector_size - 1; i++) {
fread(p_char, sizeof(char), 1, input_file);
if(*p_char < threshold_value) *(binary_data + i) = 0;
else *(binary_data + i) = 255;
fwrite((binary_data + i), sizeof(char), 1, output_file);
}

/* FREE ALLOCATED MEMORY */
free(binary_data);
}


Program output:

Reading file 512gr.bmp
Width: 512
Height: 512
File size: 263222
# Colors: 256
Vector size: 262144
Total number of pixels: 262144
Threshold value: 244


I think 244 is not a properly computed threshold value, because when function threshold_image binarize image with that all pixels converted to black.

If I skip
otsu_method
and get threshold value from user input function
threshold_image
works properly.

Function
otsu_method
is copy-pasted code, for this reason I'm not crystal clear about variables or conditions.
I'm learning about image processing and trying to figure out basics. Any information about Otsu's algorithm and any feedback about my code helps.

Answer

I found what caused the problem and changed function otsu_method:

int otsu_method(float *histogram, long int total_pixels) {
    double probability[256], mean[256];
    double max_between, between[256];
    int threshold;

    /*
    probability = class probability
    mean = class mean
    between = between class variance
    */

    for(int i = 0; i < 256; i++) {
        probability[i] = 0.0;
        mean[i] = 0.0;
        between[i] = 0.0;
    }

    probability[0] = histogram[0];

    for(int i = 1; i < 256; i++) {
        probability[i] = probability[i - 1] + histogram[i];
        mean[i] = mean[i - 1] + i * histogram[i];
    }

    threshold = 0;
    max_between = 0.0;

    for(int i = 0; i < 255; i++) {
        if(probability[i] != 0.0 && probability[i] != 1.0)
            between[i] = pow(mean[255] * probability[i] - mean[i], 2) / (probability[i] * (1.0 - probability[i]));
    else
        between[i] = 0.0;
        if(between[i] > max_between) {
            max_between = between[i];
            threshold = i;
        }
    }

    return threshold;
}

What really differs:

between[i] = pow(mean[255] * probability[i] - mean[i], 2) / (probability[i] * (1.0 - probability[i]));

Program output:

Reading file 512gr.bmp
Width: 512
Height: 512
File size: 263222
# Colors: 256
Vector size: 262144
Total number of pixels: 262144

Threshold value: 117
Probability: 0.416683
Mean: 31.9631
Between varaince: 1601.01
Comments