Initial commit: version 1.0.
This commit is contained in:
commit
a3142bc6b4
26 changed files with 5265 additions and 0 deletions
10
.gitignore
vendored
Normal file
10
.gitignore
vendored
Normal file
|
@ -0,0 +1,10 @@
|
|||
*.o
|
||||
*.lo
|
||||
quirc-demo
|
||||
quirc-scanner
|
||||
qrtest
|
||||
inspect
|
||||
libquirc.a
|
||||
libquirc.so
|
||||
.*.swp
|
||||
*~
|
16
LICENSE
Normal file
16
LICENSE
Normal 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
93
Makefile
Normal 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
188
README
Normal 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
221
demo/camera.c
Normal 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
70
demo/camera.h
Normal 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
103
demo/convert.c
Normal 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
39
demo/convert.h
Normal 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
302
demo/demo.c
Normal 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
71
demo/demoutil.c
Normal 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
34
demo/demoutil.h
Normal 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
139
demo/dthash.c
Normal 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
53
demo/dthash.h
Normal 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
278
demo/mjpeg.c
Normal 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
54
demo/mjpeg.h
Normal 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
183
demo/scanner.c
Normal 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
827
lib/decode.c
Normal 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
1120
lib/identify.c
Normal file
File diff suppressed because it is too large
Load diff
81
lib/quirc.c
Normal file
81
lib/quirc.c
Normal 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
141
lib/quirc.h
Normal 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
103
lib/quirc_internal.h
Normal 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
421
lib/version_db.c
Normal 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
142
tests/dbgutil.c
Normal 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
35
tests/dbgutil.h
Normal 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
254
tests/inspect.c
Normal 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
287
tests/qrtest.c
Normal 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);;
|
||||
}
|
Loading…
Reference in a new issue