Improve stack usage for flood filling
* Make flood filling logic iterative (vs recursive) I basically tried one-to-one conversions here to avoid mistakes. probably it has a room for later optimizations. * Use explicit malloc (vs variables on stack) to allocate the work area. * Estimate the amount of memory for the work area dynamically from the image size, instead of using a constant FLOOD_FILL_MAX_DEPTH, which is too big in the most cases.
This commit is contained in:
parent
c6fc67088a
commit
772cd3e73f
3 changed files with 119 additions and 35 deletions
117
lib/identify.c
117
lib/identify.c
|
@ -122,52 +122,99 @@ static void perspective_unmap(const double *c,
|
||||||
* Span-based floodfill routine
|
* Span-based floodfill routine
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#define FLOOD_FILL_MAX_DEPTH 4096
|
|
||||||
|
|
||||||
typedef void (*span_func_t)(void *user_data, int y, int left, int right);
|
typedef void (*span_func_t)(void *user_data, int y, int left, int right);
|
||||||
|
|
||||||
static void flood_fill_seed(struct quirc *q, int x, int y, int from, int to,
|
static void flood_fill_seed(struct quirc *q,
|
||||||
span_func_t func, void *user_data,
|
int x0, int y0,
|
||||||
int depth)
|
int from, int to,
|
||||||
|
span_func_t func, void *user_data)
|
||||||
{
|
{
|
||||||
int left = x;
|
struct quirc_flood_fill_vars *const stack = q->flood_fill_vars;
|
||||||
int right = x;
|
const size_t stack_size = q->num_flood_fill_vars;
|
||||||
|
const struct quirc_flood_fill_vars *const last_vars =
|
||||||
|
&stack[stack_size - 1];
|
||||||
|
|
||||||
|
struct quirc_flood_fill_vars *vars;
|
||||||
|
struct quirc_flood_fill_vars *next_vars;
|
||||||
int i;
|
int i;
|
||||||
quirc_pixel_t *row = q->pixels + y * q->w;
|
quirc_pixel_t *row;
|
||||||
|
|
||||||
if (depth >= FLOOD_FILL_MAX_DEPTH)
|
/* Set up the first context */
|
||||||
return;
|
next_vars = stack;
|
||||||
|
next_vars->x = x0;
|
||||||
|
next_vars->y = y0;
|
||||||
|
|
||||||
while (left > 0 && row[left - 1] == from)
|
call:
|
||||||
left--;
|
vars = next_vars;
|
||||||
|
vars->left = vars->x;
|
||||||
|
vars->right = vars->x;
|
||||||
|
|
||||||
while (right < q->w - 1 && row[right + 1] == from)
|
row = q->pixels + vars->y * q->w;
|
||||||
right++;
|
|
||||||
|
while (vars->left > 0 && row[vars->left - 1] == from)
|
||||||
|
vars->left--;
|
||||||
|
|
||||||
|
while (vars->right < q->w - 1 && row[vars->right + 1] == from)
|
||||||
|
vars->right++;
|
||||||
|
|
||||||
/* Fill the extent */
|
/* Fill the extent */
|
||||||
for (i = left; i <= right; i++)
|
for (i = vars->left; i <= vars->right; i++)
|
||||||
row[i] = to;
|
row[i] = to;
|
||||||
|
|
||||||
if (func)
|
if (func)
|
||||||
func(user_data, y, left, right);
|
func(user_data, vars->y, vars->left, vars->right);
|
||||||
|
|
||||||
/* Seed new flood-fills */
|
if (vars == last_vars) {
|
||||||
if (y > 0) {
|
return;
|
||||||
row = q->pixels + (y - 1) * q->w;
|
|
||||||
|
|
||||||
for (i = left; i <= right; i++)
|
|
||||||
if (row[i] == from)
|
|
||||||
flood_fill_seed(q, i, y - 1, from, to,
|
|
||||||
func, user_data, depth + 1);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (y < q->h - 1) {
|
/* Seed new flood-fills */
|
||||||
row = q->pixels + (y + 1) * q->w;
|
if (vars->y > 0) {
|
||||||
|
row = q->pixels + (vars->y - 1) * q->w;
|
||||||
|
|
||||||
for (i = left; i <= right; i++)
|
for (i = vars->left; i <= vars->right; i++)
|
||||||
if (row[i] == from)
|
if (row[i] == from) {
|
||||||
flood_fill_seed(q, i, y + 1, from, to,
|
/* Save the current context */
|
||||||
func, user_data, depth + 1);
|
vars->i = i;
|
||||||
|
vars->pc = 1;
|
||||||
|
|
||||||
|
/* Set up the next context */
|
||||||
|
next_vars = vars + 1;
|
||||||
|
next_vars->x = i;
|
||||||
|
next_vars->y = vars->y - 1;
|
||||||
|
goto call;
|
||||||
|
return_from_call1: ;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (vars->y < q->h - 1) {
|
||||||
|
row = q->pixels + (vars->y + 1) * q->w;
|
||||||
|
|
||||||
|
for (i = vars->left; i <= vars->right; i++)
|
||||||
|
if (row[i] == from) {
|
||||||
|
/* Save the current context */
|
||||||
|
vars->i = i;
|
||||||
|
vars->pc = 2;
|
||||||
|
|
||||||
|
/* Set up the next context */
|
||||||
|
next_vars = vars + 1;
|
||||||
|
next_vars->x = i;
|
||||||
|
next_vars->y = vars->y + 1;
|
||||||
|
goto call;
|
||||||
|
return_from_call2: ;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (vars > stack) {
|
||||||
|
/* Restore the previous context */
|
||||||
|
vars--;
|
||||||
|
i = vars->i;
|
||||||
|
if (vars->pc == 1) {
|
||||||
|
row = q->pixels + (vars->y - 1) * q->w;
|
||||||
|
goto return_from_call1;
|
||||||
|
}
|
||||||
|
row = q->pixels + (vars->y + 1) * q->w;
|
||||||
|
goto return_from_call2;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -260,7 +307,7 @@ static int region_code(struct quirc *q, int x, int y)
|
||||||
box->seed.y = y;
|
box->seed.y = y;
|
||||||
box->capstone = -1;
|
box->capstone = -1;
|
||||||
|
|
||||||
flood_fill_seed(q, x, y, pixel, region, area_count, box, 0);
|
flood_fill_seed(q, x, y, pixel, region, area_count, box);
|
||||||
|
|
||||||
return region;
|
return region;
|
||||||
}
|
}
|
||||||
|
@ -330,7 +377,7 @@ static void find_region_corners(struct quirc *q,
|
||||||
psd.scores[0] = -1;
|
psd.scores[0] = -1;
|
||||||
flood_fill_seed(q, region->seed.x, region->seed.y,
|
flood_fill_seed(q, region->seed.x, region->seed.y,
|
||||||
rcode, QUIRC_PIXEL_BLACK,
|
rcode, QUIRC_PIXEL_BLACK,
|
||||||
find_one_corner, &psd, 0);
|
find_one_corner, &psd);
|
||||||
|
|
||||||
psd.ref.x = psd.corners[0].x - psd.ref.x;
|
psd.ref.x = psd.corners[0].x - psd.ref.x;
|
||||||
psd.ref.y = psd.corners[0].y - psd.ref.y;
|
psd.ref.y = psd.corners[0].y - psd.ref.y;
|
||||||
|
@ -348,7 +395,7 @@ static void find_region_corners(struct quirc *q,
|
||||||
|
|
||||||
flood_fill_seed(q, region->seed.x, region->seed.y,
|
flood_fill_seed(q, region->seed.x, region->seed.y,
|
||||||
QUIRC_PIXEL_BLACK, rcode,
|
QUIRC_PIXEL_BLACK, rcode,
|
||||||
find_other_corners, &psd, 0);
|
find_other_corners, &psd);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void record_capstone(struct quirc *q, int ring, int stone)
|
static void record_capstone(struct quirc *q, int ring, int stone)
|
||||||
|
@ -966,10 +1013,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,
|
flood_fill_seed(q, reg->seed.x, reg->seed.y,
|
||||||
qr->align_region, QUIRC_PIXEL_BLACK,
|
qr->align_region, QUIRC_PIXEL_BLACK,
|
||||||
NULL, NULL, 0);
|
NULL, NULL);
|
||||||
flood_fill_seed(q, reg->seed.x, reg->seed.y,
|
flood_fill_seed(q, reg->seed.x, reg->seed.y,
|
||||||
QUIRC_PIXEL_BLACK, qr->align_region,
|
QUIRC_PIXEL_BLACK, qr->align_region,
|
||||||
find_leftmost_to_line, &psd, 0);
|
find_leftmost_to_line, &psd);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
23
lib/quirc.c
23
lib/quirc.c
|
@ -41,6 +41,7 @@ void quirc_destroy(struct quirc *q)
|
||||||
same size, so we need to be careful here to avoid a double free */
|
same size, so we need to be careful here to avoid a double free */
|
||||||
if (!QUIRC_PIXEL_ALIAS_IMAGE)
|
if (!QUIRC_PIXEL_ALIAS_IMAGE)
|
||||||
free(q->pixels);
|
free(q->pixels);
|
||||||
|
free(q->flood_fill_vars);
|
||||||
free(q);
|
free(q);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -48,6 +49,8 @@ int quirc_resize(struct quirc *q, int w, int h)
|
||||||
{
|
{
|
||||||
uint8_t *image = NULL;
|
uint8_t *image = NULL;
|
||||||
quirc_pixel_t *pixels = NULL;
|
quirc_pixel_t *pixels = NULL;
|
||||||
|
size_t num_vars;
|
||||||
|
struct quirc_flood_fill_vars *vars = NULL;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* XXX: w and h should be size_t (or at least unsigned) as negatives
|
* XXX: w and h should be size_t (or at least unsigned) as negatives
|
||||||
|
@ -86,6 +89,22 @@ int quirc_resize(struct quirc *q, int w, int h)
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* alloc the work area for the flood filling logic.
|
||||||
|
*
|
||||||
|
* the size was chosen with the following assumptions and observations:
|
||||||
|
*
|
||||||
|
* - rings are the regions which requires the biggest work area.
|
||||||
|
* - they consumes the most when they are rotated by about 45 degree.
|
||||||
|
* in that case, the necessary depth is about (2 * height_of_the_ring).
|
||||||
|
* - the maximum height of rings would be about 1/3 of the image height.
|
||||||
|
*/
|
||||||
|
|
||||||
|
num_vars = h * 2 / 3;
|
||||||
|
vars = malloc(sizeof(*vars) * num_vars);
|
||||||
|
if (!vars)
|
||||||
|
goto fail;
|
||||||
|
|
||||||
/* alloc succeeded, update `q` with the new size and buffers */
|
/* alloc succeeded, update `q` with the new size and buffers */
|
||||||
q->w = w;
|
q->w = w;
|
||||||
q->h = h;
|
q->h = h;
|
||||||
|
@ -95,12 +114,16 @@ int quirc_resize(struct quirc *q, int w, int h)
|
||||||
free(q->pixels);
|
free(q->pixels);
|
||||||
q->pixels = pixels;
|
q->pixels = pixels;
|
||||||
}
|
}
|
||||||
|
free(q->flood_fill_vars);
|
||||||
|
q->flood_fill_vars = vars;
|
||||||
|
q->num_flood_fill_vars = num_vars;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
/* NOTREACHED */
|
/* NOTREACHED */
|
||||||
fail:
|
fail:
|
||||||
free(image);
|
free(image);
|
||||||
free(pixels);
|
free(pixels);
|
||||||
|
free(vars);
|
||||||
|
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,6 +17,8 @@
|
||||||
#ifndef QUIRC_INTERNAL_H_
|
#ifndef QUIRC_INTERNAL_H_
|
||||||
#define QUIRC_INTERNAL_H_
|
#define QUIRC_INTERNAL_H_
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
#include "quirc.h"
|
#include "quirc.h"
|
||||||
|
|
||||||
#define QUIRC_PIXEL_WHITE 0
|
#define QUIRC_PIXEL_WHITE 0
|
||||||
|
@ -75,6 +77,15 @@ struct quirc_grid {
|
||||||
double c[QUIRC_PERSPECTIVE_PARAMS];
|
double c[QUIRC_PERSPECTIVE_PARAMS];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct quirc_flood_fill_vars {
|
||||||
|
int x;
|
||||||
|
int y;
|
||||||
|
int right;
|
||||||
|
int left;
|
||||||
|
int i;
|
||||||
|
int pc; /* caller id */
|
||||||
|
};
|
||||||
|
|
||||||
struct quirc {
|
struct quirc {
|
||||||
uint8_t *image;
|
uint8_t *image;
|
||||||
quirc_pixel_t *pixels;
|
quirc_pixel_t *pixels;
|
||||||
|
@ -89,6 +100,9 @@ struct quirc {
|
||||||
|
|
||||||
int num_grids;
|
int num_grids;
|
||||||
struct quirc_grid grids[QUIRC_MAX_GRIDS];
|
struct quirc_grid grids[QUIRC_MAX_GRIDS];
|
||||||
|
|
||||||
|
size_t num_flood_fill_vars;
|
||||||
|
struct quirc_flood_fill_vars *flood_fill_vars;
|
||||||
};
|
};
|
||||||
|
|
||||||
/************************************************************************
|
/************************************************************************
|
||||||
|
|
Loading…
Reference in a new issue