/* quirc - QR-code recognition library * Copyright (C) 2010-2012 Daniel Beer * * 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 #include #include #include #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); } //找到相同from,break到这外面 } } /************************************************************************ * 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], ®ion->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(©[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, ®->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++; } } }