Initial commit: version 1.0.

This commit is contained in:
Daniel Beer 2012-05-04 12:58:42 +12:00
commit a3142bc6b4
26 changed files with 5265 additions and 0 deletions

10
.gitignore vendored Normal file
View file

@ -0,0 +1,10 @@
*.o
*.lo
quirc-demo
quirc-scanner
qrtest
inspect
libquirc.a
libquirc.so
.*.swp
*~

16
LICENSE Normal file
View file

@ -0,0 +1,16 @@
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.

93
Makefile Normal file
View file

@ -0,0 +1,93 @@
# 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)
SDL_LIBS := $(shell pkg-config --libs sdl)
LIB_VERSION = 1.0
LIB_SONAME = libquirc.so.1
QUIRC_CFLAGS = -O3 -Wall -Ilib $(CFLAGS) $(SDL_CFLAGS)
LIB_OBJ = \
lib/decode.o \
lib/identify.o \
lib/quirc.o \
lib/version_db.o
LIB_SOBJ = $(subst .o,.lo,$(LIB_OBJ))
DEMO_OBJ = \
demo/camera.o \
demo/mjpeg.o \
demo/convert.o \
demo/dthash.o \
demo/demoutil.o
all: libquirc.so qrtest inspect quirc-demo quirc-scanner
qrtest: tests/dbgutil.o tests/qrtest.o libquirc.a
$(CC) -o $@ $^ -lm -ljpeg
inspect: tests/dbgutil.o tests/inspect.o libquirc.a
$(CC) -o $@ $^ -lm -ljpeg $(SDL_LIBS) -lSDL_gfx
quirc-demo: $(DEMO_OBJ) demo/demo.o libquirc.a
$(CC) -o $@ $^ -lm -ljpeg $(SDL_LIBS) -lSDL_gfx
quirc-scanner: $(DEMO_OBJ) demo/scanner.o libquirc.a
$(CC) -o $@ $^ -lm -ljpeg
libquirc.a: $(LIB_OBJ)
rm -f $@
ar cru $@ $^
ranlib $@
libquirc.so: $(LIB_SOBJ)
$(CC) -shared -Wl,-soname=$(LIB_SONAME) -o $@ $^ -lm
%.o: %.c
$(CC) $(QUIRC_CFLAGS) -o $*.o -c $*.c
%.lo: %.c
$(CC) -fPIC $(QUIRC_CFLAGS) -o $*.lo -c $*.c
install: libquirc.a libquirc.so quirc-demo quirc-scanner
install -o root -g root -m 0644 lib/quirc.h $(DESTDIR)$(PREFIX)/include
install -o root -g root -m 0644 libquirc.a $(DESTDIR)$(PREFIX)/lib
install -o root -g root -m 0755 libquirc.so \
$(DESTDIR)$(PREFIX)/lib/libquirc.so.$(LIB_VERSION)
ln -sf libquirc.so.$(LIB_VERSION) $(DESTDIR)$(PREFIX)/lib/$(LIB_SONAME)
ln -sf libquirc.so.$(LIB_VERSION) $(DESTDIR)$(PREFIX)/lib/libquirc.so
install -o root -g root -m 0755 quirc-demo $(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.so.$(LIB_VERSION)
rm -f $(DESTDIR)$(PREFIX)/lib/$(LIB_SONAME)
rm -f $(DESTDIR)$(PREFIX)/lib/libquirc.so
rm -f $(DESTDIR)$(PREFIX)/lib/libquirc.a
rm -f $(DESTDIR)$(PREFIX)/bin/quirc-demo
rm -f $(DESTDIR)$(PREFIX)/bin/quirc-scanner
clean:
rm -f */*.o
rm -f */*.lo
rm -f libquirc.a
rm -f libquirc.so
rm -f qrtest
rm -f inspect
rm -f quirc-demo
rm -f quirc-scanner

188
README Normal file
View file

@ -0,0 +1,188 @@
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.
* BSD-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. All of them require libjpeg, and two
(``quirc-demo`` and ``inspect``) require SDL. The camera demos use
Linux-specific APIs:
``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.
``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.
``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.
``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.
Installation
------------
To build the library and associated demos/tests, type ``make``. 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
* quirc-scanner
* quirc-demo
Library use
-----------
All of the library's functionality is exposed through a single header
file, which you should include:
#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``:
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``:
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:
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.
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.
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.

221
demo/camera.c Normal file
View file

@ -0,0 +1,221 @@
/* 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 <string.h>
#include <errno.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <sys/ioctl.h>
#include <linux/videodev.h>
#include "camera.h"
static int set_video_format(struct camera *cam, uint32_t format,
int w, int h, camera_format_t my_fmt)
{
struct v4l2_format fmt;
memset(&fmt, 0, sizeof(fmt));
fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
fmt.fmt.pix.width = w;
fmt.fmt.pix.height = h;
fmt.fmt.pix.pixelformat = format;
fmt.fmt.pix.field = V4L2_FIELD_ANY;
if (ioctl(cam->fd, VIDIOC_S_FMT, &fmt) < 0)
return -1;
/* Note that the size obtained may not match what was requested. */
cam->format = my_fmt;
cam->width = fmt.fmt.pix.width;
cam->height = fmt.fmt.pix.height;
return 0;
}
int camera_set_gain(struct camera *cam, int value)
{
struct v4l2_queryctrl query;
struct v4l2_control ctrl;
query.id = V4L2_CID_GAIN;
if (ioctl(cam->fd, VIDIOC_QUERYCTRL, &query) < 0) {
perror("ioctl: VIDIOC_QUERYCTRL");
return -1;
}
ctrl.id = V4L2_CID_GAIN;
if (value < 0)
ctrl.value = query.default_value;
else
ctrl.value = value;
if (ioctl(cam->fd, VIDIOC_S_CTRL, &ctrl) < 0) {
perror("ioctl: VIDIOC_S_CTRL");
return -1;
}
return 0;
}
int camera_init(struct camera *cam, const char *path, int width, int height)
{
struct v4l2_streamparm fps;
struct v4l2_requestbuffers rb;
int type;
int i;
cam->fd = open(path, O_RDONLY);
if (cam->fd < 0) {
fprintf(stderr, "%s: can't open camera device: %s\n", path,
strerror(errno));
return -1;
}
/* Set the video format */
if (set_video_format(cam, V4L2_PIX_FMT_MJPEG,
width, height, CAMERA_FORMAT_MJPEG) < 0 &&
set_video_format(cam, V4L2_PIX_FMT_YUYV,
width, height, CAMERA_FORMAT_YUYV) < 0) {
fprintf(stderr, "%s: can't find a supported video format\n",
path);
goto fail;
}
/* Set the frame rate */
memset(&fps, 0, sizeof(fps));
fps.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
fps.parm.capture.timeperframe.numerator = 1;
fps.parm.capture.timeperframe.denominator = 20;
if (ioctl(cam->fd, VIDIOC_S_PARM, &fps) < 0) {
fprintf(stderr, "%s: can't set video frame rate: %s\n", path,
strerror(errno));
goto fail;
}
/* Request buffer */
memset(&rb, 0, sizeof(rb));
rb.count = CAMERA_BUFFERS;
rb.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
rb.memory = V4L2_MEMORY_MMAP;
if (ioctl(cam->fd, VIDIOC_REQBUFS, &rb) < 0) {
fprintf(stderr, "%s: can't request buffers: %s\n", path,
strerror(errno));
goto fail;
}
/* Map buffers and submit them */
for (i = 0; i < CAMERA_BUFFERS; i++) {
struct v4l2_buffer buf;
memset(&buf, 0, sizeof(buf));
buf.index = i;
buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
buf.memory = V4L2_MEMORY_MMAP;
if (ioctl(cam->fd, VIDIOC_QUERYBUF, &buf) < 0) {
fprintf(stderr, "%s: can't query buffer: %s\n", path,
strerror(errno));
goto fail;
}
cam->buffer_maps[i] = mmap(0, buf.length,
PROT_READ, MAP_SHARED, cam->fd, buf.m.offset);
if (cam->buffer_maps[i] == MAP_FAILED) {
fprintf(stderr, "%s: can't map buffer memory: %s\n",
path, strerror(errno));
goto fail;
}
cam->buffer_lens[i] = buf.length;
}
/* Switch on streaming */
type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
if (ioctl(cam->fd, VIDIOC_STREAMON, &type) < 0) {
fprintf(stderr, "%s: can't enable streaming: %s\n", path,
strerror(errno));
goto fail;
}
/* Queue the buffers */
for (i = 0; i < CAMERA_BUFFERS; i++) {
struct v4l2_buffer buf;
memset(&buf, 0, sizeof(buf));
buf.index = i;
buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
buf.memory = V4L2_MEMORY_MMAP;
if (ioctl(cam->fd, VIDIOC_QBUF, &buf) < 0) {
perror("can't start buffer queue");
return -1;
}
}
cam->mem_index = -1;
return 0;
fail:
close(cam->fd);
return -1;
}
void camera_free(struct camera *cam)
{
int i;
for (i = 0; i < CAMERA_BUFFERS; i++)
munmap(cam->buffer_maps[i], cam->buffer_lens[i]);
close(cam->fd);
}
int camera_update(struct camera *cam)
{
struct v4l2_buffer buf;
int last_index = cam->mem_index;
/* De-queue a buffer */
memset(&buf, 0, sizeof(buf));
buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
buf.memory = V4L2_MEMORY_MMAP;
if (ioctl(cam->fd, VIDIOC_DQBUF, &buf) < 0) {
perror("can't de-queue buffer");
return -1;
}
cam->mem_index = buf.index;
cam->mem = cam->buffer_maps[cam->mem_index];
cam->mem_len = cam->buffer_lens[cam->mem_index];
/* Re-queue the last buffer */
if (last_index >= 0) {
memset(&buf, 0, sizeof(buf));
buf.index = last_index;
buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
buf.memory = V4L2_MEMORY_MMAP;
if (ioctl(cam->fd, VIDIOC_QBUF, &buf) < 0) {
perror("can't queue buffer");
return -1;
}
}
return 0;
}

