Merge pull request #134 from igrr/feature/customize_fp_type

Add an option to use single-precision floating point in calculations
This commit is contained in:
Alexandre Perrin 2023-03-21 10:08:36 +01:00 committed by GitHub
commit c8e77ce1d6
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 100 additions and 54 deletions

View file

@ -102,10 +102,10 @@ the default macros like CFLAGS from sys.mk can cause unintended effects.
Installation Installation
------------ ------------
To build the library and associated demos/tests, type `make`. If you need to To build the library and associated demos/tests, type `make`.
decode "large" image files build with `CFLAGS="-DQUIRC_MAX_REGIONS=65534" make`
instead. Note that this will increase the memory usage, it is discouraged for Several options can be adjusted at compile time by passing additional arguments
low resource devices (i.e. embedded). to `make`. See [Compile-time options](#compile-time-options) section below for details.
Type `make install` to install the library, header file and camera demos. Type `make install` to install the library, header file and camera demos.
@ -236,6 +236,34 @@ decode attempt with the flipped image data whenever you get an ECC failure:
printf("Data: %s\n", data.payload); printf("Data: %s\n", data.payload);
``` ```
Compile-time options
--------------------
The following compile-time options can be used to adjust the library to a
particular use case.
Each option is a C preprocessor macro. To set an option, add it to CFLAGS
using `-DOPTION=VALUE` syntax, for example:
```bash
make CFLAGS="-DQUIRC_MAX_REGIONS=65534"
```
* `QUIRC_MAX_REGIONS`: If you need to decode "large" image files, set
`QUIRC_MAX_REGIONS=65534`. Note that since this will increase the memory
usage, it is discouraged for low resource devices (i.e. embedded).
* `QUIRC_FLOAT_TYPE`: If defined, it sets the type name to use
in floating point calculations. For example, on an embedded system
with only a single precision FPU, set `QUIRC_FLOAT_TYPE=float` to
improve performance.
* `QUIRC_USE_TGMATH`: if defined, quirc will internally use `<tgmath.h>`
header instead of `<math.h>`, ensuring that the math function calls
use the same precision as the arguments. Define this option if you are
setting `QUIRC_FLOAT_TYPE=float` and the compiler supports C99 or later
language standard.
Copyright Copyright
--------- ---------
Copyright (C) 2010-2012 Daniel Beer <<dlbeer@gmail.com>> Copyright (C) 2010-2012 Daniel Beer <<dlbeer@gmail.com>>

View file

@ -18,7 +18,11 @@
#include <string.h> #include <string.h>
#include <stdbool.h> #include <stdbool.h>
#include <stdlib.h> #include <stdlib.h>
#ifdef QUIRC_USE_TGMATH
#include <tgmath.h>
#else
#include <math.h> #include <math.h>
#endif // QUIRC_USE_TGMATH
#include "quirc_internal.h" #include "quirc_internal.h"
/************************************************************************ /************************************************************************
@ -62,21 +66,21 @@ static int line_intersect(const struct quirc_point *p0,
return 1; return 1;
} }
static void perspective_setup(double *c, static void perspective_setup(quirc_float_t *c,
const struct quirc_point *rect, const struct quirc_point *rect,
double w, double h) quirc_float_t w, quirc_float_t h)
{ {
double x0 = rect[0].x; quirc_float_t x0 = rect[0].x;
double y0 = rect[0].y; quirc_float_t y0 = rect[0].y;
double x1 = rect[1].x; quirc_float_t x1 = rect[1].x;
double y1 = rect[1].y; quirc_float_t y1 = rect[1].y;
double x2 = rect[2].x; quirc_float_t x2 = rect[2].x;
double y2 = rect[2].y; quirc_float_t y2 = rect[2].y;
double x3 = rect[3].x; quirc_float_t x3 = rect[3].x;
double y3 = rect[3].y; quirc_float_t y3 = rect[3].y;
double wden = w * (x2*y3 - x3*y2 + (x3-x2)*y1 + x1*(y2-y3)); quirc_float_t wden = w * (x2*y3 - x3*y2 + (x3-x2)*y1 + x1*(y2-y3));
double hden = h * (x2*y3 + x1*(y2-y3) - x3*y2 + (x3-x2)*y1); quirc_float_t hden = h * (x2*y3 + x1*(y2-y3) - x3*y2 + (x3-x2)*y1);
c[0] = (x1*(x2*y3-x3*y2) + x0*(-x2*y3+x3*y2+(x2-x3)*y1) + c[0] = (x1*(x2*y3-x3*y2) + x0*(-x2*y3+x3*y2+(x2-x3)*y1) +
x1*(x3-x2)*y0) / wden; x1*(x3-x2)*y0) / wden;
@ -93,24 +97,24 @@ static void perspective_setup(double *c,
hden; hden;
} }
static void perspective_map(const double *c, static void perspective_map(const quirc_float_t *c,
double u, double v, struct quirc_point *ret) quirc_float_t u, quirc_float_t v, struct quirc_point *ret)
{ {
double den = c[6]*u + c[7]*v + 1.0; quirc_float_t den = c[6]*u + c[7]*v + 1.0;
double x = (c[0]*u + c[1]*v + c[2]) / den; quirc_float_t x = (c[0]*u + c[1]*v + c[2]) / den;
double y = (c[3]*u + c[4]*v + c[5]) / den; quirc_float_t y = (c[3]*u + c[4]*v + c[5]) / den;
ret->x = (int) rint(x); ret->x = (int) rint(x);
ret->y = (int) rint(y); ret->y = (int) rint(y);
} }
static void perspective_unmap(const double *c, static void perspective_unmap(const quirc_float_t *c,
const struct quirc_point *in, const struct quirc_point *in,
double *u, double *v) quirc_float_t *u, quirc_float_t *v)
{ {
double x = in->x; quirc_float_t x = in->x;
double y = in->y; quirc_float_t y = in->y;
double den = -c[0]*c[7]*y + c[1]*c[6]*y + (c[3]*c[7]-c[4]*c[6])*x + quirc_float_t den = -c[0]*c[7]*y + c[1]*c[6]*y + (c[3]*c[7]-c[4]*c[6])*x +
c[0]*c[4] - c[1]*c[3]; c[0]*c[4] - c[1]*c[3];
*u = -(c[1]*(y-c[5]) - c[2]*c[7]*y + (c[5]*c[7]-c[4])*x + c[2]*c[4]) / *u = -(c[1]*(y-c[5]) - c[2]*c[7]*y + (c[5]*c[7]-c[4])*x + c[2]*c[4]) /
@ -299,16 +303,16 @@ static uint8_t otsu(const struct quirc *q)
} }
// Calculate weighted sum of histogram values // Calculate weighted sum of histogram values
double sum = 0; quirc_float_t sum = 0;
unsigned int i = 0; unsigned int i = 0;
for (i = 0; i <= UINT8_MAX; ++i) { for (i = 0; i <= UINT8_MAX; ++i) {
sum += i * histogram[i]; sum += i * histogram[i];
} }
// Compute threshold // Compute threshold
double sumB = 0; quirc_float_t sumB = 0;
unsigned int q1 = 0; unsigned int q1 = 0;
double max = 0; quirc_float_t max = 0;
uint8_t threshold = 0; uint8_t threshold = 0;
for (i = 0; i <= UINT8_MAX; ++i) { for (i = 0; i <= UINT8_MAX; ++i) {
// Weighted background // Weighted background
@ -322,10 +326,10 @@ static uint8_t otsu(const struct quirc *q)
break; break;
sumB += i * histogram[i]; sumB += i * histogram[i];
const double m1 = sumB / q1; const quirc_float_t m1 = sumB / q1;
const double m2 = (sum - sumB) / q2; const quirc_float_t m2 = (sum - sumB) / q2;
const double m1m2 = m1 - m2; const quirc_float_t m1m2 = m1 - m2;
const double variance = m1m2 * m1m2 * q1 * q2; const quirc_float_t variance = m1m2 * m1m2 * q1 * q2;
if (variance >= max) { if (variance >= max) {
threshold = i; threshold = i;
max = variance; max = variance;
@ -582,7 +586,7 @@ static void find_alignment_pattern(struct quirc *q, int index)
int size_estimate; int size_estimate;
int step_size = 1; int step_size = 1;
int dir = 0; int dir = 0;
double u, v; quirc_float_t u, v;
/* Grab our previous estimate of the alignment pattern corner */ /* Grab our previous estimate of the alignment pattern corner */
memcpy(&b, &qr->align, sizeof(b)); memcpy(&b, &qr->align, sizeof(b));
@ -648,10 +652,10 @@ static void find_leftmost_to_line(void *user_data, int y, int left, int right)
} }
} }
static double length(struct quirc_point a, struct quirc_point b) static quirc_float_t length(struct quirc_point a, struct quirc_point b)
{ {
double x = abs(a.x - b.x) + 1; quirc_float_t x = abs(a.x - b.x) + 1;
double y = abs(a.y - b.y) + 1; quirc_float_t y = abs(a.y - b.y) + 1;
return sqrt(x * x + y * y); return sqrt(x * x + y * y);
} }
/* Estimate grid size by determing distance between capstones /* Estimate grid size by determing distance between capstones
@ -664,15 +668,15 @@ static void measure_grid_size(struct quirc *q, int index)
struct quirc_capstone *b = &(q->capstones[qr->caps[1]]); struct quirc_capstone *b = &(q->capstones[qr->caps[1]]);
struct quirc_capstone *c = &(q->capstones[qr->caps[2]]); struct quirc_capstone *c = &(q->capstones[qr->caps[2]]);
double ab = length(b->corners[0], a->corners[3]); quirc_float_t ab = length(b->corners[0], a->corners[3]);
double capstone_ab_size = (length(b->corners[0], b->corners[3]) + length(a->corners[0], a->corners[3]))/2.0; quirc_float_t capstone_ab_size = (length(b->corners[0], b->corners[3]) + length(a->corners[0], a->corners[3]))/2.0;
double ver_grid = 7.0 * ab / capstone_ab_size; quirc_float_t ver_grid = 7.0 * ab / capstone_ab_size;
double bc = length(b->corners[0], c->corners[1]); quirc_float_t bc = length(b->corners[0], c->corners[1]);
double capstone_bc_size = (length(b->corners[0], b->corners[1]) + length(c->corners[0], c->corners[1]))/2.0; quirc_float_t capstone_bc_size = (length(b->corners[0], b->corners[1]) + length(c->corners[0], c->corners[1]))/2.0;
double hor_grid = 7.0 * bc / capstone_bc_size; quirc_float_t hor_grid = 7.0 * bc / capstone_bc_size;
double grid_size_estimate = (ver_grid + hor_grid) / 2; quirc_float_t grid_size_estimate = (ver_grid + hor_grid) / 2;
int ver = (int)((grid_size_estimate - 17.0 + 2.0) / 4.0); int ver = (int)((grid_size_estimate - 17.0 + 2.0) / 4.0);
@ -703,7 +707,7 @@ static int fitness_cell(const struct quirc *q, int index, int x, int y)
for (v = 0; v < 3; v++) for (v = 0; v < 3; v++)
for (u = 0; u < 3; u++) { for (u = 0; u < 3; u++) {
static const double offsets[] = {0.3, 0.5, 0.7}; static const quirc_float_t offsets[] = {0.3, 0.5, 0.7};
struct quirc_point p; struct quirc_point p;
perspective_map(qr->c, x + offsets[u], perspective_map(qr->c, x + offsets[u],
@ -806,7 +810,7 @@ static void jiggle_perspective(struct quirc *q, int index)
struct quirc_grid *qr = &q->grids[index]; struct quirc_grid *qr = &q->grids[index];
int best = fitness_all(q, index); int best = fitness_all(q, index);
int pass; int pass;
double adjustments[8]; quirc_float_t adjustments[8];
int i; int i;
for (i = 0; i < 8; i++) for (i = 0; i < 8; i++)
@ -816,9 +820,9 @@ static void jiggle_perspective(struct quirc *q, int index)
for (i = 0; i < 16; i++) { for (i = 0; i < 16; i++) {
int j = i >> 1; int j = i >> 1;
int test; int test;
double old = qr->c[j]; quirc_float_t old = qr->c[j];
double step = adjustments[j]; quirc_float_t step = adjustments[j];
double new; quirc_float_t new;
if (i & 1) if (i & 1)
new = old + step; new = old + step;
@ -998,7 +1002,7 @@ fail:
struct neighbour { struct neighbour {
int index; int index;
double distance; quirc_float_t distance;
}; };
struct neighbour_list { struct neighbour_list {
@ -1015,7 +1019,7 @@ static void test_neighbours(struct quirc *q, int i,
const struct neighbour *hn = &hlist->n[j]; const struct neighbour *hn = &hlist->n[j];
for (int k = 0; k < vlist->count; k++) { for (int k = 0; k < vlist->count; k++) {
const struct neighbour *vn = &vlist->n[k]; const struct neighbour *vn = &vlist->n[k];
double squareness = fabs(1.0 - hn->distance / vn->distance); quirc_float_t squareness = fabs(1.0 - hn->distance / vn->distance);
if (squareness < 0.2) if (squareness < 0.2)
record_qr_grid(q, hn->index, i, vn->index); record_qr_grid(q, hn->index, i, vn->index);
} }
@ -1037,7 +1041,7 @@ static void test_grouping(struct quirc *q, unsigned int i)
*/ */
for (j = 0; j < q->num_capstones; j++) { for (j = 0; j < q->num_capstones; j++) {
struct quirc_capstone *c2 = &q->capstones[j]; struct quirc_capstone *c2 = &q->capstones[j];
double u, v; quirc_float_t u, v;
if (i == j) if (i == j)
continue; continue;

View file

@ -46,6 +46,20 @@ typedef uint16_t quirc_pixel_t;
#error "QUIRC_MAX_REGIONS > 65534 is not supported" #error "QUIRC_MAX_REGIONS > 65534 is not supported"
#endif #endif
#ifdef QUIRC_FLOAT_TYPE
/* Quirc uses double precision floating point internally by default.
* On platforms with a single precision FPU but no double precision FPU,
* this can be changed to float by defining QUIRC_FLOAT_TYPE.
*
* When setting QUIRC_FLOAT_TYPE to 'float', consider also defining QUIRC_USE_TGMATH.
* This will use the type-generic math functions (tgmath.h, C99 or later) instead of the normal ones,
* which will allow the compiler to use the correct overloaded functions for the type.
*/
typedef QUIRC_FLOAT_TYPE quirc_float_t;
#else
typedef double quirc_float_t;
#endif
struct quirc_region { struct quirc_region {
struct quirc_point seed; struct quirc_point seed;
int count; int count;
@ -58,7 +72,7 @@ struct quirc_capstone {
struct quirc_point corners[4]; struct quirc_point corners[4];
struct quirc_point center; struct quirc_point center;
double c[QUIRC_PERSPECTIVE_PARAMS]; quirc_float_t c[QUIRC_PERSPECTIVE_PARAMS];
int qr_grid; int qr_grid;
}; };
@ -76,7 +90,7 @@ struct quirc_grid {
/* Grid size and perspective transform */ /* Grid size and perspective transform */
int grid_size; int grid_size;
double c[QUIRC_PERSPECTIVE_PARAMS]; quirc_float_t c[QUIRC_PERSPECTIVE_PARAMS];
}; };
struct quirc_flood_fill_vars { struct quirc_flood_fill_vars {