This commit is contained in:
Thomas Farstrike
2025-05-12 18:15:09 +02:00
parent 1886d22278
commit 8b92724ed1
28 changed files with 7006 additions and 0 deletions
+12
View File
@@ -0,0 +1,12 @@
*.o
*.lo
quirc-demo
quirc-scanner
qrtest
inspect
libquirc.a
libquirc.so*
.*.swp
*~
.DS_Store
.idea
+19
View File
@@ -0,0 +1,19 @@
quirc -- QR-code recognition library
Copyright (C) 2010-2012 Daniel Beer <dlbeer@gmail.com>
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
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.
+124
View File
@@ -0,0 +1,124 @@
# quirc -- QR-code recognition library
# Copyright (C) 2010-2012 Daniel Beer <dlbeer@gmail.com>
#
# 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.
CC ?= gcc
PREFIX ?= /usr/local
SDL_CFLAGS := $(shell pkg-config --cflags sdl 2>&1)
SDL_LIBS = $(shell pkg-config --libs sdl)
LIB_VERSION = 1.2
ifeq ($(shell uname), Darwin)
LIB_SUFFIX := dylib
VERSIONED_LIB_SUFFIX := $(LIB_VERSION).$(LIB_SUFFIX)
else
LIB_SUFFIX := so
VERSIONED_LIB_SUFFIX := $(LIB_SUFFIX).$(LIB_VERSION)
endif
CFLAGS ?= -O3 -Wall -fPIC
QUIRC_CFLAGS = -Ilib $(CFLAGS) $(SDL_CFLAGS)
LIB_OBJ = \
lib/decode.o \
lib/identify.o \
lib/quirc.o \
lib/version_db.o
DEMO_OBJ = \
demo/camera.o \
demo/mjpeg.o \
demo/convert.o
DEMO_UTIL_OBJ = \
demo/dthash.o \
demo/demoutil.o
OPENCV_CFLAGS := $(shell pkg-config --cflags opencv4 2>&1)
OPENCV_LIBS = $(shell pkg-config --libs opencv4)
QUIRC_CXXFLAGS = $(QUIRC_CFLAGS) $(OPENCV_CFLAGS) --std=c++17
.PHONY: all v4l sdl opencv install uninstall clean
all: libquirc.$(LIB_SUFFIX) qrtest
v4l: quirc-scanner
sdl: inspect quirc-demo
opencv: inspect-opencv quirc-demo-opencv
qrtest: tests/dbgutil.o tests/qrtest.o libquirc.a
$(CC) -o $@ tests/dbgutil.o tests/qrtest.o libquirc.a $(LDFLAGS) -lm -ljpeg -lpng
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
inspect-opencv: tests/dbgutil.o tests/inspect_opencv.o libquirc.a
$(CXX) -o $@ tests/dbgutil.o tests/inspect_opencv.o libquirc.a $(LDFLAGS) -lm -ljpeg -lpng $(OPENCV_LIBS)
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 $(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 $@
ar cru $@ $(LIB_OBJ)
ranlib $@
libquirc.$(LIB_SUFFIX): libquirc.$(VERSIONED_LIB_SUFFIX)
ln -s $< $@
libquirc.$(VERSIONED_LIB_SUFFIX): $(LIB_OBJ)
$(CC) -shared -o $@ $(LIB_OBJ) $(LDFLAGS) -lm
.c.o:
$(CC) $(QUIRC_CFLAGS) -o $@ -c $<
.SUFFIXES: .cxx
.cxx.o:
$(CXX) $(QUIRC_CXXFLAGS) -o $@ -c $<
install: libquirc.a libquirc.$(LIB_SUFFIX) 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.$(VERSIONED_LIB_SUFFIX) \
$(DESTDIR)$(PREFIX)/lib
cp -d libquirc.$(LIB_SUFFIX) $(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:
rm -f $(DESTDIR)$(PREFIX)/include/quirc.h
rm -f $(DESTDIR)$(PREFIX)/lib/libquirc.{$(LIB_SUFFIX),$(VERSIONED_LIB_SUFFIX)}
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:
rm -f */*.o
rm -f */*.lo
rm -f libquirc.a
rm -f libquirc.{$(LIB_SUFFIX),$(VERSIONED_LIB_SUFFIX)}
rm -f qrtest
rm -f inspect
rm -f inspect-opencv
rm -f quirc-demo
rm -f quirc-demo-opencv
rm -f quirc-scanner
+283
View File
@@ -0,0 +1,283 @@
Quirc
=====
QR codes are a type of high-density matrix barcodes, and quirc is a library for
extracting and decoding them from images. It has several features which make it
a good choice for this purpose:
* It is fast enough to be used with realtime video: extracting and decoding
from VGA frame takes about 50 ms on a modern x86 core.
* It has a robust and tolerant recognition algorithm. It can correctly
recognise and decode QR codes which are rotated and/or oblique to the camera.
It can also distinguish and decode multiple codes within the same image.
* It is easy to use, with a simple API described in a single commented header
file (see below for an overview).
* It is small and easily embeddable, with no dependencies other than standard C
functions.
* It has a very small memory footprint: one byte per image pixel, plus a few kB
per decoder object.
* It uses no global mutable state, and is safe to use in a multithreaded
application.
* ISC-licensed, with almost no restrictions regarding use and/or modification.
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 as documented below.
### quirc-demo
This is an real-time demo which requires a camera and a graphical display. The
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: 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
### 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: libjpeg, V4L2
### qrtest
This test is used to evaluate the performance of library. Given a directory
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: libjpeg, libpng, SDL
### inspect-opencv
A test similar to `inspect`.
But this version uses OpenCV instead of other libraries.
This requires: libjpeg, libpng, OpenCV
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`.
Several options can be adjusted at compile time by passing additional arguments
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.
You can specify one or several of the following targets if you don't want, or
are unable to build everything:
* libquirc.a
* libquirc.so
* qrtest
* inspect
* inspect-opencv
* quirc-scanner
* quirc-demo
* quirc-demo-opencv
Library use
-----------
All of the library's functionality is exposed through a single header file,
which you should include:
```C
#include <quirc.h>
```
To decode images, you'll need to instantiate a `struct quirc` object, which is
done with the `quirc_new` function. Later, when you no longer need to decode
anything, you should release the allocated memory with `quirc_destroy`:
```C
struct quirc *qr;
qr = quirc_new();
if (!qr) {
perror("Failed to allocate memory");
abort();
}
/* ... */
quirc_destroy(qr);
```
Having obtained a decoder object, you need to set the image size that you'll be
working with, which is done using `quirc_resize`:
```C
if (quirc_resize(qr, 640, 480) < 0) {
perror("Failed to allocate video memory");
abort();
}
```
`quirc_resize` and `quirc_new` are the only library functions which allocate
memory. If you plan to process a series of frames (or a video stream), you
probably want to allocate and size a single decoder and hold onto it to process
each frame.
Processing frames is done in two stages. The first stage is an
image-recognition stage called identification, which takes a grayscale image
and searches for QR codes. Using `quirc_begin` and `quirc_end`, you can feed a
grayscale image directly into the buffer that `quirc` uses for image
processing:
```C
uint8_t *image;
int w, h;
image = quirc_begin(qr, &w, &h);
/* Fill out the image buffer here.
* image is a pointer to a w*h bytes.
* One byte per pixel, w pixels per line, h lines in the buffer.
*/
quirc_end(qr);
```
Note that `quirc_begin` simply returns a pointer to a previously allocated
buffer. The buffer will contain uninitialized data. After the call to
`quirc_end`, the decoder holds a list of detected QR codes which can be queried
via `quirc_count` and `quirc_extract`.
At this point, the second stage of processing occurs -- decoding. This is done
via the call to `quirc_decode`, which is not associated with a decoder object.
```C
int num_codes;
int i;
/* We've previously fed an image to the decoder via
* quirc_begin/quirc_end.
*/
num_codes = quirc_count(qr);
for (i = 0; i < num_codes; i++) {
struct quirc_code code;
struct quirc_data data;
quirc_decode_error_t err;
quirc_extract(qr, i, &code);
/* Decoding stage */
err = quirc_decode(&code, &data);
if (err)
printf("DECODE FAILED: %s\n", quirc_strerror(err));
else
printf("Data: %s\n", data.payload);
}
```
`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);
```
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 (C) 2010-2012 Daniel Beer <<dlbeer@gmail.com>>
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.
File diff suppressed because it is too large Load Diff
+104
View File
@@ -0,0 +1,104 @@
/* quirc -- QR-code recognition library
* Copyright (C) 2010-2014 Daniel Beer <dlbeer@gmail.com>
*
* 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.
*/
#ifndef CAMERA_H_
#define CAMERA_H_
#include <stddef.h>
#define CAMERA_MAX_BUFFERS 32
typedef enum {
CAMERA_FORMAT_UNKNOWN = 0,
CAMERA_FORMAT_MJPEG,
CAMERA_FORMAT_YUYV
} camera_format_t;
struct camera_parms {
camera_format_t format;
int width;
int height;
int pitch_bytes;
int interval_n;
int interval_d;
};
struct camera_buffer {
void *addr;
size_t size;
unsigned long offset;
};
struct camera {
int fd;
struct camera_parms parms;
struct camera_buffer buf_desc[CAMERA_MAX_BUFFERS];
int buf_count;
/* Stream state */
int s_on;
int s_qc;
int s_qhead;
};
/* Initialize/destroy a camera. No resources are allocated. */
void camera_init(struct camera *c);
void camera_destroy(struct camera *c);
/* Open/close the camera device */
int camera_open(struct camera *c, const char *path,
int target_w, int target_h,
int tr_n, int tr_d);
void camera_close(struct camera *c);
static inline int camera_get_fd(const struct camera *c)
{
return c->fd;
}
static inline const struct camera_parms *camera_get_parms
(const struct camera *c)
{
return &c->parms;
}
/* Map buffers */
int camera_map(struct camera *c, int buf_count);
void camera_unmap(struct camera *c);
static inline int camera_get_buf_count(const struct camera *c)
{
return c->buf_count;
}
/* Switch streaming on/off */
int camera_on(struct camera *c);
void camera_off(struct camera *c);
/* Enqueue/dequeue buffers (count = 0 means enqueue all) */
int camera_enqueue_all(struct camera *c);
int camera_dequeue_one(struct camera *c);
/* Fetch the oldest dequeued buffer */
static inline const struct camera_buffer *camera_get_head
(const struct camera *c)
{
return &c->buf_desc[(c->s_qhead + c->buf_count - 1) % c->buf_count];
}
#endif
+103
View File
@@ -0,0 +1,103 @@
/* quirc -- QR-code recognition library
* Copyright (C) 2010-2012 Daniel Beer <dlbeer@gmail.com>
*
* 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 "convert.h"
#define CHANNEL_CLAMP(dst, tmp, lum, chrom) \
(tmp) = ((lum) + (chrom)) >> 8; \
if ((tmp) < 0) \
(tmp) = 0; \
if ((tmp) > 255) \
(tmp) = 255; \
(dst) = (tmp);
void yuyv_to_rgb32(const uint8_t *src, int src_pitch,
int w, int h,
uint8_t *dst, int dst_pitch)
{
int y;
for (y = 0; y < h; y++) {
int x;
const uint8_t *srow = src + y * src_pitch;
uint8_t *drow = dst + y * dst_pitch;
for (x = 0; x < w; x += 2) {
/* ITU-R colorspace assumed */
int y0 = (int)srow[0] * 256;
int y1 = (int)srow[2] * 256;
int cr = (int)srow[3] - 128;
int cb = (int)srow[1] - 128;
int r = cr * 359;
int g = -cb * 88 - 128 * cr;
int b = 454 * cb;
int z;
CHANNEL_CLAMP(drow[0], z, y0, b);
CHANNEL_CLAMP(drow[1], z, y0, g);
CHANNEL_CLAMP(drow[2], z, y0, r);
CHANNEL_CLAMP(drow[4], z, y1, b);
CHANNEL_CLAMP(drow[5], z, y1, g);
CHANNEL_CLAMP(drow[6], z, y1, r);
srow += 4;
drow += 8;
}
}
}
void yuyv_to_luma(const uint8_t *src, int src_pitch,
int w, int h,
uint8_t *dst, int dst_pitch)
{
int y;
for (y = 0; y < h; y++) {
int x;
const uint8_t *srow = src + y * src_pitch;
uint8_t *drow = dst + y * dst_pitch;
for (x = 0; x < w; x += 2) {
*(drow++) = srow[0];
*(drow++) = srow[2];
srow += 4;
}
}
}
void rgb32_to_luma(const uint8_t *src, int src_pitch,
int w, int h,
uint8_t *dst, int dst_pitch)
{
int y;
for (y = 0; y < h; y++) {
const uint8_t *rgb32 = src + src_pitch * y;
uint8_t *gray = dst + y * dst_pitch;
int i;
for (i = 0; i < w; i++) {
/* ITU-R colorspace assumed */
int r = (int)rgb32[2];
int g = (int)rgb32[1];
int b = (int)rgb32[0];
int sum = r * 59 + g * 150 + b * 29;
*(gray++) = sum >> 8;
rgb32 += 4;
}
}
}
+39
View File
@@ -0,0 +1,39 @@
/* quirc -- QR-code recognition library
* Copyright (C) 2010-2012 Daniel Beer <dlbeer@gmail.com>
*
* 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.
*/
#ifndef CONVERT_H_
#define CONVERT_H_
#include <stdint.h>
/* Convert 4:2:2 YUYV format to RGB32 format. The source and destination
* frames are expected to be the same size.
*/
void yuyv_to_rgb32(const uint8_t *src, int src_pitch,
int w, int h,
uint8_t *dst, int dst_pitch);
/* Extract the luma channel from a 4:2:2 YUYV image. */
void yuyv_to_luma(const uint8_t *src, int src_pitch,
int w, int h,
uint8_t *dst, int dst_pitch);
/* Extract the luma channel from an RGB32 image. */
void rgb32_to_luma(const uint8_t *src, int src_pitch,
int w, int h,
uint8_t *dst, int dst_pitch);
#endif
+333
View File
@@ -0,0 +1,333 @@
/* quirc -- QR-code recognition library
* Copyright (C) 2010-2012 Daniel Beer <dlbeer@gmail.com>
*
* 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 <unistd.h>
#include <SDL.h>
#include <SDL_gfxPrimitives.h>
#include <quirc.h>
#include <time.h>
#include <getopt.h>
#include "camera.h"
#include "mjpeg.h"
#include "convert.h"
#include "dthash.h"
#include "demoutil.h"
/* Collected command-line arguments */
static const char *camera_path = "/dev/video0";
static int video_width = 640;
static int video_height = 480;
static int want_frame_rate = 0;
static int want_verbose = 0;
static int printer_timeout = 2;
static void fat_text(SDL_Surface *screen, int x, int y, const char *text)
{
int i, j;
for (i = -1; i <= 1; i++)
for (j = -1; j <= 1; j++)
stringColor(screen, x + i, y + j, text, 0xffffffff);
stringColor(screen, x, y, text, 0x008000ff);
}
static void fat_text_cent(SDL_Surface *screen, int x, int y, const char *text)
{
x -= strlen(text) * 4;
fat_text(screen, x, y, text);
}
static void draw_qr(SDL_Surface *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;
lineColor(screen, a->x, a->y, b->x, b->y, 0x008000ff);
}
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, (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(struct camera *cam, SDL_Surface *screen,
struct quirc *q, struct mjpeg_decoder *mj)
{
SDL_Event ev;
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);
for (;;) {
time_t now = time(NULL);
const struct camera_buffer *head;
const struct camera_parms *parms = camera_get_parms(cam);
if (camera_dequeue_one(cam) < 0) {
perror("camera_dequeue_one");
return -1;
}
head = camera_get_head(cam);
SDL_LockSurface(screen);
switch (parms->format) {
case CAMERA_FORMAT_MJPEG:
mjpeg_decode_rgb32(mj, head->addr, head->size,
screen->pixels, screen->pitch,
screen->w, screen->h);
break;
case CAMERA_FORMAT_YUYV:
yuyv_to_rgb32(head->addr, parms->width * 2,
parms->width, parms->height,
screen->pixels, screen->pitch);
break;
default:
fprintf(stderr, "Unknown frame format\n");
return -1;
}
if (camera_enqueue_all(cam) < 0) {
perror("camera_enqueue_all");
return -1;
}
rgb32_to_luma(screen->pixels, screen->pitch,
screen->w, screen->h,
quirc_begin(q, NULL, NULL),
screen->w);
quirc_end(q);
SDL_UnlockSurface(screen);
draw_qr(screen, q, &dt);
if (want_frame_rate)
fat_text(screen, 5, 5, rate_text);
SDL_Flip(screen);
while (SDL_PollEvent(&ev) > 0) {
if (ev.type == SDL_QUIT)
return 0;
if (ev.type == SDL_KEYDOWN && ev.key.keysym.sym == 'q')
return 0;
}
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;
struct camera cam;
struct mjpeg_decoder mj;
const struct camera_parms *parms;
SDL_Surface *screen;
camera_init(&cam);
if (camera_open(&cam, camera_path, video_width, video_height,
25, 1) < 0) {
perror("camera_open");
goto fail_qr;
}
if (camera_map(&cam, 8) < 0) {
perror("camera_map");
goto fail_qr;
}
if (camera_on(&cam) < 0) {
perror("camera_on");
goto fail_qr;
}
if (camera_enqueue_all(&cam) < 0) {
perror("camera_enqueue_all");
goto fail_qr;
}
parms = camera_get_parms(&cam);
qr = quirc_new();
if (!qr) {
perror("couldn't allocate QR decoder");
goto fail_qr;
}
if (quirc_resize(qr, parms->width, parms->height) < 0) {
perror("couldn't allocate QR buffer");
goto fail_qr_resize;
}
if (SDL_Init(SDL_INIT_VIDEO) < 0) {
perror("couldn't init SDL");
goto fail_sdl_init;
}
screen = SDL_SetVideoMode(parms->width, parms->height, 32,
SDL_SWSURFACE | SDL_DOUBLEBUF);
if (!screen) {
perror("couldn't init video mode");
goto fail_video_mode;
}
mjpeg_init(&mj);
if (main_loop(&cam, screen, qr, &mj) < 0)
goto fail_main_loop;
mjpeg_free(&mj);
SDL_Quit();
quirc_destroy(qr);
camera_destroy(&cam);
return 0;
fail_main_loop:
mjpeg_free(&mj);
fail_video_mode:
SDL_Quit();
fail_qr_resize:
fail_sdl_init:
quirc_destroy(qr);
fail_qr:
camera_destroy(&cam);
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"
" -d <device> Specify camera device path.\n"
" -s <WxH> Specify video dimensions.\n"
" -p <timeout> 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\n");
printf("Copyright (C) 2010-2014 Daniel Beer <dlbeer@gmail.com>\n");
printf("\n");
while ((opt = getopt_long(argc, argv, "d:s: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 's':
if (parse_size(optarg, &video_width, &video_height) < 0)
return -1;
break;
case 'p':
printer_timeout = atoi(optarg);
break;
case 'd':
camera_path = optarg;
break;
case '?':
fprintf(stderr, "Try --help for usage information\n");
return -1;
}
return run_demo();
}
+263
View File
@@ -0,0 +1,263 @@
/* quirc -- QR-code recognition library
* Copyright (C) 2010-2012 Daniel Beer <dlbeer@gmail.com>
*
* 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 <assert.h>
#include <unistd.h>
#include <quirc.h>
#include <time.h>
#include <getopt.h>
#include <opencv2/videoio.hpp>
#include <opencv2/imgproc.hpp>
#include <opencv2/highgui.hpp>
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<uint8_t>(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 <timeout> 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 <dlbeer@gmail.com>\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();
}
+71
View File
@@ -0,0 +1,71 @@
/* quirc -- QR-code recognition library
* Copyright (C) 2010-2012 Daniel Beer <dlbeer@gmail.com>
*
* 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 <stdio.h>
#include <ctype.h>
#include "demoutil.h"
void print_data(const struct quirc_data *data, struct dthash *dt,
int want_verbose)
{
if (dthash_seen(dt, data))
return;
printf("==> %s\n", data->payload);
if (want_verbose)
printf(" Version: %d, ECC: %c, Mask: %d, Type: %d\n\n",
data->version, "MLHQ"[data->ecc_level],
data->mask, data->data_type);
}
int parse_size(const char *text, int *video_width, int *video_height)
{
int state = 0;
int w = 0, h = 0;
int i;
for (i = 0; text[i]; i++) {
if (text[i] == 'x' || text[i] == 'X') {
if (state == 0) {
state = 1;
} else {
fprintf(stderr, "parse_size: expected WxH\n");
return -1;
}
} else if (isdigit(text[i])) {
if (state == 0)
w = w * 10 + text[i] - '0';
else
h = h * 10 + text[i] - '0';
} else {
fprintf(stderr, "Invalid character in size: %c\n",
text[i]);
return -1;
}
}
if (w <= 0 || w >= 10000 || h <= 0 || h >= 10000) {
fprintf(stderr, "Invalid size: %dx%d\n", w, h);
return -1;
}
*video_width = w;
*video_height = h;
return 0;
}
+42
View File
@@ -0,0 +1,42 @@
/* quirc -- QR-code recognition library
* Copyright (C) 2010-2012 Daniel Beer <dlbeer@gmail.com>
*
* 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.
*/
#ifndef DEMOUTIL_H_
#define DEMOUTIL_H_
#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.
*/
void print_data(const struct quirc_data *data, struct dthash *dt,
int want_verbose);
/* Parse a string of the form "WxH" and return width and height as
* integers. Returns 0 on success or -1 if a parser error occurs.
*/
int parse_size(const char *text, int *video_width, int *video_height);
#ifdef __cplusplus
}
#endif
#endif
+139
View File
@@ -0,0 +1,139 @@
/* quirc -- QR-code recognition library
* Copyright (C) 2010-2012 Daniel Beer <dlbeer@gmail.com>
*
* 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 <string.h>
#include "dthash.h"
static const uint32_t crc32_tab[] = {
0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f,
0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988,
0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2,
0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7,
0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9,
0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172,
0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c,
0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59,
0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423,
0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924,
0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, 0x01db7106,
0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433,
0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d,
0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e,
0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950,
0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65,
0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7,
0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0,
0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa,
0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81,
0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a,
0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84,
0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1,
0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb,
0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc,
0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e,
0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b,
0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55,
0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236,
0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28,
0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d,
0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f,
0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38,
0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242,
0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777,
0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69,
0x616bffd3, 0x166ccf45, 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2,
0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc,
0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693,
0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94,
0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d
};
static uint32_t calc_crc(uint32_t crc, const uint8_t *buf, int len)
{
while (len--) {
crc = crc32_tab[(crc ^ *buf) & 0xff] ^ (crc >> 8);
buf++;
}
return crc;
}
static uint32_t code_hash(const struct quirc_data *data)
{
uint8_t extra[4] = {data->version, data->ecc_level,
data->mask, data->data_type};
uint32_t crc = calc_crc(0xffffffff, extra, 4);
return calc_crc(crc, data->payload, data->payload_len);
}
static void flush_old(struct dthash *d, time_t now)
{
int i;
for (i = 0; i < d->count; i++) {
struct dthash_code *c = &d->codes[i];
if (c->when + d->timeout <= now) {
if (i + 1 < d->count)
memcpy(c, &d->codes[d->count - 1], sizeof(*c));
d->count--;
}
}
}
void dthash_init(struct dthash *d, int timeout)
{
d->count = 0;
d->timeout = timeout;
}
int dthash_seen(struct dthash *d, const struct quirc_data *data)
{
time_t now = time(NULL);
uint32_t hash = code_hash(data);
struct dthash_code *c;
int i;
flush_old(d, now);
/* If the code is already seen, update its timestamp */
for (i = 0; i < d->count; i++) {
c = &d->codes[i];
if (c->hash == hash) {
c->when = now;
return 1;
}
}
/* Otherwise, find a place to put it. If necessary, push the
* oldset code out of the table.
*/
if (d->count + 1 < DTHASH_MAX_CODES) {
c = &d->codes[d->count++];
} else {
c = &d->codes[0];
for (i = 1; i < d->count; i++)
if (d->codes[i].when < c->when)
c = &d->codes[i];
}
c->hash = hash;
c->when = now;
return 0;
}
+61
View File
@@ -0,0 +1,61 @@
/* quirc -- QR-code recognition library
* Copyright (C) 2010-2012 Daniel Beer <dlbeer@gmail.com>
*
* 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.
*/
#ifndef DTHASH_H_
#define DTHASH_H_
#include <stdint.h>
#include <time.h>
#include "quirc.h"
/* Detector hash.
*
* This structure keeps track of codes that have been seen within the
* last N seconds, and allows us to print out codes at a reasonable
* rate as we see them.
*/
#define DTHASH_MAX_CODES 32
struct dthash_code {
uint32_t hash;
time_t when;
};
struct dthash {
struct dthash_code codes[DTHASH_MAX_CODES];
int count;
int timeout;
};
#ifdef __cplusplus
extern "C" {
#endif
/* Initialise a detector hash with the given timeout. */
void dthash_init(struct dthash *d, int timeout);
/* When a code is discovered, this function should be called to see if
* it should be printed. The hash will record having seen the code, and
* return non-zero if it's the first time we've seen it within the
* configured timeout period.
*/
int dthash_seen(struct dthash *d, const struct quirc_data *data);
#ifdef __cplusplus
}
#endif
#endif
+284
View File
@@ -0,0 +1,284 @@
/* quirc -- QR-code recognition library
* Copyright (C) 2010-2012 Daniel Beer <dlbeer@gmail.com>
*
* 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 <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <setjmp.h>
#include <jpeglib.h>
#include <assert.h>
#include "mjpeg.h"
struct huffman_table {
uint8_t bits[17];
uint8_t huffval[256];
};
static const struct huffman_table dc_lum = {
.bits = {
0x00, 0x00, 0x01, 0x05, 0x01, 0x01, 0x01, 0x01,
0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00
},
.huffval = {
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
0x08, 0x09, 0x0a, 0x0b
}
};
static const struct huffman_table ac_lum = {
.bits = {
0x00, 0x00, 0x02, 0x01, 0x03, 0x03, 0x02, 0x04,
0x03, 0x05, 0x05, 0x04, 0x04, 0x00, 0x00, 0x01,
0x7d
},
.huffval = {
0x01, 0x02, 0x03, 0x00, 0x04, 0x11, 0x05, 0x12,
0x21, 0x31, 0x41, 0x06, 0x13, 0x51, 0x61, 0x07,
0x22, 0x71, 0x14, 0x32, 0x81, 0x91, 0xa1, 0x08,
0x23, 0x42, 0xb1, 0xc1, 0x15, 0x52, 0xd1, 0xf0,
0x24, 0x33, 0x62, 0x72, 0x82, 0x09, 0x0a, 0x16,
0x17, 0x18, 0x19, 0x1a, 0x25, 0x26, 0x27, 0x28,
0x29, 0x2a, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39,
0x3a, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49,
0x4a, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59,
0x5a, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69,
0x6a, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79,
0x7a, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89,
0x8a, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98,
0x99, 0x9a, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7,
0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6,
0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3, 0xc4, 0xc5,
0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xd2, 0xd3, 0xd4,
0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xe1, 0xe2,
0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea,
0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8,
0xf9, 0xfa
}
};
static const struct huffman_table dc_chroma = {
.bits = {
0x00, 0x00, 0x03, 0x01, 0x01, 0x01, 0x01, 0x01,
0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00,
0x00
},
.huffval = {
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
0x08, 0x09, 0x0a, 0x0b
}
};
static const struct huffman_table ac_chroma = {
.bits = {
0x00, 0x00, 0x02, 0x01, 0x02, 0x04, 0x04, 0x03,
0x04, 0x07, 0x05, 0x04, 0x04, 0x00, 0x01, 0x02,
0x77
},
.huffval = {
0x00, 0x01, 0x02, 0x03, 0x11, 0x04, 0x05, 0x21,
0x31, 0x06, 0x12, 0x41, 0x51, 0x07, 0x61, 0x71,
0x13, 0x22, 0x32, 0x81, 0x08, 0x14, 0x42, 0x91,
0xa1, 0xb1, 0xc1, 0x09, 0x23, 0x33, 0x52, 0xf0,
0x15, 0x62, 0x72, 0xd1, 0x0a, 0x16, 0x24, 0x34,
0xe1, 0x25, 0xf1, 0x17, 0x18, 0x19, 0x1a, 0x26,
0x27, 0x28, 0x29, 0x2a, 0x35, 0x36, 0x37, 0x38,
0x39, 0x3a, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48,
0x49, 0x4a, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58,
0x59, 0x5a, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68,
0x69, 0x6a, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78,
0x79, 0x7a, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87,
0x88, 0x89, 0x8a, 0x92, 0x93, 0x94, 0x95, 0x96,
0x97, 0x98, 0x99, 0x9a, 0xa2, 0xa3, 0xa4, 0xa5,
0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4,
0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3,
0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xd2,
0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda,
0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9,
0xea, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8,
0xf9, 0xfa
}
};
static void init_source(j_decompress_ptr cinfo)
{
}
static boolean fill_input_buffer(j_decompress_ptr cinfo)
{
static const uint8_t eoi_marker[] = {0xff, 0xd9};
cinfo->src->next_input_byte = eoi_marker;
cinfo->src->bytes_in_buffer = 2;
return TRUE;
}
static void skip_input_data(j_decompress_ptr cinfo, long num_bytes)
{
if (num_bytes < 0)
return;
if (num_bytes > cinfo->src->bytes_in_buffer)
num_bytes = cinfo->src->bytes_in_buffer;
cinfo->src->bytes_in_buffer -= num_bytes;
cinfo->src->next_input_byte += num_bytes;
}
static void term_source(j_decompress_ptr cinfo)
{
}
struct my_jpeg_error {
struct jpeg_error_mgr base;
jmp_buf env;
};
static void my_output_message(struct jpeg_common_struct *com)
{
struct mjpeg_decoder *mj = (struct mjpeg_decoder *)com->err;
char buf[JMSG_LENGTH_MAX];
mj->err.format_message(com, buf);
fprintf(stderr, "MJPEG error: %s\n", buf);
}
static void my_error_exit(struct jpeg_common_struct *com)
{
struct mjpeg_decoder *mj = (struct mjpeg_decoder *)com->err;
my_output_message(com);
longjmp(mj->env, 0);
}
static void setup_table(struct jpeg_decompress_struct *jpeg,
JHUFF_TBL **tbl_ptr, const struct huffman_table *tab)
{
assert (*tbl_ptr == NULL);
*tbl_ptr = jpeg_alloc_huff_table((j_common_ptr)jpeg);
memcpy((*tbl_ptr)->bits, tab->bits, 17);
memcpy((*tbl_ptr)->huffval, tab->huffval, 256);
}
void mjpeg_init(struct mjpeg_decoder *mj)
{
memset(mj, 0, sizeof(*mj));
/* Set up error management */
mj->dinfo.err = jpeg_std_error(&mj->err);
mj->err.error_exit = my_error_exit;
mj->err.output_message = my_output_message;
mj->src.init_source = init_source;
mj->src.fill_input_buffer = fill_input_buffer;
mj->src.skip_input_data = skip_input_data;
mj->src.resync_to_restart = jpeg_resync_to_restart;
mj->src.term_source = term_source;
jpeg_create_decompress(&mj->dinfo);
mj->dinfo.src = &mj->src;
mj->dinfo.err = &mj->err;
setup_table(&mj->dinfo, &mj->dinfo.dc_huff_tbl_ptrs[0], &dc_lum);
setup_table(&mj->dinfo, &mj->dinfo.ac_huff_tbl_ptrs[0], &ac_lum);
setup_table(&mj->dinfo, &mj->dinfo.dc_huff_tbl_ptrs[1], &dc_chroma);
setup_table(&mj->dinfo, &mj->dinfo.ac_huff_tbl_ptrs[1], &ac_chroma);
}
void mjpeg_free(struct mjpeg_decoder *mj)
{
jpeg_destroy_decompress(&mj->dinfo);
}
int mjpeg_decode_rgb32(struct mjpeg_decoder *mj,
const uint8_t *data, int datalen,
uint8_t *out, int pitch, int max_w, int max_h)
{
if (setjmp(mj->env))
return -1;
mj->dinfo.src->bytes_in_buffer = datalen;
mj->dinfo.src->next_input_byte = data;
jpeg_read_header(&mj->dinfo, TRUE);
mj->dinfo.output_components = 3;
mj->dinfo.out_color_space = JCS_RGB;
jpeg_start_decompress(&mj->dinfo);
if (mj->dinfo.image_height > max_h ||
mj->dinfo.image_width > max_w) {
fprintf(stderr, "MJPEG: frame too big\n");
return -1;
}
uint8_t *rgb = calloc(mj->dinfo.image_width, 3);
if (!rgb) {
fprintf(stderr, "memory allocation failed\n");
return -1;
}
while (mj->dinfo.output_scanline < mj->dinfo.image_height) {
uint8_t *scr = out + pitch * mj->dinfo.output_scanline;
uint8_t *output = rgb;
int i;
jpeg_read_scanlines(&mj->dinfo, &output, 1);
for (i = 0; i < mj->dinfo.image_width; i++) {
scr[0] = output[2];
scr[1] = output[1];
scr[2] = output[0];
scr += 4;
output += 3;
}
}
free(rgb);
jpeg_finish_decompress(&mj->dinfo);
return 0;
}
int mjpeg_decode_gray(struct mjpeg_decoder *mj,
const uint8_t *data, int datalen,
uint8_t *out, int pitch, int max_w, int max_h)
{
if (setjmp(mj->env))
return -1;
mj->dinfo.src->bytes_in_buffer = datalen;
mj->dinfo.src->next_input_byte = data;
jpeg_read_header(&mj->dinfo, TRUE);
mj->dinfo.output_components = 1;
mj->dinfo.out_color_space = JCS_GRAYSCALE;
jpeg_start_decompress(&mj->dinfo);
if (mj->dinfo.image_height > max_h ||
mj->dinfo.image_width > max_w) {
fprintf(stderr, "MJPEG: frame too big\n");
return -1;
}
while (mj->dinfo.output_scanline < mj->dinfo.image_height) {
uint8_t *scr = out + pitch * mj->dinfo.output_scanline;
jpeg_read_scanlines(&mj->dinfo, &scr, 1);
}
jpeg_finish_decompress(&mj->dinfo);
return 0;
}
+54
View File
@@ -0,0 +1,54 @@
/* quirc -- QR-code recognition library
* Copyright (C) 2010-2012 Daniel Beer <dlbeer@gmail.com>
*
* 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.
*/
#ifndef MJPEG_H_
#define MJPEG_H_
#include <stdint.h>
#include <stdio.h>
#include <jpeglib.h>
#include <setjmp.h>
struct mjpeg_decoder {
/* The error manager must be the first item in this struct */
struct jpeg_error_mgr err;
struct jpeg_decompress_struct dinfo;
struct jpeg_source_mgr src;
jmp_buf env;
};
/* Construct an MJPEG decoder. */
void mjpeg_init(struct mjpeg_decoder *mj);
/* Free any memory allocated while decoding MJPEG frames. */
void mjpeg_free(struct mjpeg_decoder *mj);
/* Decode a single MJPEG image to the buffer given, in RGB format.
* Returns 0 on success, -1 if an error occurs (bad data, or image too
* big for buffer).
*/
int mjpeg_decode_rgb32(struct mjpeg_decoder *mj,
const uint8_t *data, int datalen,
uint8_t *out, int pitch, int max_w, int max_h);
/* Decode a single MJPEG image to the buffer given in 8-bit grayscale.
* Returns 0 on success, -1 if an error occurs.
*/
int mjpeg_decode_gray(struct mjpeg_decoder *mj,
const uint8_t *data, int datalen,
uint8_t *out, int pitch, int max_w, int max_h);
#endif
+214
View File
@@ -0,0 +1,214 @@
/* quirc -- QR-code recognition library
* Copyright (C) 2010-2012 Daniel Beer <dlbeer@gmail.com>
*
* 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 <stdlib.h>
#include <ctype.h>
#include <unistd.h>
#include <quirc.h>
#include <time.h>
#include <getopt.h>
#include "camera.h"
#include "mjpeg.h"
#include "convert.h"
#include "dthash.h"
#include "demoutil.h"
/* Collected command-line arguments */
static const char *camera_path = "/dev/video0";
static int video_width = 640;
static int video_height = 480;
static int want_verbose = 0;
static int printer_timeout = 2;
static int main_loop(struct camera *cam,
struct quirc *q, struct mjpeg_decoder *mj)
{
struct dthash dt;
dthash_init(&dt, printer_timeout);
for (;;) {
int w, h;
int i, count;
uint8_t *buf = quirc_begin(q, &w, &h);
const struct camera_buffer *head;
const struct camera_parms *parms = camera_get_parms(cam);
if (camera_dequeue_one(cam) < 0) {
perror("camera_dequeue_one");
return -1;
}
head = camera_get_head(cam);
switch (parms->format) {
case CAMERA_FORMAT_MJPEG:
mjpeg_decode_gray(mj, head->addr, head->size,
buf, w, w, h);
break;
case CAMERA_FORMAT_YUYV:
yuyv_to_luma(head->addr, w * 2, w, h, buf, w);
break;
default:
fprintf(stderr, "Unknown frame format\n");
return -1;
}
if (camera_enqueue_all(cam) < 0) {
perror("camera_enqueue_all");
return -1;
}
quirc_end(q);
count = quirc_count(q);
for (i = 0; i < count; i++) {
struct quirc_code code;
struct quirc_data data;
quirc_extract(q, i, &code);
if (!quirc_decode(&code, &data))
print_data(&data, &dt, want_verbose);
}
}
}
static int run_scanner(void)
{
struct quirc *qr;
struct camera cam;
struct mjpeg_decoder mj;
const struct camera_parms *parms;
camera_init(&cam);
if (camera_open(&cam, camera_path, video_width, video_height,
25, 1) < 0) {
perror("camera_open");
goto fail_qr;
}
if (camera_map(&cam, 8) < 0) {
perror("camera_map");
goto fail_qr;
}
if (camera_on(&cam) < 0) {
perror("camera_on");
goto fail_qr;
}
if (camera_enqueue_all(&cam) < 0) {
perror("camera_enqueue_all");
goto fail_qr;
}
parms = camera_get_parms(&cam);
qr = quirc_new();
if (!qr) {
perror("couldn't allocate QR decoder");
goto fail_qr;
}
if (quirc_resize(qr, parms->width, parms->height) < 0) {
perror("couldn't allocate QR buffer");
goto fail_qr_resize;
}
mjpeg_init(&mj);
if (main_loop(&cam, qr, &mj) < 0)
goto fail_main_loop;
mjpeg_free(&mj);
quirc_destroy(qr);
camera_destroy(&cam);
return 0;
fail_main_loop:
mjpeg_free(&mj);
fail_qr_resize:
quirc_destroy(qr);
fail_qr:
camera_destroy(&cam);
return -1;
}
static void usage(const char *progname)
{
printf("Usage: %s [options]\n\n"
"Valid options are:\n\n"
" -v Show extra data for detected codes.\n"
" -d <device> Specify camera device path.\n"
" -s <WxH> Specify video dimensions.\n"
" -p <timeout> 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 scanner demo\n");
printf("Copyright (C) 2010-2012 Daniel Beer <dlbeer@gmail.com>\n");
printf("\n");
while ((opt = getopt_long(argc, argv, "d:s:vg: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 's':
if (parse_size(optarg, &video_width, &video_height) < 0)
return -1;
break;
case 'p':
printer_timeout = atoi(optarg);
break;
case 'd':
camera_path = optarg;
break;
case '?':
fprintf(stderr, "Try --help for usage information\n");
return -1;
}
return run_scanner();
}
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+165
View File
@@ -0,0 +1,165 @@
/* quirc -- QR-code recognition library
* Copyright (C) 2010-2012 Daniel Beer <dlbeer@gmail.com>
*
* 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 <stdlib.h>
#include <string.h>
#include "quirc_internal.h"
const char *quirc_version(void)
{
return "1.0";
}
struct quirc *quirc_new(void)
{
struct quirc *q = malloc(sizeof(*q));
if (!q)
return NULL;
memset(q, 0, sizeof(*q));
return q;
}
void quirc_destroy(struct quirc *q)
{
free(q->image);
/* q->pixels may alias q->image when their type representation is of the
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);
}
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;
/*
* XXX: w and h should be size_t (or at least unsigned) as negatives
* values would not make much sense. The downside is that it would break
* both the API and ABI. Thus, at the moment, let's just do a sanity
* check.
*/
if (w < 0 || h < 0)
goto fail;
/*
* alloc a new buffer for q->image. We avoid realloc(3) because we want
* on failure to be leave `q` in a consistant, unmodified state.
*/
image = ps_malloc(w, h);
if (!image)
goto fail;
/* compute the "old" (i.e. currently allocated) and the "new"
(i.e. requested) image dimensions */
size_t olddim = q->w * q->h;
size_t newdim = w * h;
size_t min = (olddim < newdim ? olddim : newdim);
/*
* copy the data into the new buffer, avoiding (a) to read beyond the
* old buffer when the new size is greater and (b) to write beyond the
* new buffer when the new size is smaller, hence the min computation.
*/
(void)memcpy(image, q->image, min);
/* alloc a new buffer for q->pixels if needed */
if (!QUIRC_PIXEL_ALIAS_IMAGE) {
pixels = calloc(newdim, sizeof(quirc_pixel_t));
if (!pixels)
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.
*/
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;
/* alloc succeeded, update `q` with the new size and buffers */
q->w = w;
q->h = h;
free(q->image);
q->image = image;
if (!QUIRC_PIXEL_ALIAS_IMAGE) {
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;
}
int quirc_count(const struct quirc *q)
{
return q->num_grids;
}
static const char *const error_table[] = {
[QUIRC_SUCCESS] = "Success",
[QUIRC_ERROR_INVALID_GRID_SIZE] = "Invalid grid size",
[QUIRC_ERROR_INVALID_VERSION] = "Invalid version",
[QUIRC_ERROR_FORMAT_ECC] = "Format data ECC failure",
[QUIRC_ERROR_DATA_ECC] = "ECC failure",
[QUIRC_ERROR_UNKNOWN_DATA_TYPE] = "Unknown data type",
[QUIRC_ERROR_DATA_OVERFLOW] = "Data overflow",
[QUIRC_ERROR_DATA_UNDERFLOW] = "Data underflow"
};
const char *quirc_strerror(quirc_decode_error_t err)
{
if (err >= 0 && err < sizeof(error_table) / sizeof(error_table[0]))
return error_table[err];
return "Unknown error";
}

Some files were not shown because too many files have changed in this diff Show More