70
demo/camera.h Normal file
View file

@ -0,0 +1,70 @@
/* 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 CAMERA_H_
#define CAMERA_H_
#include <stdint.h>
#define CAMERA_BUFFERS 8
typedef enum {
CAMERA_FORMAT_MJPEG,
CAMERA_FORMAT_YUYV
} camera_format_t;
struct camera {
/* File descriptor for camera device. */
int fd;
/* Width, height and frame pixel format. Once initialized,
* these values will not change.
*/
int width;
int height;
camera_format_t format;
/* Buffer information for the current frame. If mem_index < 0,
* then no frame is currently available.
*/
uint8_t *mem;
int mem_len;
int mem_index;
/* Pointers and sizes of memory-mapped buffers. */
uint8_t *buffer_maps[CAMERA_BUFFERS];
int buffer_lens[CAMERA_BUFFERS];
};
/* Set up a camera driver. This opens the video device and configures for
* the given resolution. Streaming is started immediately.
*
* Returns 0 on success or -1 if an error occurs.
*/
int camera_init(struct camera *cam, const char *path, int width, int height);
/* Set the gain control for the camera device. */
int camera_set_gain(struct camera *cam, int gain);
/* Shut down the camera device and free allocated memory. */
void camera_free(struct camera *cam);
/* Retrieve the latest frame from the video queue and fill out the mem and
* mem_len fields in the camera struct.
*/
int camera_update(struct camera *cam);
#endif

103
demo/convert.c Normal file
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
demo/convert.h Normal file
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

302
demo/demo.c Normal file
View file

@ -0,0 +1,302 @@
/* 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 gain_request = -1;
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);
if (camera_update(cam) < 0)
return -1;
SDL_LockSurface(screen);
switch (cam->format) {
case CAMERA_FORMAT_MJPEG:
mjpeg_decode_rgb32(mj, cam->mem, cam->mem_len,
screen->pixels, screen->pitch,
screen->w, screen->h);
break;
case CAMERA_FORMAT_YUYV:
yuyv_to_rgb32(cam->mem, cam->width * 2,
cam->width, cam->height,
screen->pixels, screen->pitch);
break;
}
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;
SDL_Surface *screen;
int ret;
if (camera_init(&cam, camera_path, video_width, video_height) < 0)
return -1;
camera_set_gain(&cam, gain_request);
qr = quirc_new();
if (!qr) {
perror("couldn't allocate QR decoder");
goto fail_qr;
}
if (quirc_resize(qr, cam.width, cam.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(cam.width, cam.height, 32,
SDL_SWSURFACE | SDL_DOUBLEBUF);
if (!screen) {
perror("couldn't init video mode");
goto fail_video_mode;
}
mjpeg_init(&mj);
ret = main_loop(&cam, screen, qr, &mj);
mjpeg_free(&mj);
SDL_Quit();
quirc_destroy(qr);
camera_free(&cam);
return 0;
fail_video_mode:
SDL_Quit();
fail_qr_resize:
fail_sdl_init:
quirc_destroy(qr);
fail_qr:
camera_free(&cam);
return 0;
}
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"
" -g <value> Set camera gain.\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)
{
const static 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-2012 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 'g':
gain_request = atoi(optarg);
break;
case '?':
fprintf(stderr, "Try --help for usage information\n");
return -1;
}
return run_demo();
}

71
demo/demoutil.c Normal file
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;
}

34
demo/demoutil.h Normal file
View file

@ -0,0 +1,34 @@
/* 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"
/* 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);
#endif

139
demo/dthash.c Normal file
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"
const static 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;
}

53
demo/dthash.h Normal file
View file

@ -0,0 +1,53 @@
/* 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;
};
/* 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);
#endif

278
demo/mjpeg.c Normal file
View file

@ -0,0 +1,278 @@
/* 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 <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 */
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;
}
while (mj->dinfo.output_scanline < mj->dinfo.image_height) {
uint8_t rgb[mj->dinfo.image_width * 3];
uint8_t *output = rgb;
uint8_t *scr = out + pitch * mj->dinfo.output_scanline;
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;
}
}
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
demo/mjpeg.h Normal file
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

183
demo/scanner.c Normal file
View file

