From 679e43f1444e2293c09c5cfa0bbc2500355fc968 Mon Sep 17 00:00:00 2001 From: YAMAMOTO Takashi Date: Thu, 18 Mar 2021 17:17:58 +0900 Subject: [PATCH] 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