Updated quirc to v1.1 (2019); patched to remove compiler warnings
This commit is contained in:
@@ -14,6 +14,7 @@
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <limits.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <math.h>
|
||||
@@ -98,8 +99,8 @@ static void perspective_map(const double *c,
|
||||
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);
|
||||
ret->x = (int) rint(x);
|
||||
ret->y = (int) rint(y);
|
||||
}
|
||||
|
||||
static void perspective_unmap(const double *c,
|
||||
@@ -121,234 +122,113 @@ static void perspective_unmap(const double *c,
|
||||
* Span-based floodfill routine
|
||||
*/
|
||||
|
||||
#define FLOOD_FILL_MAX_DEPTH 4096
|
||||
|
||||
typedef void (*span_func_t)(void *user_data, int y, int left, int right);
|
||||
|
||||
#if 0 // recursive flood fill
|
||||
|
||||
#define FLOOD_FILL_MAX_DEPTH 4096
|
||||
|
||||
struct flood_fill_params{
|
||||
struct quirc *q;
|
||||
int from;
|
||||
int to;
|
||||
span_func_t func;
|
||||
void *user_data;
|
||||
};
|
||||
|
||||
static struct flood_fill_params ffp;
|
||||
|
||||
static void flood_fill_rec(int x, int y, int depth)
|
||||
static void flood_fill_seed(struct quirc *q, int x, int y, int from, int to,
|
||||
span_func_t func, void *user_data,
|
||||
int depth)
|
||||
{
|
||||
int left = x;
|
||||
int right = x;
|
||||
int i;
|
||||
quirc_pixel_t *row = ffp.q->pixels + y * ffp.q->w;
|
||||
quirc_pixel_t *row = q->pixels + y * q->w;
|
||||
|
||||
if (!depth)
|
||||
if (depth >= FLOOD_FILL_MAX_DEPTH)
|
||||
return;
|
||||
|
||||
while (left > 0 && row[left - 1] == ffp.from)
|
||||
while (left > 0 && row[left - 1] == from)
|
||||
left--;
|
||||
|
||||
while (right < ffp.q->w - 1 && row[right + 1] == ffp.from)
|
||||
while (right < q->w - 1 && row[right + 1] == from)
|
||||
right++;
|
||||
|
||||
/* Fill the extent */
|
||||
for (i = left; i <= right; i++)
|
||||
row[i] = ffp.to;
|
||||
row[i] = to;
|
||||
|
||||
if (ffp.func)
|
||||
ffp.func(ffp.user_data, y, left, right);
|
||||
if (func)
|
||||
func(user_data, y, left, right);
|
||||
|
||||
/* Seed new flood-fills */
|
||||
if (y > 0) {
|
||||
row = ffp.q->pixels + (y - 1) * ffp.q->w;
|
||||
row = q->pixels + (y - 1) * q->w;
|
||||
|
||||
for (i = left; i <= right; i++)
|
||||
if (row[i] == ffp.from)
|
||||
flood_fill_rec(i, y - 1, depth - 1);
|
||||
if (row[i] == from)
|
||||
flood_fill_seed(q, i, y - 1, from, to,
|
||||
func, user_data, depth + 1);
|
||||
}
|
||||
|
||||
if (y < ffp.q->h - 1) {
|
||||
row = ffp.q->pixels + (y + 1) * ffp.q->w;
|
||||
if (y < q->h - 1) {
|
||||
row = q->pixels + (y + 1) * q->w;
|
||||
|
||||
for (i = left; i <= right; i++)
|
||||
if (row[i] == ffp.from)
|
||||
flood_fill_rec(i, y + 1, depth - 1);
|
||||
if (row[i] == from)
|
||||
flood_fill_seed(q, i, y + 1, from, to,
|
||||
func, user_data, depth + 1);
|
||||
}
|
||||
}
|
||||
|
||||
static void flood_fill_seed(struct quirc *q, int x, int y, int from, int to,
|
||||
span_func_t func, void *user_data)
|
||||
{
|
||||
ffp.q = q;
|
||||
ffp.from = from;
|
||||
ffp.to = to;
|
||||
ffp.func = func;
|
||||
ffp.user_data = user_data;
|
||||
|
||||
flood_fill_rec(x, y, FLOOD_FILL_MAX_DEPTH);
|
||||
}
|
||||
|
||||
#else // stacked flood fill
|
||||
|
||||
#define FILL_STACK_CHUNK_SIZE 0x400
|
||||
|
||||
struct fill_stack_chunk{
|
||||
int x[FILL_STACK_CHUNK_SIZE];
|
||||
int y[FILL_STACK_CHUNK_SIZE];
|
||||
struct fill_stack_chunk *prev;
|
||||
};
|
||||
|
||||
struct fill_stack{
|
||||
struct fill_stack_chunk *last_chunk;
|
||||
int index;
|
||||
};
|
||||
|
||||
static void fill_stack_init(struct fill_stack *s){
|
||||
s->last_chunk = NULL;
|
||||
}
|
||||
|
||||
static int fill_stack_is_empty(struct fill_stack *s){
|
||||
return s->last_chunk == NULL;
|
||||
}
|
||||
|
||||
static void fill_stack_push(struct fill_stack *s, int x, int y){
|
||||
struct fill_stack_chunk *c;
|
||||
if(s->last_chunk != NULL && s->index < FILL_STACK_CHUNK_SIZE - 1){
|
||||
c = s->last_chunk;
|
||||
s->index++;
|
||||
}else{
|
||||
c = (struct fill_stack_chunk*)malloc(sizeof(struct fill_stack_chunk));
|
||||
if(c == NULL){
|
||||
return;
|
||||
}
|
||||
c->prev = s->last_chunk;
|
||||
s->last_chunk = c;
|
||||
s->index = 0;
|
||||
}
|
||||
c->x[s->index] = x;
|
||||
c->y[s->index] = y;
|
||||
}
|
||||
|
||||
static void fill_stack_pop(struct fill_stack *s, int *px, int *py){
|
||||
struct fill_stack_chunk *c = s->last_chunk;
|
||||
if(c == NULL){
|
||||
return;
|
||||
}
|
||||
*px = c->x[s->index];
|
||||
*py = c->y[s->index];
|
||||
if(s->index > 0){
|
||||
s->index--;
|
||||
}else{
|
||||
s->last_chunk = c->prev;
|
||||
s->index = FILL_STACK_CHUNK_SIZE - 1;
|
||||
free(c);
|
||||
}
|
||||
}
|
||||
|
||||
static void flood_fill_seed(struct quirc *q, int start_x, int start_y, int from, int to,
|
||||
span_func_t func, void *user_data)
|
||||
{
|
||||
struct fill_stack s;
|
||||
fill_stack_init(&s);
|
||||
fill_stack_push(&s, start_x, start_y);
|
||||
|
||||
do{
|
||||
int x = 0, y = 0;
|
||||
fill_stack_pop(&s, &x, &y);
|
||||
|
||||
int left = x, right = x, i;
|
||||
quirc_pixel_t *row = q->pixels + y * q->w;
|
||||
|
||||
while (left > 0 && row[left - 1] == from)
|
||||
left--;
|
||||
|
||||
while (right < q->w - 1 && row[right + 1] == from)
|
||||
right++;
|
||||
|
||||
/* Fill the extent */
|
||||
for (i = left; i <= right; i++)
|
||||
row[i] = to;
|
||||
|
||||
if (func)
|
||||
func(user_data, y, left, right);
|
||||
|
||||
/* Seed new flood-fills */
|
||||
if (y > 0) {
|
||||
row = q->pixels + (y - 1) * q->w;
|
||||
|
||||
for (i = left; i <= right; i++)
|
||||
if (row[i] == from)
|
||||
fill_stack_push(&s, i, y - 1);
|
||||
}
|
||||
|
||||
if (y < q->h - 1) {
|
||||
row = q->pixels + (y + 1) * q->w;
|
||||
|
||||
for (i = left; i <= right; i++)
|
||||
if (row[i] == from)
|
||||
fill_stack_push(&s, i, y + 1);
|
||||
}
|
||||
}while(!fill_stack_is_empty(&s));
|
||||
}
|
||||
#endif
|
||||
|
||||
/************************************************************************
|
||||
* Adaptive thresholding
|
||||
*/
|
||||
|
||||
#define THRESHOLD_S_DEN 8
|
||||
#define THRESHOLD_T 5
|
||||
|
||||
static void threshold(struct quirc *q)
|
||||
static uint8_t otsu(const struct quirc *q)
|
||||
{
|
||||
int x, y;
|
||||
int avg_w = 0;
|
||||
int avg_u = 0;
|
||||
int threshold_s = q->w / THRESHOLD_S_DEN;
|
||||
quirc_pixel_t *row = q->pixels;
|
||||
int numPixels = q->w * q->h;
|
||||
|
||||
for (y = 0; y < q->h; y++) {
|
||||
int row_average[q->w];
|
||||
|
||||
memset(row_average, 0, sizeof(row_average));
|
||||
|
||||
for (x = 0; x < q->w; x++) {
|
||||
int w, u;
|
||||
|
||||
if (y & 1) {
|
||||
w = x;
|
||||
u = q->w - 1 - x;
|
||||
} else {
|
||||
w = q->w - 1 - x;
|
||||
u = x;
|
||||
}
|
||||
|
||||
avg_w = (avg_w * (threshold_s - 1)) /
|
||||
threshold_s + row[w];
|
||||
avg_u = (avg_u * (threshold_s - 1)) /
|
||||
threshold_s + row[u];
|
||||
|
||||
row_average[w] += avg_w;
|
||||
row_average[u] += avg_u;
|
||||
}
|
||||
|
||||
for (x = 0; x < q->w; x++) {
|
||||
if (row[x] < row_average[x] *
|
||||
(100 - THRESHOLD_T) / (200 * threshold_s))
|
||||
row[x] = QUIRC_PIXEL_BLACK;
|
||||
else
|
||||
row[x] = QUIRC_PIXEL_WHITE;
|
||||
}
|
||||
|
||||
row += q->w;
|
||||
// Calculate histogram
|
||||
const int HISTOGRAM_SIZE = 256;
|
||||
unsigned int histogram[HISTOGRAM_SIZE];
|
||||
memset(histogram, 0, (HISTOGRAM_SIZE) * sizeof(unsigned int));
|
||||
uint8_t* ptr = q->image;
|
||||
int length = numPixels;
|
||||
while (length--) {
|
||||
uint8_t value = *ptr++;
|
||||
histogram[value]++;
|
||||
}
|
||||
|
||||
// Calculate weighted sum of histogram values
|
||||
int sum = 0;
|
||||
for (int i = 0; i < HISTOGRAM_SIZE; ++i) {
|
||||
sum += i * histogram[i];
|
||||
}
|
||||
|
||||
// Compute threshold
|
||||
int sumB = 0;
|
||||
int q1 = 0;
|
||||
double max = 0;
|
||||
uint8_t threshold = 0;
|
||||
for (int i = 0; i < HISTOGRAM_SIZE; ++i) {
|
||||
// Weighted background
|
||||
q1 += histogram[i];
|
||||
if (q1 == 0)
|
||||
continue;
|
||||
|
||||
// Weighted foreground
|
||||
const int q2 = numPixels - q1;
|
||||
if (q2 == 0)
|
||||
break;
|
||||
|
||||
sumB += i * histogram[i];
|
||||
const double m1 = (double)sumB / q1;
|
||||
const double m2 = ((double)sum - sumB) / q2;
|
||||
const double m1m2 = m1 - m2;
|
||||
const double variance = m1m2 * m1m2 * q1 * q2;
|
||||
if (variance >= max) {
|
||||
threshold = i;
|
||||
max = variance;
|
||||
}
|
||||
}
|
||||
|
||||
return threshold;
|
||||
}
|
||||
|
||||
static void area_count(void *user_data, int y, int left, int right)
|
||||
{
|
||||
(void)y;
|
||||
(void)y;
|
||||
((struct quirc_region *)user_data)->count += right - left + 1;
|
||||
}
|
||||
|
||||
@@ -381,7 +261,7 @@ static int region_code(struct quirc *q, int x, int y)
|
||||
box->seed.y = y;
|
||||
box->capstone = -1;
|
||||
|
||||
flood_fill_seed(q, x, y, pixel, region, area_count, box);
|
||||
flood_fill_seed(q, x, y, pixel, region, area_count, box, 0);
|
||||
|
||||
return region;
|
||||
}
|
||||
@@ -451,7 +331,7 @@ static void find_region_corners(struct quirc *q,
|
||||
psd.scores[0] = -1;
|
||||
flood_fill_seed(q, region->seed.x, region->seed.y,
|
||||
rcode, QUIRC_PIXEL_BLACK,
|
||||
find_one_corner, &psd);
|
||||
find_one_corner, &psd, 0);
|
||||
|
||||
psd.ref.x = psd.corners[0].x - psd.ref.x;
|
||||
psd.ref.y = psd.corners[0].y - psd.ref.y;
|
||||
@@ -469,7 +349,7 @@ static void find_region_corners(struct quirc *q,
|
||||
|
||||
flood_fill_seed(q, region->seed.x, region->seed.y,
|
||||
QUIRC_PIXEL_BLACK, rcode,
|
||||
find_other_corners, &psd);
|
||||
find_other_corners, &psd, 0);
|
||||
}
|
||||
|
||||
static void record_capstone(struct quirc *q, int ring, int stone)
|
||||
@@ -542,7 +422,7 @@ static void finder_scan(struct quirc *q, int y)
|
||||
{
|
||||
quirc_pixel_t *row = q->pixels + y * q->w;
|
||||
int x;
|
||||
int last_color;
|
||||
int last_color = 0;
|
||||
int run_length = 0;
|
||||
int run_count = 0;
|
||||
int pb[5];
|
||||
@@ -888,7 +768,7 @@ static int fitness_all(const struct quirc *q, int index)
|
||||
|
||||
/* Check alignment patterns */
|
||||
ap_count = 0;
|
||||
while (info->apat[ap_count])
|
||||
while ((ap_count < QUIRC_MAX_ALIGNMENT) && info->apat[ap_count])
|
||||
ap_count++;
|
||||
|
||||
for (i = 1; i + 1 < ap_count; i++) {
|
||||
@@ -974,7 +854,7 @@ static void rotate_capstone(struct quirc_capstone *cap,
|
||||
struct quirc_point copy[4];
|
||||
int j;
|
||||
int best = 0;
|
||||
int best_score = 0;
|
||||
int best_score = INT_MAX;
|
||||
|
||||
for (j = 0; j < 4; j++) {
|
||||
struct quirc_point *p = &cap->corners[j];
|
||||
@@ -1082,10 +962,10 @@ static void record_qr_grid(struct quirc *q, int a, int b, int c)
|
||||
|
||||
flood_fill_seed(q, reg->seed.x, reg->seed.y,
|
||||
qr->align_region, QUIRC_PIXEL_BLACK,
|
||||
NULL, NULL);
|
||||
NULL, NULL, 0);
|
||||
flood_fill_seed(q, reg->seed.x, reg->seed.y,
|
||||
QUIRC_PIXEL_BLACK, qr->align_region,
|
||||
find_leftmost_to_line, &psd);
|
||||
find_leftmost_to_line, &psd, 0);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1191,17 +1071,18 @@ static void test_grouping(struct quirc *q, int i)
|
||||
test_neighbours(q, i, &hlist, &vlist);
|
||||
}
|
||||
|
||||
static void pixels_setup(struct quirc *q)
|
||||
static void pixels_setup(struct quirc *q, uint8_t threshold)
|
||||
{
|
||||
if (sizeof(*q->image) == sizeof(*q->pixels)) {
|
||||
if (QUIRC_PIXEL_ALIAS_IMAGE) {
|
||||
q->pixels = (quirc_pixel_t *)q->image;
|
||||
} else {
|
||||
int x, y;
|
||||
for (y = 0; y < q->h; y++) {
|
||||
for (x = 0; x < q->w; x++) {
|
||||
q->pixels[y * q->w + x] = q->image[y * q->w + x];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t* source = q->image;
|
||||
quirc_pixel_t* dest = q->pixels;
|
||||
int length = q->w * q->h;
|
||||
while (length--) {
|
||||
uint8_t value = *source++;
|
||||
*dest++ = (value < threshold) ? QUIRC_PIXEL_BLACK : QUIRC_PIXEL_WHITE;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1223,8 +1104,8 @@ void quirc_end(struct quirc *q)
|
||||
{
|
||||
int i;
|
||||
|
||||
pixels_setup(q);
|
||||
threshold(q);
|
||||
uint8_t threshold = otsu(q);
|
||||
pixels_setup(q, threshold);
|
||||
|
||||
for (i = 0; i < q->h; i++)
|
||||
finder_scan(q, i);
|
||||
|
||||
Reference in New Issue
Block a user