@ -0,0 +1,183 @@
/* 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 gain_request = -1;
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);
if (camera_update(cam) < 0)
return -1;
switch (cam->format) {
case CAMERA_FORMAT_MJPEG:
mjpeg_decode_gray(mj, cam->mem, cam->mem_len,
buf, w, w, h);
break;
case CAMERA_FORMAT_YUYV:
yuyv_to_luma(cam->mem, w * 2, w, h, buf, w);
break;
}
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;
int ret;
if (camera_init(&cam, camera_path, video_width, video_height) < 0)
return -1;
camera_set_gain(&cam, gain_request);
qr = quirc_new();
if (!qr) {
perror("couldn't allocate QR decoder");
goto fail_qr;
}
if (quirc_resize(qr, cam.width, cam.height) < 0) {
perror("couldn't allocate QR buffer");
goto fail_qr_resize;
}
mjpeg_init(&mj);
ret = main_loop(&cam, qr, &mj);
mjpeg_free(&mj);
quirc_destroy(qr);
camera_free(&cam);
return 0;
fail_qr_resize:
quirc_destroy(qr);
fail_qr:
camera_free(&cam);
return 0;
}
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"
" -g <value> Set camera gain.\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)
{
const static 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 'g':
gain_request = atoi(optarg);
break;
case '?':
fprintf(stderr, "Try --help for usage information\n");
return -1;
}
return run_scanner();
}

827
lib/decode.c Normal file
View file

@ -0,0 +1,827 @@
/* 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 "quirc_internal.h"
#include <string.h>
#include <stdlib.h>
#define MAX_POLY 64
/************************************************************************
* Galois fields
*/
struct galois_field {
int p;
const uint8_t *log;
const uint8_t *exp;
};
static const uint8_t gf16_exp[16] = {
0x01, 0x02, 0x04, 0x08, 0x03, 0x06, 0x0c, 0x0b,
0x05, 0x0a, 0x07, 0x0e, 0x0f, 0x0d, 0x09, 0x01
};
static const uint8_t gf16_log[16] = {
0x00, 0x0f, 0x01, 0x04, 0x02, 0x08, 0x05, 0x0a,
0x03, 0x0e, 0x09, 0x07, 0x06, 0x0d, 0x0b, 0x0c
};
static const struct galois_field gf16 = {
.p = 15,
.log = gf16_log,
.exp = gf16_exp
};
static const uint8_t gf256_exp[256] = {
0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80,
0x1d, 0x3a, 0x74, 0xe8, 0xcd, 0x87, 0x13, 0x26,
0x4c, 0x98, 0x2d, 0x5a, 0xb4, 0x75, 0xea, 0xc9,
0x8f, 0x03, 0x06, 0x0c, 0x18, 0x30, 0x60, 0xc0,
0x9d, 0x27, 0x4e, 0x9c, 0x25, 0x4a, 0x94, 0x35,
0x6a, 0xd4, 0xb5, 0x77, 0xee, 0xc1, 0x9f, 0x23,
0x46, 0x8c, 0x05, 0x0a, 0x14, 0x28, 0x50, 0xa0,
0x5d, 0xba, 0x69, 0xd2, 0xb9, 0x6f, 0xde, 0xa1,
0x5f, 0xbe, 0x61, 0xc2, 0x99, 0x2f, 0x5e, 0xbc,
0x65, 0xca, 0x89, 0x0f, 0x1e, 0x3c, 0x78, 0xf0,
0xfd, 0xe7, 0xd3, 0xbb, 0x6b, 0xd6, 0xb1, 0x7f,
0xfe, 0xe1, 0xdf, 0xa3, 0x5b, 0xb6, 0x71, 0xe2,
0xd9, 0xaf, 0x43, 0x86, 0x11, 0x22, 0x44, 0x88,
0x0d, 0x1a, 0x34, 0x68, 0xd0, 0xbd, 0x67, 0xce,
0x81, 0x1f, 0x3e, 0x7c, 0xf8, 0xed, 0xc7, 0x93,
0x3b, 0x76, 0xec, 0xc5, 0x97, 0x33, 0x66, 0xcc,
0x85, 0x17, 0x2e, 0x5c, 0xb8, 0x6d, 0xda, 0xa9,
0x4f, 0x9e, 0x21, 0x42, 0x84, 0x15, 0x2a, 0x54,
0xa8, 0x4d, 0x9a, 0x29, 0x52, 0xa4, 0x55, 0xaa,
0x49, 0x92, 0x39, 0x72, 0xe4, 0xd5, 0xb7, 0x73,
0xe6, 0xd1, 0xbf, 0x63, 0xc6, 0x91, 0x3f, 0x7e,
0xfc, 0xe5, 0xd7, 0xb3, 0x7b, 0xf6, 0xf1, 0xff,
0xe3, 0xdb, 0xab, 0x4b, 0x96, 0x31, 0x62, 0xc4,
0x95, 0x37, 0x6e, 0xdc, 0xa5, 0x57, 0xae, 0x41,
0x82, 0x19, 0x32, 0x64, 0xc8, 0x8d, 0x07, 0x0e,
0x1c, 0x38, 0x70, 0xe0, 0xdd, 0xa7, 0x53, 0xa6,
0x51, 0xa2, 0x59, 0xb2, 0x79, 0xf2, 0xf9, 0xef,
0xc3, 0x9b, 0x2b, 0x56, 0xac, 0x45, 0x8a, 0x09,
0x12, 0x24, 0x48, 0x90, 0x3d, 0x7a, 0xf4, 0xf5,
0xf7, 0xf3, 0xfb, 0xeb, 0xcb, 0x8b, 0x0b, 0x16,
0x2c, 0x58, 0xb0, 0x7d, 0xfa, 0xe9, 0xcf, 0x83,
0x1b, 0x36, 0x6c, 0xd8, 0xad, 0x47, 0x8e, 0x01
};
static const uint8_t gf256_log[256] = {
0x00, 0xff, 0x01, 0x19, 0x02, 0x32, 0x1a, 0xc6,
0x03, 0xdf, 0x33, 0xee, 0x1b, 0x68, 0xc7, 0x4b,
0x04, 0x64, 0xe0, 0x0e, 0x34, 0x8d, 0xef, 0x81,
0x1c, 0xc1, 0x69, 0xf8, 0xc8, 0x08, 0x4c, 0x71,
0x05, 0x8a, 0x65, 0x2f, 0xe1, 0x24, 0x0f, 0x21,
0x35, 0x93, 0x8e, 0xda, 0xf0, 0x12, 0x82, 0x45,
0x1d, 0xb5, 0xc2, 0x7d, 0x6a, 0x27, 0xf9, 0xb9,
0xc9, 0x9a, 0x09, 0x78, 0x4d, 0xe4, 0x72, 0xa6,
0x06, 0xbf, 0x8b, 0x62, 0x66, 0xdd, 0x30, 0xfd,
0xe2, 0x98, 0x25, 0xb3, 0x10, 0x91, 0x22, 0x88,
0x36, 0xd0, 0x94, 0xce, 0x8f, 0x96, 0xdb, 0xbd,
0xf1, 0xd2, 0x13, 0x5c, 0x83, 0x38, 0x46, 0x40,
0x1e, 0x42, 0xb6, 0xa3, 0xc3, 0x48, 0x7e, 0x6e,
0x6b, 0x3a, 0x28, 0x54, 0xfa, 0x85, 0xba, 0x3d,
0xca, 0x5e, 0x9b, 0x9f, 0x0a, 0x15, 0x79, 0x2b,
0x4e, 0xd4, 0xe5, 0xac, 0x73, 0xf3, 0xa7, 0x57,
0x07, 0x70, 0xc0, 0xf7, 0x8c, 0x80, 0x63, 0x0d,
0x67, 0x4a, 0xde, 0xed, 0x31, 0xc5, 0xfe, 0x18,
0xe3, 0xa5, 0x99, 0x77, 0x26, 0xb8, 0xb4, 0x7c,
0x11, 0x44, 0x92, 0xd9, 0x23, 0x20, 0x89, 0x2e,
0x37, 0x3f, 0xd1, 0x5b, 0x95, 0xbc, 0xcf, 0xcd,
0x90, 0x87, 0x97, 0xb2, 0xdc, 0xfc, 0xbe, 0x61,
0xf2, 0x56, 0xd3, 0xab, 0x14, 0x2a, 0x5d, 0x9e,
0x84, 0x3c, 0x39, 0x53, 0x47, 0x6d, 0x41, 0xa2,
0x1f, 0x2d, 0x43, 0xd8, 0xb7, 0x7b, 0xa4, 0x76,
0xc4, 0x17, 0x49, 0xec, 0x7f, 0x0c, 0x6f, 0xf6,
0x6c, 0xa1, 0x3b, 0x52, 0x29, 0x9d, 0x55, 0xaa,
0xfb, 0x60, 0x86, 0xb1, 0xbb, 0xcc, 0x3e, 0x5a,
0xcb, 0x59, 0x5f, 0xb0, 0x9c, 0xa9, 0xa0, 0x51,
0x0b, 0xf5, 0x16, 0xeb, 0x7a, 0x75, 0x2c, 0xd7,
0x4f, 0xae, 0xd5, 0xe9, 0xe6, 0xe7, 0xad, 0xe8,
0x74, 0xd6, 0xf4, 0xea, 0xa8, 0x50, 0x58, 0xaf
};
const static struct galois_field gf256 = {
.p = 255,
.log = gf256_log,
.exp = gf256_exp
};
/************************************************************************
* Polynomial operations
*/
static void poly_mult(uint8_t *r, const uint8_t *a, const uint8_t *b,
const struct galois_field *gf)
{
int i;
memset(r, 0, MAX_POLY);
for (i = 0; i < MAX_POLY; i++) {
int j;
for (j = 0; j + i < MAX_POLY; j++) {
uint8_t ca = a[i];
uint8_t cb = b[j];
if (!(ca && cb))
continue;
r[i + j] ^= gf->exp[(gf->log[ca] +
gf->log[cb]) %
gf->p];
}
}
}
static void poly_add(uint8_t *dst, const uint8_t *src, uint8_t c,
int shift, const struct galois_field *gf)
{
int i;
int log_c = gf->log[c];
if (!c)
return;
for (i = 0; i < MAX_POLY; i++) {
int p = i + shift;
uint8_t v = src[i];
if (p < 0 || p >= MAX_POLY)
continue;
if (!v)
continue;
dst[p] ^= gf->exp[(gf->log[v] + log_c) % gf->p];
}
}
static uint8_t poly_eval(const uint8_t *s, uint8_t x,
const struct galois_field *gf)
{
int i;
uint8_t sum = 0;
uint8_t log_x = gf->log[x];
if (!x)
return s[0];
for (i = 0; i < MAX_POLY; i++) {
uint8_t c = s[i];
if (!c)
continue;
sum ^= gf->exp[(gf->log[c] + log_x * i) % gf->p];
}
return sum;
}
/************************************************************************
* Berlekamp-Massey algorithm for finding error locator polynomials.
*/
static void berlekamp_massey(const uint8_t *s, int N,
const struct galois_field *gf,
uint8_t *sigma)
{
uint8_t C[MAX_POLY];
uint8_t B[MAX_POLY];
int L = 0;
int m = 1;
uint8_t b = 1;
int n;
memset(B, 0, sizeof(B));
memset(C, 0, sizeof(C));
B[0] = 1;
C[0] = 1;
for (n = 0; n < N; n++) {
uint8_t d = s[n];
uint8_t mult;
int i;
for (i = 1; i <= L; i++) {
if (!(C[i] && s[n - i]))
continue;
d ^= gf->exp[(gf->log[C[i]] +
gf->log[s[n - i]]) %
gf->p];
}
mult = gf->exp[(gf->p - gf->log[b] + gf->log[d]) % gf->p];
if (!d) {
m++;
} else if (L * 2 <= n) {
uint8_t T[MAX_POLY];
memcpy(T, C, sizeof(T));
poly_add(C, B, mult, m, gf);
memcpy(B, T, sizeof(B));
L = n + 1 - L;
b = d;
m = 1;
} else {
poly_add(C, B, mult, m, gf);
m++;
}
}
memcpy(sigma, C, MAX_POLY);
}
/************************************************************************
* Code stream error correction
*
* Generator polynomial for GF(2^8) is x^8 + x^4 + x^3 + x^2 + 1
*/
static int block_syndromes(const uint8_t *data, int bs, int npar, uint8_t *s)
{
int nonzero = 0;
int i;
memset(s, 0, MAX_POLY);
for (i = 0; i < npar; i++) {
int j;
for (j = 0; j < bs; j++) {
uint8_t c = data[bs - j - 1];
if (!c)
continue;
s[i] ^= gf256_exp[((int)gf256_log[c] +
(i + 1) * j) % 255];
}
if (s[i])
nonzero = 1;
}
return nonzero;
}
static quirc_decode_error_t correct_block(uint8_t *data, const struct quirc_rs_params *ecc)
{
int npar = ecc->ce;
uint8_t s[MAX_POLY];
uint8_t sigma[MAX_POLY];
uint8_t sigma_deriv[MAX_POLY];
uint8_t omega[MAX_POLY];
int i;
/* Compute syndrome vector */
if (!block_syndromes(data, ecc->bs, npar, s))
return QUIRC_SUCCESS;
berlekamp_massey(s, npar, &gf256, sigma);
/* Compute derivative of sigma */
memset(sigma_deriv, 0, MAX_POLY);
for (i = 0; i + 1 < MAX_POLY; i += 2)
sigma_deriv[i] = sigma[i + 1];
/* Compute error evaluator polynomial */
poly_mult(omega, sigma, s, &gf256);
memset(omega + npar, 0, MAX_POLY - npar);
/* Find error locations and magnitudes */
for (i = 0; i < ecc->bs; i++) {
uint8_t xinv = gf256_exp[255 - i];
if (!poly_eval(sigma, xinv, &gf256)) {
uint8_t sd_x = poly_eval(sigma_deriv, xinv, &gf256);
uint8_t omega_x = poly_eval(omega, xinv, &gf256);
uint8_t error = gf256_exp[(255 - gf256_log[sd_x] +
gf256_log[omega_x]) % 255];
data[ecc->bs - i - 1] ^= error;
}
}
if (block_syndromes(data, ecc->bs, npar, s))
return QUIRC_ERROR_DATA_ECC;
return QUIRC_SUCCESS;
}
/************************************************************************
* Format value error correction
*
* Generator polynomial for GF(2^4) is x^4 + x + 1
*/
#define FORMAT_MAX_ERROR 3
#define FORMAT_SYNDROMES (FORMAT_MAX_ERROR * 2)
#define FORMAT_BITS 15
static int format_syndromes(uint16_t u, uint8_t *s)
{
int i;
int nonzero = 0;
memset(s, 0, MAX_POLY);
for (i = 0; i < FORMAT_SYNDROMES; i++) {
int j;
s[i] = 0;
for (j = 0; j < FORMAT_BITS; j++)
if (u & (1 << j))
s[i] ^= gf16_exp[((i + 1) * j) % 15];
if (s[i])
nonzero = 1;
}
return nonzero;
}
static quirc_decode_error_t correct_format(uint16_t *f_ret)
{
uint16_t u = *f_ret;
int i;
uint8_t s[MAX_POLY];
uint8_t sigma[MAX_POLY];
/* Evaluate U (received codeword) at each of alpha_1 .. alpha_6
* to get S_1 .. S_6 (but we index them from 0).
*/
if (!format_syndromes(u, s))
return QUIRC_SUCCESS;
berlekamp_massey(s, FORMAT_SYNDROMES, &gf16, sigma);
/* Now, find the roots of the polynomial */
for (i = 0; i < 15; i++)
if (!poly_eval(sigma, gf16_exp[15 - i], &gf16))
u ^= (1 << i);
if (format_syndromes(u, s))
return QUIRC_ERROR_FORMAT_ECC;
*f_ret = u;
return QUIRC_SUCCESS;
}
/************************************************************************
* Decoder algorithm
*/
struct datastream {
uint8_t raw[QUIRC_MAX_PAYLOAD];
int data_bits;
uint8_t data[QUIRC_MAX_PAYLOAD];
};
static inline int grid_bit(const struct quirc_code *code, int x, int y)
{
int p = y * code->size + x;
return (code->cell_bitmap[p >> 3] >> (p & 7)) & 1;
}
static quirc_decode_error_t read_format(const struct quirc_code *code,
struct quirc_data *data, int which)
{
int i;
uint16_t format = 0;
uint16_t fdata;
quirc_decode_error_t err;
if (which) {
for (i = 0; i < 7; i++)
format = (format << 1) |
grid_bit(code, 8, code->size - 1 - i);
for (i = 0; i < 8; i++)
format = (format << 1) |
grid_bit(code, code->size - 8 + i, 8);
} else {
static const int xs[15] = {
8, 8, 8, 8, 8, 8, 8, 8, 7, 5, 4, 3, 2, 1, 0
};
static const int ys[15] = {
0, 1, 2, 3, 4, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8
};
for (i = 14; i >= 0; i--)
format = (format << 1) | grid_bit(code, xs[i], ys[i]);
}
format ^= 0x5412;
err = correct_format(&format);
if (err)
return err;
fdata = format >> 10;
data->ecc_level = fdata >> 3;
data->mask = fdata & 7;
return QUIRC_SUCCESS;
}
static int mask_bit(int mask, int i, int j)
{
switch (mask) {
case 0: return !((i + j) % 2);
case 1: return !(i % 2);
case 2: return !(j % 3);
case 3: return !((i + j) % 3);
case 4: return !(((i / 2) + (j / 3)) % 2);
case 5: return !((i * j) % 2 + (i * j) % 3);
case 6: return !(((i * j) % 2 + (i * j) % 3) % 2);
case 7: return !(((i * j) % 3 + (i + j) % 2) % 2);
}
return 0;
}
static int reserved_cell(int version, int i, int j)
{
const struct quirc_version_info *ver = &quirc_version_db[version];
int size = version * 4 + 17;
int ai = -1, aj = -1, a;
/* Finder + format: top left */
if (i < 9 && j < 9)
return 1;
/* Finder + format: bottom left */
if (i + 8 >= size && j < 9)
return 1;
/* Finder + format: top right */
if (i < 9 && j + 8 >= size)
return 1;
/* Exclude timing patterns */
if (i == 6 || j == 6)
return 1;
/* Exclude version info, if it exists. Version info sits adjacent to
* the top-right and bottom-left finders in three rows, bounded by
* the timing pattern.
*/
if (version >= 7) {
if (i < 6 && j + 11 >= size)
return 1;
if (i + 11 >= size && j < 6)
return 1;
}
/* Exclude alignment patterns */
for (a = 0; a < QUIRC_MAX_ALIGNMENT && ver->apat[a]; a++) {
int p = ver->apat[a];
if (abs(p - i) < 3)
ai = a;
if (abs(p - j) < 3)
aj = a;
}
if (ai >= 0 && aj >= 0) {
a--;
if (ai > 0 && ai < a)
return 1;
if (aj > 0 && aj < a)
return 1;
if (aj == a && ai == a)
return 1;
}
return 0;
}
static void read_bit(const struct quirc_code *code,
struct quirc_data *data,
struct datastream *ds, int i, int j)
{
int bitpos = ds->data_bits & 7;
int bytepos = ds->data_bits >> 3;
int v = grid_bit(code, j, i);
if (mask_bit(data->mask, i, j))
v ^= 1;
if (v)
ds->raw[bytepos] |= (0x80 >> bitpos);
ds->data_bits++;
}
static void read_data(const struct quirc_code *code,
struct quirc_data *data,
struct datastream *ds)
{
int y = code->size - 1;
int x = code->size - 1;
int dir = -1;
while (x > 0) {
if (x == 6)
x--;
if (!reserved_cell(data->version, y, x))
read_bit(code, data, ds, y, x);
if (!reserved_cell(data->version, y, x - 1))
read_bit(code, data, ds, y, x - 1);
y += dir;
if (y < 0 || y >= code->size) {
dir = -dir;
x -= 2;
y += dir;
}
}
}
static quirc_decode_error_t codestream_ecc(struct quirc_data *data,
struct datastream *ds)
{
const struct quirc_version_info *ver =
&quirc_version_db[data->version];
const struct quirc_rs_params *sb_ecc = &ver->ecc[data->ecc_level];
struct quirc_rs_params lb_ecc;
int bc = ver->data_bytes / sb_ecc->bs;
int dst_offset = 0;
int lb_count = ver->data_bytes - bc * sb_ecc->bs;
int small_dw_total = bc * sb_ecc->dw;
int i;
memcpy(&lb_ecc, sb_ecc, sizeof(lb_ecc));
lb_ecc.dw++;
lb_ecc.bs++;
for (i = 0; i < bc; i++) {
uint8_t *dst = ds->data + dst_offset;
const struct quirc_rs_params *ecc = sb_ecc;
quirc_decode_error_t err;
int j = 0;
int k;
for (k = 0; k < sb_ecc->dw; k++)
dst[j++] = ds->raw[k * bc + i];
if (i + lb_count >= bc) {
dst[j++] = ds->raw[small_dw_total + i - lb_count];
ecc = &lb_ecc;
}
for (k = 0; k < sb_ecc->bs - sb_ecc->dw; k++)
dst[j++] = ds->raw[small_dw_total + lb_count + i +
k * bc];
err = correct_block(dst, ecc);
if (err)
return err;
dst_offset += ecc->dw;
}
ds->data_bits = dst_offset * 8;
return QUIRC_SUCCESS;
}
static int get_bits(uint8_t *data, int pos, int len)
{
int ret = 0;
while (len--) {
uint8_t b = data[pos >> 3];
int bitpos = pos & 7;
ret <<= 1;
if ((b << bitpos) & 0x80)
ret |= 1;
pos++;
}
return ret;
}
static quirc_decode_error_t decode_numeric(struct quirc_data *data,
struct datastream *ds)
{
int bits = 14;
int count;
int tuples;
int i;
if (data->version < 10)
bits = 10;
else if (data->version < 27)
bits = 12;
count = get_bits(ds->data, 4, bits);
if (count + 1 > QUIRC_MAX_PAYLOAD)
return QUIRC_ERROR_DATA_OVERFLOW;
tuples = (count + 2) / 3;
if (tuples * 10 + bits + 4 > ds->data_bits)
return QUIRC_ERROR_DATA_UNDERFLOW;
for (i = 0; i < tuples; i++) {
int d = get_bits(ds->data, i * 10 + bits + 4, 10);
int p = i * 3 + 2;
int j;
for (j = 0; j < 3; j++) {
if (p < count)
data->payload[p] = d % 10 + '0';
d /= 10;
p--;
}
}
data->payload[count] = 0;
data->payload_len = count;
return QUIRC_SUCCESS;
}
static quirc_decode_error_t decode_alpha(struct quirc_data *data,
struct datastream *ds)
{
int bits = 13;
int count;
int tuples;
int i;
if (data->version < 7)
bits = 9;
else if (data->version < 11)
bits = 10;
count = get_bits(ds->data, 4, bits);
if (count + 1 > QUIRC_MAX_PAYLOAD)
return QUIRC_ERROR_DATA_OVERFLOW;
tuples = (count + 1) / 2;
if (tuples * 11 + bits + 4 > ds->data_bits)
return QUIRC_ERROR_DATA_UNDERFLOW;
for (i = 0; i < tuples; i++) {
static const char *alpha_map =
"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ $%*+-./:";
int d = get_bits(ds->data, i * 11 + bits + 4, 11);
int p = i * 2;
if (p + 1 < count)
data->payload[p + 1] = alpha_map[d % 45];
data->payload[p] = alpha_map[(d / 45) % 45];
}
data->payload[count] = 0;
data->payload_len = count;
return QUIRC_SUCCESS;
}
static quirc_decode_error_t decode_byte(struct quirc_data *data,
struct datastream *ds)
{
int bits = 16;
int count;
int i;
if (data->version < 10)
bits = 8;
count = get_bits(ds->data, 4, bits);
if (count + 1 > QUIRC_MAX_PAYLOAD)
return QUIRC_ERROR_DATA_OVERFLOW;
if (count * 8 + bits + 4 > ds->data_bits)
return QUIRC_ERROR_DATA_UNDERFLOW;
for (i = 0; i < count; i++)
data->payload[i] = get_bits(ds->data, i * 8 + bits + 4, 8);
data->payload[count] = 0;
data->payload_len = count;
return QUIRC_SUCCESS;
}
static quirc_decode_error_t decode_kanji(struct quirc_data *data,
struct datastream *ds)
{
int bits = 12;
int count;
int i;
if (data->version < 10)
bits = 8;
else if (data->version < 27)
bits = 10;
count = get_bits(ds->data, 4, bits);
if (count * 2 + 1 > QUIRC_MAX_PAYLOAD)
return QUIRC_ERROR_DATA_OVERFLOW;
if (count * 13 + bits + 4 > ds->data_bits)
return QUIRC_ERROR_DATA_UNDERFLOW;
for (i = 0; i < count; i++) {
int d = get_bits(ds->data, i * 13 + bits + 4, 13);
uint16_t sjw;
if (d + 0x8140 >= 0x9ffc)
sjw = d + 0x8140;
else
sjw = d + 0xc140;
data->payload[i * 2] = sjw >> 8;
data->payload[i * 2 + 1] = sjw & 0xff;
}
data->payload[count * 2] = 0;
data->payload_len = count * 2;
return QUIRC_SUCCESS;
}
static quirc_decode_error_t decode_payload(struct quirc_data *data,
struct datastream *ds)
{
data->data_type = get_bits(ds->data, 0, 4);
switch (data->data_type) {
case QUIRC_DATA_TYPE_NUMERIC:
return decode_numeric(data, ds);
case QUIRC_DATA_TYPE_ALPHA:
return decode_alpha(data, ds);
case QUIRC_DATA_TYPE_BYTE:
return decode_byte(data, ds);
case QUIRC_DATA_TYPE_KANJI:
return decode_kanji(data, ds);
}
return QUIRC_ERROR_UNKNOWN_DATA_TYPE;
}
quirc_decode_error_t quirc_decode(const struct quirc_code *code,
struct quirc_data *data)
{
quirc_decode_error_t err;
struct datastream ds;
if ((code->size - 17) % 4)
return QUIRC_ERROR_INVALID_GRID_SIZE;
memset(data, 0, sizeof(*data));
memset(&ds, 0, sizeof(ds));
data->version = (code->size - 17) / 4;
if (data->version < 1 ||
data->version > QUIRC_MAX_VERSION)
return QUIRC_ERROR_INVALID_VERSION;
/* Read format information -- try both locations */
err = read_format(code, data, 0);
if (err)
err = read_format(code, data, 1);
if (err)
return err;
read_data(code, data, &ds);
err = codestream_ecc(data, &ds);
if (err)
return err;
err = decode_payload(data, &ds);
if (err)
return err;
return QUIRC_SUCCESS;
}

