You've already forked MicroPythonOS
mirror of
https://github.com/m5stack/MicroPythonOS.git
synced 2026-05-20 11:51:27 -07:00
Add upstream https://github.com/dlbeer/quirc
This commit is contained in:
@@ -0,0 +1,12 @@
|
||||
*.o
|
||||
*.lo
|
||||
quirc-demo
|
||||
quirc-scanner
|
||||
qrtest
|
||||
inspect
|
||||
libquirc.a
|
||||
libquirc.so*
|
||||
.*.swp
|
||||
*~
|
||||
.DS_Store
|
||||
.idea
|
||||
@@ -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.
|
||||
@@ -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
|
||||
@@ -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
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
@@ -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();
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
|
||||
@@ -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
@@ -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
Reference in New Issue
Block a user