QRCode optimizations (#687)

* QRCode optimizations

* Added fast RGB to gray macro
This commit is contained in:
Larry Bank 2020-02-04 21:31:59 +01:00 committed by GitHub
parent 10a7ac74df
commit 2c61d5ec11
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 78 additions and 36 deletions

View File

@ -560,6 +560,9 @@ float IMAGE_Y_RATIO = ((float) _source_rect->s.h) / ((float) _target_rect->s.h);
_row_ptr + ((_image->w + UINT32_T_MASK) >> UINT32_T_SHIFT); \ _row_ptr + ((_image->w + UINT32_T_MASK) >> UINT32_T_SHIFT); \
}) })
#define RGB565_TO_Y_FAST(pixel) \
(((pixel & 0x1f00) >> 5) + (pixel & 0xf8) + ((pixel & 0x7) << 6) + ((pixel & 0xe000) >> 10)) / 4;
#define IMAGE_GET_BINARY_PIXEL_FAST(row_ptr, x) \ #define IMAGE_GET_BINARY_PIXEL_FAST(row_ptr, x) \
({ \ ({ \
__typeof__ (row_ptr) _row_ptr = (row_ptr); \ __typeof__ (row_ptr) _row_ptr = (row_ptr); \

View File

@ -838,11 +838,34 @@ typedef struct xylf
} }
xylf_t; xylf_t;
static void lifo_enqueue_fast(lifo_t *ptr, void *data)
{
// we know the structure size is 8 bytes, so don't waste time calling memcpy
uint32_t *d = (uint32_t *)(ptr->data + (ptr->len * ptr->data_len));
uint32_t *s = (uint32_t *)data;
// memcpy(ptr->data + (ptr->len * ptr->data_len), data, ptr->data_len);
d[0] = s[0]; d[1] = s[1]; // copy 8 bytes
ptr->len += 1;
}
static void lifo_dequeue_fast(lifo_t *ptr, void *data)
{
// we know the structure size is 8 bytes, so don't waste time calling memcpy
uint32_t *s = (uint32_t *)(ptr->data + ((ptr->len-1) * ptr->data_len));
uint32_t *d = (uint32_t *)data;
// if (data) {
// memcpy(data, ptr->data + ((ptr->len - 1) * ptr->data_len), ptr->data_len);
// }
d[0] = s[0]; d[1] = s[1]; // copy 8 bytes
ptr->len -= 1;
}
static void flood_fill_seed(struct quirc *q, int x, int y, int from, int to, static void flood_fill_seed(struct quirc *q, int x, int y, int from, int to,
span_func_t func, void *user_data, span_func_t func, void *user_data,
int depth) int depth)
{ {
(void) depth; // unused (void) depth; // unused
uint8_t from8 = from, to8=to;
lifo_t lifo; lifo_t lifo;
size_t lifo_len; size_t lifo_len;
@ -854,34 +877,34 @@ static void flood_fill_seed(struct quirc *q, int x, int y, int from, int to,
int i; int i;
quirc_pixel_t *row = q->pixels + y * q->w; quirc_pixel_t *row = q->pixels + y * q->w;
while (left > 0 && row[left - 1] == from) while (left > 0 && row[left - 1] == from8)
left--; left--;
while (right < q->w - 1 && row[right + 1] == from) while (right < q->w - 1 && row[right + 1] == from8)
right++; right++;
/* Fill the extent */ /* Fill the extent */
for (i = left; i <= right; i++) for (i = left; i <= right; i++)
row[i] = to; row[i] = to8;
if (func) if (func)
func(user_data, y, left, right); func(user_data, y, left, right);
for(;;) { for(;;) {
if (lifo_size(&lifo) < lifo_len) { if (/*lifo_size(&lifo)*/ lifo.len < lifo_len) {
/* Seed new flood-fills */ /* Seed new flood-fills */
if (y > 0) { if (y > 0) {
row = q->pixels + (y - 1) * q->w; row = q->pixels + (y - 1) * q->w;
bool recurse = false; bool recurse = false;
for (i = left; i <= right; i++) for (i = left; i <= right; i++)
if (row[i] == from) { if (row[i] == from8) {
xylf_t context; xylf_t context;
context.x = x; context.x = x;
context.y = y; context.y = y;
context.l = left; context.l = left;
context.r = right; context.r = right;
lifo_enqueue(&lifo, &context); lifo_enqueue_fast(&lifo, &context);
x = i; x = i;
y = y - 1; y = y - 1;
recurse = true; recurse = true;
@ -896,13 +919,13 @@ static void flood_fill_seed(struct quirc *q, int x, int y, int from, int to,
bool recurse = false; bool recurse = false;
for (i = left; i <= right; i++) for (i = left; i <= right; i++)
if (row[i] == from) { if (row[i] == from8) {
xylf_t context; xylf_t context;
context.x = x; context.x = x;
context.y = y; context.y = y;
context.l = left; context.l = left;
context.r = right; context.r = right;
lifo_enqueue(&lifo, &context); lifo_enqueue_fast(&lifo, &context);
x = i; x = i;
y = y + 1; y = y + 1;
recurse = true; recurse = true;
@ -913,13 +936,13 @@ static void flood_fill_seed(struct quirc *q, int x, int y, int from, int to,
} }
} }
if (!lifo_size(&lifo)) { if (!lifo.len /*lifo_size(&lifo)*/) {
lifo_free(&lifo); lifo_free(&lifo);
return; return;
} }
xylf_t context; xylf_t context;
lifo_dequeue(&lifo, &context); lifo_dequeue_fast(&lifo, &context);
x = context.x; x = context.x;
y = context.y; y = context.y;
left = context.l; left = context.l;
@ -942,7 +965,9 @@ static void threshold(struct quirc *q)
int avg_w = 0; int avg_w = 0;
int avg_u = 0; int avg_u = 0;
int threshold_s = q->w / THRESHOLD_S_DEN; int threshold_s = q->w / THRESHOLD_S_DEN;
int fracmul, fracmul2;
quirc_pixel_t *row = q->pixels; quirc_pixel_t *row = q->pixels;
int width = q->w;
/* /*
* Ensure a sane, non-zero value for threshold_s. * Ensure a sane, non-zero value for threshold_s.
@ -953,42 +978,49 @@ static void threshold(struct quirc *q)
if (threshold_s < THRESHOLD_S_MIN) if (threshold_s < THRESHOLD_S_MIN)
threshold_s = THRESHOLD_S_MIN; threshold_s = THRESHOLD_S_MIN;
fracmul = (32768 * (threshold_s - 1)) / threshold_s; // to use multipy instead of divide (not too many bits or we'll overflow)
// to get the effect used below (a fraction of threshold_s-1/threshold_s
// The second constant is to reduce the averaged values to compare with the current pixel
fracmul2 = (0x100000 * (100 - THRESHOLD_T)) / (200 * threshold_s); // use as many bits as possible without overflowing
for (y = 0; y < q->h; y++) { for (y = 0; y < q->h; y++) {
int row_average[q->w]; int row_average[q->w];
memset(row_average, 0, sizeof(row_average)); memset(row_average, 0, sizeof(row_average));
for (x = 0; x < q->w; x++) { for (x = 0; x < width; x++) {
int w, u; int w, u;
if (y & 1) { if (y & 1) {
w = x; w = x;
u = q->w - 1 - x; u = width - 1 - x;
} else { } else {
w = q->w - 1 - x; w = width - 1 - x;
u = x; u = x;
} }
avg_w = (avg_w * (threshold_s - 1)) / // avg_w = (avg_w * (threshold_s - 1)) / threshold_s + row[w];
threshold_s + row[w]; // avg_u = (avg_u * (threshold_s - 1)) / threshold_s + row[u];
avg_u = (avg_u * (threshold_s - 1)) / // The original mul/div operation sought to reduce the average value by a small fraction (e.g. 1/79)
threshold_s + row[u]; // This mul/shift approximation achieves the same goal with only a small percentage difference
avg_w = ((avg_w * fracmul) >> 15) + row[w];
avg_u = ((avg_u * fracmul) >> 15) + row[u];
row_average[w] += avg_w; row_average[w] += avg_w;
row_average[u] += avg_u; row_average[u] += avg_u;
} }
for (x = 0; x < q->w; x++) { for (x = 0; x < width; x++) {
if (row[x] < row_average[x] * // if (row[x] < row_average[x] * (100 - THRESHOLD_T) / (200 * threshold_s))
(100 - THRESHOLD_T) / (200 * threshold_s)) if (row[x] < ((row_average[x] * fracmul2) >> 20))
row[x] = QUIRC_PIXEL_BLACK; row[x] = QUIRC_PIXEL_BLACK;
else else
row[x] = QUIRC_PIXEL_WHITE; row[x] = QUIRC_PIXEL_WHITE;
} }
row += q->w; row += width;
}
} }
} /* threshold() */
static void area_count(void *user_data, int y, int left, int right) static void area_count(void *user_data, int y, int left, int right)
{ {
@ -1146,20 +1178,24 @@ static void record_capstone(struct quirc *q, int ring, int stone)
static void test_capstone(struct quirc *q, int x, int y, int *pb) static void test_capstone(struct quirc *q, int x, int y, int *pb)
{ {
int ring_right = region_code(q, x - pb[4], y); int ring_right, ring_left, stone;
int stone = region_code(q, x - pb[4] - pb[3] - pb[2], y); ring_right = region_code(q, x - pb[4], y);
int ring_left = region_code(q, x - pb[4] - pb[3] - ring_left = region_code(q, x - pb[4] - pb[3] -
pb[2] - pb[1] - pb[0], pb[2] - pb[1] - pb[0],
y); y);
struct quirc_region *stone_reg; struct quirc_region *stone_reg;
struct quirc_region *ring_reg; struct quirc_region *ring_reg;
int ratio; int ratio;
if (ring_left < 0 || ring_right < 0 || stone < 0) if (ring_left < 0 || ring_right < 0)// || stone < 0)
return; return;
/* Left and ring of ring should be connected */ /* Left and ring of ring should be connected */
if (ring_left != ring_right) if (ring_left != ring_right) // <-- most of the time, it exits here
return;
stone = region_code(q, x - pb[4] - pb[3] - pb[2], y);
if (stone < 0)
return; return;
/* Ring should be disconnected from stone */ /* Ring should be disconnected from stone */
@ -1185,16 +1221,17 @@ static void finder_scan(struct quirc *q, int y)
{ {
quirc_pixel_t *row = q->pixels + y * q->w; quirc_pixel_t *row = q->pixels + y * q->w;
int x; int x;
int last_color = 0; uint8_t color, last_color;
int run_length = 0; int run_length = 1;
int run_count = 0; int run_count = 0;
int pb[5]; int pb[5];
memset(pb, 0, sizeof(pb)); memset(pb, 0, sizeof(pb));
for (x = 0; x < q->w; x++) { last_color = row[0];
int color = row[x] ? 1 : 0; for (x = 1; x < q->w; x++) {
color = row[x];
if (x && color != last_color) { if (/* x && */ color != last_color) {
memmove(pb, pb + 1, sizeof(pb[0]) * 4); memmove(pb, pb + 1, sizeof(pb[0]) * 4);
pb[4] = run_length; pb[4] = run_length;
run_length = 0; run_length = 0;
@ -2947,9 +2984,11 @@ void imlib_find_qrcodes(list_t *out, image_t *ptr, rectangle_t *roi)
case IMAGE_BPP_GRAYSCALE: { case IMAGE_BPP_GRAYSCALE: {
for (int y = roi->y, yy = roi->y + roi->h; y < yy; y++) { for (int y = roi->y, yy = roi->y + roi->h; y < yy; y++) {
uint8_t *row_ptr = IMAGE_COMPUTE_GRAYSCALE_PIXEL_ROW_PTR(ptr, y); uint8_t *row_ptr = IMAGE_COMPUTE_GRAYSCALE_PIXEL_ROW_PTR(ptr, y);
for (int x = roi->x, xx = roi->x + roi->w; x < xx; x++) { memcpy(grayscale_image, &row_ptr[roi->x], roi->w);
*(grayscale_image++) = IMAGE_GET_GRAYSCALE_PIXEL_FAST(row_ptr, x); grayscale_image += roi->w;
} // for (int x = roi->x, xx = roi->x + roi->w; x < xx; x++) {
// *(grayscale_image++) = IMAGE_GET_GRAYSCALE_PIXEL_FAST(row_ptr, x);
// }
} }
break; break;
} }
@ -2957,7 +2996,7 @@ void imlib_find_qrcodes(list_t *out, image_t *ptr, rectangle_t *roi)
for (int y = roi->y, yy = roi->y + roi->h; y < yy; y++) { for (int y = roi->y, yy = roi->y + roi->h; y < yy; y++) {
uint16_t *row_ptr = IMAGE_COMPUTE_RGB565_PIXEL_ROW_PTR(ptr, y); uint16_t *row_ptr = IMAGE_COMPUTE_RGB565_PIXEL_ROW_PTR(ptr, y);
for (int x = roi->x, xx = roi->x + roi->w; x < xx; x++) { for (int x = roi->x, xx = roi->x + roi->w; x < xx; x++) {
*(grayscale_image++) = COLOR_RGB565_TO_GRAYSCALE(IMAGE_GET_RGB565_PIXEL_FAST(row_ptr, x)); *(grayscale_image++) = RGB565_TO_Y_FAST(IMAGE_GET_RGB565_PIXEL_FAST(row_ptr, x));
} }
} }
break; break;