1120
lib/identify.c Normal file

File diff suppressed because it is too large Load diff

81
lib/quirc.c Normal file
View file

@ -0,0 +1,81 @@
/* 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)
{
if (q->image)
free(q->image);
free(q);
}
int quirc_resize(struct quirc *q, int w, int h)
{
uint8_t *new_image = realloc(q->image, w * h);
if (!new_image)
return -1;
q->image = new_image;
q->w = w;
q->h = h;
return 0;
}
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";
}

141
lib/quirc.h Normal file
View file

@ -0,0 +1,141 @@
/* 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 QUIRC_H_
#define QUIRC_H_
#include <stdint.h>
struct quirc;
/* Obtain the library version string. */
const char *quirc_version(void);
/* Construct a new QR-code recognizer. This function will return NULL
* if sufficient memory could not be allocated.
*/
struct quirc *quirc_new(void);
/* Destroy a QR-code recognizer. */
void quirc_destroy(struct quirc *q);
/* Resize the QR-code recognizer. The size of an image must be
* specified before codes can be analyzed.
*
* This function returns 0 on success, or -1 if sufficient memory could
* not be allocated.
*/
int quirc_resize(struct quirc *q, int w, int h);
/* These functions are used to process images for QR-code recognition.
* quirc_begin() must first be called to obtain access to a buffer into
* which the input image should be placed. Optionally, the current
* width and height may be returned.
*
* After filling the buffer, quirc_end() should be called to process
* the image for QR-code recognition. The locations and content of each
* code may be obtained using accessor functions described below.
*/
uint8_t *quirc_begin(struct quirc *q, int *w, int *h);
void quirc_end(struct quirc *q);
/* This structure describes a location in the input image buffer. */
struct quirc_point {
int x;
int y;
};
/* This enum describes the various decoder errors which may occur. */
typedef enum {
QUIRC_SUCCESS = 0,
QUIRC_ERROR_INVALID_GRID_SIZE,
QUIRC_ERROR_INVALID_VERSION,
QUIRC_ERROR_FORMAT_ECC,
QUIRC_ERROR_DATA_ECC,
QUIRC_ERROR_UNKNOWN_DATA_TYPE,
QUIRC_ERROR_DATA_OVERFLOW,
QUIRC_ERROR_DATA_UNDERFLOW
} quirc_decode_error_t;
/* Return a string error message for an error code. */
const char *quirc_strerror(quirc_decode_error_t err);
/* Limits on the maximum size of QR-codes and their content. */
#define QUIRC_MAX_BITMAP 3917
#define QUIRC_MAX_PAYLOAD 8896
/* QR-code ECC types. */
#define QUIRC_ECC_LEVEL_M 0
#define QUIRC_ECC_LEVEL_L 1
#define QUIRC_ECC_LEVEL_H 2
#define QUIRC_ECC_LEVEL_Q 3
/* QR-code data types. */
#define QUIRC_DATA_TYPE_NUMERIC 1
#define QUIRC_DATA_TYPE_ALPHA 2
#define QUIRC_DATA_TYPE_BYTE 4
#define QUIRC_DATA_TYPE_KANJI 8
/* This structure is used to return information about detected QR codes
* in the input image.
*/
struct quirc_code {
/* The four corners of the QR-code, from top left, clockwise */
struct quirc_point corners[4];
/* The number of cells across in the QR-code. The cell bitmap
* is a bitmask giving the actual values of cells. If the cell
* at (x, y) is black, then the following bit is set:
*
* cell_bitmap[i << 3] & (1 << (i & 7))
*
* where i = (y * size) + x.
*/
int size;
uint8_t cell_bitmap[QUIRC_MAX_BITMAP];
};
/* This structure holds the decoded QR-code data */
struct quirc_data {
/* Various parameters of the QR-code. These can mostly be
* ignored if you only care about the data.
*/
int version;
int ecc_level;
int mask;
int data_type;
/* Data payload. For the Kanji datatype, payload is encoded as
* Shift-JIS. For all other datatypes, payload is ASCII text.
*/
uint8_t payload[QUIRC_MAX_PAYLOAD];
int payload_len;
};
/* Return the number of QR-codes identified in the last processed
* image.
*/
int quirc_count(const struct quirc *q);
/* Extract the QR-code specified by the given index. */
void quirc_extract(const struct quirc *q, int index,
struct quirc_code *code);
/* Decode a QR-code, returning the payload data. */
quirc_decode_error_t quirc_decode(const struct quirc_code *code,
struct quirc_data *data);
#endif

