Mirror with some correction

Dependencies:   mbed FastIO FastPWM USBDevice

Committer:
mjr
Date:
Fri Apr 21 18:50:37 2017 +0000
Revision:
86:e30a1f60f783
Parent:
82:4f6209cb5c33
Child:
87:8d35c74403af
Capture a bunch of alternative bar code decoder tests, mostly unsuccessful

Who changed what in which revision?

UserRevisionLine numberNew contents of line
mjr 82:4f6209cb5c33 1 // Plunger sensor type for bar-code based absolute position encoders.
mjr 82:4f6209cb5c33 2 // This type of sensor uses an optical sensor that moves with the plunger
mjr 82:4f6209cb5c33 3 // along a guide rail with printed bar codes along its length that encode
mjr 82:4f6209cb5c33 4 // the absolute position at each point. We figure the plunger position
mjr 82:4f6209cb5c33 5 // by reading the bar code and decoding it into a position figure.
mjr 82:4f6209cb5c33 6 //
mjr 82:4f6209cb5c33 7 // The bar code has to be encoded in a specific format that we recognize.
mjr 82:4f6209cb5c33 8 // We use a 10-bit reflected Gray code, optically encoded using a Manchester-
mjr 82:4f6209cb5c33 9 // type of coding. Each bit is represented as a fixed-width area on the
mjr 82:4f6209cb5c33 10 // bar, half white and half black. The bit value is encoded in the order
mjr 82:4f6209cb5c33 11 // of the colors: Black/White is '0', and White/Black is '1'.
mjr 82:4f6209cb5c33 12 //
mjr 86:e30a1f60f783 13 // Gray codes are ideal for this type of application. Gray codes are
mjr 86:e30a1f60f783 14 // defined such that each code point differs in exactly one bit from each
mjr 86:e30a1f60f783 15 // adjacent code point. This provides natural error correction when used
mjr 86:e30a1f60f783 16 // as a position scale, since any single-bit error will yield a code point
mjr 86:e30a1f60f783 17 // reading that's only one spot off from the true position. So a bit read
mjr 86:e30a1f60f783 18 // error acts like a reduction in precision. Likewise, any time the sensor
mjr 86:e30a1f60f783 19 // is halfway between two code points, only one bit will be ambiguous, so
mjr 86:e30a1f60f783 20 // the reading will come out as one of points on either side of the true
mjr 86:e30a1f60f783 21 // position. Finally, motion blur will have the same effect, of creating
mjr 86:e30a1f60f783 22 // ambiguity in the least significant bits, and thus giving us a reading
mjr 86:e30a1f60f783 23 // that's correct to as many bits as we can read with teh blur.
mjr 82:4f6209cb5c33 24 //
mjr 82:4f6209cb5c33 25 // We use the Manchester-type optical coding because it has good properties
mjr 86:e30a1f60f783 26 // for low-contrast images, and doesn't require uniform lighting. Each bit's
mjr 86:e30a1f60f783 27 // pixel span contains equal numbers of light and dark pixels, so each bit
mjr 86:e30a1f60f783 28 // provides its own local level reference. This means we don't care about
mjr 86:e30a1f60f783 29 // lighting uniformity over the whole image, because we don't need a global
mjr 86:e30a1f60f783 30 // notion of light and dark, just a local one over a single bit at a time.
mjr 82:4f6209cb5c33 31 //
mjr 82:4f6209cb5c33 32
mjr 82:4f6209cb5c33 33 #ifndef _BARCODESENSOR_H_
mjr 82:4f6209cb5c33 34 #define _BARCODESENSOR_H_
mjr 82:4f6209cb5c33 35
mjr 82:4f6209cb5c33 36 #include "plunger.h"
mjr 82:4f6209cb5c33 37 #include "tsl14xxSensor.h"
mjr 82:4f6209cb5c33 38
mjr 82:4f6209cb5c33 39 // Base class for bar-code sensors
mjr 86:e30a1f60f783 40 //
mjr 86:e30a1f60f783 41 // This is a template class with template parameters for the bar
mjr 86:e30a1f60f783 42 // code pixel structure. The bar code layout is fixed for a given
mjr 86:e30a1f60f783 43 // sensor type. We can assume fixed pixel sizes because we don't
mjr 86:e30a1f60f783 44 // have to process arbitrary images. We only have to read scales
mjr 86:e30a1f60f783 45 // specially prepared for this application, so we can count on them
mjr 86:e30a1f60f783 46 // being printed at an exact size relative to the sensor pixels.
mjr 86:e30a1f60f783 47 //
mjr 86:e30a1f60f783 48 // nBits = Number of bits in the code
mjr 86:e30a1f60f783 49 //
mjr 86:e30a1f60f783 50 // leftBarWidth = Width in pixels of delimiting left bar. The code is
mjr 86:e30a1f60f783 51 // delimited by a black bar on the "left" end, nearest pixel 0. This
mjr 86:e30a1f60f783 52 // gives the pixel width of the bar.
mjr 86:e30a1f60f783 53 //
mjr 86:e30a1f60f783 54 // leftBarMaxOfs = Maximum offset of the delimiting bar from the left
mjr 86:e30a1f60f783 55 // edge of the sensor (pixel 0), in pixels
mjr 86:e30a1f60f783 56 //
mjr 86:e30a1f60f783 57 // bitWidth = Width of each bit in pixels. This is the width of the
mjr 86:e30a1f60f783 58 // full bit, including both "half bits" - it's the full white/black or
mjr 86:e30a1f60f783 59 // black/white pattern area.
mjr 86:e30a1f60f783 60
mjr 86:e30a1f60f783 61 template <int nBits, int leftBarWidth, int leftBarMaxOfs, int bitWidth>
mjr 82:4f6209cb5c33 62 class PlungerSensorBarCode
mjr 82:4f6209cb5c33 63 {
mjr 82:4f6209cb5c33 64 public:
mjr 86:e30a1f60f783 65 // process the image
mjr 82:4f6209cb5c33 66 bool process(const uint8_t *pix, int npix, int &pos)
mjr 82:4f6209cb5c33 67 {
mjr 86:e30a1f60f783 68 #if 0 // $$$
mjr 86:e30a1f60f783 69
mjr 86:e30a1f60f783 70 // scan from the left edge until we find the fixed '0' start bit
mjr 86:e30a1f60f783 71 for (int i = 0 ; i < leftBarMaxOfs ; ++i, ++pix)
mjr 86:e30a1f60f783 72 {
mjr 86:e30a1f60f783 73 // check for the '0' bit
mjr 86:e30a1f60f783 74 if (readBit8(pix) == 0)
mjr 86:e30a1f60f783 75 {
mjr 86:e30a1f60f783 76 // got it - skip the start bit
mjr 86:e30a1f60f783 77 pix += bitWidth;
mjr 86:e30a1f60f783 78
mjr 86:e30a1f60f783 79 // read the gray code bits
mjr 86:e30a1f60f783 80 int gray = 0;
mjr 86:e30a1f60f783 81 for (int j = 0 ; j < nBits ; ++j, pix += bitWidth)
mjr 86:e30a1f60f783 82 {
mjr 86:e30a1f60f783 83 // read the bit; return failure if we can't decode a bit
mjr 86:e30a1f60f783 84 int bit = readBit8(pix);
mjr 86:e30a1f60f783 85 if (bit < 0)
mjr 86:e30a1f60f783 86 return false;
mjr 86:e30a1f60f783 87
mjr 86:e30a1f60f783 88 // shift it into the code
mjr 86:e30a1f60f783 89 gray = (gray << 1) | bit;
mjr 86:e30a1f60f783 90 }
mjr 86:e30a1f60f783 91 }
mjr 86:e30a1f60f783 92
mjr 86:e30a1f60f783 93 // convert the gray code to binary
mjr 86:e30a1f60f783 94 int bin = grayToBin(gray);
mjr 86:e30a1f60f783 95
mjr 86:e30a1f60f783 96 // compute the parity of the binary value
mjr 86:e30a1f60f783 97 int parity = 0;
mjr 86:e30a1f60f783 98 for (int j = 0 ; j < nBits ; ++j)
mjr 86:e30a1f60f783 99 parity ^= bin >> j;
mjr 86:e30a1f60f783 100
mjr 86:e30a1f60f783 101 // figure the bit required for odd parity
mjr 86:e30a1f60f783 102 int odd = (parity & 0x01) ^ 0x01;
mjr 86:e30a1f60f783 103
mjr 86:e30a1f60f783 104 // read the check bit
mjr 86:e30a1f60f783 105 int bit = readBit8(pix);
mjr 86:e30a1f60f783 106 if (pix < 0)
mjr 86:e30a1f60f783 107 return false;
mjr 86:e30a1f60f783 108
mjr 86:e30a1f60f783 109 // check that it matches the expected parity
mjr 86:e30a1f60f783 110 if (bit != odd)
mjr 86:e30a1f60f783 111 return false;
mjr 86:e30a1f60f783 112
mjr 86:e30a1f60f783 113 // success
mjr 86:e30a1f60f783 114 pos = bin;
mjr 86:e30a1f60f783 115 return true;
mjr 86:e30a1f60f783 116 }
mjr 86:e30a1f60f783 117
mjr 86:e30a1f60f783 118 // no code found
mjr 82:4f6209cb5c33 119 return false;
mjr 86:e30a1f60f783 120
mjr 86:e30a1f60f783 121 #else
mjr 86:e30a1f60f783 122 int barStart = leftBarMaxOfs/2;
mjr 86:e30a1f60f783 123 if (leftBarWidth != 0) // $$$
mjr 86:e30a1f60f783 124 {
mjr 86:e30a1f60f783 125 // Find the black bar on the left side (nearest pixel 0) that
mjr 86:e30a1f60f783 126 // delimits the start of the bar code. To find it, first figure
mjr 86:e30a1f60f783 127 // the average brightness over the left margin up to the maximum
mjr 86:e30a1f60f783 128 // allowable offset, then look for the bar by finding the first
mjr 86:e30a1f60f783 129 // bar-width run of pixels that are darker than the average.
mjr 86:e30a1f60f783 130 int lsum = 0;
mjr 86:e30a1f60f783 131 for (int x = 1 ; x <= leftBarMaxOfs ; ++x)
mjr 86:e30a1f60f783 132 lsum += pix[x];
mjr 86:e30a1f60f783 133 int lavg = lsum / leftBarMaxOfs;
mjr 86:e30a1f60f783 134
mjr 86:e30a1f60f783 135 // now find the first dark edge
mjr 86:e30a1f60f783 136 for (int x = 0 ; x < leftBarMaxOfs ; ++x)
mjr 86:e30a1f60f783 137 {
mjr 86:e30a1f60f783 138 // if this pixel is dark, and one of the next two is dark,
mjr 86:e30a1f60f783 139 // take it as the edge
mjr 86:e30a1f60f783 140 if (pix[x] < lavg && (pix[x+1] < lavg || pix[x+2] < lavg))
mjr 86:e30a1f60f783 141 {
mjr 86:e30a1f60f783 142 // move past the delimier
mjr 86:e30a1f60f783 143 barStart = x + leftBarWidth;
mjr 86:e30a1f60f783 144 break;
mjr 86:e30a1f60f783 145 }
mjr 86:e30a1f60f783 146 }
mjr 86:e30a1f60f783 147 }
mjr 86:e30a1f60f783 148 else
mjr 86:e30a1f60f783 149 {
mjr 86:e30a1f60f783 150 barStart = 4; // $$$ should be configurable via config tool
mjr 86:e30a1f60f783 151 }
mjr 86:e30a1f60f783 152
mjr 86:e30a1f60f783 153 // Scan the bits
mjr 86:e30a1f60f783 154 int barcode = 0;
mjr 86:e30a1f60f783 155 for (int bit = 0, x0 = barStart; bit < nBits ; ++bit, x0 += bitWidth)
mjr 86:e30a1f60f783 156 {
mjr 86:e30a1f60f783 157 // figure the extent of this bit
mjr 86:e30a1f60f783 158 int x1 = x0 + bitWidth / 2;
mjr 86:e30a1f60f783 159 int x2 = x0 + bitWidth;
mjr 86:e30a1f60f783 160 if (x1 > npix) x1 = npix;
mjr 86:e30a1f60f783 161 if (x2 > npix) x2 = npix;
mjr 86:e30a1f60f783 162
mjr 86:e30a1f60f783 163 // get the average of the pixels over the bit
mjr 86:e30a1f60f783 164 int sum = 0;
mjr 86:e30a1f60f783 165 for (int x = x0 ; x < x2 ; ++x)
mjr 86:e30a1f60f783 166 sum += pix[x];
mjr 86:e30a1f60f783 167 int avg = sum / bitWidth;
mjr 86:e30a1f60f783 168
mjr 86:e30a1f60f783 169 // Scan the left and right sections. Classify each
mjr 86:e30a1f60f783 170 // section according to whether the majority of its
mjr 86:e30a1f60f783 171 // pixels are above or below the local average.
mjr 86:e30a1f60f783 172 int lsum = 0, rsum = 0;
mjr 86:e30a1f60f783 173 for (int x = x0 + 1 ; x < x1 - 1 ; ++x)
mjr 86:e30a1f60f783 174 lsum += (pix[x] < avg ? 0 : 1);
mjr 86:e30a1f60f783 175 for (int x = x1 + 1 ; x < x2 - 1 ; ++x)
mjr 86:e30a1f60f783 176 rsum += (pix[x] < avg ? 0 : 1);
mjr 86:e30a1f60f783 177
mjr 86:e30a1f60f783 178 // if we don't have a winner, fail
mjr 86:e30a1f60f783 179 if (lsum == rsum)
mjr 86:e30a1f60f783 180 return false;
mjr 86:e30a1f60f783 181
mjr 86:e30a1f60f783 182 // black/white = 0, white/black = 1
mjr 86:e30a1f60f783 183 barcode = (barcode << 1) | (lsum < rsum ? 0 : 1);
mjr 86:e30a1f60f783 184 }
mjr 86:e30a1f60f783 185
mjr 86:e30a1f60f783 186 // decode the Gray code value to binary
mjr 86:e30a1f60f783 187 pos = grayToBin(barcode);
mjr 86:e30a1f60f783 188
mjr 86:e30a1f60f783 189 // success
mjr 86:e30a1f60f783 190 return true;
mjr 86:e30a1f60f783 191 #endif
mjr 86:e30a1f60f783 192 }
mjr 86:e30a1f60f783 193
mjr 86:e30a1f60f783 194 // read a bar starting at the given pixel
mjr 86:e30a1f60f783 195 int readBit8(const uint8_t *pix)
mjr 86:e30a1f60f783 196 {
mjr 86:e30a1f60f783 197 // pull out the pixels for the bar
mjr 86:e30a1f60f783 198 uint8_t s[8];
mjr 86:e30a1f60f783 199 memcpy(s, pix, 8);
mjr 86:e30a1f60f783 200
mjr 86:e30a1f60f783 201 // sort them in brightness order (using an 8-element network sort)
mjr 86:e30a1f60f783 202 #define SWAP(a, b) if (s[a] > s[b]) { uint8_t tmp = s[a]; s[a] = s[b]; s[b] = tmp; }
mjr 86:e30a1f60f783 203 SWAP(0, 1);
mjr 86:e30a1f60f783 204 SWAP(2, 3);
mjr 86:e30a1f60f783 205 SWAP(0, 2);
mjr 86:e30a1f60f783 206 SWAP(1, 3);
mjr 86:e30a1f60f783 207 SWAP(1, 2);
mjr 86:e30a1f60f783 208 SWAP(4, 5);
mjr 86:e30a1f60f783 209 SWAP(6, 7);
mjr 86:e30a1f60f783 210 SWAP(4, 6);
mjr 86:e30a1f60f783 211 SWAP(5, 7);
mjr 86:e30a1f60f783 212 SWAP(5, 6);
mjr 86:e30a1f60f783 213 SWAP(0, 4);
mjr 86:e30a1f60f783 214 SWAP(1, 5);
mjr 86:e30a1f60f783 215 SWAP(1, 4);
mjr 86:e30a1f60f783 216 SWAP(2, 6);
mjr 86:e30a1f60f783 217 SWAP(3, 7);
mjr 86:e30a1f60f783 218 SWAP(3, 6);
mjr 86:e30a1f60f783 219 SWAP(2, 4);
mjr 86:e30a1f60f783 220 SWAP(3, 5);
mjr 86:e30a1f60f783 221 SWAP(3, 4);
mjr 86:e30a1f60f783 222 #undef SWAP
mjr 86:e30a1f60f783 223
mjr 86:e30a1f60f783 224 // figure the median brightness
mjr 86:e30a1f60f783 225 int median = (int(s[3]) + s[4] + 1) / 2;
mjr 86:e30a1f60f783 226
mjr 86:e30a1f60f783 227 // count pixels below the median on each side
mjr 86:e30a1f60f783 228 int ldark = 0, rdark = 0;
mjr 86:e30a1f60f783 229 for (int i = 0 ; i < 3 ; ++i)
mjr 86:e30a1f60f783 230 {
mjr 86:e30a1f60f783 231 if (pix[i] < median)
mjr 86:e30a1f60f783 232 ldark++;
mjr 86:e30a1f60f783 233 }
mjr 86:e30a1f60f783 234 for (int i = 4 ; i < 8 ; ++i)
mjr 86:e30a1f60f783 235 {
mjr 86:e30a1f60f783 236 if (pix[i] < median)
mjr 86:e30a1f60f783 237 rdark++;
mjr 86:e30a1f60f783 238 }
mjr 86:e30a1f60f783 239
mjr 86:e30a1f60f783 240 // we need >=3 dark + >=3 light or vice versa
mjr 86:e30a1f60f783 241 if (ldark >= 3 && rdark <= 1)
mjr 86:e30a1f60f783 242 {
mjr 86:e30a1f60f783 243 // dark + light = '0' bit
mjr 86:e30a1f60f783 244 return 0;
mjr 86:e30a1f60f783 245 }
mjr 86:e30a1f60f783 246 if (ldark <= 1 && rdark >= 3)
mjr 86:e30a1f60f783 247 {
mjr 86:e30a1f60f783 248 // light + dark = '1' bit
mjr 86:e30a1f60f783 249 return 1;
mjr 86:e30a1f60f783 250 }
mjr 86:e30a1f60f783 251 else
mjr 86:e30a1f60f783 252 {
mjr 86:e30a1f60f783 253 // ambiguous bit
mjr 86:e30a1f60f783 254 return -1;
mjr 86:e30a1f60f783 255 }
mjr 86:e30a1f60f783 256 }
mjr 86:e30a1f60f783 257
mjr 86:e30a1f60f783 258 // convert a reflected Gray code value (up to 16 bits) to binary
mjr 86:e30a1f60f783 259 int grayToBin(int grayval)
mjr 86:e30a1f60f783 260 {
mjr 86:e30a1f60f783 261 int temp = grayval ^ (grayval >> 8);
mjr 86:e30a1f60f783 262 temp ^= (temp >> 4);
mjr 86:e30a1f60f783 263 temp ^= (temp >> 2);
mjr 86:e30a1f60f783 264 temp ^= (temp >> 1);
mjr 86:e30a1f60f783 265 return temp;
mjr 82:4f6209cb5c33 266 }
mjr 82:4f6209cb5c33 267 };
mjr 82:4f6209cb5c33 268
mjr 86:e30a1f60f783 269 // Auto-exposure counter
mjr 86:e30a1f60f783 270 class BarCodeExposureCounter
mjr 86:e30a1f60f783 271 {
mjr 86:e30a1f60f783 272 public:
mjr 86:e30a1f60f783 273 BarCodeExposureCounter()
mjr 86:e30a1f60f783 274 {
mjr 86:e30a1f60f783 275 nDark = 0;
mjr 86:e30a1f60f783 276 nBright = 0;
mjr 86:e30a1f60f783 277 nZero = 0;
mjr 86:e30a1f60f783 278 nSat = 0;
mjr 86:e30a1f60f783 279 }
mjr 86:e30a1f60f783 280
mjr 86:e30a1f60f783 281 inline void count(int pix)
mjr 86:e30a1f60f783 282 {
mjr 86:e30a1f60f783 283 if (pix <= 2)
mjr 86:e30a1f60f783 284 ++nZero;
mjr 86:e30a1f60f783 285 else if (pix < 12)
mjr 86:e30a1f60f783 286 ++nDark;
mjr 86:e30a1f60f783 287 else if (pix >= 253)
mjr 86:e30a1f60f783 288 ++nSat;
mjr 86:e30a1f60f783 289 else if (pix > 200)
mjr 86:e30a1f60f783 290 ++nBright;
mjr 86:e30a1f60f783 291 }
mjr 86:e30a1f60f783 292
mjr 86:e30a1f60f783 293 int nDark; // dark pixels
mjr 86:e30a1f60f783 294 int nBright; // bright pixels
mjr 86:e30a1f60f783 295 int nZero; // pixels at zero brightness
mjr 86:e30a1f60f783 296 int nSat; // pixels at full saturation
mjr 86:e30a1f60f783 297 };
mjr 86:e30a1f60f783 298
mjr 86:e30a1f60f783 299 // PlungerSensor interface implementation for bar code readers.
mjr 86:e30a1f60f783 300 //
mjr 86:e30a1f60f783 301 // Bar code readers are image sensors, so we have a pixel size for
mjr 86:e30a1f60f783 302 // the sensor. However, this isn't the scale for the readings. The
mjr 86:e30a1f60f783 303 // scale for the readings is determined by the number of bits in the
mjr 86:e30a1f60f783 304 // bar code, since an n-bit bar code can encode 2^n distinct positions.
mjr 86:e30a1f60f783 305 //
mjr 86:e30a1f60f783 306 template <int nBits, int leftBarWidth, int leftBarMaxOfs, int bitWidth>
mjr 86:e30a1f60f783 307 class PlungerSensorBarCodeTSL14xx: public PlungerSensorTSL14xxSmall,
mjr 86:e30a1f60f783 308 PlungerSensorBarCode<nBits, leftBarWidth, leftBarMaxOfs, bitWidth>
mjr 82:4f6209cb5c33 309 {
mjr 82:4f6209cb5c33 310 public:
mjr 82:4f6209cb5c33 311 PlungerSensorBarCodeTSL14xx(int nativePix, PinName si, PinName clock, PinName ao)
mjr 86:e30a1f60f783 312 : PlungerSensorTSL14xxSmall(nativePix, (1 << nBits) - 1, si, clock, ao)
mjr 82:4f6209cb5c33 313 {
mjr 86:e30a1f60f783 314 // the native scale is the number of positions we can
mjr 86:e30a1f60f783 315 // encode in the bar code
mjr 86:e30a1f60f783 316 nativeScale = 1023;
mjr 82:4f6209cb5c33 317 }
mjr 82:4f6209cb5c33 318
mjr 82:4f6209cb5c33 319 protected:
mjr 86:e30a1f60f783 320
mjr 82:4f6209cb5c33 321 // process the image through the bar code reader
mjr 82:4f6209cb5c33 322 virtual bool process(const uint8_t *pix, int npix, int &pos)
mjr 82:4f6209cb5c33 323 {
mjr 82:4f6209cb5c33 324 // adjust the exposure
mjr 82:4f6209cb5c33 325 adjustExposure(pix, npix);
mjr 82:4f6209cb5c33 326
mjr 82:4f6209cb5c33 327 // do the standard bar code processing
mjr 86:e30a1f60f783 328 return PlungerSensorBarCode<nBits, leftBarWidth, leftBarMaxOfs, bitWidth>
mjr 86:e30a1f60f783 329 ::process(pix, npix, pos);
mjr 82:4f6209cb5c33 330 }
mjr 82:4f6209cb5c33 331
mjr 86:e30a1f60f783 332 // bar code sensor orientation is fixed
mjr 86:e30a1f60f783 333 virtual int getOrientation() const { return 1; }
mjr 86:e30a1f60f783 334
mjr 82:4f6209cb5c33 335 // adjust the exposure
mjr 82:4f6209cb5c33 336 void adjustExposure(const uint8_t *pix, int npix)
mjr 82:4f6209cb5c33 337 {
mjr 86:e30a1f60f783 338 #if 1
mjr 86:e30a1f60f783 339 // The Manchester code has a nice property for auto exposure
mjr 86:e30a1f60f783 340 // control: each bit area has equal numbers of white and black
mjr 86:e30a1f60f783 341 // pixels. So we know exactly how the overall population of
mjr 86:e30a1f60f783 342 // pixels has to look: the bit area will be 50% black and 50%
mjr 86:e30a1f60f783 343 // white, and the margins will be all white. For maximum
mjr 86:e30a1f60f783 344 // contrast, target an exposure level where the black pixels
mjr 86:e30a1f60f783 345 // are all below the middle brightness level and the white
mjr 86:e30a1f60f783 346 // pixels are all above. Start by figuring the number of
mjr 86:e30a1f60f783 347 // pixels above and below.
mjr 86:e30a1f60f783 348 int nDark = 0;
mjr 86:e30a1f60f783 349 for (int i = 0 ; i < npix ; ++i)
mjr 86:e30a1f60f783 350 {
mjr 86:e30a1f60f783 351 if (pix[i] < 200)
mjr 86:e30a1f60f783 352 ++nDark;
mjr 86:e30a1f60f783 353 }
mjr 86:e30a1f60f783 354
mjr 86:e30a1f60f783 355 // Figure the percentage of black pixels: the left bar is
mjr 86:e30a1f60f783 356 // all black pixels, and 50% of each bit is black pixels.
mjr 86:e30a1f60f783 357 int targetDark = leftBarWidth + (nBits * bitWidth)/2;
mjr 86:e30a1f60f783 358
mjr 86:e30a1f60f783 359 // Increase exposure time if too many pixels are below the
mjr 86:e30a1f60f783 360 // halfway point; decrease it if too many pixels are above.
mjr 86:e30a1f60f783 361 int d = nDark - targetDark;
mjr 86:e30a1f60f783 362 if (d > 5 || d < -5)
mjr 86:e30a1f60f783 363 {
mjr 86:e30a1f60f783 364 axcTime += d;
mjr 86:e30a1f60f783 365 }
mjr 86:e30a1f60f783 366
mjr 86:e30a1f60f783 367
mjr 86:e30a1f60f783 368 #elif 0 //$$$
mjr 86:e30a1f60f783 369 // Count exposure levels of pixels in the left and right margins
mjr 86:e30a1f60f783 370 BarCodeExposureCounter counter;
mjr 86:e30a1f60f783 371 for (int i = 0 ; i < leftBarMaxOfs/2 ; ++i)
mjr 86:e30a1f60f783 372 {
mjr 86:e30a1f60f783 373 // count the pixels at the left and right margins
mjr 86:e30a1f60f783 374 counter.count(pix[i]);
mjr 86:e30a1f60f783 375 counter.count(pix[npix - i - 1]);
mjr 86:e30a1f60f783 376 }
mjr 86:e30a1f60f783 377
mjr 86:e30a1f60f783 378 // The margin is all white, so try to get all of these pixels
mjr 86:e30a1f60f783 379 // in the bright range, but not saturated. That should give us
mjr 86:e30a1f60f783 380 // the best overall contrast throughout the image.
mjr 86:e30a1f60f783 381 if (counter.nSat > 0)
mjr 86:e30a1f60f783 382 {
mjr 86:e30a1f60f783 383 // overexposed - reduce exposure time
mjr 86:e30a1f60f783 384 if (axcTime > 5)
mjr 86:e30a1f60f783 385 axcTime -= 5;
mjr 86:e30a1f60f783 386 else
mjr 86:e30a1f60f783 387 axcTime = 0;
mjr 86:e30a1f60f783 388 }
mjr 86:e30a1f60f783 389 else if (counter.nBright < leftBarMaxOfs)
mjr 86:e30a1f60f783 390 {
mjr 86:e30a1f60f783 391 // they're not all in the bright range - increase exposure time
mjr 86:e30a1f60f783 392 axcTime += 5;
mjr 86:e30a1f60f783 393 }
mjr 86:e30a1f60f783 394
mjr 86:e30a1f60f783 395 #else // $$$
mjr 82:4f6209cb5c33 396 // Count the number of pixels near total darkness and
mjr 82:4f6209cb5c33 397 // total saturation
mjr 86:e30a1f60f783 398 int nZero = 0, nDark = 0, nBri = 0, nSat = 0;
mjr 82:4f6209cb5c33 399 for (int i = 0 ; i < npix ; ++i)
mjr 82:4f6209cb5c33 400 {
mjr 82:4f6209cb5c33 401 int pi = pix[i];
mjr 86:e30a1f60f783 402 if (pi <= 2)
mjr 86:e30a1f60f783 403 ++nZero;
mjr 86:e30a1f60f783 404 else if (pi < 12)
mjr 82:4f6209cb5c33 405 ++nDark;
mjr 86:e30a1f60f783 406 else if (pi >= 254)
mjr 82:4f6209cb5c33 407 ++nSat;
mjr 86:e30a1f60f783 408 else if (pi > 242)
mjr 86:e30a1f60f783 409 ++nBri;
mjr 82:4f6209cb5c33 410 }
mjr 82:4f6209cb5c33 411
mjr 82:4f6209cb5c33 412 // If more than 30% of pixels are near total darkness, increase
mjr 82:4f6209cb5c33 413 // the exposure time. If more than 30% are near total saturation,
mjr 82:4f6209cb5c33 414 // decrease the exposure time.
mjr 86:e30a1f60f783 415 int pct5 = uint32_t(npix * 3277) >> 16;
mjr 82:4f6209cb5c33 416 int pct30 = uint32_t(npix * 19661) >> 16;
mjr 82:4f6209cb5c33 417 int pct50 = uint32_t(npix) >> 1;
mjr 86:e30a1f60f783 418 if (nSat == 0)
mjr 86:e30a1f60f783 419 {
mjr 86:e30a1f60f783 420 // no saturated pixels - increase exposure time
mjr 86:e30a1f60f783 421 axcTime += 5;
mjr 86:e30a1f60f783 422 }
mjr 86:e30a1f60f783 423 else if (nSat > pct5)
mjr 86:e30a1f60f783 424 {
mjr 86:e30a1f60f783 425 if (axcTime > 5)
mjr 86:e30a1f60f783 426 axcTime -= 5;
mjr 86:e30a1f60f783 427 else
mjr 86:e30a1f60f783 428 axcTime = 0;
mjr 86:e30a1f60f783 429 }
mjr 86:e30a1f60f783 430 else if (nZero == 0)
mjr 86:e30a1f60f783 431 {
mjr 86:e30a1f60f783 432 // no totally dark pixels - decrease exposure time
mjr 86:e30a1f60f783 433 if (axcTime > 5)
mjr 86:e30a1f60f783 434 axcTime -= 5;
mjr 86:e30a1f60f783 435 else
mjr 86:e30a1f60f783 436 axcTime = 0;
mjr 86:e30a1f60f783 437 }
mjr 86:e30a1f60f783 438 else if (nZero > pct5)
mjr 86:e30a1f60f783 439 {
mjr 86:e30a1f60f783 440 axcTime += 5;
mjr 86:e30a1f60f783 441 }
mjr 86:e30a1f60f783 442 else if (nZero > pct30 || (nDark > pct50 && nSat < pct30))
mjr 82:4f6209cb5c33 443 {
mjr 82:4f6209cb5c33 444 // very dark - increase exposure time a lot
mjr 82:4f6209cb5c33 445 if (axcTime < 450)
mjr 82:4f6209cb5c33 446 axcTime += 50;
mjr 82:4f6209cb5c33 447 }
mjr 82:4f6209cb5c33 448 else if (nDark > pct30 && nSat < pct30)
mjr 82:4f6209cb5c33 449 {
mjr 82:4f6209cb5c33 450 // dark - increase exposure time a bit
mjr 82:4f6209cb5c33 451 if (axcTime < 490)
mjr 82:4f6209cb5c33 452 axcTime += 10;
mjr 82:4f6209cb5c33 453 }
mjr 86:e30a1f60f783 454 else if (nSat > pct30 || (nBri > pct50 && nDark < pct30))
mjr 82:4f6209cb5c33 455 {
mjr 82:4f6209cb5c33 456 // very overexposed - decrease exposure time a lot
mjr 82:4f6209cb5c33 457 if (axcTime > 50)
mjr 82:4f6209cb5c33 458 axcTime -= 50;
mjr 82:4f6209cb5c33 459 else
mjr 82:4f6209cb5c33 460 axcTime = 0;
mjr 82:4f6209cb5c33 461 }
mjr 86:e30a1f60f783 462 else if (nBri > pct30 && nDark < pct30)
mjr 82:4f6209cb5c33 463 {
mjr 82:4f6209cb5c33 464 // overexposed - decrease exposure time a little
mjr 82:4f6209cb5c33 465 if (axcTime > 10)
mjr 82:4f6209cb5c33 466 axcTime -= 10;
mjr 82:4f6209cb5c33 467 else
mjr 82:4f6209cb5c33 468 axcTime = 0;
mjr 82:4f6209cb5c33 469 }
mjr 86:e30a1f60f783 470 #endif
mjr 86:e30a1f60f783 471
mjr 86:e30a1f60f783 472 // don't allow the exposure time to go over 2.5ms
mjr 86:e30a1f60f783 473 if (int(axcTime) < 0)
mjr 86:e30a1f60f783 474 axcTime = 0;
mjr 86:e30a1f60f783 475 if (axcTime > 2500)
mjr 86:e30a1f60f783 476 axcTime = 2500;
mjr 82:4f6209cb5c33 477 }
mjr 82:4f6209cb5c33 478 };
mjr 82:4f6209cb5c33 479
mjr 86:e30a1f60f783 480 // TSL1401CL - 128-bit image sensor, used as a bar code reader
mjr 86:e30a1f60f783 481 class PlungerSensorTSL1401CL: public PlungerSensorBarCodeTSL14xx<
mjr 86:e30a1f60f783 482 10, // number of bits in code
mjr 86:e30a1f60f783 483 0, // left delimiter bar width in pixels (0 for none)
mjr 86:e30a1f60f783 484 24, // maximum left margin width in pixels
mjr 86:e30a1f60f783 485 12> // pixel width of each bit
mjr 82:4f6209cb5c33 486 {
mjr 82:4f6209cb5c33 487 public:
mjr 82:4f6209cb5c33 488 PlungerSensorTSL1401CL(PinName si, PinName clock, PinName a0)
mjr 82:4f6209cb5c33 489 : PlungerSensorBarCodeTSL14xx(128, si, clock, a0)
mjr 82:4f6209cb5c33 490 {
mjr 82:4f6209cb5c33 491 }
mjr 82:4f6209cb5c33 492 };
mjr 82:4f6209cb5c33 493
mjr 82:4f6209cb5c33 494 #endif