diff --git a/.gitignore b/.gitignore index e43b0f9..3f19815 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,5 @@ .DS_Store +*.png +*.jpg +*.o +otsus diff --git a/LICENSE b/LICENSE index fc2ba12..d22a84a 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,7 @@ The MIT License (MIT) -Copyright (c) 2015 Juan Pablo Balarini +Copyright (c) 2015 Juan Pablo Balarini, Sergio Nesmachnow +jpbalarini@fing.edu.uy, sergion@fing.edu.uy Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/README.md b/README.md index 3707505..7b8f3f7 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ The code is written in C++ and it uses Miguel Colom's [image processing framewor ## Setup 1. Download the code 2. make -3. ./otsus input_image output_image [-t overrided_threshold] +3. ./otsus [-t overrided_threshold] input_image output_image ## Licence The MIT License diff --git a/algo.cpp b/algo.cpp index 8ce6f3f..f6886ed 100644 --- a/algo.cpp +++ b/algo.cpp @@ -1,3 +1,28 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2015 Juan Pablo Balarini, Sergio Nesmachnow + * jpbalarini@fing.edu.uy, sergion@fing.edu.uy + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + #include #include #include @@ -7,74 +32,39 @@ #include "framework/CImage.h" #include "framework/libparser.h" -void computeHistogram(CImage &input, float *hist){ +int MAX_INTENSITY = 255; + +/*! Computes image histogram + \param input Input image + \param hist Image histogram that is returned +*/ +void computeHistogram(CImage &input, unsigned *hist){ + // Compute number of pixels long int N = input.get_width() * input.get_height(); int i = 0; // Initialize array - for(i = 0; i <= 255; i++) hist[i] = 0; + for(i = 0; i <= MAX_INTENSITY; i++) hist[i] = 0; // Use first channel float *in = input.get_channel(0); // Iterate image for (i = 0; i < N; i++) { int value = (int) in[i]; - hist[value] = hist[value] + 1; + hist[value]++; } printf("Total # of pixels: %ld\n", N); - - // printf("Printing normalized histogram\n"); - // for (int i = 0; i <= 255; i++){ - // // printf("%d\t%d\n", i, (int)(hist[i]*N)); - // // Normalized histogram - // printf("%d\t%f\n", i, hist[i]); - // } } -void computeOtsusSegmentation(CImage &input, float* hist, CImage *output, int overrided_threshold){ +/*! Segments the image using the computed threshold + \param input Input image + \param output Output segmented image + \param threshold Threshold used for segmentation +*/ +void segmentImage(CImage &input, CImage *output, int threshold) { + // Compute number of pixels long int N = input.get_width() * input.get_height(); - - float sum = 0; - for (int i=0; i<256; i++){ - sum += i * ((int)hist[i]); - } - - float sumB = 0; - int q1 = 0; - int q2 = 0; - - float varMax = 0; - int threshold = 0; - - for (int i = 0 ; i < 256 ; i++) { - q1 += hist[i]; - if (q1 == 0) - continue; - q2 = N - q1; - - if (q2 == 0) - break; - - sumB += (float) (i * ((int)hist[i])); - float m1 = sumB / q1; - float m2 = (sum - sumB) / q2; - - float varBetween = (float) q1 * (float) q2 * (m1 - m2) * (m1 - m2); - - if (varBetween > varMax) { - varMax = varBetween; - threshold = i; - } - } - - if (overrided_threshold != 0){ - threshold = overrided_threshold; - } - - // int threshold = optimizedthresh; - printf("Threshold is: %d\n", threshold); - // Modify output image // Use first channel float *in = input.get_channel(0); @@ -82,6 +72,7 @@ void computeOtsusSegmentation(CImage &input, float* hist, CImage *output, int ov // Iterate image for (int i = 0; i < N; i++) { int value = (int) in[i]; + // Build the segmented image if (value > threshold){ out[i] = 255.0f; }else{ @@ -90,6 +81,63 @@ void computeOtsusSegmentation(CImage &input, float* hist, CImage *output, int ov } } +/*! Computes Otsus segmentation + \param input Input image + \param hist Image histogram + \param output Output segmented image + \param overrided_threshold Input param that overrides threshold +*/ +void computeOtsusSegmentation(CImage &input, unsigned* hist, CImage *output, int overrided_threshold){ + // Compute number of pixels + long int N = input.get_width() * input.get_height(); + int threshold = 0; + + if (overrided_threshold != 0){ + // If threshold was manually entered + threshold = overrided_threshold; + }else{ + // Compute threshold + // Init variables + float sum = 0; + float sumB = 0; + int q1 = 0; + int q2 = 0; + float varMax = 0; + + // Auxiliary value for computing m2 + for (int i = 0; i <= MAX_INTENSITY; i++){ + sum += i * ((int)hist[i]); + } + + for (int i = 0 ; i <= MAX_INTENSITY ; i++) { + // Update q1 + q1 += hist[i]; + if (q1 == 0) + continue; + // Update q2 + q2 = N - q1; + + if (q2 == 0) + break; + // Update m1 and m2 + sumB += (float) (i * ((int)hist[i])); + float m1 = sumB / q1; + float m2 = (sum - sumB) / q2; + + // Update the between class variance + float varBetween = (float) q1 * (float) q2 * (m1 - m2) * (m1 - m2); + + // Update the threshold if necessary + if (varBetween > varMax) { + varMax = varBetween; + threshold = i; + } + } + } + + // Perform the segmentation + segmentImage(input, output, threshold); +} //! Otsus segmentation algorithm. /*! @@ -120,7 +168,7 @@ void algorithm(int argc, char **argv) { if (override_thres){ overrided_threshold = atoi(oThreshold.value); // Check threshold bounds - if (overrided_threshold > 255 || overrided_threshold < 0){ + if (overrided_threshold > MAX_INTENSITY || overrided_threshold < 0){ printf("Invalid threshold value\n"); exit(-1); } @@ -138,7 +186,7 @@ void algorithm(int argc, char **argv) { long int N = Nx * Ny; if (num_channels > 1){ - printf("Algorithm works only with grayscale images\n"); + fprintf(stderr, "Algorithm works only with grayscale images\n"); exit(-1); } // Create output @@ -146,12 +194,12 @@ void algorithm(int argc, char **argv) { bits, num_channels); - float hist[256]; + unsigned hist[MAX_INTENSITY + 1]; computeHistogram(input, hist); computeOtsusSegmentation(input, hist, output, overrided_threshold); // Save output - output->save((char*)pout.value, input.get_bits_per_channel()); + output->save((char*)pout.value, bits); delete output; }