From 97ffaabfb59c3069ec37b49e0e15e7776b521648 Mon Sep 17 00:00:00 2001 From: Claudio Felber Date: Tue, 30 Apr 2019 13:02:11 +0200 Subject: [PATCH 1/3] Replace existing threshold function with Otsu threshold algorithm --- lib/identify.c | 116 ++++++++++++++++++++++++------------------------- 1 file changed, 56 insertions(+), 60 deletions(-) diff --git a/lib/identify.c b/lib/identify.c index d6f5516..63c9e4f 100644 --- a/lib/identify.c +++ b/lib/identify.c @@ -174,60 +174,55 @@ static void flood_fill_seed(struct quirc *q, int x, int y, int from, int to, * Adaptive thresholding */ -#define THRESHOLD_S_MIN 1 -#define THRESHOLD_S_DEN 8 -#define THRESHOLD_T 5 - -static void threshold(struct quirc *q) +uint8_t otsu(struct quirc *q) { - int x, y; - int avg_w = 0; - int avg_u = 0; - int threshold_s = q->w / THRESHOLD_S_DEN; - quirc_pixel_t *row = q->pixels; + int numPixels = q->w * q->h; - /* - * Ensure a sane, non-zero value for threshold_s. - * - * threshold_s can be zero if the image width is small. We need to avoid - * SIGFPE as it will be used as divisor. - */ - if (threshold_s < THRESHOLD_S_MIN) - threshold_s = THRESHOLD_S_MIN; + // Calculate histogram + const int HISTOGRAM_SIZE = 256; + unsigned int histogram[HISTOGRAM_SIZE]; + memset(histogram, 0, (HISTOGRAM_SIZE) * sizeof(unsigned int)); + uint8_t* ptr = q->image; + int length = numPixels; + while (length--) { + uint8_t value = *ptr++; + histogram[value]++; + } - for (y = 0; y < q->h; y++) { - memset(q->row_average, 0, q->w * sizeof(int)); + // Calculate weighted sum of histogram values + int sum = 0; + for (int i = 0; i < HISTOGRAM_SIZE; ++i) { + sum += i * histogram[i]; + } - for (x = 0; x < q->w; x++) { - int w, u; + // Compute threshold + int sumB = 0; + int q1 = 0; + double max = 0; + uint8_t threshold = 0; + for (int i = 0; i < HISTOGRAM_SIZE; ++i) { + // Weighted background + q1 += histogram[i]; + if (q1 == 0) + continue; - if (y & 1) { - w = x; - u = q->w - 1 - x; - } else { - w = q->w - 1 - x; - u = x; - } + // Weighted foreground + const int q2 = numPixels - q1; + if (q2 == 0) + break; - avg_w = (avg_w * (threshold_s - 1)) / - threshold_s + row[w]; - avg_u = (avg_u * (threshold_s - 1)) / - threshold_s + row[u]; + sumB += i * histogram[i]; + const double m1 = (double)sumB / q1; + const double m2 = ((double)sum - sumB) / q2; + const double m1m2 = m1 - m2; + const double variance = m1m2 * m1m2 * q1 * q2; + if (variance > max) { + threshold = i; + max = variance; + } + } - q->row_average[w] += avg_w; - q->row_average[u] += avg_u; - } - - for (x = 0; x < q->w; x++) { - if (row[x] < q->row_average[x] * - (100 - THRESHOLD_T) / (200 * threshold_s)) - row[x] = QUIRC_PIXEL_BLACK; - else - row[x] = QUIRC_PIXEL_WHITE; - } - - row += q->w; - } + return threshold; } static void area_count(void *user_data, int y, int left, int right) @@ -1074,18 +1069,19 @@ static void test_grouping(struct quirc *q, int i) test_neighbours(q, i, &hlist, &vlist); } -static void pixels_setup(struct quirc *q) +static void pixels_setup(struct quirc *q, uint8_t threshold) { - if (sizeof(*q->image) == sizeof(*q->pixels)) { - q->pixels = (quirc_pixel_t *)q->image; - } else { - int x, y; - for (y = 0; y < q->h; y++) { - for (x = 0; x < q->w; x++) { - q->pixels[y * q->w + x] = q->image[y * q->w + x]; - } - } - } + if (sizeof(*q->image) == sizeof(*q->pixels)) { + q->pixels = (quirc_pixel_t *)q->image; + } + + uint8_t* source = q->image; + quirc_pixel_t* dest = q->pixels; + int length = q->w * q->h; + while (length--) { + uint8_t value = *source++; + *dest++ = (value < threshold) ? QUIRC_PIXEL_BLACK : QUIRC_PIXEL_WHITE; + } } uint8_t *quirc_begin(struct quirc *q, int *w, int *h) @@ -1106,8 +1102,8 @@ void quirc_end(struct quirc *q) { int i; - pixels_setup(q); - threshold(q); + uint8_t threshold = otsu(q); + pixels_setup(q, threshold); for (i = 0; i < q->h; i++) finder_scan(q, i); From 96a5a380fbf6d93f9ac31c32f5b409fe028fd5ab Mon Sep 17 00:00:00 2001 From: Claudio Felber Date: Tue, 30 Apr 2019 18:43:57 +0200 Subject: [PATCH 2/3] Remove quirc.row_average and associated code quirc.row_average was used by the former threshold function and has become obsolete with the introduction of the Otsu threshold function. --- lib/quirc.c | 10 ---------- lib/quirc_internal.h | 1 - 2 files changed, 11 deletions(-) diff --git a/lib/quirc.c b/lib/quirc.c index 9d1c102..1029785 100644 --- a/lib/quirc.c +++ b/lib/quirc.c @@ -41,7 +41,6 @@ void quirc_destroy(struct quirc *q) same size, so we need to be careful here to avoid a double free */ if (sizeof(*q->image) != sizeof(*q->pixels)) free(q->pixels); - free(q->row_average); free(q); } @@ -49,7 +48,6 @@ int quirc_resize(struct quirc *q, int w, int h) { uint8_t *image = NULL; quirc_pixel_t *pixels = NULL; - int *row_average = NULL; /* * XXX: w and h should be size_t (or at least unsigned) as negatives @@ -88,11 +86,6 @@ int quirc_resize(struct quirc *q, int w, int h) goto fail; } - /* alloc a new buffer for q->row_average */ - row_average = calloc(w, sizeof(int)); - if (!row_average) - goto fail; - /* alloc succeeded, update `q` with the new size and buffers */ q->w = w; q->h = h; @@ -102,15 +95,12 @@ int quirc_resize(struct quirc *q, int w, int h) free(q->pixels); q->pixels = pixels; } - free(q->row_average); - q->row_average = row_average; return 0; /* NOTREACHED */ fail: free(image); free(pixels); - free(row_average); return -1; } diff --git a/lib/quirc_internal.h b/lib/quirc_internal.h index 70d481d..3df54f3 100644 --- a/lib/quirc_internal.h +++ b/lib/quirc_internal.h @@ -77,7 +77,6 @@ struct quirc_grid { struct quirc { uint8_t *image; quirc_pixel_t *pixels; - int *row_average; /* used by threshold() */ int w; int h; From 766f79ce7c30194f1a85687c253700a2b15435f1 Mon Sep 17 00:00:00 2001 From: Claudio Felber Date: Tue, 30 Apr 2019 18:44:59 +0200 Subject: [PATCH 3/3] Use const struct quirc parameter in otsu() function and fix tabs --- lib/identify.c | 108 ++++++++++++++++++++++++------------------------- 1 file changed, 54 insertions(+), 54 deletions(-) diff --git a/lib/identify.c b/lib/identify.c index 63c9e4f..f2d47cf 100644 --- a/lib/identify.c +++ b/lib/identify.c @@ -174,55 +174,55 @@ static void flood_fill_seed(struct quirc *q, int x, int y, int from, int to, * Adaptive thresholding */ -uint8_t otsu(struct quirc *q) +uint8_t otsu(const struct quirc *q) { - int numPixels = q->w * q->h; + int numPixels = q->w * q->h; - // Calculate histogram - const int HISTOGRAM_SIZE = 256; - unsigned int histogram[HISTOGRAM_SIZE]; - memset(histogram, 0, (HISTOGRAM_SIZE) * sizeof(unsigned int)); - uint8_t* ptr = q->image; - int length = numPixels; - while (length--) { - uint8_t value = *ptr++; - histogram[value]++; - } + // Calculate histogram + const int HISTOGRAM_SIZE = 256; + unsigned int histogram[HISTOGRAM_SIZE]; + memset(histogram, 0, (HISTOGRAM_SIZE) * sizeof(unsigned int)); + uint8_t* ptr = q->image; + int length = numPixels; + while (length--) { + uint8_t value = *ptr++; + histogram[value]++; + } - // Calculate weighted sum of histogram values - int sum = 0; - for (int i = 0; i < HISTOGRAM_SIZE; ++i) { - sum += i * histogram[i]; - } + // Calculate weighted sum of histogram values + int sum = 0; + for (int i = 0; i < HISTOGRAM_SIZE; ++i) { + sum += i * histogram[i]; + } - // Compute threshold - int sumB = 0; - int q1 = 0; - double max = 0; - uint8_t threshold = 0; - for (int i = 0; i < HISTOGRAM_SIZE; ++i) { - // Weighted background - q1 += histogram[i]; - if (q1 == 0) - continue; + // Compute threshold + int sumB = 0; + int q1 = 0; + double max = 0; + uint8_t threshold = 0; + for (int i = 0; i < HISTOGRAM_SIZE; ++i) { + // Weighted background + q1 += histogram[i]; + if (q1 == 0) + continue; - // Weighted foreground - const int q2 = numPixels - q1; - if (q2 == 0) - break; + // Weighted foreground + const int q2 = numPixels - q1; + if (q2 == 0) + break; - sumB += i * histogram[i]; - const double m1 = (double)sumB / q1; - const double m2 = ((double)sum - sumB) / q2; - const double m1m2 = m1 - m2; - const double variance = m1m2 * m1m2 * q1 * q2; - if (variance > max) { - threshold = i; - max = variance; - } - } + sumB += i * histogram[i]; + const double m1 = (double)sumB / q1; + const double m2 = ((double)sum - sumB) / q2; + const double m1m2 = m1 - m2; + const double variance = m1m2 * m1m2 * q1 * q2; + if (variance > max) { + threshold = i; + max = variance; + } + } - return threshold; + return threshold; } static void area_count(void *user_data, int y, int left, int right) @@ -1071,17 +1071,17 @@ static void test_grouping(struct quirc *q, int i) static void pixels_setup(struct quirc *q, uint8_t threshold) { - if (sizeof(*q->image) == sizeof(*q->pixels)) { - q->pixels = (quirc_pixel_t *)q->image; - } + if (sizeof(*q->image) == sizeof(*q->pixels)) { + q->pixels = (quirc_pixel_t *)q->image; + } - uint8_t* source = q->image; - quirc_pixel_t* dest = q->pixels; - int length = q->w * q->h; - while (length--) { - uint8_t value = *source++; - *dest++ = (value < threshold) ? QUIRC_PIXEL_BLACK : QUIRC_PIXEL_WHITE; - } + uint8_t* source = q->image; + quirc_pixel_t* dest = q->pixels; + int length = q->w * q->h; + while (length--) { + uint8_t value = *source++; + *dest++ = (value < threshold) ? QUIRC_PIXEL_BLACK : QUIRC_PIXEL_WHITE; + } } uint8_t *quirc_begin(struct quirc *q, int *w, int *h) @@ -1102,8 +1102,8 @@ void quirc_end(struct quirc *q) { int i; - uint8_t threshold = otsu(q); - pixels_setup(q, threshold); + uint8_t threshold = otsu(q); + pixels_setup(q, threshold); for (i = 0; i < q->h; i++) finder_scan(q, i);