103
lib/quirc_internal.h Normal file
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.
*/
#ifndef QUIRC_INTERNAL_H_
#define QUIRC_INTERNAL_H_
#include "quirc.h"
#define QUIRC_PIXEL_WHITE 0
#define QUIRC_PIXEL_BLACK 1
#define QUIRC_PIXEL_REGION 2
#define QUIRC_MAX_REGIONS 254
#define QUIRC_MAX_CAPSTONES 32
#define QUIRC_MAX_GRIDS 8
#define QUIRC_PERSPECTIVE_PARAMS 8
struct quirc_region {
struct quirc_point seed;
int count;
int capstone;
};
struct quirc_capstone {
int ring;
int stone;
struct quirc_point corners[4];
struct quirc_point center;
double c[QUIRC_PERSPECTIVE_PARAMS];
int qr_grid;
};
struct quirc_grid {
/* Capstone indices */
int caps[3];
/* Alignment pattern region and corner */
int align_region;
struct quirc_point align;
/* Timing pattern endpoints */
struct quirc_point tpep[3];
int hscan;
int vscan;
/* Grid size and perspective transform */
int grid_size;
double c[QUIRC_PERSPECTIVE_PARAMS];
};
struct quirc {
uint8_t *image;
int w;
int h;
int num_regions;
struct quirc_region regions[QUIRC_MAX_REGIONS];
int num_capstones;
struct quirc_capstone capstones[QUIRC_MAX_CAPSTONES];
int num_grids;
struct quirc_grid grids[QUIRC_MAX_GRIDS];
};
/************************************************************************
* QR-code version information database
*/
#define QUIRC_MAX_VERSION 40
#define QUIRC_MAX_ALIGNMENT 7
struct quirc_rs_params {
int bs; /* Block size */
int dw; /* Data words */
int ce; /* Correctable errors */
};
struct quirc_version_info {
int data_bytes;
int apat[QUIRC_MAX_ALIGNMENT];
struct quirc_rs_params ecc[4];
};
extern const struct quirc_version_info quirc_version_db[QUIRC_MAX_VERSION + 1];
#endif

421
lib/version_db.c Normal file
View file

