From 963d6ed1260407d5996ce7e1c5446e476775263e Mon Sep 17 00:00:00 2001 From: Claudio Felber Date: Tue, 15 Sep 2020 00:27:15 +0200 Subject: [PATCH 01/31] Fix stack corruption and bus errors while scanning oversized QR codes --- lib/decode.c | 1 - lib/identify.c | 12 +++++++----- lib/quirc.h | 4 +++- lib/quirc_internal.h | 1 - 4 files changed, 10 insertions(+), 8 deletions(-) diff --git a/lib/decode.c b/lib/decode.c index 05d3112..6266611 100644 --- a/lib/decode.c +++ b/lib/decode.c @@ -409,7 +409,6 @@ struct datastream { static inline int grid_bit(const struct quirc_code *code, int x, int y) { int p = y * code->size + x; - return (code->cell_bitmap[p >> 3] >> (p & 7)) & 1; } diff --git a/lib/identify.c b/lib/identify.c index fa409d7..d33f3d7 100644 --- a/lib/identify.c +++ b/lib/identify.c @@ -656,8 +656,11 @@ static int measure_timing_pattern(struct quirc *q, int index) /* Choose the nearest allowable grid size */ size = scan * 2 + 13; ver = (size - 15) / 4; - qr->grid_size = ver * 4 + 17; + if (ver > QUIRC_MAX_VERSION) { + return -1; + } + qr->grid_size = ver * 4 + 17; return 0; } @@ -1135,11 +1138,10 @@ void quirc_extract(const struct quirc *q, int index, for (y = 0; y < qr->grid_size; y++) { int x; - for (x = 0; x < qr->grid_size; x++) { - if (read_cell(q, index, x, y) > 0) - code->cell_bitmap[i >> 3] |= (1 << (i & 7)); - + if (read_cell(q, index, x, y) > 0) { + code->cell_bitmap[i >> 3] |= (1 << (i & 7)); + } i++; } } diff --git a/lib/quirc.h b/lib/quirc.h index 0e7cb94..97b4bbe 100644 --- a/lib/quirc.h +++ b/lib/quirc.h @@ -78,7 +78,9 @@ typedef enum { const char *quirc_strerror(quirc_decode_error_t err); /* Limits on the maximum size of QR-codes and their content. */ -#define QUIRC_MAX_BITMAP 3917 +#define QUIRC_MAX_VERSION 40 +#define QUIRC_MAX_GRID_SIZE (QUIRC_MAX_VERSION * 4 + 17) +#define QUIRC_MAX_BITMAP (((QUIRC_MAX_GRID_SIZE * QUIRC_MAX_GRID_SIZE) + 7) / 8) #define QUIRC_MAX_PAYLOAD 8896 /* QR-code ECC types. */ diff --git a/lib/quirc_internal.h b/lib/quirc_internal.h index a9a54ab..371572f 100644 --- a/lib/quirc_internal.h +++ b/lib/quirc_internal.h @@ -28,7 +28,6 @@ #endif #define QUIRC_MAX_CAPSTONES 32 #define QUIRC_MAX_GRIDS 8 - #define QUIRC_PERSPECTIVE_PARAMS 8 #if QUIRC_MAX_REGIONS < UINT8_MAX From 5beb59e3b46b77251e19481bd5339decaf8f3df6 Mon Sep 17 00:00:00 2001 From: Claudio Felber Date: Mon, 21 Sep 2020 16:54:55 +0200 Subject: [PATCH 02/31] Support horizontally flipped QR-codes according to ISO 18004:2015 --- lib/decode.c | 16 ++++++++++++++++ lib/quirc.h | 3 +++ 2 files changed, 19 insertions(+) diff --git a/lib/decode.c b/lib/decode.c index 6266611..244ade3 100644 --- a/lib/decode.c +++ b/lib/decode.c @@ -916,3 +916,19 @@ quirc_decode_error_t quirc_decode(const struct quirc_code *code, return QUIRC_SUCCESS; } + +void quirc_flip(struct quirc_code *code) +{ + struct quirc_code flipped; + memset(&flipped, 0, sizeof(flipped)); + int offset = 0; + for (int y = 0, sx = 0; y < code->size; y++, sx++) { + for (int x = 0, sy = 0; x < code->size; x++, sy++) { + if (grid_bit(code, sx, sy)) { + flipped.cell_bitmap[offset >> 3] |= (1 << (offset & 7)); + } + offset++; + } + } + memcpy(&code->cell_bitmap, &flipped.cell_bitmap, sizeof(flipped.cell_bitmap)); +} \ No newline at end of file diff --git a/lib/quirc.h b/lib/quirc.h index 97b4bbe..d8d250d 100644 --- a/lib/quirc.h +++ b/lib/quirc.h @@ -168,6 +168,9 @@ void quirc_extract(const struct quirc *q, int index, quirc_decode_error_t quirc_decode(const struct quirc_code *code, struct quirc_data *data); +/* Flip a QR-code according to optional mirror feature of ISO 18004:2015 */ +void quirc_flip(struct quirc_code *code); + #ifdef __cplusplus } #endif From 4bc17cf8c679690d89ac2774f6acecabb2b01099 Mon Sep 17 00:00:00 2001 From: Claudio Felber Date: Tue, 22 Sep 2020 12:14:09 +0200 Subject: [PATCH 03/31] Optimize quirc_flip() --- lib/decode.c | 25 ++++++++++++------------- lib/identify.c | 2 +- 2 files changed, 13 insertions(+), 14 deletions(-) diff --git a/lib/decode.c b/lib/decode.c index 244ade3..0d654c5 100644 --- a/lib/decode.c +++ b/lib/decode.c @@ -919,16 +919,15 @@ quirc_decode_error_t quirc_decode(const struct quirc_code *code, void quirc_flip(struct quirc_code *code) { - struct quirc_code flipped; - memset(&flipped, 0, sizeof(flipped)); - int offset = 0; - for (int y = 0, sx = 0; y < code->size; y++, sx++) { - for (int x = 0, sy = 0; x < code->size; x++, sy++) { - if (grid_bit(code, sx, sy)) { - flipped.cell_bitmap[offset >> 3] |= (1 << (offset & 7)); - } - offset++; - } - } - memcpy(&code->cell_bitmap, &flipped.cell_bitmap, sizeof(flipped.cell_bitmap)); -} \ No newline at end of file + struct quirc_code flipped = {0}; + unsigned int offset = 0; + for (int y = 0; y < code->size; y++) { + for (int x = 0; x < code->size; x++) { + if (grid_bit(code, y, x)) { + flipped.cell_bitmap[offset >> 3u] |= (1u << (offset & 7u)); + } + offset++; + } + } + memcpy(&code->cell_bitmap, &flipped.cell_bitmap, sizeof(flipped.cell_bitmap)); +} diff --git a/lib/identify.c b/lib/identify.c index d33f3d7..b725fea 100644 --- a/lib/identify.c +++ b/lib/identify.c @@ -1140,7 +1140,7 @@ void quirc_extract(const struct quirc *q, int index, int x; for (x = 0; x < qr->grid_size; x++) { if (read_cell(q, index, x, y) > 0) { - code->cell_bitmap[i >> 3] |= (1 << (i & 7)); + code->cell_bitmap[i >> 3] |= (1 << (i & 7)); } i++; } From 2fd06df8de80cbe8cec119642558a6254e5773e7 Mon Sep 17 00:00:00 2001 From: Claudio Felber Date: Tue, 22 Sep 2020 12:18:23 +0200 Subject: [PATCH 04/31] Add support for flipped QR-codes in inspect and qrtest tools --- tests/inspect.c | 4 ++++ tests/qrtest.c | 19 ++++++++++++++----- 2 files changed, 18 insertions(+), 5 deletions(-) diff --git a/tests/inspect.c b/tests/inspect.c index 866ee16..ce5357a 100644 --- a/tests/inspect.c +++ b/tests/inspect.c @@ -34,6 +34,10 @@ static void dump_info(struct quirc *q) quirc_extract(q, i, &code); err = quirc_decode(&code, &data); + if (err == QUIRC_ERROR_DATA_ECC) { + quirc_flip(&code); + err = quirc_decode(&code, &data); + } dump_cells(&code); printf("\n"); diff --git a/tests/qrtest.c b/tests/qrtest.c index 2e50698..1bb178e 100644 --- a/tests/qrtest.c +++ b/tests/qrtest.c @@ -125,8 +125,15 @@ static int scan_file(const char *path, const char *filename, quirc_extract(decoder, i, &code); - if (!quirc_decode(&code, &data)) + quirc_decode_error_t err = quirc_decode(&code, &data); + if (err == QUIRC_ERROR_DATA_ECC) { + quirc_flip(&code); + err = quirc_decode(&code, &data); + } + + if (!err) { info->decode_count++; + } } (void)clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &tp); @@ -150,12 +157,14 @@ static int scan_file(const char *path, const char *filename, if (want_verbose) { struct quirc_data data; - quirc_decode_error_t err = - quirc_decode(&code, &data); + quirc_decode_error_t err = quirc_decode(&code, &data); + if (err == QUIRC_ERROR_DATA_ECC) { + quirc_flip(&code); + err = quirc_decode(&code, &data); + } if (err) { - printf(" ERROR: %s\n\n", - quirc_strerror(err)); + printf(" ERROR: %s\n\n", quirc_strerror(err)); } else { printf(" Decode successful:\n"); dump_data(&data); From 8e8a638a5f2a4eb58de0e017cce5a990baadd52c Mon Sep 17 00:00:00 2001 From: Claudio Felber Date: Tue, 22 Sep 2020 12:22:06 +0200 Subject: [PATCH 05/31] Update README.md with how to use quirc_flip() --- .gitignore | 1 + README.md | 17 +++++++++++++++++ 2 files changed, 18 insertions(+) diff --git a/.gitignore b/.gitignore index a7494bc..54855db 100644 --- a/.gitignore +++ b/.gitignore @@ -9,3 +9,4 @@ libquirc.so* .*.swp *~ .DS_Store +.idea \ No newline at end of file diff --git a/README.md b/README.md index d70c5c3..78051ad 100644 --- a/README.md +++ b/README.md @@ -174,6 +174,23 @@ for (i = 0; i < num_codes; i++) { `quirc_code` and `quirc_data` are flat structures which don't need to be initialized or freed after use. +In case you also need to support horizontally flipped QR-codes (mirrored +images according to ISO 18004:2015, pages 6 and 62), you can make a second +decode attempt with the flipped image data whenever you get an ECC failure: + +```C + err = quirc_decode(&code, &data); + if (err == QUIRC_ERROR_DATA_ECC) { + quirc_flip(&code); + err = quirc_decode(&code, &data); + } + + if (err) + printf("DECODE FAILED: %s\n", quirc_strerror(err)); + else + printf("Data: %s\n", data.payload); +``` + Copyright --------- Copyright (C) 2010-2012 Daniel Beer <> From 679e43f1444e2293c09c5cfa0bbc2500355fc968 Mon Sep 17 00:00:00 2001 From: YAMAMOTO Takashi Date: Thu, 18 Mar 2021 17:17:58 +0900 Subject: [PATCH 06/31] quirc-demo-opencv: Add OpenCV version of the demo My motivation is to experiment quirc on macOS, where neither of SDL1 or V4L2 work. ``` % brew install opencv % brew install pkg-config % brew install bsdmake % bsdmake quirc-demo-opencv % ./quirc-demo-opencv ``` Note: the macOS version of GNU make (/usr/bin/make) is a bit old for quirc's Makefile. --- Makefile | 25 +++- demo/demo_opencv.cxx | 263 +++++++++++++++++++++++++++++++++++++++++++ demo/demoutil.h | 8 ++ demo/dthash.h | 8 ++ 4 files changed, 299 insertions(+), 5 deletions(-) create mode 100644 demo/demo_opencv.cxx diff --git a/Makefile b/Makefile index 6f5c2ec..8677978 100644 --- a/Makefile +++ b/Makefile @@ -30,10 +30,15 @@ LIB_OBJ = \ DEMO_OBJ = \ demo/camera.o \ demo/mjpeg.o \ - demo/convert.o \ + demo/convert.o +DEMO_UTIL_OBJ = \ demo/dthash.o \ demo/demoutil.o +OPENCV_CFLAGS != pkg-config --cflags opencv4 +OPENCV_LIBS != pkg-config --libs opencv4 +QUIRC_CXXFLAGS = $(QUIRC_CFLAGS) $(OPENCV_CFLAGS) --std=c++17 + all: libquirc.so qrtest inspect quirc-demo quirc-scanner qrtest: tests/dbgutil.o tests/qrtest.o libquirc.a @@ -42,11 +47,14 @@ qrtest: tests/dbgutil.o tests/qrtest.o libquirc.a inspect: tests/dbgutil.o tests/inspect.o libquirc.a $(CC) -o $@ tests/dbgutil.o tests/inspect.o libquirc.a $(LDFLAGS) -lm -ljpeg -lpng $(SDL_LIBS) -lSDL_gfx -quirc-demo: $(DEMO_OBJ) demo/demo.o libquirc.a - $(CC) -o $@ $(DEMO_OBJ) demo/demo.o libquirc.a $(LDFLAGS) -lm -ljpeg $(SDL_LIBS) -lSDL_gfx +quirc-demo: $(DEMO_OBJ) $(DEMO_UTIL_OBJ) demo/demo.o libquirc.a + $(CC) -o $@ $(DEMO_OBJ) $(DEMO_UTIL_OBJ) demo/demo.o libquirc.a $(LDFLAGS) -lm -ljpeg $(SDL_LIBS) -lSDL_gfx -quirc-scanner: $(DEMO_OBJ) demo/scanner.o libquirc.a - $(CC) -o $@ $(DEMO_OBJ) demo/scanner.o libquirc.a $(LDFLAGS) -lm -ljpeg +quirc-demo-opencv: $(DEMO_UTIL_OBJ) demo/demo_opencv.o libquirc.a + $(CXX) -o $@ $(DEMO_UTIL_OBJ) demo/demo_opencv.o libquirc.a $(LDFLAGS) -lm -ljpeg $(OPENCV_LIBS) + +quirc-scanner: $(DEMO_OBJ) $(DEMO_UTIL_OBJ) demo/scanner.o libquirc.a + $(CC) -o $@ $(DEMO_OBJ) $(DEMO_UTIL_OBJ) demo/scanner.o libquirc.a $(LDFLAGS) -lm -ljpeg libquirc.a: $(LIB_OBJ) rm -f $@ @@ -62,12 +70,17 @@ libquirc.so.$(LIB_VERSION): $(LIB_OBJ) .c.o: $(CC) $(QUIRC_CFLAGS) -o $@ -c $< +.SUFFIXES: .cxx +.cxx.o: + $(CXX) $(QUIRC_CXXFLAGS) -o $@ -c $< + install: libquirc.a libquirc.so.$(LIB_VERSION) quirc-demo quirc-scanner install -o root -g root -m 0644 lib/quirc.h $(DESTDIR)$(PREFIX)/include install -o root -g root -m 0644 libquirc.a $(DESTDIR)$(PREFIX)/lib install -o root -g root -m 0755 libquirc.so.$(LIB_VERSION) \ $(DESTDIR)$(PREFIX)/lib install -o root -g root -m 0755 quirc-demo $(DESTDIR)$(PREFIX)/bin + # install -o root -g root -m 0755 quirc-demo-opencv $(DESTDIR)$(PREFIX)/bin install -o root -g root -m 0755 quirc-scanner $(DESTDIR)$(PREFIX)/bin uninstall: @@ -75,6 +88,7 @@ uninstall: rm -f $(DESTDIR)$(PREFIX)/lib/libquirc.so.$(LIB_VERSION) rm -f $(DESTDIR)$(PREFIX)/lib/libquirc.a rm -f $(DESTDIR)$(PREFIX)/bin/quirc-demo + rm -f $(DESTDIR)$(PREFIX)/bin/quirc-demo-opencv rm -f $(DESTDIR)$(PREFIX)/bin/quirc-scanner clean: @@ -85,4 +99,5 @@ clean: rm -f qrtest rm -f inspect rm -f quirc-demo + rm -f quirc-demo-opencv rm -f quirc-scanner diff --git a/demo/demo_opencv.cxx b/demo/demo_opencv.cxx new file mode 100644 index 0000000..d6dbe8b --- /dev/null +++ b/demo/demo_opencv.cxx @@ -0,0 +1,263 @@ +/* quirc -- QR-code recognition library + * Copyright (C) 2010-2012 Daniel Beer + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include +#include +#include +#include + +#include +#include +#include + +using namespace cv; + +#include "camera.h" +#include "convert.h" +#include "dthash.h" +#include "demoutil.h" + +/* Collected command-line arguments */ +static int want_frame_rate = 0; +static int want_verbose = 0; +static int printer_timeout = 2; + +static const int font = FONT_HERSHEY_PLAIN; +static const int thickness = 2; +static const double font_scale = 1.5; +static Scalar blue = Scalar(255, 0, 8); + +static void fat_text(Mat &screen, int x, int y, const char *text) +{ + putText(screen, text, Point(x, y), font, font_scale, blue, thickness); +} + +static void fat_text_cent(Mat &screen, int x, int y, const char *text) +{ + int baseline; + + Size size = getTextSize(text, font, font_scale, thickness, &baseline); + putText(screen, text, Point(x - size.width / 2, y - size.height /2), + font, font_scale, blue, thickness); +} + +static void draw_qr(Mat &screen, struct quirc *q, struct dthash *dt) +{ + int count = quirc_count(q); + int i; + + for (i = 0; i < count; i++) { + struct quirc_code code; + struct quirc_data data; + quirc_decode_error_t err; + int j; + int xc = 0; + int yc = 0; + char buf[128]; + + quirc_extract(q, i, &code); + + for (j = 0; j < 4; j++) { + struct quirc_point *a = &code.corners[j]; + struct quirc_point *b = &code.corners[(j + 1) % 4]; + + xc += a->x; + yc += a->y; + line(screen, Point(a->x, a->y), Point(b->x, b->y), blue, 8); + } + + xc /= 4; + yc /= 4; + + if (want_verbose) { + snprintf(buf, sizeof(buf), "Code size: %d cells", + code.size); + fat_text_cent(screen, xc, yc - 20, buf); + } + + err = quirc_decode(&code, &data); + + if (err) { + if (want_verbose) + fat_text_cent(screen, xc, yc, quirc_strerror(err)); + } else { + fat_text_cent(screen, xc, yc, (const char *)data.payload); + print_data(&data, dt, want_verbose); + + if (want_verbose) { + snprintf(buf, sizeof(buf), + "Ver: %d, ECC: %c, Mask: %d, Type: %d", + data.version, "MLHQ"[data.ecc_level], + data.mask, data.data_type); + fat_text_cent(screen, xc, yc + 20, buf); + } + } + } +} + +static int main_loop(VideoCapture &cap, struct quirc *q) +{ + time_t last_rate = 0; + int frame_count = 0; + char rate_text[64]; + struct dthash dt; + + rate_text[0] = 0; + dthash_init(&dt, printer_timeout); + + Mat frame; + for (;;) { + time_t now = time(NULL); + + cap.read(frame); + if (frame.empty()) { + perror("empty frame"); + return 0; + } + + int w; + int h; + uint8_t *buf = quirc_begin(q, &w, &h); + + /* convert frame into buf */ + assert(frame.cols == w); + assert(frame.rows == h); + Mat gray; + cvtColor(frame, gray, COLOR_BGR2GRAY, 0); + for (int y = 0; y < gray.rows; y++) { + for (int x = 0; x < gray.cols; x++) { + buf[(y * w + x)] = gray.at(y, x); + } + } + + quirc_end(q); + + draw_qr(frame, q, &dt); + if (want_frame_rate) + fat_text(frame, 20, 20, rate_text); + + imshow("quirc-demo-opencv", frame); + waitKey(5); + + if (now != last_rate) { + snprintf(rate_text, sizeof(rate_text), + "Frame rate: %d fps", frame_count); + frame_count = 0; + last_rate = now; + } + + frame_count++; + } +} + +static int run_demo(void) +{ + struct quirc *qr; + VideoCapture cap(0); + unsigned int width; + unsigned int height; + + if (!cap.isOpened()) { + perror("camera_open"); + goto fail_qr; + } + + width = cap.get(CAP_PROP_FRAME_WIDTH); + height = cap.get(CAP_PROP_FRAME_HEIGHT); + + qr = quirc_new(); + if (!qr) { + perror("couldn't allocate QR decoder"); + goto fail_qr; + } + + if (quirc_resize(qr, width, height) < 0) { + perror("couldn't allocate QR buffer"); + goto fail_qr_resize; + } + + if (main_loop(cap, qr) < 0) { + goto fail_main_loop; + } + + quirc_destroy(qr); + + return 0; + +fail_main_loop: +fail_qr_resize: + quirc_destroy(qr); +fail_qr: + + return -1; +} + +static void usage(const char *progname) +{ + printf("Usage: %s [options]\n\n" +"Valid options are:\n\n" +" -f Show frame rate on screen.\n" +" -v Show extra data for detected codes.\n" +" -p Set printer timeout (seconds).\n" +" --help Show this information.\n" +" --version Show library version information.\n", + progname); +} + +int main(int argc, char **argv) +{ + static const struct option longopts[] = { + {"help", 0, 0, 'H'}, + {"version", 0, 0, 'V'}, + {NULL, 0, 0, 0} + }; + int opt; + + printf("quirc demo with OpenCV\n"); + printf("Copyright (C) 2010-2014 Daniel Beer \n"); + printf("\n"); + + while ((opt = getopt_long(argc, argv, "fvg:p:", + longopts, NULL)) >= 0) + switch (opt) { + case 'V': + printf("Library version: %s\n", quirc_version()); + return 0; + + case 'H': + usage(argv[0]); + return 0; + + case 'v': + want_verbose = 1; + break; + + case 'f': + want_frame_rate = 1; + break; + + case 'p': + printer_timeout = atoi(optarg); + break; + + case '?': + fprintf(stderr, "Try --help for usage information\n"); + return -1; + } + + return run_demo(); +} diff --git a/demo/demoutil.h b/demo/demoutil.h index b6f7dc9..888eddd 100644 --- a/demo/demoutil.h +++ b/demo/demoutil.h @@ -20,6 +20,10 @@ #include "dthash.h" #include "quirc.h" +#ifdef __cplusplus +extern "C" { +#endif + /* Check if we've seen the given code, and if not, print it on stdout. * Include version info if requested. */ @@ -31,4 +35,8 @@ void print_data(const struct quirc_data *data, struct dthash *dt, */ int parse_size(const char *text, int *video_width, int *video_height); +#ifdef __cplusplus +} +#endif + #endif diff --git a/demo/dthash.h b/demo/dthash.h index 661f691..431db1e 100644 --- a/demo/dthash.h +++ b/demo/dthash.h @@ -40,6 +40,10 @@ struct dthash { int timeout; }; +#ifdef __cplusplus +extern "C" { +#endif + /* Initialise a detector hash with the given timeout. */ void dthash_init(struct dthash *d, int timeout); @@ -50,4 +54,8 @@ void dthash_init(struct dthash *d, int timeout); */ int dthash_seen(struct dthash *d, const struct quirc_data *data); +#ifdef __cplusplus +} +#endif + #endif From f10e7dc39af801b63f837045e61b619ab5d8e466 Mon Sep 17 00:00:00 2001 From: YAMAMOTO Takashi Date: Mon, 22 Mar 2021 14:43:35 +0900 Subject: [PATCH 07/31] README.md: mention requirements of each test/demo --- README.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/README.md b/README.md index 78051ad..7d79121 100644 --- a/README.md +++ b/README.md @@ -38,12 +38,16 @@ video stream is displayed on screen as it's received, and any QR codes recognised are highlighted in the image, with the decoded information both displayed on the image and printed on stdout. +This requires SDL and V4L2. + ### quirc-scanner This program turns your camera into a barcode scanner. It's almost the same as the `demo` application, but it doesn't display the video stream, and thus doesn't require a graphical display. +This requires V4L2. + ### qrtest This test is used to evaluate the performance of library. Given a directory @@ -57,6 +61,8 @@ This test is used for debugging. Given a single JPEG image, it will display a diagram showing the internal state of the decoder as well as printing additional information on stdout. +This requires SDL. + Installation ------------ To build the library and associated demos/tests, type `make`. If you need to From a2b1358580105547fecf2158f272d00f7de9538a Mon Sep 17 00:00:00 2001 From: YAMAMOTO Takashi Date: Thu, 8 Apr 2021 09:35:40 +0900 Subject: [PATCH 08/31] README.md: Update for quirc-demo-opencv --- README.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/README.md b/README.md index 7d79121..3b370e5 100644 --- a/README.md +++ b/README.md @@ -40,6 +40,13 @@ displayed on the image and printed on stdout. This requires SDL and V4L2. +### quirc-demo-opencv + +A demo similar to `quirc-demo`. +But this version uses OpenCV instead of other libraries. + +This requires OpenCV. + ### quirc-scanner This program turns your camera into a barcode scanner. It's almost the same as @@ -81,6 +88,7 @@ are unable to build everything: * inspect * quirc-scanner * quirc-demo +* quirc-demo-opencv Library use ----------- From ef1b97015ed6679bebc03334229faad5647b23ff Mon Sep 17 00:00:00 2001 From: YAMAMOTO Takashi Date: Thu, 8 Apr 2021 09:39:44 +0900 Subject: [PATCH 09/31] Makefile: quirc-demo-opencv doesn't use libjpeg --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 8677978..2fc4a14 100644 --- a/Makefile +++ b/Makefile @@ -51,7 +51,7 @@ quirc-demo: $(DEMO_OBJ) $(DEMO_UTIL_OBJ) demo/demo.o libquirc.a $(CC) -o $@ $(DEMO_OBJ) $(DEMO_UTIL_OBJ) demo/demo.o libquirc.a $(LDFLAGS) -lm -ljpeg $(SDL_LIBS) -lSDL_gfx quirc-demo-opencv: $(DEMO_UTIL_OBJ) demo/demo_opencv.o libquirc.a - $(CXX) -o $@ $(DEMO_UTIL_OBJ) demo/demo_opencv.o libquirc.a $(LDFLAGS) -lm -ljpeg $(OPENCV_LIBS) + $(CXX) -o $@ $(DEMO_UTIL_OBJ) demo/demo_opencv.o libquirc.a $(LDFLAGS) -lm $(OPENCV_LIBS) quirc-scanner: $(DEMO_OBJ) $(DEMO_UTIL_OBJ) demo/scanner.o libquirc.a $(CC) -o $@ $(DEMO_OBJ) $(DEMO_UTIL_OBJ) demo/scanner.o libquirc.a $(LDFLAGS) -lm -ljpeg From a3623ea4f4858bbf9f8719868c2db3ef1c5894a8 Mon Sep 17 00:00:00 2001 From: YAMAMOTO Takashi Date: Thu, 8 Apr 2021 09:46:30 +0900 Subject: [PATCH 10/31] README.md: Update extra dependencies for each programs --- README.md | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 3b370e5..baa72ce 100644 --- a/README.md +++ b/README.md @@ -28,8 +28,7 @@ a good choice for this purpose: The distribution comes with, in addition to the library, several test programs. While the core library is very portable, these programs have some additional -dependencies. All of them require libjpeg, and two (`quirc-demo` and `inspect`) -require SDL. The camera demos use Linux-specific APIs: +dependencies as documented below. ### quirc-demo @@ -38,14 +37,14 @@ video stream is displayed on screen as it's received, and any QR codes recognised are highlighted in the image, with the decoded information both displayed on the image and printed on stdout. -This requires SDL and V4L2. +This requires: libjpeg, libpng, SDL, V4L2 ### quirc-demo-opencv A demo similar to `quirc-demo`. But this version uses OpenCV instead of other libraries. -This requires OpenCV. +This requires: OpenCV ### quirc-scanner @@ -53,7 +52,7 @@ This program turns your camera into a barcode scanner. It's almost the same as the `demo` application, but it doesn't display the video stream, and thus doesn't require a graphical display. -This requires V4L2. +This requires: libjpeg, V4L2 ### qrtest @@ -62,13 +61,15 @@ tree containing a bunch of JPEG images, it will attempt to locate and decode QR codes in each image. Speed and success statistics are collected and printed on stdout. +This requires: libjpeg, libpng + ### inspect This test is used for debugging. Given a single JPEG image, it will display a diagram showing the internal state of the decoder as well as printing additional information on stdout. -This requires SDL. +This requires: libjpeg, libpng, SDL Installation ------------ From de68db7b690719fc780fcb8308c5715267e67ed6 Mon Sep 17 00:00:00 2001 From: YAMAMOTO Takashi Date: Thu, 8 Apr 2021 13:24:32 +0900 Subject: [PATCH 11/31] otsu: Use double for weighted sums My phone camera produces 12M-pixel images. They can easily overflow an int. --- lib/identify.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/identify.c b/lib/identify.c index b725fea..584994f 100644 --- a/lib/identify.c +++ b/lib/identify.c @@ -190,14 +190,14 @@ static uint8_t otsu(const struct quirc *q) } // Calculate weighted sum of histogram values - unsigned int sum = 0; + double sum = 0; unsigned int i = 0; for (i = 0; i <= UINT8_MAX; ++i) { sum += i * histogram[i]; } // Compute threshold - int sumB = 0; + double sumB = 0; int q1 = 0; double max = 0; uint8_t threshold = 0; @@ -213,8 +213,8 @@ static uint8_t otsu(const struct quirc *q) break; sumB += i * histogram[i]; - const double m1 = (double)sumB / q1; - const double m2 = ((double)sum - sumB) / q2; + const double m1 = sumB / q1; + const double m2 = (sum - sumB) / q2; const double m1m2 = m1 - m2; const double variance = m1m2 * m1m2 * q1 * q2; if (variance >= max) { From ea9b6ee5efbcff60b2737dd1378b3dbf22c6f93c Mon Sep 17 00:00:00 2001 From: YAMAMOTO Takashi Date: Thu, 8 Apr 2021 13:35:12 +0900 Subject: [PATCH 12/31] otsu: Use unsigned for the pixel numbers Because singed numbers don't make sense here. --- lib/identify.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/identify.c b/lib/identify.c index 584994f..5c66865 100644 --- a/lib/identify.c +++ b/lib/identify.c @@ -177,13 +177,13 @@ static void flood_fill_seed(struct quirc *q, int x, int y, int from, int to, static uint8_t otsu(const struct quirc *q) { - int numPixels = q->w * q->h; + unsigned int numPixels = q->w * q->h; // Calculate histogram unsigned int histogram[UINT8_MAX + 1]; (void)memset(histogram, 0, sizeof(histogram)); uint8_t* ptr = q->image; - int length = numPixels; + unsigned int length = numPixels; while (length--) { uint8_t value = *ptr++; histogram[value]++; @@ -198,7 +198,7 @@ static uint8_t otsu(const struct quirc *q) // Compute threshold double sumB = 0; - int q1 = 0; + unsigned int q1 = 0; double max = 0; uint8_t threshold = 0; for (i = 0; i <= UINT8_MAX; ++i) { @@ -208,7 +208,7 @@ static uint8_t otsu(const struct quirc *q) continue; // Weighted foreground - const int q2 = numPixels - q1; + const unsigned int q2 = numPixels - q1; if (q2 == 0) break; From 6fb24d26c8d4ecb4f201a9c82777eb3ef514ec0f Mon Sep 17 00:00:00 2001 From: YAMAMOTO Takashi Date: Thu, 8 Apr 2021 09:07:13 +0900 Subject: [PATCH 13/31] README.md: document requirement on make command --- README.md | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/README.md b/README.md index baa72ce..c965561 100644 --- a/README.md +++ b/README.md @@ -71,6 +71,28 @@ additional information on stdout. This requires: libjpeg, libpng, SDL +Build-time requirements +----------------------- + +### make + +While we are trying to keep our makefiles portable, +it might be incompatible with some versions of make. + +#### GNU make + +Version 4.x and later works. We recommend to use it. + +Version prior to 4.0 doesn't work because it doesn't support `!=`. + +*Note*: macOS's default version of make is GNU make 3.81 as of writing this. + +#### BSD make + +It also works. +You might need to specify the `-r` make option because some of +the default macros like CFLAGS from sys.mk can cause unintended effects. + Installation ------------ To build the library and associated demos/tests, type `make`. If you need to From 02ed1066ba50eb90e040eb7a1de047416700fb81 Mon Sep 17 00:00:00 2001 From: YAMAMOTO Takashi Date: Fri, 23 Apr 2021 10:01:31 +0900 Subject: [PATCH 14/31] test_grouping: use unsigned variables where it makes more sense --- lib/identify.c | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/lib/identify.c b/lib/identify.c index b725fea..b69f37f 100644 --- a/lib/identify.c +++ b/lib/identify.c @@ -380,7 +380,8 @@ static void record_capstone(struct quirc *q, int ring, int stone) perspective_map(capstone->c, 3.5, 3.5, &capstone->center); } -static void test_capstone(struct quirc *q, int x, int y, int *pb) +static void test_capstone(struct quirc *q, unsigned int x, unsigned int y, + unsigned int *pb) { int ring_right = region_code(q, x - pb[4], y); int stone = region_code(q, x - pb[4] - pb[3] - pb[2], y); @@ -389,7 +390,7 @@ static void test_capstone(struct quirc *q, int x, int y, int *pb) y); struct quirc_region *stone_reg; struct quirc_region *ring_reg; - int ratio; + unsigned int ratio; if (ring_left < 0 || ring_right < 0 || stone < 0) return; @@ -417,14 +418,14 @@ static void test_capstone(struct quirc *q, int x, int y, int *pb) record_capstone(q, ring_left, stone); } -static void finder_scan(struct quirc *q, int y) +static void finder_scan(struct quirc *q, unsigned int y) { quirc_pixel_t *row = q->pixels + y * q->w; - int x; + unsigned int x; int last_color = 0; - int run_length = 0; - int run_count = 0; - int pb[5]; + unsigned int run_length = 0; + unsigned int run_count = 0; + unsigned int pb[5]; memset(pb, 0, sizeof(pb)); for (x = 0; x < q->w; x++) { @@ -437,9 +438,9 @@ static void finder_scan(struct quirc *q, int y) run_count++; if (!color && run_count >= 5) { - static int check[5] = {1, 1, 3, 1, 1}; - int avg, err; - int i; + static unsigned int check[5] = {1, 1, 3, 1, 1}; + unsigned int avg, err; + unsigned int i; int ok = 1; avg = (pb[0] + pb[1] + pb[3] + pb[4]) / 4; @@ -1024,7 +1025,7 @@ static void test_neighbours(struct quirc *q, int i, record_qr_grid(q, best_h, i, best_v); } -static void test_grouping(struct quirc *q, int i) +static void test_grouping(struct quirc *q, unsigned int i) { struct quirc_capstone *c1 = &q->capstones[i]; int j; From 734258c7755e56cbb185eaecf99d813109585175 Mon Sep 17 00:00:00 2001 From: YAMAMOTO Takashi Date: Fri, 23 Apr 2021 10:03:00 +0900 Subject: [PATCH 15/31] finder_scan: constify the "check" array --- lib/identify.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/identify.c b/lib/identify.c index b69f37f..bddd3c6 100644 --- a/lib/identify.c +++ b/lib/identify.c @@ -438,7 +438,7 @@ static void finder_scan(struct quirc *q, unsigned int y) run_count++; if (!color && run_count >= 5) { - static unsigned int check[5] = {1, 1, 3, 1, 1}; + static const unsigned int check[5] = {1, 1, 3, 1, 1}; unsigned int avg, err; unsigned int i; int ok = 1; From 65379a7c3966b202f3f223769652e6c01b7cd28e Mon Sep 17 00:00:00 2001 From: YAMAMOTO Takashi Date: Fri, 23 Apr 2021 10:06:50 +0900 Subject: [PATCH 16/31] finder_scan: Improve capstone detection on small images When using small captured images, I somehow frequently see failures to recognize a capstone due to rounding errors. eg. when pb[] = {2 3 8 3 2}. This commit tries to improve it by using fixed-point arithmetics. The scaling factor was chosen somehow arbitrary. A moderate scaling should be enough. --- lib/identify.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/lib/identify.c b/lib/identify.c index bddd3c6..7a1096e 100644 --- a/lib/identify.c +++ b/lib/identify.c @@ -438,17 +438,18 @@ static void finder_scan(struct quirc *q, unsigned int y) run_count++; if (!color && run_count >= 5) { + const int scale = 16; static const unsigned int check[5] = {1, 1, 3, 1, 1}; unsigned int avg, err; unsigned int i; int ok = 1; - avg = (pb[0] + pb[1] + pb[3] + pb[4]) / 4; + avg = (pb[0] + pb[1] + pb[3] + pb[4]) * scale / 4; err = avg * 3 / 4; for (i = 0; i < 5; i++) - if (pb[i] < check[i] * avg - err || - pb[i] > check[i] * avg + err) + if (pb[i] * scale < check[i] * avg - err || + pb[i] * scale > check[i] * avg + err) ok = 0; if (ok) From 21b7581b041addfe13cafc6d487d6547bf6f515d Mon Sep 17 00:00:00 2001 From: YAMAMOTO Takashi Date: Fri, 23 Apr 2021 13:40:10 +0900 Subject: [PATCH 17/31] Shrink struct datastream by borrowing the user buffer --- lib/decode.c | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/lib/decode.c b/lib/decode.c index 0d654c5..1914096 100644 --- a/lib/decode.c +++ b/lib/decode.c @@ -399,7 +399,7 @@ static quirc_decode_error_t correct_format(uint16_t *f_ret) */ struct datastream { - uint8_t raw[QUIRC_MAX_PAYLOAD]; + uint8_t *raw; int data_bits; int ptr; @@ -905,11 +905,23 @@ quirc_decode_error_t quirc_decode(const struct quirc_code *code, if (err) return err; + /* + * Borrow data->payload to store the raw bits. + * It's only used during read_data + coddestream_ecc below. + * + * This trick saves the size of struct datastream, which we allocate + * on the stack. + */ + + ds.raw = data->payload; + read_data(code, data, &ds); err = codestream_ecc(data, &ds); if (err) return err; + ds.raw = NULL; /* We've done with this buffer. */ + err = decode_payload(data, &ds); if (err) return err; From e43a33bbc4e4c3607a6c9d84c41cdbf1b0d920b5 Mon Sep 17 00:00:00 2001 From: Daniel Beer Date: Tue, 11 May 2021 10:34:42 +1200 Subject: [PATCH 18/31] Add name of license to LICENSE file. --- LICENSE | 3 +++ 1 file changed, 3 insertions(+) diff --git a/LICENSE b/LICENSE index d47c026..2db5a8f 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,9 @@ quirc -- QR-code recognition library Copyright (C) 2010-2012 Daniel Beer +ISC License +=========== + Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all From 772cd3e73f810c142ff3f7b45c3bad73146098e0 Mon Sep 17 00:00:00 2001 From: YAMAMOTO Takashi Date: Mon, 22 Mar 2021 10:56:51 +0900 Subject: [PATCH 19/31] Improve stack usage for flood filling * Make flood filling logic iterative (vs recursive) I basically tried one-to-one conversions here to avoid mistakes. probably it has a room for later optimizations. * Use explicit malloc (vs variables on stack) to allocate the work area. * Estimate the amount of memory for the work area dynamically from the image size, instead of using a constant FLOOD_FILL_MAX_DEPTH, which is too big in the most cases. --- lib/identify.c | 117 ++++++++++++++++++++++++++++++------------- lib/quirc.c | 23 +++++++++ lib/quirc_internal.h | 14 ++++++ 3 files changed, 119 insertions(+), 35 deletions(-) diff --git a/lib/identify.c b/lib/identify.c index 3cf2d28..00c4dd4 100644 --- a/lib/identify.c +++ b/lib/identify.c @@ -122,52 +122,99 @@ static void perspective_unmap(const double *c, * Span-based floodfill routine */ -#define FLOOD_FILL_MAX_DEPTH 4096 - typedef void (*span_func_t)(void *user_data, int y, int left, int right); -static void flood_fill_seed(struct quirc *q, int x, int y, int from, int to, - span_func_t func, void *user_data, - int depth) +static void flood_fill_seed(struct quirc *q, + int x0, int y0, + int from, int to, + span_func_t func, void *user_data) { - int left = x; - int right = x; + struct quirc_flood_fill_vars *const stack = q->flood_fill_vars; + const size_t stack_size = q->num_flood_fill_vars; + const struct quirc_flood_fill_vars *const last_vars = + &stack[stack_size - 1]; + + struct quirc_flood_fill_vars *vars; + struct quirc_flood_fill_vars *next_vars; int i; - quirc_pixel_t *row = q->pixels + y * q->w; + quirc_pixel_t *row; - if (depth >= FLOOD_FILL_MAX_DEPTH) - return; + /* Set up the first context */ + next_vars = stack; + next_vars->x = x0; + next_vars->y = y0; - while (left > 0 && row[left - 1] == from) - left--; +call: + vars = next_vars; + vars->left = vars->x; + vars->right = vars->x; - while (right < q->w - 1 && row[right + 1] == from) - right++; + row = q->pixels + vars->y * q->w; + + while (vars->left > 0 && row[vars->left - 1] == from) + vars->left--; + + while (vars->right < q->w - 1 && row[vars->right + 1] == from) + vars->right++; /* Fill the extent */ - for (i = left; i <= right; i++) + for (i = vars->left; i <= vars->right; i++) row[i] = to; if (func) - func(user_data, y, left, right); + func(user_data, vars->y, vars->left, vars->right); - /* Seed new flood-fills */ - if (y > 0) { - row = q->pixels + (y - 1) * q->w; - - for (i = left; i <= right; i++) - if (row[i] == from) - flood_fill_seed(q, i, y - 1, from, to, - func, user_data, depth + 1); + if (vars == last_vars) { + return; } - if (y < q->h - 1) { - row = q->pixels + (y + 1) * q->w; + /* Seed new flood-fills */ + if (vars->y > 0) { + row = q->pixels + (vars->y - 1) * q->w; - for (i = left; i <= right; i++) - if (row[i] == from) - flood_fill_seed(q, i, y + 1, from, to, - func, user_data, depth + 1); + for (i = vars->left; i <= vars->right; i++) + if (row[i] == from) { + /* Save the current context */ + vars->i = i; + vars->pc = 1; + + /* Set up the next context */ + next_vars = vars + 1; + next_vars->x = i; + next_vars->y = vars->y - 1; + goto call; +return_from_call1: ; + } + } + + if (vars->y < q->h - 1) { + row = q->pixels + (vars->y + 1) * q->w; + + for (i = vars->left; i <= vars->right; i++) + if (row[i] == from) { + /* Save the current context */ + vars->i = i; + vars->pc = 2; + + /* Set up the next context */ + next_vars = vars + 1; + next_vars->x = i; + next_vars->y = vars->y + 1; + goto call; +return_from_call2: ; + } + } + + if (vars > stack) { + /* Restore the previous context */ + vars--; + i = vars->i; + if (vars->pc == 1) { + row = q->pixels + (vars->y - 1) * q->w; + goto return_from_call1; + } + row = q->pixels + (vars->y + 1) * q->w; + goto return_from_call2; } } @@ -260,7 +307,7 @@ static int region_code(struct quirc *q, int x, int y) box->seed.y = y; box->capstone = -1; - flood_fill_seed(q, x, y, pixel, region, area_count, box, 0); + flood_fill_seed(q, x, y, pixel, region, area_count, box); return region; } @@ -330,7 +377,7 @@ static void find_region_corners(struct quirc *q, psd.scores[0] = -1; flood_fill_seed(q, region->seed.x, region->seed.y, rcode, QUIRC_PIXEL_BLACK, - find_one_corner, &psd, 0); + find_one_corner, &psd); psd.ref.x = psd.corners[0].x - psd.ref.x; psd.ref.y = psd.corners[0].y - psd.ref.y; @@ -348,7 +395,7 @@ static void find_region_corners(struct quirc *q, flood_fill_seed(q, region->seed.x, region->seed.y, QUIRC_PIXEL_BLACK, rcode, - find_other_corners, &psd, 0); + find_other_corners, &psd); } static void record_capstone(struct quirc *q, int ring, int stone) @@ -966,10 +1013,10 @@ static void record_qr_grid(struct quirc *q, int a, int b, int c) flood_fill_seed(q, reg->seed.x, reg->seed.y, qr->align_region, QUIRC_PIXEL_BLACK, - NULL, NULL, 0); + NULL, NULL); flood_fill_seed(q, reg->seed.x, reg->seed.y, QUIRC_PIXEL_BLACK, qr->align_region, - find_leftmost_to_line, &psd, 0); + find_leftmost_to_line, &psd); } } diff --git a/lib/quirc.c b/lib/quirc.c index bbe1fe9..6108cfb 100644 --- a/lib/quirc.c +++ b/lib/quirc.c @@ -41,6 +41,7 @@ void quirc_destroy(struct quirc *q) same size, so we need to be careful here to avoid a double free */ if (!QUIRC_PIXEL_ALIAS_IMAGE) free(q->pixels); + free(q->flood_fill_vars); free(q); } @@ -48,6 +49,8 @@ int quirc_resize(struct quirc *q, int w, int h) { uint8_t *image = NULL; quirc_pixel_t *pixels = NULL; + size_t num_vars; + struct quirc_flood_fill_vars *vars = NULL; /* * XXX: w and h should be size_t (or at least unsigned) as negatives @@ -86,6 +89,22 @@ int quirc_resize(struct quirc *q, int w, int h) goto fail; } + /* + * alloc the work area for the flood filling logic. + * + * the size was chosen with the following assumptions and observations: + * + * - rings are the regions which requires the biggest work area. + * - they consumes the most when they are rotated by about 45 degree. + * in that case, the necessary depth is about (2 * height_of_the_ring). + * - the maximum height of rings would be about 1/3 of the image height. + */ + + num_vars = h * 2 / 3; + vars = malloc(sizeof(*vars) * num_vars); + if (!vars) + goto fail; + /* alloc succeeded, update `q` with the new size and buffers */ q->w = w; q->h = h; @@ -95,12 +114,16 @@ int quirc_resize(struct quirc *q, int w, int h) free(q->pixels); q->pixels = pixels; } + free(q->flood_fill_vars); + q->flood_fill_vars = vars; + q->num_flood_fill_vars = num_vars; return 0; /* NOTREACHED */ fail: free(image); free(pixels); + free(vars); return -1; } diff --git a/lib/quirc_internal.h b/lib/quirc_internal.h index 371572f..a94d8e9 100644 --- a/lib/quirc_internal.h +++ b/lib/quirc_internal.h @@ -17,6 +17,8 @@ #ifndef QUIRC_INTERNAL_H_ #define QUIRC_INTERNAL_H_ +#include + #include "quirc.h" #define QUIRC_PIXEL_WHITE 0 @@ -75,6 +77,15 @@ struct quirc_grid { double c[QUIRC_PERSPECTIVE_PARAMS]; }; +struct quirc_flood_fill_vars { + int x; + int y; + int right; + int left; + int i; + int pc; /* caller id */ +}; + struct quirc { uint8_t *image; quirc_pixel_t *pixels; @@ -89,6 +100,9 @@ struct quirc { int num_grids; struct quirc_grid grids[QUIRC_MAX_GRIDS]; + + size_t num_flood_fill_vars; + struct quirc_flood_fill_vars *flood_fill_vars; }; /************************************************************************ From f1dd37fbdb2a8c18c896f79aa48010d516b6cd1c Mon Sep 17 00:00:00 2001 From: YAMAMOTO Takashi Date: Thu, 6 May 2021 09:54:00 +0900 Subject: [PATCH 20/31] quirc_resize: Make this a bit more careful about integer overflows Also, avoid malloc(0), which is not too portable. --- lib/quirc.c | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/lib/quirc.c b/lib/quirc.c index 6108cfb..3cf75b9 100644 --- a/lib/quirc.c +++ b/lib/quirc.c @@ -50,6 +50,7 @@ int quirc_resize(struct quirc *q, int w, int h) uint8_t *image = NULL; quirc_pixel_t *pixels = NULL; size_t num_vars; + size_t vars_byte_size; struct quirc_flood_fill_vars *vars = NULL; /* @@ -100,8 +101,19 @@ int quirc_resize(struct quirc *q, int w, int h) * - the maximum height of rings would be about 1/3 of the image height. */ - num_vars = h * 2 / 3; - vars = malloc(sizeof(*vars) * num_vars); + if ((size_t)h * 2 / 2 != h) { + goto fail; /* size_t overflow */ + } + num_vars = (size_t)h * 2 / 3; + if (num_vars == 0) { + num_vars = 1; + } + + vars_byte_size = sizeof(*vars) * num_vars; + if (vars_byte_size / sizeof(*vars) != num_vars) { + goto fail; /* size_t overflow */ + } + vars = malloc(vars_byte_size); if (!vars) goto fail; From aad3fc63b6f7653f610462b8a35c2a69235be07d Mon Sep 17 00:00:00 2001 From: YAMAMOTO Takashi Date: Tue, 11 May 2021 12:53:15 +0900 Subject: [PATCH 21/31] flood_fill_seed: separate a logic to fill a line --- lib/identify.c | 43 +++++++++++++++++++++++++++++++------------ 1 file changed, 31 insertions(+), 12 deletions(-) diff --git a/lib/identify.c b/lib/identify.c index 00c4dd4..d3631d0 100644 --- a/lib/identify.c +++ b/lib/identify.c @@ -124,6 +124,35 @@ static void perspective_unmap(const double *c, typedef void (*span_func_t)(void *user_data, int y, int left, int right); +static void flood_fill_line(struct quirc *q, int x, int y, + int from, int to, + int *leftp, int *rightp) +{ + quirc_pixel_t *row; + int left; + int right; + int i; + + left = x; + right = x; + + row = q->pixels + y * q->w; + + while (left > 0 && row[left - 1] == from) + left--; + + while (right < q->w - 1 && row[right + 1] == from) + right++; + + /* Fill the extent */ + for (i = left; i <= right; i++) + row[i] = to; + + /* Return the processed range */ + *leftp = left; + *rightp = right; +} + static void flood_fill_seed(struct quirc *q, int x0, int y0, int from, int to, @@ -146,20 +175,10 @@ static void flood_fill_seed(struct quirc *q, call: vars = next_vars; - vars->left = vars->x; - vars->right = vars->x; - - row = q->pixels + vars->y * q->w; - - while (vars->left > 0 && row[vars->left - 1] == from) - vars->left--; - - while (vars->right < q->w - 1 && row[vars->right + 1] == from) - vars->right++; /* Fill the extent */ - for (i = vars->left; i <= vars->right; i++) - row[i] = to; + flood_fill_line(q, vars->x, vars->y, from, to, &vars->left, + &vars->right); if (func) func(user_data, vars->y, vars->left, vars->right); From 40def012e77516e12b0fe685b441834bcdca1353 Mon Sep 17 00:00:00 2001 From: YAMAMOTO Takashi Date: Tue, 11 May 2021 13:03:10 +0900 Subject: [PATCH 22/31] flood_fill_seed: Unify "left" and "x" vars This reduces the memory usage a bit. --- lib/identify.c | 8 ++++---- lib/quirc_internal.h | 1 - 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/lib/identify.c b/lib/identify.c index d3631d0..90f41ff 100644 --- a/lib/identify.c +++ b/lib/identify.c @@ -170,14 +170,14 @@ static void flood_fill_seed(struct quirc *q, /* Set up the first context */ next_vars = stack; - next_vars->x = x0; + next_vars->left = x0; next_vars->y = y0; call: vars = next_vars; /* Fill the extent */ - flood_fill_line(q, vars->x, vars->y, from, to, &vars->left, + flood_fill_line(q, vars->left, vars->y, from, to, &vars->left, &vars->right); if (func) @@ -199,7 +199,7 @@ call: /* Set up the next context */ next_vars = vars + 1; - next_vars->x = i; + next_vars->left = i; next_vars->y = vars->y - 1; goto call; return_from_call1: ; @@ -217,7 +217,7 @@ return_from_call1: ; /* Set up the next context */ next_vars = vars + 1; - next_vars->x = i; + next_vars->left = i; next_vars->y = vars->y + 1; goto call; return_from_call2: ; diff --git a/lib/quirc_internal.h b/lib/quirc_internal.h index a94d8e9..ed23319 100644 --- a/lib/quirc_internal.h +++ b/lib/quirc_internal.h @@ -78,7 +78,6 @@ struct quirc_grid { }; struct quirc_flood_fill_vars { - int x; int y; int right; int left; From dd6c64cafec02aa9ebedf98b931487283575a938 Mon Sep 17 00:00:00 2001 From: YAMAMOTO Takashi Date: Tue, 11 May 2021 13:22:54 +0900 Subject: [PATCH 23/31] Add QUIRC_ASSERT --- lib/quirc_internal.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/quirc_internal.h b/lib/quirc_internal.h index ed23319..9eec32a 100644 --- a/lib/quirc_internal.h +++ b/lib/quirc_internal.h @@ -17,10 +17,13 @@ #ifndef QUIRC_INTERNAL_H_ #define QUIRC_INTERNAL_H_ +#include #include #include "quirc.h" +#define QUIRC_ASSERT(a) assert(a) + #define QUIRC_PIXEL_WHITE 0 #define QUIRC_PIXEL_BLACK 1 #define QUIRC_PIXEL_REGION 2 From 1fa9b0c10931c5015fca73cdbe05799c3709f366 Mon Sep 17 00:00:00 2001 From: YAMAMOTO Takashi Date: Tue, 11 May 2021 13:12:35 +0900 Subject: [PATCH 24/31] flood_fill_seed: Add assertions --- lib/identify.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/lib/identify.c b/lib/identify.c index 90f41ff..41b87dd 100644 --- a/lib/identify.c +++ b/lib/identify.c @@ -133,11 +133,12 @@ static void flood_fill_line(struct quirc *q, int x, int y, int right; int i; + row = q->pixels + y * q->w; + QUIRC_ASSERT(row[x] == from); + left = x; right = x; - row = q->pixels + y * q->w; - while (left > 0 && row[left - 1] == from) left--; @@ -163,6 +164,9 @@ static void flood_fill_seed(struct quirc *q, const struct quirc_flood_fill_vars *const last_vars = &stack[stack_size - 1]; + QUIRC_ASSERT(from != to); + QUIRC_ASSERT(q->pixels[y0 * q->w + x0] == from); + struct quirc_flood_fill_vars *vars; struct quirc_flood_fill_vars *next_vars; int i; From 38f882b3d6bb33e6973a5506a40772d715a65a7f Mon Sep 17 00:00:00 2001 From: YAMAMOTO Takashi Date: Tue, 11 May 2021 13:46:31 +0900 Subject: [PATCH 25/31] flood_fill_seed: Simplify the flow by having a single "return point". --- lib/identify.c | 51 +++++++++++++++++++++----------------------- lib/quirc_internal.h | 5 ++--- 2 files changed, 26 insertions(+), 30 deletions(-) diff --git a/lib/identify.c b/lib/identify.c index 41b87dd..f42fbea 100644 --- a/lib/identify.c +++ b/lib/identify.c @@ -169,75 +169,72 @@ static void flood_fill_seed(struct quirc *q, struct quirc_flood_fill_vars *vars; struct quirc_flood_fill_vars *next_vars; - int i; quirc_pixel_t *row; /* Set up the first context */ next_vars = stack; - next_vars->left = x0; + next_vars->left_up = x0; next_vars->y = y0; call: vars = next_vars; + /* + * Note about inputs: + * + * - vars->left_up contains x value to look at. + * - vars->left_down is still a garbage here. + */ + /* Fill the extent */ - flood_fill_line(q, vars->left, vars->y, from, to, &vars->left, + flood_fill_line(q, vars->left_up, vars->y, from, to, &vars->left_up, &vars->right); if (func) - func(user_data, vars->y, vars->left, vars->right); + func(user_data, vars->y, vars->left_up, vars->right); if (vars == last_vars) { return; } + vars->left_down = vars->left_up; + /* Seed new flood-fills */ +return_from_call: if (vars->y > 0) { row = q->pixels + (vars->y - 1) * q->w; - for (i = vars->left; i <= vars->right; i++) - if (row[i] == from) { - /* Save the current context */ - vars->i = i; - vars->pc = 1; - + while (vars->left_up <= vars->right) { + if (row[vars->left_up] == from) { /* Set up the next context */ next_vars = vars + 1; - next_vars->left = i; + next_vars->left_up = vars->left_up; next_vars->y = vars->y - 1; goto call; -return_from_call1: ; } + vars->left_up++; + } } if (vars->y < q->h - 1) { row = q->pixels + (vars->y + 1) * q->w; - for (i = vars->left; i <= vars->right; i++) - if (row[i] == from) { - /* Save the current context */ - vars->i = i; - vars->pc = 2; - + while (vars->left_down <= vars->right) { + if (row[vars->left_down] == from) { /* Set up the next context */ next_vars = vars + 1; - next_vars->left = i; + next_vars->left_up = vars->left_down; next_vars->y = vars->y + 1; goto call; -return_from_call2: ; } + vars->left_down++; + } } if (vars > stack) { /* Restore the previous context */ vars--; - i = vars->i; - if (vars->pc == 1) { - row = q->pixels + (vars->y - 1) * q->w; - goto return_from_call1; - } - row = q->pixels + (vars->y + 1) * q->w; - goto return_from_call2; + goto return_from_call; } } diff --git a/lib/quirc_internal.h b/lib/quirc_internal.h index 9eec32a..0d8a229 100644 --- a/lib/quirc_internal.h +++ b/lib/quirc_internal.h @@ -83,9 +83,8 @@ struct quirc_grid { struct quirc_flood_fill_vars { int y; int right; - int left; - int i; - int pc; /* caller id */ + int left_up; + int left_down; }; struct quirc { From 0b699408764f0d048b0d74c8f21c68bec9b4603c Mon Sep 17 00:00:00 2001 From: YAMAMOTO Takashi Date: Tue, 11 May 2021 14:05:02 +0900 Subject: [PATCH 26/31] flood_fill_line: call user callback as well --- lib/identify.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/lib/identify.c b/lib/identify.c index f42fbea..2377296 100644 --- a/lib/identify.c +++ b/lib/identify.c @@ -126,6 +126,7 @@ typedef void (*span_func_t)(void *user_data, int y, int left, int right); static void flood_fill_line(struct quirc *q, int x, int y, int from, int to, + span_func_t func, void *user_data, int *leftp, int *rightp) { quirc_pixel_t *row; @@ -152,6 +153,9 @@ static void flood_fill_line(struct quirc *q, int x, int y, /* Return the processed range */ *leftp = left; *rightp = right; + + if (func) + func(user_data, y, left, right); } static void flood_fill_seed(struct quirc *q, @@ -187,11 +191,9 @@ call: */ /* Fill the extent */ - flood_fill_line(q, vars->left_up, vars->y, from, to, &vars->left_up, - &vars->right); - - if (func) - func(user_data, vars->y, vars->left_up, vars->right); + flood_fill_line(q, vars->left_up, vars->y, from, to, + func, user_data, + &vars->left_up, &vars->right); if (vars == last_vars) { return; From eadbc2caca1bd0eb817d5fb2ce211a9ee15c63ec Mon Sep 17 00:00:00 2001 From: YAMAMOTO Takashi Date: Tue, 11 May 2021 14:09:24 +0900 Subject: [PATCH 27/31] flood_fill_seed: Move flood_fill_line to the "caller" This restructure allows us to unify "call" and "return_from_call". --- lib/identify.c | 44 ++++++++++++++++++++++++++++---------------- 1 file changed, 28 insertions(+), 16 deletions(-) diff --git a/lib/identify.c b/lib/identify.c index 2377296..fa2b9f5 100644 --- a/lib/identify.c +++ b/lib/identify.c @@ -180,29 +180,21 @@ static void flood_fill_seed(struct quirc *q, next_vars->left_up = x0; next_vars->y = y0; -call: - vars = next_vars; - - /* - * Note about inputs: - * - * - vars->left_up contains x value to look at. - * - vars->left_down is still a garbage here. - */ - /* Fill the extent */ - flood_fill_line(q, vars->left_up, vars->y, from, to, + flood_fill_line(q, next_vars->left_up, next_vars->y, from, to, func, user_data, - &vars->left_up, &vars->right); + &next_vars->left_up, &next_vars->right); + next_vars->left_down = next_vars->left_up; + +call: +return_from_call: + vars = next_vars; if (vars == last_vars) { return; } - vars->left_down = vars->left_up; - /* Seed new flood-fills */ -return_from_call: if (vars->y > 0) { row = q->pixels + (vars->y - 1) * q->w; @@ -212,6 +204,16 @@ return_from_call: next_vars = vars + 1; next_vars->left_up = vars->left_up; next_vars->y = vars->y - 1; + + /* Fill the extent */ + flood_fill_line(q, + next_vars->left_up, + next_vars->y, from, to, + func, user_data, + &next_vars->left_up, + &next_vars->right); + next_vars->left_down = next_vars->left_up; + goto call; } vars->left_up++; @@ -227,6 +229,16 @@ return_from_call: next_vars = vars + 1; next_vars->left_up = vars->left_down; next_vars->y = vars->y + 1; + + /* Fill the extent */ + flood_fill_line(q, + next_vars->left_up, + next_vars->y, from, to, + func, user_data, + &next_vars->left_up, + &next_vars->right); + next_vars->left_down = next_vars->left_up; + goto call; } vars->left_down++; @@ -235,7 +247,7 @@ return_from_call: if (vars > stack) { /* Restore the previous context */ - vars--; + next_vars = vars - 1; goto return_from_call; } } From a436fde8a08fa0ec6b0d0462e85436df7e076826 Mon Sep 17 00:00:00 2001 From: YAMAMOTO Takashi Date: Tue, 11 May 2021 14:41:01 +0900 Subject: [PATCH 28/31] flood_fill_seed: Reduce code duplicatino This is also a preparation to eliminate the uses of goto. --- lib/identify.c | 92 +++++++++++++++++++++++++++++--------------------- 1 file changed, 54 insertions(+), 38 deletions(-) diff --git a/lib/identify.c b/lib/identify.c index fa2b9f5..29150b4 100644 --- a/lib/identify.c +++ b/lib/identify.c @@ -158,6 +158,48 @@ static void flood_fill_line(struct quirc *q, int x, int y, func(user_data, y, left, right); } +static struct quirc_flood_fill_vars *flood_fill_call_next( + struct quirc *q, + quirc_pixel_t *row, + int from, int to, + span_func_t func, void *user_data, + struct quirc_flood_fill_vars *vars, + int direction) +{ + int *leftp; + + if (direction < 0) { + leftp = &vars->left_up; + } else { + leftp = &vars->left_down; + } + + while (*leftp <= vars->right) { + if (row[*leftp] == from) { + struct quirc_flood_fill_vars *next_vars; + + /* Set up the next context */ + next_vars = vars + 1; + next_vars->left_up = *leftp; + next_vars->y = vars->y + direction; + + /* Fill the extent */ + flood_fill_line(q, + next_vars->left_up, + next_vars->y, + from, to, + func, user_data, + &next_vars->left_up, + &next_vars->right); + next_vars->left_down = next_vars->left_up; + + return next_vars; + } + (*leftp)++; + } + return NULL; +} + static void flood_fill_seed(struct quirc *q, int x0, int y0, int from, int to, @@ -198,50 +240,24 @@ return_from_call: if (vars->y > 0) { row = q->pixels + (vars->y - 1) * q->w; - while (vars->left_up <= vars->right) { - if (row[vars->left_up] == from) { - /* Set up the next context */ - next_vars = vars + 1; - next_vars->left_up = vars->left_up; - next_vars->y = vars->y - 1; - - /* Fill the extent */ - flood_fill_line(q, - next_vars->left_up, - next_vars->y, from, to, - func, user_data, - &next_vars->left_up, - &next_vars->right); - next_vars->left_down = next_vars->left_up; - - goto call; - } - vars->left_up++; + next_vars = flood_fill_call_next(q, row, + from, to, + func, user_data, + vars, -1); + if (next_vars != NULL) { + goto call; } } if (vars->y < q->h - 1) { row = q->pixels + (vars->y + 1) * q->w; - while (vars->left_down <= vars->right) { - if (row[vars->left_down] == from) { - /* Set up the next context */ - next_vars = vars + 1; - next_vars->left_up = vars->left_down; - next_vars->y = vars->y + 1; - - /* Fill the extent */ - flood_fill_line(q, - next_vars->left_up, - next_vars->y, from, to, - func, user_data, - &next_vars->left_up, - &next_vars->right); - next_vars->left_down = next_vars->left_up; - - goto call; - } - vars->left_down++; + next_vars = flood_fill_call_next(q, row, + from, to, + func, user_data, + vars, 1); + if (next_vars != NULL) { + goto call; } } From 87ca2b6c2db7061fdbd1d7e07978b0d8d6f6e46d Mon Sep 17 00:00:00 2001 From: YAMAMOTO Takashi Date: Tue, 11 May 2021 14:48:55 +0900 Subject: [PATCH 29/31] flood_fill_seed: Switch from goto to while --- lib/identify.c | 68 ++++++++++++++++++++++++++------------------------ 1 file changed, 35 insertions(+), 33 deletions(-) diff --git a/lib/identify.c b/lib/identify.c index 29150b4..3b69d36 100644 --- a/lib/identify.c +++ b/lib/identify.c @@ -16,6 +16,7 @@ #include #include +#include #include #include #include "quirc_internal.h" @@ -213,9 +214,7 @@ static void flood_fill_seed(struct quirc *q, QUIRC_ASSERT(from != to); QUIRC_ASSERT(q->pixels[y0 * q->w + x0] == from); - struct quirc_flood_fill_vars *vars; struct quirc_flood_fill_vars *next_vars; - quirc_pixel_t *row; /* Set up the first context */ next_vars = stack; @@ -228,43 +227,46 @@ static void flood_fill_seed(struct quirc *q, &next_vars->left_up, &next_vars->right); next_vars->left_down = next_vars->left_up; -call: -return_from_call: - vars = next_vars; + while (true) { + struct quirc_flood_fill_vars * const vars = next_vars; + quirc_pixel_t *row; - if (vars == last_vars) { - return; - } - - /* Seed new flood-fills */ - if (vars->y > 0) { - row = q->pixels + (vars->y - 1) * q->w; - - next_vars = flood_fill_call_next(q, row, - from, to, - func, user_data, - vars, -1); - if (next_vars != NULL) { - goto call; + if (vars == last_vars) { + break; } - } - if (vars->y < q->h - 1) { - row = q->pixels + (vars->y + 1) * q->w; + /* Seed new flood-fills */ + if (vars->y > 0) { + row = q->pixels + (vars->y - 1) * q->w; - next_vars = flood_fill_call_next(q, row, - from, to, - func, user_data, - vars, 1); - if (next_vars != NULL) { - goto call; + next_vars = flood_fill_call_next(q, row, + from, to, + func, user_data, + vars, -1); + if (next_vars != NULL) { + continue; + } } - } - if (vars > stack) { - /* Restore the previous context */ - next_vars = vars - 1; - goto return_from_call; + if (vars->y < q->h - 1) { + row = q->pixels + (vars->y + 1) * q->w; + + next_vars = flood_fill_call_next(q, row, + from, to, + func, user_data, + vars, 1); + if (next_vars != NULL) { + continue; + } + } + + if (vars > stack) { + /* Restore the previous context */ + next_vars = vars - 1; + continue; + } + + break; } } From 22269c9a43c93221bfd8f2fadbb6c13f84e1b60a Mon Sep 17 00:00:00 2001 From: YAMAMOTO Takashi Date: Tue, 11 May 2021 14:53:23 +0900 Subject: [PATCH 30/31] flood_fill_seed: initialize left_up and left_down together Just for readability. --- lib/identify.c | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/lib/identify.c b/lib/identify.c index 3b69d36..b6993f7 100644 --- a/lib/identify.c +++ b/lib/identify.c @@ -178,21 +178,22 @@ static struct quirc_flood_fill_vars *flood_fill_call_next( while (*leftp <= vars->right) { if (row[*leftp] == from) { struct quirc_flood_fill_vars *next_vars; + int next_left; /* Set up the next context */ next_vars = vars + 1; - next_vars->left_up = *leftp; next_vars->y = vars->y + direction; /* Fill the extent */ flood_fill_line(q, - next_vars->left_up, + *leftp, next_vars->y, from, to, func, user_data, - &next_vars->left_up, + &next_left, &next_vars->right); - next_vars->left_down = next_vars->left_up; + next_vars->left_down = next_left; + next_vars->left_up = next_left; return next_vars; } @@ -215,17 +216,18 @@ static void flood_fill_seed(struct quirc *q, QUIRC_ASSERT(q->pixels[y0 * q->w + x0] == from); struct quirc_flood_fill_vars *next_vars; + int next_left; /* Set up the first context */ next_vars = stack; - next_vars->left_up = x0; next_vars->y = y0; /* Fill the extent */ - flood_fill_line(q, next_vars->left_up, next_vars->y, from, to, + flood_fill_line(q, x0, next_vars->y, from, to, func, user_data, - &next_vars->left_up, &next_vars->right); - next_vars->left_down = next_vars->left_up; + &next_left, &next_vars->right); + next_vars->left_down = next_left; + next_vars->left_up = next_left; while (true) { struct quirc_flood_fill_vars * const vars = next_vars; From 6b3575cd59b54fca37408fff564bfbcfed42556c Mon Sep 17 00:00:00 2001 From: YAMAMOTO Takashi Date: Tue, 11 May 2021 15:03:09 +0900 Subject: [PATCH 31/31] flood_fill_seed: Add comments on exit conditions --- lib/identify.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/lib/identify.c b/lib/identify.c index b6993f7..44fa48d 100644 --- a/lib/identify.c +++ b/lib/identify.c @@ -234,6 +234,13 @@ static void flood_fill_seed(struct quirc *q, quirc_pixel_t *row; if (vars == last_vars) { + /* + * "Stack overflow". + * Just stop and return. + * This can be caused by very complex shapes in + * the image, which is not likely a part of + * a valid QR code anyway. + */ break; } @@ -268,6 +275,7 @@ static void flood_fill_seed(struct quirc *q, continue; } + /* We've done. */ break; } }