ESP32_Project/ESP32QRReaderDBWriterVideoStreamer/identify.c
2024-02-07 11:25:05 +01:00

1287 lines
33 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/* 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 <limits.h>
#include <string.h>
#include <stdlib.h>
#include <math.h>
#include "fmath.h"
#include "collections.h"
#include "quirc_internal.h"
/************************************************************************
* Linear algebra routines
*/
static int line_intersect(const struct quirc_point *p0,
const struct quirc_point *p1,
const struct quirc_point *q0,
const struct quirc_point *q1,
struct quirc_point *r)
{
/* (a, b) is perpendicular to line p */
int a = -(p1->y - p0->y);
int b = p1->x - p0->x;
/* (c, d) is perpendicular to line q */
int c = -(q1->y - q0->y);
int d = q1->x - q0->x;
/* e and f are dot products of the respective vectors with p and q */
int e = a * p1->x + b * p1->y;
int f = c * q1->x + d * q1->y;
/* Now we need to solve:
* [a b] [rx] [e]
* [c d] [ry] = [f]
*
* We do this by inverting the matrix and applying it to (e, f):
* [ d -b] [e] [rx]
* 1/det [-c a] [f] = [ry]
*/
int det = (a * d) - (b * c);
if (!det)
return 0;
r->x = (d * e - b * f) / det;
r->y = (-c * e + a * f) / det;
return 1;
}
static void perspective_setup(float *c,
const struct quirc_point *rect,
float w, float h)
{
float x0 = rect[0].x;
float y0 = rect[0].y;
float x1 = rect[1].x;
float y1 = rect[1].y;
float x2 = rect[2].x;
float y2 = rect[2].y;
float x3 = rect[3].x;
float y3 = rect[3].y;
float wden = w * (x2 * y3 - x3 * y2 + (x3 - x2) * y1 + x1 * (y2 - y3));
float hden = h * (x2 * y3 + x1 * (y2 - y3) - x3 * y2 + (x3 - x2) * y1);
c[0] = (x1 * (x2 * y3 - x3 * y2) + x0 * (-x2 * y3 + x3 * y2 + (x2 - x3) * y1) +
x1 * (x3 - x2) * y0) /
wden;
c[1] = -(x0 * (x2 * y3 + x1 * (y2 - y3) - x2 * y1) - x1 * x3 * y2 + x2 * x3 * y1 + (x1 * x3 - x2 * x3) * y0) / hden;
c[2] = x0;
c[3] = (y0 * (x1 * (y3 - y2) - x2 * y3 + x3 * y2) + y1 * (x2 * y3 - x3 * y2) +
x0 * y1 * (y2 - y3)) /
wden;
c[4] = (x0 * (y1 * y3 - y2 * y3) + x1 * y2 * y3 - x2 * y1 * y3 +
y0 * (x3 * y2 - x1 * y2 + (x2 - x3) * y1)) /
hden;
c[5] = y0;
c[6] = (x1 * (y3 - y2) + x0 * (y2 - y3) + (x2 - x3) * y1 + (x3 - x2) * y0) / wden;
c[7] = (-x2 * y3 + x1 * y3 + x3 * y2 + x0 * (y1 - y2) - x3 * y1 + (x2 - x1) * y0) /
hden;
}
static void perspective_map(const float *c,
float u, float v, struct quirc_point *ret)
{
float den = c[6] * u + c[7] * v + 1.0;
float x = (c[0] * u + c[1] * v + c[2]) / den;
float y = (c[3] * u + c[4] * v + c[5]) / den;
ret->x = fast_roundf(x);
ret->y = fast_roundf(y);
}
static void perspective_unmap(const float *c,
const struct quirc_point *in,
float *u, float *v)
{
float x = in->x;
float y = in->y;
float den = -c[0] * c[7] * y + c[1] * c[6] * y + (c[3] * c[7] - c[4] * c[6]) * x +
c[0] * c[4] - c[1] * c[3];
*u = -(c[1] * (y - c[5]) - c[2] * c[7] * y + (c[5] * c[7] - c[4]) * x + c[2] * c[4]) /
den;
*v = (c[0] * (y - c[5]) - c[2] * c[6] * y + (c[5] * c[6] - c[3]) * x + c[2] * c[3]) /
den;
}
/************************************************************************
* Span-based floodfill routine
*/
typedef void (*span_func_t)(void *user_data, int y, int left, int right);
typedef struct xylf
{
int16_t x, y, l, r;
} __attribute__((aligned(8)))
xylf_t;
//计算该区域的面积from是像素颜色to是区块标号user_data是申请的区块结构体func是计算面积的函数
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)
{
(void)depth; // unused
lifo_t lifo;
size_t lifo_len;
lifo_alloc_all(&lifo, &lifo_len, sizeof(xylf_t));
//late in first out. 申请xylf_t的lifo一次申请完长度存储在lifo_len中
for (;;)
{
int left = x;
int right = x;
int 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);
for (;;)
{
if (lifo_size(&lifo) < lifo_len)
{ //栈中的数量
/* Seed new flood-fills */
if (y > 0)
{ //查找上一行有没有在同一区域的点
row = q->pixels + (y - 1) * q->w;
bool recurse = false;
for (i = left; i <= right; i++)
if (row[i] == from)
{ //相同区域,则入栈原来的区块
xylf_t context;
context.x = x;
context.y = y;
context.l = left;
context.r = right;
lifo_enqueue(&lifo, &context);
//mp_printf(&mp_plat_print, "#x=%x,y=%d;x1=%d,y1=%d\n",x,y,i,y-1);
x = i;
y = y - 1;
recurse = true;
break;
}
if (recurse)
break;
}
//查找下一行有没有在同一区域的点
if (y < q->h - 1)
{
row = q->pixels + (y + 1) * q->w;
bool recurse = false;
for (i = left; i <= right; i++)
if (row[i] == from)
{
xylf_t context;
context.x = x;
context.y = y;
context.l = left;
context.r = right;
lifo_enqueue(&lifo, &context);
//mp_printf(&mp_plat_print, "#x=%x,y=%d;x1=%d,y1=%d\n",x,y,i,y+1);
x = i;
y = y + 1;
recurse = true;
break;
}
if (recurse)
break;
}
}
if (!lifo_size(&lifo))
{
lifo_free(&lifo); //如果最起始为止就没找到,那么返回
return;
}
//本次迭代,往上,往下找边界(相同颜色像素点),直到找不到为止
//找到边界后,出栈上层像素点,回退回去
xylf_t context;
lifo_dequeue(&lifo, &context); //这里存疑如果都没有的话dequeue就会反向溢出吧。。
x = context.x;
y = context.y;
left = context.l;
right = context.r;
//mp_printf(&mp_plat_print, "#deq: x=%x,y=%d\n",x,y);
} //找到相同frombreak到这外面
}
}
/************************************************************************
* Adaptive thresholding
*/
#define THRESHOLD_S_MIN 1
#define THRESHOLD_S_DEN 8
#define THRESHOLD_T 5
static void threshold(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;
/*
* Ensure a sane, non-zero value for threshold_s.
*
* threshold_s can be zero if the image width is small. We need to avoid
* SIGFPE as it will be used as divisor.
*/
if (threshold_s < THRESHOLD_S_MIN)
threshold_s = THRESHOLD_S_MIN;
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;
}
}
static void area_count(void *user_data, int y, int left, int right)
{
((struct quirc_region *)user_data)->count += right - left + 1;
}
static int region_code(struct quirc *q, int x, int y)
{ //region指的是QRcode的区域成员为区域的坐标像素面积是否顶点
int pixel;
struct quirc_region *box;
int region;
if (x < 0 || y < 0 || x >= q->w || y >= q->h)
return -1;
pixel = q->pixels[y * q->w + x];
//预先判断非正常的像素情况,退出
if (pixel >= QUIRC_PIXEL_REGION)
return pixel;
if (pixel == QUIRC_PIXEL_WHITE)
return -1;
if (q->num_regions >= QUIRC_MAX_REGIONS)
return -1;
//新建一个区域
region = q->num_regions;
box = &q->regions[q->num_regions++];
memset(box, 0, sizeof(*box));
box->seed.x = x;
box->seed.y = y;
box->capstone = -1;
//计算该区域的面积
flood_fill_seed(q, x, y, pixel, region, area_count, box, 0);
return region;
}
struct polygon_score_data
{
struct quirc_point ref;
int scores[4];
struct quirc_point *corners;
} __attribute__((aligned(8)));
static void find_one_corner(void *user_data, int y, int left, int right)
{
struct polygon_score_data *psd =
(struct polygon_score_data *)user_data;
int xs[2] = {left, right};
int dy = y - psd->ref.y;
int i;
for (i = 0; i < 2; i++)
{
int dx = xs[i] - psd->ref.x;
int d = dx * dx + dy * dy;
if (d > psd->scores[0])
{
psd->scores[0] = d;
psd->corners[0].x = xs[i];
psd->corners[0].y = y;
}
}
}
static void find_other_corners(void *user_data, int y, int left, int right)
{
struct polygon_score_data *psd =
(struct polygon_score_data *)user_data;
int xs[2] = {left, right};
int i;
for (i = 0; i < 2; i++)
{
int up = xs[i] * psd->ref.x + y * psd->ref.y;
int right = xs[i] * -psd->ref.y + y * psd->ref.x;
int scores[4] = {up, right, -up, -right};
int j;
for (j = 0; j < 4; j++)
{
if (scores[j] > psd->scores[j])
{
psd->scores[j] = scores[j];
psd->corners[j].x = xs[i];
psd->corners[j].y = y;
}
}
}
}
static void find_region_corners(struct quirc *q,
int rcode, const struct quirc_point *ref,
struct quirc_point *corners)
{
struct quirc_region *region = &q->regions[rcode];
struct polygon_score_data psd;
int i;
memset(&psd, 0, sizeof(psd));
psd.corners = corners;
memcpy(&psd.ref, ref, sizeof(psd.ref));
psd.scores[0] = -1;
flood_fill_seed(q, region->seed.x, region->seed.y,
rcode, QUIRC_PIXEL_BLACK,
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;
for (i = 0; i < 4; i++)
memcpy(&psd.corners[i], &region->seed,
sizeof(psd.corners[i]));
i = region->seed.x * psd.ref.x + region->seed.y * psd.ref.y;
psd.scores[0] = i;
psd.scores[2] = -i;
i = region->seed.x * -psd.ref.y + region->seed.y * psd.ref.x;
psd.scores[1] = i;
psd.scores[3] = -i;
flood_fill_seed(q, region->seed.x, region->seed.y,
QUIRC_PIXEL_BLACK, rcode,
find_other_corners, &psd, 0);
}
static void record_capstone(struct quirc *q, int ring, int stone)
{
struct quirc_region *stone_reg = &q->regions[stone];
struct quirc_region *ring_reg = &q->regions[ring];
struct quirc_capstone *capstone;
int cs_index;
if (q->num_capstones >= QUIRC_MAX_CAPSTONES)
return;
cs_index = q->num_capstones;
capstone = &q->capstones[q->num_capstones++];
memset(capstone, 0, sizeof(*capstone));
capstone->qr_grid = -1;
capstone->ring = ring;
capstone->stone = stone;
stone_reg->capstone = cs_index;
ring_reg->capstone = cs_index;
/* Find the corners of the ring */
find_region_corners(q, ring, &stone_reg->seed, capstone->corners);
/* Set up the perspective transform and find the center */
perspective_setup(capstone->c, capstone->corners, 7.0, 7.0);
perspective_map(capstone->c, 3.5, 3.5, &capstone->center);
}
static void test_capstone(struct quirc *q, int x, int y, int *pb)
{
int ring_right = region_code(q, x - pb[4], y); //x-pb[4]是标记环右边的左侧
int stone = region_code(q, x - pb[4] - pb[3] - pb[2], y); //实心点左侧
int ring_left = region_code(q, x - pb[4] - pb[3] - pb[2] - pb[1] - pb[0], y); //环左侧
struct quirc_region *stone_reg;
struct quirc_region *ring_reg;
int ratio;
//以下检测顶点标记是否符合规范环称为ring中间称为stone
if (ring_left < 0 || ring_right < 0 || stone < 0)
return;
/* Left and ring of ring should be connected */
if (ring_left != ring_right)
return;
/* Ring should be disconnected from stone */
if (ring_left == stone)
return;
stone_reg = &q->regions[stone];
ring_reg = &q->regions[ring_left];
/* Already detected */
if (stone_reg->capstone >= 0 || ring_reg->capstone >= 0)
return;
/* Ratio should ideally be 37.5 中间实心点占面积比例应该在37.5%左右*/
ratio = stone_reg->count * 100 / ring_reg->count;
if (ratio < 10 || ratio > 70)
return;
record_capstone(q, ring_left, stone);
}
static void finder_scan(struct quirc *q, int y)
{
quirc_pixel_t *row = q->pixels + y * q->w;
int x;
int last_color = 0;
int run_length = 0;
int run_count = 0;
int pb[5]; //means QRcode's pixel width
memset(pb, 0, sizeof(pb));
for (x = 0; x < q->w; x++)
{
int color = row[x] ? 1 : 0;
if (x && color != last_color)
{ // color is different
memmove(pb, pb + 1, sizeof(pb[0]) * 4); //left move in one data
pb[4] = run_length; //run how many pix to get different color
run_length = 0;
run_count++; //get more than 5 time color jump
if (!color && run_count >= 5)
{ // find the marker of QRcode(three corner's marker)
static int check[5] = {1, 1, 3, 1, 1};
int avg, err;
int i;
int ok = 1;
avg = (pb[0] + pb[1] + pb[3] + pb[4]) / 4;
err = avg * 3 / 4;
for (i = 0; i < 5; i++)
if (pb[i] < check[i] * avg - err ||
pb[i] > check[i] * avg + err)
ok = 0;
if (ok)
test_capstone(q, x, y, pb);
}
}
run_length++;
last_color = color;
}
}
static void find_alignment_pattern(struct quirc *q, int index)
{
struct quirc_grid *qr = &q->grids[index];
struct quirc_capstone *c0 = &q->capstones[qr->caps[0]];
struct quirc_capstone *c2 = &q->capstones[qr->caps[2]];
struct quirc_point a;
struct quirc_point b;
struct quirc_point c;
int size_estimate;
int step_size = 1;
int dir = 0;
float u, v;
/* Grab our previous estimate of the alignment pattern corner */
memcpy(&b, &qr->align, sizeof(b));
/* Guess another two corners of the alignment pattern so that we
* can estimate its size.
*/
perspective_unmap(c0->c, &b, &u, &v);
perspective_map(c0->c, u, v + 1.0, &a);
perspective_unmap(c2->c, &b, &u, &v);
perspective_map(c2->c, u + 1.0, v, &c);
size_estimate = abs((a.x - b.x) * -(c.y - b.y) +
(a.y - b.y) * (c.x - b.x));
/* Spiral outwards from the estimate point until we find something
* roughly the right size. Don't look too far from the estimate
* point.
*/
while (step_size * step_size < size_estimate * 100)
{
static const int dx_map[] = {1, 0, -1, 0};
static const int dy_map[] = {0, -1, 0, 1};
int i;
for (i = 0; i < step_size; i++)
{
int code = region_code(q, b.x, b.y);
if (code >= 0)
{
struct quirc_region *reg = &q->regions[code];
if (reg->count >= size_estimate / 2 &&
reg->count <= size_estimate * 2)
{
qr->align_region = code;
return;
}
}
b.x += dx_map[dir];
b.y += dy_map[dir];
}
dir = (dir + 1) % 4;
if (!(dir & 1))
step_size++;
}
}
static void find_leftmost_to_line(void *user_data, int y, int left, int right)
{
struct polygon_score_data *psd =
(struct polygon_score_data *)user_data;
int xs[2] = {left, right};
int i;
for (i = 0; i < 2; i++)
{
int d = -psd->ref.y * xs[i] + psd->ref.x * y;
if (d < psd->scores[0])
{
psd->scores[0] = d;
psd->corners[0].x = xs[i];
psd->corners[0].y = y;
}
}
}
/* Do a Bresenham scan from one point to another and count the number
* of black/white transitions.
*/
static int timing_scan(const struct quirc *q,
const struct quirc_point *p0,
const struct quirc_point *p1)
{
int n = p1->x - p0->x;
int d = p1->y - p0->y;
int x = p0->x;
int y = p0->y;
int *dom, *nondom;
int dom_step;
int nondom_step;
int a = 0;
int i;
int run_length = 0;
int count = 0;
if (p0->x < 0 || p0->y < 0 || p0->x >= q->w || p0->y >= q->h)
return -1;
if (p1->x < 0 || p1->y < 0 || p1->x >= q->w || p1->y >= q->h)
return -1;
if (abs(n) > abs(d))
{
int swap = n;
n = d;
d = swap;
dom = &x;
nondom = &y;
}
else
{
dom = &y;
nondom = &x;
}
if (n < 0)
{
n = -n;
nondom_step = -1;
}
else
{
nondom_step = 1;
}
if (d < 0)
{
d = -d;
dom_step = -1;
}
else
{
dom_step = 1;
}
x = p0->x;
y = p0->y;
for (i = 0; i <= d; i++)
{
int pixel;
if (y < 0 || y >= q->h || x < 0 || x >= q->w)
break;
pixel = q->pixels[y * q->w + x];
if (pixel)
{
if (run_length >= 2)
count++;
run_length = 0;
}
else
{
run_length++;
}
a += n;
*dom += dom_step;
if (a >= d)
{
*nondom += nondom_step;
a -= d;
}
}
return count;
}
/* Try the measure the timing pattern for a given QR code. This does
* not require the global perspective to have been set up, but it
* does require that the capstone corners have been set to their
* canonical rotation.
*
* For each capstone, we find a point in the middle of the ring band
* which is nearest the centre of the code. Using these points, we do
* a horizontal and a vertical timing scan.
*/
static int measure_timing_pattern(struct quirc *q, int index)
{
struct quirc_grid *qr = &q->grids[index];
int i;
int scan;
int ver;
int size;
for (i = 0; i < 3; i++)
{
static const float us[] = {6.5, 6.5, 0.5};
static const float vs[] = {0.5, 6.5, 6.5};
struct quirc_capstone *cap = &q->capstones[qr->caps[i]];
perspective_map(cap->c, us[i], vs[i], &qr->tpep[i]);
}
qr->hscan = timing_scan(q, &qr->tpep[1], &qr->tpep[2]);
qr->vscan = timing_scan(q, &qr->tpep[1], &qr->tpep[0]);
scan = qr->hscan;
if (qr->vscan > scan)
scan = qr->vscan;
/* If neither scan worked, we can't go any further. */
if (scan < 0)
return -1;
/* Choose the nearest allowable grid size */
size = scan * 2 + 13;
ver = (size - 15) / 4;
qr->grid_size = ver * 4 + 17;
return 0;
}
/* Read a cell from a grid using the currently set perspective
* transform. Returns +/- 1 for black/white, 0 for cells which are
* out of image bounds.
*/
static int read_cell(const struct quirc *q, int index, int x, int y)
{
const struct quirc_grid *qr = &q->grids[index];
struct quirc_point p;
perspective_map(qr->c, x + 0.5, y + 0.5, &p);
if (p.y < 0 || p.y >= q->h || p.x < 0 || p.x >= q->w)
return 0;
return q->pixels[p.y * q->w + p.x] ? 1 : -1;
}
static int fitness_cell(const struct quirc *q, int index, int x, int y)
{
const struct quirc_grid *qr = &q->grids[index];
int score = 0;
int u, v;
for (v = 0; v < 3; v++)
for (u = 0; u < 3; u++)
{
static const float offsets[] = {0.3, 0.5, 0.7};
struct quirc_point p;
perspective_map(qr->c, x + offsets[u],
y + offsets[v], &p);
if (p.y < 0 || p.y >= q->h || p.x < 0 || p.x >= q->w)
continue;
if (q->pixels[p.y * q->w + p.x])
score++;
else
score--;
}
return score;
}
static int fitness_ring(const struct quirc *q, int index, int cx, int cy,
int radius)
{
int i;
int score = 0;
for (i = 0; i < radius * 2; i++)
{
score += fitness_cell(q, index, cx - radius + i, cy - radius);
score += fitness_cell(q, index, cx - radius, cy + radius - i);
score += fitness_cell(q, index, cx + radius, cy - radius + i);
score += fitness_cell(q, index, cx + radius - i, cy + radius);
}
return score;
}
static int fitness_apat(const struct quirc *q, int index, int cx, int cy)
{
return fitness_cell(q, index, cx, cy) -
fitness_ring(q, index, cx, cy, 1) +
fitness_ring(q, index, cx, cy, 2);
}
static int fitness_capstone(const struct quirc *q, int index, int x, int y)
{
x += 3;
y += 3;
return fitness_cell(q, index, x, y) +
fitness_ring(q, index, x, y, 1) -
fitness_ring(q, index, x, y, 2) +
fitness_ring(q, index, x, y, 3);
}
/* Compute a fitness score for the currently configured perspective
* transform, using the features we expect to find by scanning the
* grid.
*/
static int fitness_all(const struct quirc *q, int index)
{
const struct quirc_grid *qr = &q->grids[index];
int version = (qr->grid_size - 17) / 4;
const struct quirc_version_info *info = &quirc_version_db[version];
int score = 0;
int i, j;
int ap_count;
/* Check the timing pattern */
for (i = 0; i < qr->grid_size - 14; i++)
{
int expect = (i & 1) ? 1 : -1;
score += fitness_cell(q, index, i + 7, 6) * expect;
score += fitness_cell(q, index, 6, i + 7) * expect;
}
/* Check capstones */
score += fitness_capstone(q, index, 0, 0);
score += fitness_capstone(q, index, qr->grid_size - 7, 0);
score += fitness_capstone(q, index, 0, qr->grid_size - 7);
if (version < 0 || version > QUIRC_MAX_VERSION)
return score;
/* Check alignment patterns */
ap_count = 0;
while ((ap_count < QUIRC_MAX_ALIGNMENT) && info->apat[ap_count])
ap_count++;
for (i = 1; i + 1 < ap_count; i++)
{
score += fitness_apat(q, index, 6, info->apat[i]);
score += fitness_apat(q, index, info->apat[i], 6);
}
for (i = 1; i < ap_count; i++)
for (j = 1; j < ap_count; j++)
score += fitness_apat(q, index,
info->apat[i], info->apat[j]);
//mp_printf(&mp_plat_print, "##score=%d\n",score);
return score;
}
static void jiggle_perspective(struct quirc *q, int index)
{
struct quirc_grid *qr = &q->grids[index];
int best = fitness_all(q, index);
int pass;
float adjustments[8];
int i;
for (i = 0; i < 8; i++)
adjustments[i] = qr->c[i] * 0.02;
for (pass = 0; pass < 5; pass++)
{
for (i = 0; i < 16; i++)
{
int j = i >> 1;
int test;
float old = qr->c[j];
float step = adjustments[j];
float new;
if (i & 1)
new = old + step;
else
new = old - step;
qr->c[j] = new;
test = fitness_all(q, index);
if (test > best)
best = test;
else
qr->c[j] = old;
}
for (i = 0; i < 8; i++)
adjustments[i] *= 0.5;
}
}
/* Once the capstones are in place and an alignment point has been
* chosen, we call this function to set up a grid-reading perspective
* transform.
*/
static void setup_qr_perspective(struct quirc *q, int index)
{
struct quirc_grid *qr = &q->grids[index];
struct quirc_point rect[4];
/* Set up the perspective map for reading the grid */
memcpy(&rect[0], &q->capstones[qr->caps[1]].corners[0],
sizeof(rect[0]));
memcpy(&rect[1], &q->capstones[qr->caps[2]].corners[0],
sizeof(rect[0]));
memcpy(&rect[2], &qr->align, sizeof(rect[0]));
memcpy(&rect[3], &q->capstones[qr->caps[0]].corners[0],
sizeof(rect[0]));
perspective_setup(qr->c, rect, qr->grid_size - 7, qr->grid_size - 7);
jiggle_perspective(q, index);
}
/* Rotate the capstone with so that corner 0 is the leftmost with respect
* to the given reference line.
*/
static void rotate_capstone(struct quirc_capstone *cap,
const struct quirc_point *h0,
const struct quirc_point *hd)
{
struct quirc_point copy[4];
int j;
int best = 0;
int best_score = 0;
for (j = 0; j < 4; j++)
{
struct quirc_point *p = &cap->corners[j];
int score = (p->x - h0->x) * -hd->y +
(p->y - h0->y) * hd->x;
if (!j || score < best_score)
{
best = j;
best_score = score;
}
}
/* Rotate the capstone */
for (j = 0; j < 4; j++)
memcpy(&copy[j], &cap->corners[(j + best) % 4],
sizeof(copy[j]));
memcpy(cap->corners, copy, sizeof(cap->corners));
perspective_setup(cap->c, cap->corners, 7.0, 7.0);
}
static void record_qr_grid(struct quirc *q, int a, int b, int c)
{
struct quirc_point h0, hd;
int i;
int qr_index;
struct quirc_grid *qr;
if (q->num_grids >= QUIRC_MAX_GRIDS)
return;
/* Construct the hypotenuse line from A to C. B should be to
* the left of this line.
*/
memcpy(&h0, &q->capstones[a].center, sizeof(h0));
hd.x = q->capstones[c].center.x - q->capstones[a].center.x;
hd.y = q->capstones[c].center.y - q->capstones[a].center.y;
/* Make sure A-B-C is clockwise */
if ((q->capstones[b].center.x - h0.x) * -hd.y +
(q->capstones[b].center.y - h0.y) * hd.x >
0)
{
int swap = a;
a = c;
c = swap;
hd.x = -hd.x;
hd.y = -hd.y;
}
/* Record the grid and its components */
qr_index = q->num_grids;
qr = &q->grids[q->num_grids++];
memset(qr, 0, sizeof(*qr));
qr->caps[0] = a;
qr->caps[1] = b;
qr->caps[2] = c;
qr->align_region = -1;
/* Rotate each capstone so that corner 0 is top-left with respect
* to the grid.
*/
for (i = 0; i < 3; i++)
{
struct quirc_capstone *cap = &q->capstones[qr->caps[i]];
rotate_capstone(cap, &h0, &hd);
cap->qr_grid = qr_index;
}
/* Check the timing pattern. This doesn't require a perspective
* transform.
*/
if (measure_timing_pattern(q, qr_index) < 0)
goto fail;
/* Make an estimate based for the alignment pattern based on extending
* lines from capstones A and C.
*/
if (!line_intersect(&q->capstones[a].corners[0],
&q->capstones[a].corners[1],
&q->capstones[c].corners[0],
&q->capstones[c].corners[3],
&qr->align))
goto fail;
/* On V2+ grids, we should use the alignment pattern. */
if (qr->grid_size > 21)
{
/* Try to find the actual location of the alignment pattern. */
find_alignment_pattern(q, qr_index);
/* Find the point of the alignment pattern closest to the
* top-left of the QR grid.
*/
if (qr->align_region >= 0)
{
struct polygon_score_data psd;
struct quirc_region *reg =
&q->regions[qr->align_region];
/* Start from some point inside the alignment pattern */
memcpy(&qr->align, &reg->seed, sizeof(qr->align));
memcpy(&psd.ref, &hd, sizeof(psd.ref));
psd.corners = &qr->align;
psd.scores[0] = -hd.y * qr->align.x +
hd.x * qr->align.y;
flood_fill_seed(q, reg->seed.x, reg->seed.y,
qr->align_region, QUIRC_PIXEL_BLACK,
NULL, NULL, 0);
flood_fill_seed(q, reg->seed.x, reg->seed.y,
QUIRC_PIXEL_BLACK, qr->align_region,
find_leftmost_to_line, &psd, 0);
}
}
setup_qr_perspective(q, qr_index);
return;
fail:
/* We've been unable to complete setup for this grid. Undo what we've
* recorded and pretend it never happened.
*/
for (i = 0; i < 3; i++)
q->capstones[qr->caps[i]].qr_grid = -1;
q->num_grids--;
}
struct neighbour
{
int index;
float distance;
} __attribute__((aligned(8)));
struct neighbour_list
{
struct neighbour n[QUIRC_MAX_CAPSTONES];
int count;
} __attribute__((aligned(8)));
static void test_neighbours(struct quirc *q, int i,
const struct neighbour_list *hlist,
const struct neighbour_list *vlist)
{
int j, k;
float best_score = 0.0;
int best_h = -1, best_v = -1;
/* Test each possible grouping */
for (j = 0; j < hlist->count; j++)
for (k = 0; k < vlist->count; k++)
{
const struct neighbour *hn = &hlist->n[j];
const struct neighbour *vn = &vlist->n[k];
float score = fast_fabsf(1.0 - hn->distance / vn->distance);
if (score > 2.5)
continue;
if (best_h < 0 || score < best_score)
{
best_h = hn->index;
best_v = vn->index;
best_score = score;
}
}
if (best_h < 0 || best_v < 0)
return;
record_qr_grid(q, best_h, i, best_v);
}
static void test_grouping(struct quirc *q, int i)
{
struct quirc_capstone *c1 = &q->capstones[i];
int j;
struct neighbour_list hlist;
struct neighbour_list vlist;
if (c1->qr_grid >= 0)
return;
hlist.count = 0;
vlist.count = 0;
/* Look for potential neighbours by examining the relative gradients
* from this capstone to others.
*/
for (j = 0; j < q->num_capstones; j++)
{
struct quirc_capstone *c2 = &q->capstones[j];
float u, v;
if (i == j || c2->qr_grid >= 0)
continue;
perspective_unmap(c1->c, &c2->center, &u, &v);
u = fast_fabsf(u - 3.5);
v = fast_fabsf(v - 3.5);
if (u < 0.2 * v)
{
struct neighbour *n = &hlist.n[hlist.count++];
n->index = j;
n->distance = v;
}
if (v < 0.2 * u)
{
struct neighbour *n = &vlist.n[vlist.count++];
n->index = j;
n->distance = u;
}
}
if (!(hlist.count && vlist.count))
return;
test_neighbours(q, i, &hlist, &vlist);
}
static void pixels_setup(struct quirc *q)
{
if (sizeof(*q->image) == sizeof(*q->pixels))
{
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 *quirc_begin(struct quirc *q, int *w, int *h)
{
q->num_regions = QUIRC_PIXEL_REGION;
q->num_capstones = 0;
q->num_grids = 0;
if (w)
*w = q->w;
if (h)
*h = q->h;
return q->image;
}
void quirc_end(struct quirc *q)
{
int i;
pixels_setup(q);
threshold(q);
for (i = 0; i < q->h; i++)
{
finder_scan(q, i);
}
for (i = 0; i < q->num_capstones; i++)
{
test_grouping(q, i);
}
}
void quirc_extract(const struct quirc *q, int index,
struct quirc_code *code)
{
const struct quirc_grid *qr = &q->grids[index];
int y;
int i = 0;
if (index < 0 || index > q->num_grids)
return;
memset(code, 0, sizeof(*code));
perspective_map(qr->c, 0.0, 0.0, &code->corners[0]);
perspective_map(qr->c, qr->grid_size, 0.0, &code->corners[1]);
perspective_map(qr->c, qr->grid_size, qr->grid_size,
&code->corners[2]);
perspective_map(qr->c, 0.0, qr->grid_size, &code->corners[3]);
code->size = qr->grid_size;
for (y = 0; y < qr->grid_size; y++)
{
int x;
for (x = 0; x < qr->grid_size; x++)
{
if (read_cell(q, index, x, y) > 0)
code->cell_bitmap[i >> 3] |= (1 << (i & 7));
i++;
}
}
}