@ -0,0 +1,421 @@
/* 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 "quirc_internal.h"
const struct quirc_version_info quirc_version_db[QUIRC_MAX_VERSION + 1] = {
{0},
{ /* Version 1 */
.data_bytes = 26,
.apat = {0},
.ecc = {
{.bs = 26, .dw = 16, .ce = 4},
{.bs = 26, .dw = 19, .ce = 2},
{.bs = 26, .dw = 9, .ce = 8},
{.bs = 26, .dw = 13, .ce = 6}
}
},
{ /* Version 2 */
.data_bytes = 44,
.apat = {6, 18, 0},
.ecc = {
{.bs = 44, .dw = 28, .ce = 8},
{.bs = 44, .dw = 34, .ce = 4},
{.bs = 44, .dw = 16, .ce = 14},
{.bs = 44, .dw = 22, .ce = 11}
}
},
{ /* Version 3 */
.data_bytes = 70,
.apat = {6, 22, 0},
.ecc = {
{.bs = 70, .dw = 44, .ce = 13},
{.bs = 70, .dw = 55, .ce = 7},
{.bs = 35, .dw = 13, .ce = 11},
{.bs = 35, .dw = 17, .ce = 9}
}
},
{ /* Version 4 */
.data_bytes = 100,
.apat = {6, 26, 0},
.ecc = {
{.bs = 50, .dw = 32, .ce = 9},
{.bs = 100, .dw = 80, .ce = 10},
{.bs = 25, .dw = 9, .ce = 8},
{.bs = 50, .dw = 24, .ce = 13}
}
},
{ /* Version 5 */
.data_bytes = 134,
.apat = {6, 30, 0},
.ecc = {
{.bs = 67, .dw = 43, .ce = 12},
{.bs = 134, .dw = 108, .ce = 13},
{.bs = 33, .dw = 11, .ce = 11},
{.bs = 33, .dw = 15, .ce = 9}
}
},
{ /* Version 6 */
.data_bytes = 172,
.apat = {6, 34, 0},
.ecc = {
{.bs = 43, .dw = 27, .ce = 8},
{.bs = 86, .dw = 68, .ce = 9},
{.bs = 43, .dw = 15, .ce = 14},
{.bs = 43, .dw = 19, .ce = 12}
}
},
{ /* Version 7 */
.data_bytes = 196,
.apat = {6, 22, 38, 0},
.ecc = {
{.bs = 49, .dw = 31, .ce = 9},
{.bs = 98, .dw = 78, .ce = 10},
{.bs = 39, .dw = 13, .ce = 13},
{.bs = 32, .dw = 14, .ce = 9}
}
},
{ /* Version 8 */
.data_bytes = 242,
.apat = {6, 24, 42, 0},
.ecc = {
{.bs = 60, .dw = 38, .ce = 11},
{.bs = 121, .dw = 97, .ce = 12},
{.bs = 40, .dw = 14, .ce = 13},
{.bs = 40, .dw = 18, .ce = 11}
}
},
{ /* Version 9 */
.data_bytes = 292,
.apat = {6, 26, 46, 0},
.ecc = {
{.bs = 58, .dw = 36, .ce = 11},
{.bs = 146, .dw = 116, .ce = 15},
{.bs = 36, .dw = 12, .ce = 12},
{.bs = 36, .dw = 16, .ce = 10}
}
},
{ /* Version 10 */
.data_bytes = 346,
.apat = {6, 28, 50, 0},
.ecc = {
{.bs = 69, .dw = 43, .ce = 13},
{.bs = 86, .dw = 68, .ce = 9},
{.bs = 43, .dw = 15, .ce = 14},
{.bs = 43, .dw = 19, .ce = 12}
}
},
{ /* Version 11 */
.data_bytes = 404,
.apat = {6, 30, 54, 0},
.ecc = {
{.bs = 80, .dw = 50, .ce = 15},
{.bs = 101, .dw = 81, .ce = 10},
{.bs = 36, .dw = 12, .ce = 12},
{.bs = 50, .dw = 22, .ce = 14}
}
},
{ /* Version 12 */
.data_bytes = 466,
.apat = {6, 32, 58, 0},
.ecc = {
{.bs = 58, .dw = 36, .ce = 11},
{.bs = 116, .dw = 92, .ce = 12},
{.bs = 42, .dw = 14, .ce = 14},
{.bs = 46, .dw = 20, .ce = 14}
}
},
{ /* Version 13 */
.data_bytes = 532,
.apat = {6, 34, 62, 0},
.ecc = {
{.bs = 59, .dw = 37, .ce = 11},
{.bs = 133, .dw = 107, .ce = 13},
{.bs = 33, .dw = 11, .ce = 11},
{.bs = 44, .dw = 20, .ce = 12}
}
},
{ /* Version 14 */
.data_bytes = 581,
.apat = {6, 26, 46, 66, 0},
.ecc = {
{.bs = 65, .dw = 41, .ce = 12},
{.bs = 109, .dw = 87, .ce = 11},
{.bs = 36, .dw = 12, .ce = 12},
{.bs = 54, .dw = 24, .ce = 15}
}
},
{ /* Version 15 */
.data_bytes = 655,
.apat = {6, 26, 48, 70, 0},
.ecc = {
{.bs = 65, .dw = 41, .ce = 12},
{.bs = 109, .dw = 87, .ce = 11},
{.bs = 36, .dw = 12, .ce = 12},
{.bs = 54, .dw = 24, .ce = 15}
}
},
{ /* Version 16 */
.data_bytes = 733,
.apat = {6, 26, 50, 74, 0},
.ecc = {
{.bs = 73, .dw = 45, .ce = 14},
{.bs = 122, .dw = 98, .ce = 12},
{.bs = 45, .dw = 15, .ce = 15},
{.bs = 43, .dw = 19, .ce = 12}
}
},
{ /* Version 17 */
.data_bytes = 815,
.apat = {6, 30, 54, 78, 0},
.ecc = {
{.bs = 74, .dw = 46, .ce = 14},
{.bs = 135, .dw = 107, .ce = 14},
{.bs = 42, .dw = 14, .ce = 14},
{.bs = 50, .dw = 22, .ce = 14}
}
},
{ /* Version 18 */
.data_bytes = 901,
.apat = {6, 30, 56, 82, 0},
.ecc = {
{.bs = 69, .dw = 43, .ce = 13},
{.bs = 150, .dw = 120, .ce = 15},
{.bs = 42, .dw = 14, .ce = 14},
{.bs = 50, .dw = 22, .ce = 14}
}
},
{ /* Version 19 */
.data_bytes = 991,
.apat = {6, 30, 58, 86, 0},
.ecc = {
{.bs = 70, .dw = 44, .ce = 13},
{.bs = 141, .dw = 113, .ce = 14},
{.bs = 39, .dw = 13, .ce = 13},
{.bs = 47, .dw = 21, .ce = 13}
}
},
{ /* Version 20 */
.data_bytes = 1085,
.apat = {6, 34, 62, 90, 0},
.ecc = {
{.bs = 67, .dw = 41, .ce = 13},
{.bs = 135, .dw = 107, .ce = 14},
{.bs = 43, .dw = 15, .ce = 14},
{.bs = 54, .dw = 24, .ce = 15}
}
},
{ /* Version 21 */
.data_bytes = 1156,
.apat = {6, 28, 50, 72, 92, 0},
.ecc = {
{.bs = 68, .dw = 42, .ce = 13},
{.bs = 144, .dw = 116, .ce = 14},
{.bs = 46, .dw = 16, .ce = 15},
{.bs = 50, .dw = 22, .ce = 14}
}
},
{ /* Version 22 */
.data_bytes = 1258,
.apat = {6, 26, 50, 74, 98, 0},
.ecc = {
{.bs = 74, .dw = 46, .ce = 14},
{.bs = 139, .dw = 111, .ce = 14},
{.bs = 37, .dw = 13, .ce = 12},
{.bs = 54, .dw = 24, .ce = 15}
}
},
{ /* Version 23 */
.data_bytes = 1364,
.apat = {6, 30, 54, 78, 102, 0},
.ecc = {
{.bs = 75, .dw = 47, .ce = 14},
{.bs = 151, .dw = 121, .ce = 15},
{.bs = 45, .dw = 15, .ce = 15},
{.bs = 54, .dw = 24, .ce = 15}
}
},
{ /* Version 24 */
.data_bytes = 1474,
.apat = {6, 28, 54, 80, 106, 0},
.ecc = {
{.bs = 73, .dw = 45, .ce = 14},
{.bs = 147, .dw = 117, .ce = 15},
{.bs = 46, .dw = 16, .ce = 15},
{.bs = 54, .dw = 24, .ce = 15}
}
},
{ /* Version 25 */
.data_bytes = 1588,
.apat = {6, 32, 58, 84, 110, 0},
.ecc = {
{.bs = 75, .dw = 47, .ce = 14},
{.bs = 132, .dw = 106, .ce = 13},
{.bs = 45, .dw = 15, .ce = 15},
{.bs = 54, .dw = 24, .ce = 15}
}
},
{ /* Version 26 */
.data_bytes = 1706,
.apat = {6, 30, 58, 86, 114, 0},
.ecc = {
{.bs = 74, .dw = 46, .ce = 14},
{.bs = 142, .dw = 114, .ce = 14},
{.bs = 46, .dw = 16, .ce = 15},
{.bs = 50, .dw = 22, .ce = 14}
}
},
{ /* Version 27 */
.data_bytes = 1828,
.apat = {6, 34, 62, 90, 118, 0},
.ecc = {
{.bs = 73, .dw = 45, .ce = 14},
{.bs = 152, .dw = 122, .ce = 15},
{.bs = 45, .dw = 15, .ce = 15},
{.bs = 53, .dw = 23, .ce = 15}
}
},
{ /* Version 28 */
.data_bytes = 1921,
.apat = {6, 26, 50, 74, 98, 122, 0},
.ecc = {
{.bs = 73, .dw = 45, .ce = 14},
{.bs = 147, .dw = 117, .ce = 15},
{.bs = 45, .dw = 15, .ce = 15},
{.bs = 54, .dw = 24, .ce = 15}
}
},
{ /* Version 29 */
.data_bytes = 2051,
.apat = {6, 30, 54, 78, 102, 126, 0},
.ecc = {
{.bs = 73, .dw = 45, .ce = 14},
{.bs = 146, .dw = 116, .ce = 15},
{.bs = 45, .dw = 15, .ce = 15},
{.bs = 73, .dw = 45, .ce = 14}
}
},
{ /* Version 30 */
.data_bytes = 2185,
.apat = {6, 26, 52, 78, 104, 130, 0},
.ecc = {
{.bs = 75, .dw = 47, .ce = 14},
{.bs = 145, .dw = 115, .ce = 15},
{.bs = 45, .dw = 15, .ce = 15},
{.bs = 54, .dw = 24, .ce = 15}
}
},
{ /* Version 31 */
.data_bytes = 2323,
.apat = {6, 30, 56, 82, 108, 134, 0},
.ecc = {
{.bs = 74, .dw = 46, .ce = 14},
{.bs = 145, .dw = 115, .ce = 15},
{.bs = 45, .dw = 15, .ce = 15},
{.bs = 54, .dw = 24, .ce = 15}
}
},
{ /* Version 32 */
.data_bytes = 2465,
.apat = {6, 34, 60, 86, 112, 138, 0},
.ecc = {
{.bs = 74, .dw = 46, .ce = 14},
{.bs = 145, .dw = 115, .ce = 15},
{.bs = 45, .dw = 15, .ce = 15},
{.bs = 54, .dw = 24, .ce = 15}
}
},
{ /* Version 33 */
.data_bytes = 2611,
.apat = {6, 30, 58, 96, 114, 142, 0},
.ecc = {
{.bs = 74, .dw = 46, .ce = 14},
{.bs = 145, .dw = 115, .ce = 15},
{.bs = 45, .dw = 15, .ce = 15},
{.bs = 54, .dw = 24, .ce = 15}
}
},
{ /* Version 34 */
.data_bytes = 2761,
.apat = {6, 34, 62, 90, 118, 146, 0},
.ecc = {
{.bs = 74, .dw = 46, .ce = 14},
{.bs = 145, .dw = 115, .ce = 15},
{.bs = 46, .dw = 16, .ce = 15},
{.bs = 54, .dw = 24, .ce = 15}
}
},
{ /* Version 35 */
.data_bytes = 2876,
.apat = {6, 30, 54, 78, 102, 126, 150},
.ecc = {
{.bs = 75, .dw = 47, .ce = 14},
{.bs = 151, .dw = 121, .ce = 15},
{.bs = 45, .dw = 15, .ce = 15},
{.bs = 54, .dw = 24, .ce = 15}
}
},
{ /* Version 36 */
.data_bytes = 3034,
.apat = {6, 24, 50, 76, 102, 128, 154},
.ecc = {
{.bs = 75, .dw = 47, .ce = 14},
{.bs = 151, .dw = 121, .ce = 15},
{.bs = 45, .dw = 15, .ce = 15},
{.bs = 54, .dw = 24, .ce = 15}
}
},
{ /* Version 37 */
.data_bytes = 3196,
.apat = {6, 28, 54, 80, 106, 132, 158},
.ecc = {
{.bs = 74, .dw = 46, .ce = 14},
{.bs = 152, .dw = 122, .ce = 15},
{.bs = 45, .dw = 15, .ce = 15},
{.bs = 54, .dw = 24, .ce = 15}
}
},
{ /* Version 38 */
.data_bytes = 3362,
.apat = {6, 32, 58, 84, 110, 136, 162},
.ecc = {
{.bs = 74, .dw = 46, .ce = 14},
{.bs = 152, .dw = 122, .ce = 15},
{.bs = 45, .dw = 15, .ce = 15},
{.bs = 54, .dw = 24, .ce = 15}
}
},
{ /* Version 39 */
.data_bytes = 3532,
.apat = {6, 26, 54, 82, 110, 138, 166},
.ecc = {
{.bs = 75, .dw = 47, .ce = 14},
{.bs = 147, .dw = 117, .ce = 15},
{.bs = 45, .dw = 15, .ce = 15},
{.bs = 54, .dw = 24, .ce = 15}
}
},
{ /* Version 40 */
.data_bytes = 3706,
.apat = {6, 30, 58, 86, 114, 142, 170},
.ecc = {
{.bs = 75, .dw = 47, .ce = 14},
{.bs = 148, .dw = 118, .ce = 15},
{.bs = 45, .dw = 15, .ce = 15},
{.bs = 54, .dw = 24, .ce = 15}
}
}
};

