2012-05-04 02:58:42 +02:00
|
|
|
/* 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;
|
|
|
|
|
2016-08-15 00:28:24 +02:00
|
|
|
#define MS(ts) (unsigned int)((ts.tv_sec * 1000) + (ts.tv_nsec / 1000000))
|
2012-05-04 02:58:42 +02:00
|
|
|
|
|
|
|
static struct quirc *decoder;
|
|
|
|
|
|
|
|
struct result_info {
|
|
|
|
int file_count;
|
|
|
|
int id_count;
|
|
|
|
int decode_count;
|
|
|
|
|
2016-08-15 00:28:24 +02:00
|
|
|
unsigned int load_time;
|
|
|
|
unsigned int identify_time;
|
|
|
|
unsigned int total_time;
|
2012-05-04 02:58:42 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
static void print_result(const char *name, struct result_info *info)
|
|
|
|
{
|
|
|
|
puts("----------------------------------------"
|
|
|
|
"---------------------------------------");
|
2012-11-12 22:43:36 +01:00
|
|
|
printf("%s: %d files, %d codes, %d decoded (%d failures)",
|
2012-05-04 02:58:42 +02:00
|
|
|
name, info->file_count, info->id_count, info->decode_count,
|
2012-11-12 22:43:36 +01:00
|
|
|
(info->id_count - info->decode_count));
|
|
|
|
if (info->id_count)
|
|
|
|
printf(", %d%% success rate",
|
|
|
|
(info->decode_count * 100 + info->id_count / 2) /
|
|
|
|
info->id_count);
|
|
|
|
printf("\n");
|
2016-08-15 00:28:24 +02:00
|
|
|
printf("Total time [load: %u, identify: %u, total: %u]\n",
|
|
|
|
info->load_time,
|
|
|
|
info->identify_time,
|
|
|
|
info->total_time);
|
2012-11-12 22:43:36 +01:00
|
|
|
if (info->file_count)
|
2016-08-15 00:28:24 +02:00
|
|
|
printf("Average time [load: %u, identify: %u, total: %u]\n",
|
|
|
|
info->load_time / info->file_count,
|
|
|
|
info->identify_time / info->file_count,
|
|
|
|
info->total_time / info->file_count);
|
2012-05-04 02:58:42 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
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)
|
|
|
|
{
|
2016-07-12 09:34:11 +02:00
|
|
|
int (*loader)(struct quirc *, const char *);
|
2012-05-04 02:58:42 +02:00
|
|
|
int len = strlen(filename);
|
|
|
|
const char *ext;
|
2016-08-13 23:14:55 +02:00
|
|
|
struct timespec tp;
|
|
|
|
long start;
|
|
|
|
long total_start;
|
2012-05-04 02:58:42 +02:00
|
|
|
int ret;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
while (len >= 0 && filename[len] != '.')
|
|
|
|
len--;
|
|
|
|
ext = filename + len + 1;
|
2016-07-12 09:34:11 +02:00
|
|
|
if (strcasecmp(ext, "jpg") == 0 || strcasecmp(ext, "jpeg") == 0)
|
|
|
|
loader = load_jpeg;
|
|
|
|
else if (strcasecmp(ext, "png") == 0)
|
|
|
|
loader = load_png;
|
|
|
|
else
|
2012-05-04 02:58:42 +02:00
|
|
|
return 0;
|
|
|
|
|
2016-08-13 23:14:55 +02:00
|
|
|
(void)clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &tp);
|
2016-08-15 00:28:24 +02:00
|
|
|
total_start = start = MS(tp);
|
2016-07-12 09:34:11 +02:00
|
|
|
ret = loader(decoder, path);
|
2016-08-13 23:14:55 +02:00
|
|
|
(void)clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &tp);
|
2016-08-15 00:28:24 +02:00
|
|
|
info->load_time = MS(tp) - start;
|
2012-05-04 02:58:42 +02:00
|
|
|
|
|
|
|
if (ret < 0) {
|
2016-07-12 09:34:11 +02:00
|
|
|
fprintf(stderr, "%s: load failed\n", filename);
|
2012-05-04 02:58:42 +02:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2016-08-13 23:14:55 +02:00
|
|
|
(void)clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &tp);
|
2016-08-15 00:28:24 +02:00
|
|
|
start = MS(tp);
|
2012-05-04 02:58:42 +02:00
|
|
|
quirc_end(decoder);
|
2016-08-13 23:14:55 +02:00
|
|
|
(void)clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &tp);
|
2016-08-15 00:28:24 +02:00
|
|
|
info->identify_time = MS(tp) - start;
|
2012-05-04 02:58:42 +02:00
|
|
|
|
|
|
|
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++;
|
|
|
|
}
|
|
|
|
|
2016-08-13 23:14:55 +02:00
|
|
|
(void)clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &tp);
|
2016-08-15 00:28:24 +02:00
|
|
|
info->total_time += MS(tp) - total_start;
|
2012-05-04 02:58:42 +02:00
|
|
|
|
2016-08-15 00:28:24 +02:00
|
|
|
printf(" %-30s: %5u %5u %5u %5d %5d\n", filename,
|
|
|
|
info->load_time,
|
|
|
|
info->identify_time,
|
|
|
|
info->total_time,
|
2012-05-04 02:58:42 +02:00
|
|
|
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);;
|
|
|
|
}
|