/** * \brief Increment counters used to determine module values * \param img * \param reg * \param tally * \param xOrigin * \param yOrigin * \param mapWidth * \param mapHeight * \param dir * \return void */ void TallyModuleJumps(DmtxDecode *dec, DmtxRegion *reg, int tally[][24], int xOrigin, int yOrigin, int mapWidth, int mapHeight, DmtxDirection dir) { int extent, weight; int travelStep; int symbolRow, symbolCol; int mapRow, mapCol; int lineStart, lineStop; int travelStart, travelStop; int *line, *travel; int jumpThreshold; int darkOnLight; int color; int statusPrev, statusModule; int tPrev, tModule; assert(dir == DmtxDirUp || dir == DmtxDirLeft || dir == DmtxDirDown || dir == DmtxDirRight); travelStep = (dir == DmtxDirUp || dir == DmtxDirRight) ? 1 : -1; /* Abstract row and column progress using pointers to allow grid traversal in all 4 directions using same logic */ if((dir & DmtxDirHorizontal) != 0x00) { line = &symbolRow; travel = &symbolCol; extent = mapWidth; lineStart = yOrigin; lineStop = yOrigin + mapHeight; travelStart = (travelStep == 1) ? xOrigin - 1 : xOrigin + mapWidth; travelStop = (travelStep == 1) ? xOrigin + mapWidth : xOrigin - 1; } else { assert(dir & DmtxDirVertical); line = &symbolCol; travel = &symbolRow; extent = mapHeight; lineStart = xOrigin; lineStop = xOrigin + mapWidth; travelStart = (travelStep == 1) ? yOrigin - 1: yOrigin + mapHeight; travelStop = (travelStep == 1) ? yOrigin + mapHeight : yOrigin - 1; } darkOnLight = (int)(reg->offColor > reg->onColor); jumpThreshold = abs((int)(0.4 * (reg->offColor - reg->onColor) + 0.5)); assert(jumpThreshold >= 0); for(*line = lineStart; *line < lineStop; (*line)++) { /* Capture tModule for each leading border module as normal but decide status based on predictable barcode border pattern */ *travel = travelStart; color = ReadModuleColor(dec, reg, symbolRow, symbolCol, reg->sizeIdx, reg->flowBegin.plane); tModule = (darkOnLight) ? reg->offColor - color : color - reg->offColor; statusModule = (travelStep == 1 || (*line & 0x01) == 0) ? DmtxModuleOnRGB : DmtxModuleOff; weight = extent; while((*travel += travelStep) != travelStop) { tPrev = tModule; statusPrev = statusModule; /* For normal data-bearing modules capture color and decide module status based on comparison to previous "known" module */ color = ReadModuleColor(dec, reg, symbolRow, symbolCol, reg->sizeIdx, reg->flowBegin.plane); tModule = (darkOnLight) ? reg->offColor - color : color - reg->offColor; if(statusPrev == DmtxModuleOnRGB) { if(tModule < tPrev - jumpThreshold) statusModule = DmtxModuleOff; else statusModule = DmtxModuleOnRGB; } else if(statusPrev == DmtxModuleOff) { if(tModule > tPrev + jumpThreshold) statusModule = DmtxModuleOnRGB; else statusModule = DmtxModuleOff; } mapRow = symbolRow - yOrigin; mapCol = symbolCol - xOrigin; assert(mapRow < 24 && mapCol < 24); if(statusModule == DmtxModuleOnRGB) tally[mapRow][mapCol] += (2 * weight); weight--; } assert(weight == 0); } }
/** * @brief XXX * @param image * @param reg * @return DMTX_SUCCESS | DMTX_FAILURE */ static int MatrixRegionFindSize(DmtxImage *image, DmtxRegion *reg) { int sizeIdx; int errors[30] = { 0 }; int minErrorsSizeIdx; int row, col, symbolRows, symbolCols; double tOff, tOn, jumpThreshold; DmtxColor3 colorOn, colorOff; DmtxColor3 colorOnAvg, colorOffAvg; DmtxColor3 black = { 0.0, 0.0, 0.0 }; DmtxGradient gradient, testGradient; int sizeIdxAttempts[] = { 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, 13, 29, 12, 11, 10, 28, 27, 9, 25, 8, 26, 7, 6, 5, 4, 24, 3, 2, 1, 0 }; int *ptr; /* First try all sizes to determine which sizeIdx with best contrast */ ptr = sizeIdxAttempts; gradient.tMin = gradient.tMid = gradient.tMax = 0; do { sizeIdx = *ptr; symbolRows = dmtxGetSymbolAttribute(DmtxSymAttribSymbolRows, sizeIdx); symbolCols = dmtxGetSymbolAttribute(DmtxSymAttribSymbolCols, sizeIdx); colorOnAvg = colorOffAvg = black; for(row = 0, col = 0; col < symbolCols; col++) { colorOn = ReadModuleColor(image, reg, row, col, sizeIdx); colorOff = ReadModuleColor(image, reg, row-1, col, sizeIdx); dmtxColor3AddTo(&colorOnAvg, &colorOn); dmtxColor3AddTo(&colorOffAvg, &colorOff); } for(row = 0, col = 0; row < symbolRows; row++) { colorOn = ReadModuleColor(image, reg, row, col, sizeIdx); colorOff = ReadModuleColor(image, reg, row, col-1, sizeIdx); dmtxColor3AddTo(&colorOnAvg, &colorOn); dmtxColor3AddTo(&colorOffAvg, &colorOff); } dmtxColor3ScaleBy(&colorOnAvg, 1.0/(symbolRows + symbolCols)); dmtxColor3ScaleBy(&colorOffAvg, 1.0/(symbolRows + symbolCols)); testGradient.ray.p = colorOffAvg; dmtxColor3Sub(&testGradient.ray.c, &colorOnAvg, &colorOffAvg); if(dmtxColor3Mag(&testGradient.ray.c) < 20) continue; dmtxColor3Norm(&testGradient.ray.c); testGradient.tMin = 0; testGradient.tMax = dmtxDistanceAlongRay3(&testGradient.ray, &colorOnAvg); testGradient.tMid = (testGradient.tMin + testGradient.tMax) / 2.0; if(testGradient.tMax > gradient.tMax) gradient = testGradient; } while(*(ptr++) != 0); jumpThreshold = 0.4 * (gradient.tMax - gradient.tMin); if(jumpThreshold < 20) return DMTX_FAILURE; /* Start with largest possible pattern size and work downward. If done in other direction then false positive is possible. */ ptr = sizeIdxAttempts; minErrorsSizeIdx = *ptr; do { sizeIdx = *ptr; symbolRows = dmtxGetSymbolAttribute(DmtxSymAttribSymbolRows, sizeIdx); symbolCols = dmtxGetSymbolAttribute(DmtxSymAttribSymbolCols, sizeIdx); /* Test each pair of ON/OFF modules in the calibration bars */ /* Top calibration row */ row = symbolRows - 1; for(col = 0; col < symbolCols; col += 2) { colorOff = ReadModuleColor(image, reg, row, col + 1, sizeIdx); tOff = dmtxDistanceAlongRay3(&gradient.ray, &colorOff); colorOn = ReadModuleColor(image, reg, row, col, sizeIdx); tOn = dmtxDistanceAlongRay3(&gradient.ray, &colorOn); if(tOn - tOff < jumpThreshold) errors[sizeIdx]++; if(errors[sizeIdx] > errors[minErrorsSizeIdx]) break; } /* Right calibration column */ col = symbolCols - 1; for(row = 0; row < symbolRows; row += 2) { colorOff = ReadModuleColor(image, reg, row + 1, col, sizeIdx); tOff = dmtxDistanceAlongRay3(&gradient.ray, &colorOff); colorOn = ReadModuleColor(image, reg, row, col, sizeIdx); tOn = dmtxDistanceAlongRay3(&gradient.ray, &colorOn); if(tOn - tOff < jumpThreshold) errors[sizeIdx]++; if(errors[sizeIdx] > errors[minErrorsSizeIdx]) break; } /* Track of which sizeIdx has the fewest errors */ if(errors[sizeIdx] < errors[minErrorsSizeIdx]) minErrorsSizeIdx = sizeIdx; } while(*(ptr++) != 0); reg->gradient = gradient; reg->sizeIdx = minErrorsSizeIdx; reg->symbolRows = dmtxGetSymbolAttribute(DmtxSymAttribSymbolRows, reg->sizeIdx); reg->symbolCols = dmtxGetSymbolAttribute(DmtxSymAttribSymbolCols, reg->sizeIdx); reg->mappingRows = dmtxGetSymbolAttribute(DmtxSymAttribMappingMatrixRows, reg->sizeIdx); reg->mappingCols = dmtxGetSymbolAttribute(DmtxSymAttribMappingMatrixCols, reg->sizeIdx); if(errors[minErrorsSizeIdx] >= 4) return DMTX_FAILURE; return DMTX_SUCCESS; }