142
tests/dbgutil.c Normal file
View file

@ -0,0 +1,142 @@
/* 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 <stdio.h>
#include <setjmp.h>
#include <jpeglib.h>
#include "dbgutil.h"
void dump_data(const struct quirc_data *data)
{
printf(" Version: %d\n", data->version);
printf(" ECC level: %c\n", "MLHQ"[data->ecc_level]);
printf(" Mask: %d\n", data->mask);
printf(" Data type: %d\n", data->data_type);
printf(" Length: %d\n", data->payload_len);
printf(" Payload: %s\n", data->payload);
}
void dump_cells(const struct quirc_code *code)
{
int u, v;
printf(" %d cells, corners:", code->size);
for (u = 0; u < 4; u++)
printf(" (%d,%d)", code->corners[u].x,
code->corners[u].y);
printf("\n");
for (v = 0; v < code->size; v++) {
printf(" ");
for (u = 0; u < code->size; u++) {
int p = v * code->size + u;
if (code->cell_bitmap[p >> 3] & (1 << (p & 7)))
printf("[]");
else
printf(" ");
}
printf("\n");
}
}
struct my_jpeg_error {
struct jpeg_error_mgr base;
jmp_buf env;
};
static void my_output_message(struct jpeg_common_struct *com)
{
struct my_jpeg_error *err = (struct my_jpeg_error *)com->err;
char buf[JMSG_LENGTH_MAX];
err->base.format_message(com, buf);
fprintf(stderr, "JPEG error: %s", buf);
}
static void my_error_exit(struct jpeg_common_struct *com)
{
struct my_jpeg_error *err = (struct my_jpeg_error *)com->err;
my_output_message(com);
longjmp(err->env, 0);
}
static struct jpeg_error_mgr *my_error_mgr(struct my_jpeg_error *err)
{
jpeg_std_error(&err->base);
err->base.error_exit = my_error_exit;
err->base.output_message = my_output_message;
return &err->base;
}
int load_jpeg(struct quirc *q, const char *filename)
{
FILE *infile = fopen(filename, "rb");
struct jpeg_decompress_struct dinfo;
struct my_jpeg_error err;
uint8_t *image;
int y;
if (!infile) {
perror("can't open input file");
return -1;
}
memset(&dinfo, 0, sizeof(dinfo));
dinfo.err = my_error_mgr(&err);
if (setjmp(err.env))
goto fail;
jpeg_create_decompress(&dinfo);
jpeg_stdio_src(&dinfo, infile);
jpeg_read_header(&dinfo, TRUE);
dinfo.output_components = 1;
dinfo.out_color_space = JCS_GRAYSCALE;
jpeg_start_decompress(&dinfo);
if (dinfo.output_components != 1) {
fprintf(stderr, "Unexpected number of output components: %d",
dinfo.output_components);
goto fail;
}
if (quirc_resize(q, dinfo.output_width, dinfo.output_height) < 0)
goto fail;
image = quirc_begin(q, NULL, NULL);
for (y = 0; y < dinfo.output_height; y++) {
JSAMPROW row_pointer = image + y * dinfo.output_width;
jpeg_read_scanlines(&dinfo, &row_pointer, 1);
}
fclose(infile);
jpeg_finish_decompress(&dinfo);
jpeg_destroy_decompress(&dinfo);
return 0;
fail:
fclose(infile);
jpeg_destroy_decompress(&dinfo);
return -1;
}

35
tests/dbgutil.h Normal file
View file

@ -0,0 +1,35 @@
/* 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 DBGUTIL_H_
#define DBGUTIL_H_
#include "quirc.h"
/* Dump decoded information on stdout. */
void dump_data(const struct quirc_data *data);
/* Dump a grid cell map on stdout. */
void dump_cells(const struct quirc_code *code);
/* Read a JPEG image into the decoder.
*
* Note that you must call quirc_end() if the function returns
* successfully (0).
*/
int load_jpeg(struct quirc *q, const char *filename);
#endif

254
tests/inspect.c Normal file
View file

