A streamlined version (for embedded use) of Jeff Brown's ZBar library. Visit <http://zbar.sourceforge.net> for more details.

Dependents:   BarcodeReader_F103

Committer:
hudakz
Date:
Fri Jan 10 22:06:18 2020 +0000
Revision:
1:4f5c042a2d34
Parent:
0:e33621169e44
Streamlined barcode reader library.

Who changed what in which revision?

UserRevisionLine numberNew contents of line
hudakz 0:e33621169e44 1 /*------------------------------------------------------------------------
hudakz 0:e33621169e44 2 * Copyright 2007-2009 (c) Jeff Brown <spadix@users.sourceforge.net>
hudakz 0:e33621169e44 3 *
hudakz 0:e33621169e44 4 * This file is part of the ZBar Bar Code Reader.
hudakz 0:e33621169e44 5 *
hudakz 0:e33621169e44 6 * The ZBar Bar Code Reader is free software; you can redistribute it
hudakz 0:e33621169e44 7 * and/or modify it under the terms of the GNU Lesser Public License as
hudakz 0:e33621169e44 8 * published by the Free Software Foundation; either version 2.1 of
hudakz 0:e33621169e44 9 * the License, or (at your option) any later version.
hudakz 0:e33621169e44 10 *
hudakz 0:e33621169e44 11 * The ZBar Bar Code Reader is distributed in the hope that it will be
hudakz 0:e33621169e44 12 * useful, but WITHOUT ANY WARRANTY; without even the implied warranty
hudakz 0:e33621169e44 13 * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
hudakz 0:e33621169e44 14 * GNU Lesser Public License for more details.
hudakz 0:e33621169e44 15 *
hudakz 0:e33621169e44 16 * You should have received a copy of the GNU Lesser Public License
hudakz 0:e33621169e44 17 * along with the ZBar Bar Code Reader; if not, write to the Free
hudakz 0:e33621169e44 18 * Software Foundation, Inc., 51 Franklin St, Fifth Floor,
hudakz 0:e33621169e44 19 * Boston, MA 02110-1301 USA
hudakz 0:e33621169e44 20 *
hudakz 0:e33621169e44 21 * http://sourceforge.net/projects/zbar
hudakz 0:e33621169e44 22 *------------------------------------------------------------------------*/
hudakz 0:e33621169e44 23
hudakz 0:e33621169e44 24 #include <config.h>
hudakz 0:e33621169e44 25 #include <stdlib.h> /* malloc, free, abs */
hudakz 0:e33621169e44 26 #include <string.h> /* memset */
hudakz 0:e33621169e44 27
hudakz 0:e33621169e44 28 #include <zbar.h>
hudakz 0:e33621169e44 29 //#include "svg.h"
hudakz 0:e33621169e44 30
hudakz 0:e33621169e44 31 #ifdef DEBUG_SCANNER
hudakz 0:e33621169e44 32 # define DEBUG_LEVEL (DEBUG_SCANNER)
hudakz 0:e33621169e44 33 #endif
hudakz 0:e33621169e44 34 #include "debug.h"
hudakz 0:e33621169e44 35
hudakz 0:e33621169e44 36 #ifndef ZBAR_FIXED
hudakz 0:e33621169e44 37 # define ZBAR_FIXED 5
hudakz 0:e33621169e44 38 #endif
hudakz 0:e33621169e44 39 #define ROUND (1 << (ZBAR_FIXED - 1))
hudakz 0:e33621169e44 40
hudakz 0:e33621169e44 41 /* FIXME add runtime config API for these */
hudakz 0:e33621169e44 42 #ifndef ZBAR_SCANNER_THRESH_MIN
hudakz 0:e33621169e44 43 # define ZBAR_SCANNER_THRESH_MIN 4
hudakz 0:e33621169e44 44 #endif
hudakz 0:e33621169e44 45
hudakz 0:e33621169e44 46 #ifndef ZBAR_SCANNER_THRESH_INIT_WEIGHT
hudakz 0:e33621169e44 47 # define ZBAR_SCANNER_THRESH_INIT_WEIGHT .44
hudakz 0:e33621169e44 48 #endif
hudakz 0:e33621169e44 49 #define THRESH_INIT ((unsigned)((ZBAR_SCANNER_THRESH_INIT_WEIGHT \
hudakz 0:e33621169e44 50 * (1 << (ZBAR_FIXED + 1)) + 1) / 2))
hudakz 0:e33621169e44 51
hudakz 0:e33621169e44 52 #ifndef ZBAR_SCANNER_THRESH_FADE
hudakz 0:e33621169e44 53 # define ZBAR_SCANNER_THRESH_FADE 8
hudakz 0:e33621169e44 54 #endif
hudakz 0:e33621169e44 55
hudakz 0:e33621169e44 56 #ifndef ZBAR_SCANNER_EWMA_WEIGHT
hudakz 0:e33621169e44 57 # define ZBAR_SCANNER_EWMA_WEIGHT .78
hudakz 0:e33621169e44 58 #endif
hudakz 0:e33621169e44 59 #define EWMA_WEIGHT ((unsigned)((ZBAR_SCANNER_EWMA_WEIGHT \
hudakz 0:e33621169e44 60 * (1 << (ZBAR_FIXED + 1)) + 1) / 2))
hudakz 0:e33621169e44 61
hudakz 0:e33621169e44 62 /* scanner state */
hudakz 0:e33621169e44 63 struct zbar_scanner_s {
hudakz 0:e33621169e44 64 zbar_decoder_t *decoder; /* associated bar width decoder */
hudakz 0:e33621169e44 65 unsigned y1_min_thresh; /* minimum threshold */
hudakz 0:e33621169e44 66
hudakz 0:e33621169e44 67 unsigned x; /* relative scan position of next sample */
hudakz 0:e33621169e44 68 int y0[4]; /* short circular buffer of average intensities */
hudakz 0:e33621169e44 69
hudakz 0:e33621169e44 70 int y1_sign; /* slope at last crossing */
hudakz 0:e33621169e44 71 unsigned y1_thresh; /* current slope threshold */
hudakz 0:e33621169e44 72
hudakz 0:e33621169e44 73 unsigned cur_edge; /* interpolated position of tracking edge */
hudakz 0:e33621169e44 74 unsigned last_edge; /* interpolated position of last located edge */
hudakz 0:e33621169e44 75 unsigned width; /* last element width */
hudakz 0:e33621169e44 76 };
hudakz 0:e33621169e44 77
hudakz 0:e33621169e44 78 zbar_scanner_t *zbar_scanner_create (zbar_decoder_t *dcode)
hudakz 0:e33621169e44 79 {
hudakz 0:e33621169e44 80 zbar_scanner_t *scn = malloc(sizeof(zbar_scanner_t));
hudakz 0:e33621169e44 81 scn->decoder = dcode;
hudakz 0:e33621169e44 82 scn->y1_min_thresh = ZBAR_SCANNER_THRESH_MIN;
hudakz 0:e33621169e44 83 zbar_scanner_reset(scn);
hudakz 0:e33621169e44 84 return(scn);
hudakz 0:e33621169e44 85 }
hudakz 0:e33621169e44 86
hudakz 0:e33621169e44 87 void zbar_scanner_destroy (zbar_scanner_t *scn)
hudakz 0:e33621169e44 88 {
hudakz 0:e33621169e44 89 free(scn);
hudakz 0:e33621169e44 90 }
hudakz 0:e33621169e44 91
hudakz 0:e33621169e44 92 zbar_symbol_type_t zbar_scanner_reset (zbar_scanner_t *scn)
hudakz 0:e33621169e44 93 {
hudakz 0:e33621169e44 94 memset(&scn->x, 0, sizeof(zbar_scanner_t) + (void*)scn - (void*)&scn->x);
hudakz 0:e33621169e44 95 scn->y1_thresh = scn->y1_min_thresh;
hudakz 0:e33621169e44 96 if(scn->decoder)
hudakz 0:e33621169e44 97 zbar_decoder_reset(scn->decoder);
hudakz 0:e33621169e44 98 return(ZBAR_NONE);
hudakz 0:e33621169e44 99 }
hudakz 0:e33621169e44 100
hudakz 0:e33621169e44 101 unsigned zbar_scanner_get_width (const zbar_scanner_t *scn)
hudakz 0:e33621169e44 102 {
hudakz 0:e33621169e44 103 return(scn->width);
hudakz 0:e33621169e44 104 }
hudakz 0:e33621169e44 105
hudakz 0:e33621169e44 106 unsigned zbar_scanner_get_edge (const zbar_scanner_t *scn,
hudakz 0:e33621169e44 107 unsigned offset,
hudakz 0:e33621169e44 108 int prec)
hudakz 0:e33621169e44 109 {
hudakz 0:e33621169e44 110 unsigned edge = scn->last_edge - offset - (1 << ZBAR_FIXED) - ROUND;
hudakz 0:e33621169e44 111 prec = ZBAR_FIXED - prec;
hudakz 0:e33621169e44 112 if(prec > 0)
hudakz 0:e33621169e44 113 return(edge >> prec);
hudakz 0:e33621169e44 114 else if(!prec)
hudakz 0:e33621169e44 115 return(edge);
hudakz 0:e33621169e44 116 else
hudakz 0:e33621169e44 117 return(edge << -prec);
hudakz 0:e33621169e44 118 }
hudakz 0:e33621169e44 119
hudakz 0:e33621169e44 120 zbar_color_t zbar_scanner_get_color (const zbar_scanner_t *scn)
hudakz 0:e33621169e44 121 {
hudakz 0:e33621169e44 122 return((scn->y1_sign <= 0) ? ZBAR_SPACE : ZBAR_BAR);
hudakz 0:e33621169e44 123 }
hudakz 0:e33621169e44 124
hudakz 0:e33621169e44 125 static inline unsigned calc_thresh (zbar_scanner_t *scn)
hudakz 0:e33621169e44 126 {
hudakz 0:e33621169e44 127 /* threshold 1st to improve noise rejection */
hudakz 0:e33621169e44 128 unsigned thresh = scn->y1_thresh;
hudakz 0:e33621169e44 129 if((thresh <= scn->y1_min_thresh) || !scn->width) {
hudakz 0:e33621169e44 130 dprintf(1, " tmin=%d", scn->y1_min_thresh);
hudakz 0:e33621169e44 131 return(scn->y1_min_thresh);
hudakz 0:e33621169e44 132 }
hudakz 0:e33621169e44 133 /* slowly return threshold to min */
hudakz 0:e33621169e44 134 unsigned dx = (scn->x << ZBAR_FIXED) - scn->last_edge;
hudakz 0:e33621169e44 135 unsigned long t = thresh * dx;
hudakz 0:e33621169e44 136 t /= scn->width;
hudakz 0:e33621169e44 137 t /= ZBAR_SCANNER_THRESH_FADE;
hudakz 0:e33621169e44 138 dprintf(1, " thr=%d t=%ld x=%d last=%d.%d (%d)",
hudakz 0:e33621169e44 139 thresh, t, scn->x, scn->last_edge >> ZBAR_FIXED,
hudakz 0:e33621169e44 140 scn->last_edge & ((1 << ZBAR_FIXED) - 1), dx);
hudakz 0:e33621169e44 141 if(thresh > t) {
hudakz 0:e33621169e44 142 thresh -= t;
hudakz 0:e33621169e44 143 if(thresh > scn->y1_min_thresh)
hudakz 0:e33621169e44 144 return(thresh);
hudakz 0:e33621169e44 145 }
hudakz 0:e33621169e44 146 scn->y1_thresh = scn->y1_min_thresh;
hudakz 0:e33621169e44 147 return(scn->y1_min_thresh);
hudakz 0:e33621169e44 148 }
hudakz 0:e33621169e44 149
hudakz 0:e33621169e44 150 static inline zbar_symbol_type_t process_edge (zbar_scanner_t *scn,
hudakz 0:e33621169e44 151 int y1)
hudakz 0:e33621169e44 152 {
hudakz 0:e33621169e44 153 if(!scn->y1_sign)
hudakz 0:e33621169e44 154 scn->last_edge = scn->cur_edge = (1 << ZBAR_FIXED) + ROUND;
hudakz 0:e33621169e44 155 else if(!scn->last_edge)
hudakz 0:e33621169e44 156 scn->last_edge = scn->cur_edge;
hudakz 0:e33621169e44 157
hudakz 0:e33621169e44 158 scn->width = scn->cur_edge - scn->last_edge;
hudakz 0:e33621169e44 159 dprintf(1, " sgn=%d cur=%d.%d w=%d (%s)\n",
hudakz 0:e33621169e44 160 scn->y1_sign, scn->cur_edge >> ZBAR_FIXED,
hudakz 0:e33621169e44 161 scn->cur_edge & ((1 << ZBAR_FIXED) - 1), scn->width,
hudakz 0:e33621169e44 162 ((y1 > 0) ? "SPACE" : "BAR"));
hudakz 0:e33621169e44 163 scn->last_edge = scn->cur_edge;
hudakz 0:e33621169e44 164
hudakz 0:e33621169e44 165 #if DEBUG_SVG > 1
hudakz 0:e33621169e44 166 svg_path_moveto(SVG_ABS, scn->last_edge - (1 << ZBAR_FIXED) - ROUND, 0);
hudakz 0:e33621169e44 167 #endif
hudakz 0:e33621169e44 168
hudakz 0:e33621169e44 169 /* pass to decoder */
hudakz 0:e33621169e44 170 if(scn->decoder)
hudakz 0:e33621169e44 171 return(zbar_decode_width(scn->decoder, scn->width));
hudakz 0:e33621169e44 172 return(ZBAR_PARTIAL);
hudakz 0:e33621169e44 173 }
hudakz 0:e33621169e44 174
hudakz 0:e33621169e44 175 inline zbar_symbol_type_t zbar_scanner_flush (zbar_scanner_t *scn)
hudakz 0:e33621169e44 176 {
hudakz 0:e33621169e44 177 if(!scn->y1_sign)
hudakz 0:e33621169e44 178 return(ZBAR_NONE);
hudakz 0:e33621169e44 179
hudakz 0:e33621169e44 180 unsigned x = (scn->x << ZBAR_FIXED) + ROUND;
hudakz 0:e33621169e44 181
hudakz 0:e33621169e44 182 if(scn->cur_edge != x || scn->y1_sign > 0) {
hudakz 0:e33621169e44 183 dprintf(1, "flush0:");
hudakz 0:e33621169e44 184 zbar_symbol_type_t edge = process_edge(scn, -scn->y1_sign);
hudakz 0:e33621169e44 185 scn->cur_edge = x;
hudakz 0:e33621169e44 186 scn->y1_sign = -scn->y1_sign;
hudakz 0:e33621169e44 187 return(edge);
hudakz 0:e33621169e44 188 }
hudakz 0:e33621169e44 189
hudakz 0:e33621169e44 190 scn->y1_sign = scn->width = 0;
hudakz 0:e33621169e44 191 if(scn->decoder)
hudakz 0:e33621169e44 192 return(zbar_decode_width(scn->decoder, 0));
hudakz 0:e33621169e44 193 return(ZBAR_PARTIAL);
hudakz 0:e33621169e44 194 }
hudakz 0:e33621169e44 195
hudakz 0:e33621169e44 196 zbar_symbol_type_t zbar_scanner_new_scan (zbar_scanner_t *scn)
hudakz 0:e33621169e44 197 {
hudakz 0:e33621169e44 198 zbar_symbol_type_t edge = ZBAR_NONE;
hudakz 0:e33621169e44 199 while(scn->y1_sign) {
hudakz 0:e33621169e44 200 zbar_symbol_type_t tmp = zbar_scanner_flush(scn);
hudakz 0:e33621169e44 201 if(tmp < 0 || tmp > edge)
hudakz 0:e33621169e44 202 edge = tmp;
hudakz 0:e33621169e44 203 }
hudakz 0:e33621169e44 204
hudakz 0:e33621169e44 205 /* reset scanner and associated decoder */
hudakz 0:e33621169e44 206 memset(&scn->x, 0, sizeof(zbar_scanner_t) + (void*)scn - (void*)&scn->x);
hudakz 0:e33621169e44 207 scn->y1_thresh = scn->y1_min_thresh;
hudakz 0:e33621169e44 208 if(scn->decoder)
hudakz 0:e33621169e44 209 zbar_decoder_new_scan(scn->decoder);
hudakz 0:e33621169e44 210 return(edge);
hudakz 0:e33621169e44 211 }
hudakz 0:e33621169e44 212
hudakz 0:e33621169e44 213 zbar_symbol_type_t zbar_scan_y (zbar_scanner_t *scn,
hudakz 0:e33621169e44 214 int y)
hudakz 0:e33621169e44 215 {
hudakz 0:e33621169e44 216 /* FIXME calc and clip to max y range... */
hudakz 0:e33621169e44 217 /* retrieve short value history */
hudakz 0:e33621169e44 218 register int x = scn->x;
hudakz 0:e33621169e44 219 register int y0_1 = scn->y0[(x - 1) & 3];
hudakz 0:e33621169e44 220 register int y0_0 = y0_1;
hudakz 0:e33621169e44 221 if(x) {
hudakz 0:e33621169e44 222 /* update weighted moving average */
hudakz 0:e33621169e44 223 y0_0 += ((int)((y - y0_1) * EWMA_WEIGHT)) >> ZBAR_FIXED;
hudakz 0:e33621169e44 224 scn->y0[x & 3] = y0_0;
hudakz 0:e33621169e44 225 }
hudakz 0:e33621169e44 226 else
hudakz 0:e33621169e44 227 y0_0 = y0_1 = scn->y0[0] = scn->y0[1] = scn->y0[2] = scn->y0[3] = y;
hudakz 0:e33621169e44 228 register int y0_2 = scn->y0[(x - 2) & 3];
hudakz 0:e33621169e44 229 register int y0_3 = scn->y0[(x - 3) & 3];
hudakz 0:e33621169e44 230 /* 1st differential @ x-1 */
hudakz 0:e33621169e44 231 register int y1_1 = y0_1 - y0_2;
hudakz 0:e33621169e44 232 {
hudakz 0:e33621169e44 233 register int y1_2 = y0_2 - y0_3;
hudakz 0:e33621169e44 234 if((abs(y1_1) < abs(y1_2)) &&
hudakz 0:e33621169e44 235 ((y1_1 >= 0) == (y1_2 >= 0)))
hudakz 0:e33621169e44 236 y1_1 = y1_2;
hudakz 0:e33621169e44 237 }
hudakz 0:e33621169e44 238
hudakz 0:e33621169e44 239 /* 2nd differentials @ x-1 & x-2 */
hudakz 0:e33621169e44 240 register int y2_1 = y0_0 - (y0_1 * 2) + y0_2;
hudakz 0:e33621169e44 241 register int y2_2 = y0_1 - (y0_2 * 2) + y0_3;
hudakz 0:e33621169e44 242
hudakz 0:e33621169e44 243 dprintf(1, "scan: x=%d y=%d y0=%d y1=%d y2=%d",
hudakz 0:e33621169e44 244 x, y, y0_1, y1_1, y2_1);
hudakz 0:e33621169e44 245
hudakz 0:e33621169e44 246 zbar_symbol_type_t edge = ZBAR_NONE;
hudakz 0:e33621169e44 247 /* 2nd zero-crossing is 1st local min/max - could be edge */
hudakz 0:e33621169e44 248 if((!y2_1 ||
hudakz 0:e33621169e44 249 ((y2_1 > 0) ? y2_2 < 0 : y2_2 > 0)) &&
hudakz 0:e33621169e44 250 (calc_thresh(scn) <= abs(y1_1)))
hudakz 0:e33621169e44 251 {
hudakz 0:e33621169e44 252 /* check for 1st sign change */
hudakz 0:e33621169e44 253 char y1_rev = (scn->y1_sign > 0) ? y1_1 < 0 : y1_1 > 0;
hudakz 0:e33621169e44 254 if(y1_rev)
hudakz 0:e33621169e44 255 /* intensity change reversal - finalize previous edge */
hudakz 0:e33621169e44 256 edge = process_edge(scn, y1_1);
hudakz 0:e33621169e44 257
hudakz 0:e33621169e44 258 if(y1_rev || (abs(scn->y1_sign) < abs(y1_1))) {
hudakz 0:e33621169e44 259 scn->y1_sign = y1_1;
hudakz 0:e33621169e44 260
hudakz 0:e33621169e44 261 /* adaptive thresholding */
hudakz 0:e33621169e44 262 /* start at multiple of new min/max */
hudakz 0:e33621169e44 263 scn->y1_thresh = (abs(y1_1) * THRESH_INIT + ROUND) >> ZBAR_FIXED;
hudakz 0:e33621169e44 264 dprintf(1, "\tthr=%d", scn->y1_thresh);
hudakz 0:e33621169e44 265 if(scn->y1_thresh < scn->y1_min_thresh)
hudakz 0:e33621169e44 266 scn->y1_thresh = scn->y1_min_thresh;
hudakz 0:e33621169e44 267
hudakz 0:e33621169e44 268 /* update current edge */
hudakz 0:e33621169e44 269 int d = y2_1 - y2_2;
hudakz 0:e33621169e44 270 scn->cur_edge = 1 << ZBAR_FIXED;
hudakz 0:e33621169e44 271 if(!d)
hudakz 0:e33621169e44 272 scn->cur_edge >>= 1;
hudakz 0:e33621169e44 273 else if(y2_1)
hudakz 0:e33621169e44 274 /* interpolate zero crossing */
hudakz 0:e33621169e44 275 scn->cur_edge -= ((y2_1 << ZBAR_FIXED) + 1) / d;
hudakz 0:e33621169e44 276 scn->cur_edge += x << ZBAR_FIXED;
hudakz 0:e33621169e44 277 dprintf(1, "\n");
hudakz 0:e33621169e44 278 }
hudakz 0:e33621169e44 279 }
hudakz 0:e33621169e44 280 else
hudakz 0:e33621169e44 281 dprintf(1, "\n");
hudakz 0:e33621169e44 282 /* FIXME add fall-thru pass to decoder after heuristic "idle" period
hudakz 0:e33621169e44 283 (eg, 6-8 * last width) */
hudakz 0:e33621169e44 284 scn->x = x + 1;
hudakz 0:e33621169e44 285 return(edge);
hudakz 0:e33621169e44 286 }
hudakz 0:e33621169e44 287
hudakz 0:e33621169e44 288 /* undocumented API for drawing cutesy debug graphics */
hudakz 0:e33621169e44 289 void zbar_scanner_get_state (const zbar_scanner_t *scn,
hudakz 0:e33621169e44 290 unsigned *x,
hudakz 0:e33621169e44 291 unsigned *cur_edge,
hudakz 0:e33621169e44 292 unsigned *last_edge,
hudakz 0:e33621169e44 293 int *y0,
hudakz 0:e33621169e44 294 int *y1,
hudakz 0:e33621169e44 295 int *y2,
hudakz 0:e33621169e44 296 int *y1_thresh)
hudakz 0:e33621169e44 297 {
hudakz 0:e33621169e44 298 register int y0_0 = scn->y0[(scn->x - 1) & 3];
hudakz 0:e33621169e44 299 register int y0_1 = scn->y0[(scn->x - 2) & 3];
hudakz 0:e33621169e44 300 register int y0_2 = scn->y0[(scn->x - 3) & 3];
hudakz 0:e33621169e44 301 if(x) *x = scn->x - 1;
hudakz 0:e33621169e44 302 if(cur_edge) *cur_edge = scn->cur_edge;
hudakz 0:e33621169e44 303 if(last_edge) *last_edge = scn->last_edge;
hudakz 0:e33621169e44 304 if(y0) *y0 = y0_1;
hudakz 0:e33621169e44 305 if(y1) *y1 = y0_1 - y0_2;
hudakz 0:e33621169e44 306 if(y2) *y2 = y0_0 - (y0_1 * 2) + y0_2;
hudakz 0:e33621169e44 307 /* NB not quite accurate (uses updated x) */
hudakz 0:e33621169e44 308 zbar_scanner_t *mut_scn = (zbar_scanner_t*)scn;
hudakz 0:e33621169e44 309 if(y1_thresh) *y1_thresh = calc_thresh(mut_scn);
hudakz 0:e33621169e44 310 dprintf(1, "\n");
hudakz 0:e33621169e44 311 }