@ -0,0 +1,254 @@
/* 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 <string.h>
#include <SDL.h>
#include <SDL_gfxPrimitives.h>
#include "quirc_internal.h"
#include "dbgutil.h"
static void dump_info(struct quirc *q)
{
int count = quirc_count(q);
int i;
printf("%d QR-codes found:\n\n", count);
for (i = 0; i < count; i++) {
struct quirc_code code;
struct quirc_data data;
quirc_decode_error_t err;
quirc_extract(q, i, &code);
err = quirc_decode(&code, &data);
dump_cells(&code);
printf("\n");
if (err) {
printf(" Decoding FAILED: %s\n", quirc_strerror(err));
} else {
printf(" Decoding successful:\n");
dump_data(&data);
}
printf("\n");
}
}
static void draw_frame(SDL_Surface *screen, struct quirc *q)
{
uint8_t *pix;
uint8_t *raw = q->image;
int x, y;
SDL_LockSurface(screen);
pix = screen->pixels;
for (y = 0; y < q->h; y++) {
uint32_t *row = (uint32_t *)pix;
for (x = 0; x < q->w; x++) {
uint8_t v = *(raw++);
uint32_t color = (v << 16) | (v << 8) | v;
struct quirc_region *reg = &q->regions[v];
switch (v) {
case QUIRC_PIXEL_WHITE:
color = 0x00ffffff;
break;
case QUIRC_PIXEL_BLACK:
color = 0x00000000;
break;
default:
if (reg->capstone >= 0)
color = 0x00008000;
else
color = 0x00808080;
break;
}
*(row++) = color;
}
pix += screen->pitch;
}
SDL_UnlockSurface(screen);
}
static void draw_blob(SDL_Surface *screen, int x, int y)
{
int i, j;
for (i = -2; i <= 2; i++)
for (j = -2; j <= 2; j++)
pixelColor(screen, x + i, y + j, 0x0000ffff);
}
static void draw_mark(SDL_Surface *screen, int x, int y)
{
pixelColor(screen, x, y, 0xff0000ff);
pixelColor(screen, x + 1, y, 0xff0000ff);
pixelColor(screen, x - 1, y, 0xff0000ff);
pixelColor(screen, x, y + 1, 0xff0000ff);
pixelColor(screen, x, y - 1, 0xff0000ff);
}
static void draw_capstone(SDL_Surface *screen, struct quirc *q, int index)
{
struct quirc_capstone *cap = &q->capstones[index];
int j;
char buf[8];
for (j = 0; j < 4; j++) {
struct quirc_point *p0 = &cap->corners[j];
struct quirc_point *p1 = &cap->corners[(j + 1) % 4];
lineColor(screen, p0->x, p0->y, p1->x, p1->y,
0x800080ff);
}
draw_blob(screen, cap->corners[0].x, cap->corners[0].y);
if (cap->qr_grid < 0) {
snprintf(buf, sizeof(buf), "?%d", index);
stringColor(screen, cap->center.x, cap->center.y, buf,
0x000000ff);
}
}
static void perspective_map(const double *c,
double u, double v, struct quirc_point *ret)
{
double den = c[6]*u + c[7]*v + 1.0;
double x = (c[0]*u + c[1]*v + c[2]) / den;
double y = (c[3]*u + c[4]*v + c[5]) / den;
ret->x = rint(x);
ret->y = rint(y);
}
static void draw_grid(SDL_Surface *screen, struct quirc *q, int index)
{
struct quirc_grid *qr = &q->grids[index];
int x, y;
int i;
for (i = 0; i < 3; i++) {
struct quirc_capstone *cap = &q->capstones[qr->caps[i]];
char buf[8];
snprintf(buf, sizeof(buf), "%d.%c", index, "ABC"[i]);
stringColor(screen, cap->center.x, cap->center.y, buf,
0x000000ff);
}
lineColor(screen, qr->tpep[0].x, qr->tpep[0].y,
qr->tpep[1].x, qr->tpep[1].y, 0xff00ffff);
lineColor(screen, qr->tpep[1].x, qr->tpep[1].y,
qr->tpep[2].x, qr->tpep[2].y, 0xff00ffff);
if (qr->align_region >= 0)
draw_blob(screen, qr->align.x, qr->align.y);
for (y = 0; y < qr->grid_size; y++) {
for (x = 0; x < qr->grid_size; x++) {
double u = x + 0.5;
double v = y + 0.5;
struct quirc_point p;
perspective_map(qr->c, u, v, &p);
draw_mark(screen, p.x, p.y);
}
}
}
static int sdl_examine(struct quirc *q)
{
SDL_Surface *screen;
SDL_Event ev;
if (SDL_Init(SDL_INIT_VIDEO) < 0) {
fprintf(stderr, "couldn't init SDL: %s\n", SDL_GetError());
return -1;
}
screen = SDL_SetVideoMode(q->w, q->h, 32, SDL_SWSURFACE);
if (!screen) {
fprintf(stderr, "couldn't init video mode: %s\n",
SDL_GetError());
return -1;
}
while (SDL_WaitEvent(&ev) >= 0) {
int i;
if (ev.type == SDL_QUIT)
break;
if (ev.type == SDL_KEYDOWN &&
ev.key.keysym.sym == 'q')
break;
draw_frame(screen, q);
for (i = 0; i < q->num_capstones; i++)
draw_capstone(screen, q, i);
for (i = 0; i < q->num_grids; i++)
draw_grid(screen, q, i);
SDL_Flip(screen);
}
SDL_Quit();
return 0;
}
int main(int argc, char **argv)
{
struct quirc *q;
printf("quirc inspection program\n");
printf("Copyright (C) 2010-2012 Daniel Beer <dlbeer@gmail.com>\n");
printf("Library version: %s\n", quirc_version());
printf("\n");
if (argc < 2) {
fprintf(stderr, "Usage: %s <testfile.jpg>\n", argv[0]);
return -1;
}
q = quirc_new();
if (!q) {
perror("can't create quirc object");
return -1;
}
if (load_jpeg(q, argv[1]) < 0) {
quirc_destroy(q);
return -1;
}
quirc_end(q);
dump_info(q);
if (sdl_examine(q) < 0) {
quirc_destroy(q);
return -1;
}
quirc_destroy(q);
return 0;
}

287
tests/qrtest.c Normal file
View file

@ -0,0 +1,287 @@
/* 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 <errno.h>
#include <string.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <dirent.h>
#include <ctype.h>
#include <quirc.h>
#include <jpeglib.h>
#include <setjmp.h>
#include <time.h>
#include "dbgutil.h"
static int want_verbose = 0;
static int want_cell_dump = 0;
#define CLOCK_TO_MS(c) ((c) / (CLOCKS_PER_SEC / 1000))
static struct quirc *decoder;
struct result_info {
int file_count;
int id_count;
int decode_count;
clock_t load_time;
clock_t identify_time;
clock_t total_time;
};
static void print_result(const char *name, struct result_info *info)
{
puts("----------------------------------------"
"---------------------------------------");
printf("%s: %d files, %d codes, %d decoded (%d failures), "
"%d%% success rate\n",
name, info->file_count, info->id_count, info->decode_count,
(info->id_count - info->decode_count),
(info->decode_count * 100 + info->id_count / 2) /
info->id_count);
printf("Total time [load: %ld, identify: %ld, total: %ld]\n",
CLOCK_TO_MS(info->load_time),
CLOCK_TO_MS(info->identify_time),
CLOCK_TO_MS(info->total_time));
printf("Average time [load: %ld, identify: %ld, total: %ld]\n",
CLOCK_TO_MS(info->load_time / info->file_count),
CLOCK_TO_MS(info->identify_time / info->file_count),
CLOCK_TO_MS(info->total_time / info->file_count));
}
static void add_result(struct result_info *sum, struct result_info *inf)
{
sum->file_count += inf->file_count;
sum->id_count += inf->id_count;
sum->decode_count += inf->decode_count;
sum->load_time += inf->load_time;
sum->identify_time += inf->identify_time;
sum->total_time += inf->total_time;
}
static int scan_file(const char *path, const char *filename,
struct result_info *info)
{
int len = strlen(filename);
const char *ext;
clock_t start;
clock_t total_start;
int ret;
int i;
while (len >= 0 && filename[len] != '.')
len--;
ext = filename + len + 1;
if (!(toupper(ext[0] == 'j') && toupper(ext[1] == 'p') &&
(toupper(ext[2] == 'e') || toupper(ext[2] == 'g'))))
return 0;
total_start = start = clock();
ret = load_jpeg(decoder, path);
info->load_time = clock() - start;
if (ret < 0) {
fprintf(stderr, "%s: load_jpeg failed\n", filename);
return -1;
}
start = clock();
quirc_end(decoder);
info->identify_time = clock() - start;
info->id_count = quirc_count(decoder);
for (i = 0; i < info->id_count; i++) {
struct quirc_code code;
struct quirc_data data;
quirc_extract(decoder, i, &code);
if (!quirc_decode(&code, &data))
info->decode_count++;
}
info->total_time += clock() - total_start;
printf(" %-30s: %5ld %5ld %5ld %5d %5d\n", filename,
CLOCK_TO_MS(info->load_time),
CLOCK_TO_MS(info->identify_time),
CLOCK_TO_MS(info->total_time),
info->id_count, info->decode_count);
if (want_cell_dump || want_verbose) {
for (i = 0; i < info->id_count; i++) {
struct quirc_code code;
quirc_extract(decoder, i, &code);
if (want_cell_dump) {
dump_cells(&code);
printf("\n");
}
if (want_verbose) {
struct quirc_data data;
quirc_decode_error_t err =
quirc_decode(&code, &data);
if (err) {
printf(" ERROR: %s\n\n",
quirc_strerror(err));
} else {
printf(" Decode successful:\n");
dump_data(&data);
printf("\n");
}
}
}
}
info->file_count = 1;
return 1;
}
static int test_scan(const char *path, struct result_info *info);
static int scan_dir(const char *path, const char *filename,
struct result_info *info)
{
DIR *d = opendir(path);
struct dirent *ent;
int count = 0;
if (!d) {
fprintf(stderr, "%s: opendir: %s\n", path, strerror(errno));
return -1;
}
printf("%s:\n", path);
while ((ent = readdir(d))) {
if (ent->d_name[0] != '.') {
char fullpath[1024];
struct result_info sub;
snprintf(fullpath, sizeof(fullpath), "%s/%s",
path, ent->d_name);
if (test_scan(fullpath, &sub) > 0) {
add_result(info, &sub);
count++;
}
}
}
closedir(d);
if (count > 1) {
print_result(filename, info);
puts("");
}
return count > 0;
}
static int test_scan(const char *path, struct result_info *info)
{
int len = strlen(path);
struct stat st;
const char *filename;
memset(info, 0, sizeof(*info));
while (len >= 0 && path[len] != '/')
len--;
filename = path + len + 1;
if (lstat(path, &st) < 0) {
fprintf(stderr, "%s: lstat: %s\n", path, strerror(errno));
return -1;
}
if (S_ISREG(st.st_mode))
return scan_file(path, filename, info);
if (S_ISDIR(st.st_mode))
return scan_dir(path, filename, info);
return 0;
}
static int run_tests(int argc, char **argv)
{
struct result_info sum;
int count = 0;
int i;
decoder = quirc_new();
if (!decoder) {
perror("quirc_new");
return -1;
}
printf(" %-30s %17s %11s\n", "", "Time (ms)", "Count");
printf(" %-30s %5s %5s %5s %5s %5s\n",
"Filename", "Load", "ID", "Total", "ID", "Dec");
puts("----------------------------------------"
"---------------------------------------");
memset(&sum, 0, sizeof(sum));
for (i = 0; i < argc; i++) {
struct result_info info;
if (test_scan(argv[i], &info) > 0) {
add_result(&sum, &info);
count++;
}
}
if (count > 1)
print_result("TOTAL", &sum);
quirc_destroy(decoder);
return 0;
}
int main(int argc, char **argv)
{
int opt;
printf("quirc test program\n");
printf("Copyright (C) 2010-2012 Daniel Beer <dlbeer@gmail.com>\n");
printf("Library version: %s\n", quirc_version());
printf("\n");
while ((opt = getopt(argc, argv, "vd")) >= 0)
switch (opt) {
case 'v':
want_verbose = 1;
break;
case 'd':
want_cell_dump = 1;
break;
case '?':
return -1;
}
argv += optind;
argc -= optind;
return run_tests(argc, argv);;
}