📄 dmtxregion.c
字号:
/*libdmtx - Data Matrix Encoding/Decoding LibraryCopyright (c) 2008 Mike LaughtonThis library is free software; you can redistribute it and/ormodify it under the terms of the GNU Lesser General PublicLicense as published by the Free Software Foundation; eitherversion 2.1 of the License, or (at your option) any later version.This library is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; without even the implied warranty ofMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNULesser General Public License for more details.You should have received a copy of the GNU Lesser General PublicLicense along with this library; if not, write to the Free SoftwareFoundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USAContact: mike@dragonflylogic.com*//* $Id: dmtxregion.c 514 2008-11-19 17:10:44Z mblaughton $ */#define DMTX_HOUGH_RES 180/** * @file dmtxregion.c * @brief Detect barcode regions * * This file contains region detection logic. *//** * @brief Find next barcode region * @param dec Pointer to DmtxDecode information struct * @param timeout Pointer to timeout time (NULL if none) * @return Detected region (if found) */extern DmtxRegiondmtxDecodeFindNextRegion(DmtxDecode *dec, DmtxTime *timeout){ DmtxScanGrid *grid; DmtxPixelLoc loc, locNext; DmtxRegion reg;/** int size, i = 0; char imagePath[128];*/ grid = &(dec->grid); /* Continue scanning until we run out of time or run out of image */ for(loc = GetGridCoordinates(grid);; loc = locNext) { /* Quit if grid has been completely traversed */ if(loc.status == DMTX_RANGE_EOF) { reg.found = DMTX_REGION_EOF; break; } /* First move away from this location to prevent repeat visits */ locNext = IncrementPixelProgress(grid); /* Scan this pixel for presence of a valid barcode edge */ reg = dmtxRegionScanPixel(dec, loc);/** if(reg.found == DMTX_REGION_FOUND || reg.found > DMTX_REGION_DROPPED_FINDER) { size = snprintf(imagePath, 128, "debug_%06d.pnm", i++); if(size >= 128) exit(1); WriteDiagnosticImage(dec, ®, imagePath); }*/ /* Found a barcode region? */ if(reg.found == DMTX_REGION_FOUND) break; /* Ran out of time? */ if(timeout != NULL && dmtxTimeExceeded(*timeout)) { reg.found = DMTX_REGION_TIMEOUT; break; } } return reg;}/** * @brief Scan individual pixel for presence of barcode edge * @param dec Pointer to DmtxDecode information struct * @param loc Pixel location * @return Detected region (if any) */extern DmtxRegiondmtxRegionScanPixel(DmtxDecode *dec, DmtxPixelLoc loc){ int offset; DmtxRegion reg; DmtxPointFlow flowBegin; memset(®, 0x00, sizeof(DmtxRegion)); offset = dmtxImageGetOffset(dec->image, loc.X, loc.Y);/* if(offset == DMTX_BAD_OFFSET || dec->image->cache[offset] & 0x40) { */ if(offset == DMTX_BAD_OFFSET) { reg.found = DMTX_REGION_NOT_FOUND; return reg; } if((int)(dec->image->cache[offset] & 0x80) != 0x00) { reg.found = DMTX_REGION_NOT_FOUND; return reg; } /* Test for presence of any reasonable edge at this location */ flowBegin = MatrixRegionSeekEdge(dec, loc); if(flowBegin.mag < 10) { reg.found = DMTX_REGION_DROPPED_EDGE; return reg; } /* Determine barcode orientation */ if(MatrixRegionOrientation(dec, ®, flowBegin) != DMTX_SUCCESS) { reg.found = DMTX_REGION_DROPPED_FINDER; return reg; } if(dmtxRegionUpdateXfrms(dec, ®) != DMTX_SUCCESS) { reg.found = DMTX_REGION_DROPPED_FINDER; return reg; } /* Define top edge */ if(MatrixRegionAlignCalibEdge(dec, ®, DmtxEdgeTop) != DMTX_SUCCESS) { reg.found = DMTX_REGION_DROPPED_TOP; return reg; } if(dmtxRegionUpdateXfrms(dec, ®) != DMTX_SUCCESS) { reg.found = DMTX_REGION_DROPPED_TOP; return reg; } /* Define right edge */ if(MatrixRegionAlignCalibEdge(dec, ®, DmtxEdgeRight) != DMTX_SUCCESS) { reg.found = DMTX_REGION_DROPPED_RIGHT; return reg; } if(dmtxRegionUpdateXfrms(dec, ®) != DMTX_SUCCESS) { reg.found = DMTX_REGION_DROPPED_RIGHT; return reg; } CALLBACK_MATRIX(®); /* Calculate the best fitting symbol size */ if(MatrixRegionFindSize(dec, ®) != DMTX_SUCCESS) { reg.found = DMTX_REGION_DROPPED_SIZE; return reg; } /* Found a valid matrix region */ reg.found = DMTX_REGION_FOUND; return reg;}/** * * */static DmtxPointFlowMatrixRegionSeekEdge(DmtxDecode *dec, DmtxPixelLoc loc){ int i; int strongIdx; DmtxPointFlow flow, flowPlane[3]; DmtxPointFlow flowPos, flowPosBack; DmtxPointFlow flowNeg, flowNegBack; /* Find whether red, green, or blue shows the strongest edge */ strongIdx = 0; for(i = 0; i < 3; i++) { flowPlane[i] = GetPointFlow(dec, i, loc, dmtxNeighborNone); if(i > 0 && flowPlane[i].mag > flowPlane[strongIdx].mag) strongIdx = i; } if(flowPlane[strongIdx].mag < 10) return dmtxBlankEdge; flow = flowPlane[strongIdx]; flowPos = FindStrongestNeighbor(dec, flow, +1); flowNeg = FindStrongestNeighbor(dec, flow, -1); if(flowPos.mag != 0 && flowNeg.mag != 0) { flowPosBack = FindStrongestNeighbor(dec, flowPos, -1); flowNegBack = FindStrongestNeighbor(dec, flowNeg, +1); if(flowPos.arrive == (flowPosBack.arrive+4)%8 && flowNeg.arrive == (flowNegBack.arrive+4)%8) { flow.arrive = dmtxNeighborNone; CALLBACK_POINT_PLOT(flow.loc, 1, 1, DMTX_DISPLAY_SQUARE); return flow; } } return dmtxBlankEdge;}/** * * */static intMatrixRegionOrientation(DmtxDecode *dec, DmtxRegion *reg, DmtxPointFlow begin){ int err; int cross; int minArea; int scale; int symbolShape; int maxDiagonal; DmtxBestLine line1x, line2x; DmtxBestLine line2n, line2p; DmtxFollow fTmp; if(dec->sizeIdxExpected == DmtxSymbolSquareAuto || (dec->sizeIdxExpected >= DmtxSymbol10x10 && dec->sizeIdxExpected <= DmtxSymbol144x144)) symbolShape = DmtxSymbolSquareAuto; else if(dec->sizeIdxExpected == DmtxSymbolRectAuto || (dec->sizeIdxExpected >= DmtxSymbol8x18 && dec->sizeIdxExpected <= DmtxSymbol16x48)) symbolShape = DmtxSymbolRectAuto; else symbolShape = DmtxSymbolShapeAuto; if(dec->edgeMax != -1) { if(symbolShape == DmtxSymbolRectAuto) maxDiagonal = (int)(1.23 * dec->edgeMax + 0.5); /* sqrt(5/4) + 10% */ else maxDiagonal = (int)(1.56 * dec->edgeMax + 0.5); /* sqrt(2) + 10% */ } else { maxDiagonal = -1; } /* Follow to end in both directions */ err = TrailBlazeContinuous(dec, reg, begin, maxDiagonal); if(err == DMTX_FAILURE || reg->stepsTotal < 40) { TrailClear(dec, reg, 0x40); return DMTX_FAILURE; } /* Filter out region candidates that are smaller than expected */ if(dec->edgeMin != -1) { scale = dmtxImageGetProp(dec->image, DmtxPropScale); if(symbolShape == DmtxSymbolSquareAuto) minArea = (dec->edgeMin * dec->edgeMin)/(scale * scale); else minArea = (2 * dec->edgeMin * dec->edgeMin)/(scale * scale); if((reg->boundMax.X - reg->boundMin.X) * (reg->boundMax.Y - reg->boundMin.Y) < minArea) { TrailClear(dec, reg, 0x40); return DMTX_FAILURE; } } line1x = FindBestSolidLine(dec, reg, 0, 0, +1, -1); if(line1x.mag < 5) { TrailClear(dec, reg, 0x40); return DMTX_FAILURE; } err = FindTravelLimits(dec, reg, &line1x); if(line1x.distSq < 100 || line1x.devn * 10 >= sqrt((double)line1x.distSq)) { TrailClear(dec, reg, 0x40); return DMTX_FAILURE; } assert(line1x.stepPos >= line1x.stepNeg); fTmp = FollowSeek(dec, reg, line1x.stepPos + 5); line2p = FindBestSolidLine(dec, reg, fTmp.step, line1x.stepNeg, +1, line1x.angle); fTmp = FollowSeek(dec, reg, line1x.stepNeg - 5); line2n = FindBestSolidLine(dec, reg, fTmp.step, line1x.stepPos, -1, line1x.angle); if(max(line2p.mag, line2n.mag) < 5) return DMTX_FAILURE; if(line2p.mag > line2n.mag) { line2x = line2p; err = FindTravelLimits(dec, reg, &line2x); if(line2x.distSq < 100 || line2x.devn * 10 >= sqrt((double)line2x.distSq)) return DMTX_FAILURE; cross = ((line1x.locPos.X - line1x.locNeg.X) * (line2x.locPos.Y - line2x.locNeg.Y)) - ((line1x.locPos.Y - line1x.locNeg.Y) * (line2x.locPos.X - line2x.locNeg.X)); if(cross > 0) { /* Condition 2 */ reg->polarity = +1; reg->locR = line2x.locPos; reg->stepR = line2x.stepPos; reg->locT = line1x.locNeg; reg->stepT = line1x.stepNeg; reg->leftLoc = line1x.locBeg; reg->leftAngle = line1x.angle; reg->bottomLoc = line2x.locBeg; reg->bottomAngle = line2x.angle; reg->leftLine = line1x; reg->bottomLine = line2x; } else { /* Condition 3 */ reg->polarity = -1; reg->locR = line1x.locNeg; reg->stepR = line1x.stepNeg; reg->locT = line2x.locPos; reg->stepT = line2x.stepPos; reg->leftLoc = line2x.locBeg; reg->leftAngle = line2x.angle; reg->bottomLoc = line1x.locBeg; reg->bottomAngle = line1x.angle; reg->leftLine = line2x; reg->bottomLine = line1x; } } else { line2x = line2n; err = FindTravelLimits(dec, reg, &line2x); if(line2x.distSq < 100 || line2x.devn / sqrt((double)line2x.distSq) >= 0.1) return DMTX_FAILURE; cross = ((line1x.locNeg.X - line1x.locPos.X) * (line2x.locNeg.Y - line2x.locPos.Y)) - ((line1x.locNeg.Y - line1x.locPos.Y) * (line2x.locNeg.X - line2x.locPos.X)); if(cross > 0) { /* Condition 1 */ reg->polarity = -1; reg->locR = line2x.locNeg; reg->stepR = line2x.stepNeg; reg->locT = line1x.locPos; reg->stepT = line1x.stepPos; reg->leftLoc = line1x.locBeg; reg->leftAngle = line1x.angle; reg->bottomLoc = line2x.locBeg; reg->bottomAngle = line2x.angle; reg->leftLine = line1x; reg->bottomLine = line2x; } else { /* Condition 4 */ reg->polarity = +1; reg->locR = line1x.locPos; reg->stepR = line1x.stepPos; reg->locT = line2x.locNeg; reg->stepT = line2x.stepNeg; reg->leftLoc = line2x.locBeg; reg->leftAngle = line2x.angle; reg->bottomLoc = line1x.locBeg; reg->bottomAngle = line1x.angle; reg->leftLine = line2x; reg->bottomLine = line1x; } }/* CALLBACK_POINT_PLOT(reg->locR, 2, 1, DMTX_DISPLAY_SQUARE); CALLBACK_POINT_PLOT(reg->locT, 2, 1, DMTX_DISPLAY_SQUARE); */ reg->leftKnown = reg->bottomKnown = 1; return DMTX_SUCCESS;}/** * * */static longDistanceSquared(DmtxPixelLoc a, DmtxPixelLoc b){ long xDelta, yDelta; xDelta = a.X - b.X; yDelta = a.Y - b.Y; return (xDelta * xDelta) + (yDelta * yDelta);}/** * * */static unsigned char *GetCacheAddress(DmtxDecode *dec, int x, int y){ int offset; offset = dmtxImageGetOffset(dec->image, x, y); if(offset == DMTX_BAD_OFFSET) return NULL; return &(dec->image->cache[offset]);}/** * * */extern intdmtxRegionUpdateCorners(DmtxDecode *dec, DmtxRegion *reg, DmtxVector2 p00, DmtxVector2 p10, DmtxVector2 p11, DmtxVector2 p01){ DmtxVector2 vOT, vOR, vTX, vRX, vTmp; double tx, ty, phi, shx, scx, scy, skx, sky; double dimOT, dimOR, dimTX, dimRX, ratio; DmtxMatrix3 m, mtxy, mphi, mshx, mscx, mscy, mscxy, msky, mskx; if(dmtxImageContainsFloat(dec->image, p00.X, p00.Y) == DMTX_FALSE || dmtxImageContainsFloat(dec->image, p01.X, p01.Y) == DMTX_FALSE || dmtxImageContainsFloat(dec->image, p10.X, p10.Y) == DMTX_FALSE) return DMTX_FAILURE; dimOT = dmtxVector2Mag(dmtxVector2Sub(&vOT, &p01, &p00)); /* XXX could use MagSquared() */ dimOR = dmtxVector2Mag(dmtxVector2Sub(&vOR, &p10, &p00)); dimTX = dmtxVector2Mag(dmtxVector2Sub(&vTX, &p11, &p01)); dimRX = dmtxVector2Mag(dmtxVector2Sub(&vRX, &p11, &p10)); /* Verify that sides are reasonably long */ if(dimOT <= 8.0 || dimOR <= 8.0 || dimTX <= 8.0 || dimRX <= 8.0) return DMTX_FAILURE; /* Verify that the 4 corners define a reasonably fat quadrilateral */ ratio = dimOT / dimRX; if(ratio <= 0.5 || ratio >= 2.0) return DMTX_FAILURE; ratio = dimOR / dimTX; if(ratio <= 0.5 || ratio >= 2.0) return DMTX_FAILURE; /* Verify this is not a bowtie shape */ if(dmtxVector2Cross(&vOR, &vRX) <= 0.0 || dmtxVector2Cross(&vOT, &vTX) >= 0.0) return DMTX_FAILURE; if(RightAngleTrueness(p00, p10, p11, M_PI_2) <= dec->squareDevn) return DMTX_FAILURE; if(RightAngleTrueness(p10, p11, p01, M_PI_2) <= dec->squareDevn) return DMTX_FAILURE; /* Calculate values needed for transformations */ tx = -1 * p00.X; ty = -1 * p00.Y; dmtxMatrix3Translate(mtxy, tx, ty); phi = atan2(vOT.X, vOT.Y); dmtxMatrix3Rotate(mphi, phi); dmtxMatrix3Multiply(m, mtxy, mphi); dmtxMatrix3VMultiply(&vTmp, &p10, m); shx = -vTmp.Y / vTmp.X; dmtxMatrix3Shear(mshx, 0.0, shx); dmtxMatrix3MultiplyBy(m, mshx); scx = 1.0/vTmp.X; dmtxMatrix3Scale(mscx, scx, 1.0); dmtxMatrix3MultiplyBy(m, mscx); dmtxMatrix3VMultiply(&vTmp, &p11, m); scy = 1.0/vTmp.Y; dmtxMatrix3Scale(mscy, 1.0, scy); dmtxMatrix3MultiplyBy(m, mscy); dmtxMatrix3VMultiply(&vTmp, &p11, m); skx = vTmp.X; dmtxMatrix3LineSkewSide(mskx, 1.0, skx, 1.0); dmtxMatrix3MultiplyBy(m, mskx); dmtxMatrix3VMultiply(&vTmp, &p01, m); sky = vTmp.Y; dmtxMatrix3LineSkewTop(msky, sky, 1.0, 1.0); dmtxMatrix3Multiply(reg->raw2fit, m, msky); /* Create inverse matrix by reverse (avoid straight matrix inversion) */ dmtxMatrix3LineSkewTopInv(msky, sky, 1.0, 1.0); dmtxMatrix3LineSkewSideInv(mskx, 1.0, skx, 1.0); dmtxMatrix3Multiply(m, msky, mskx); dmtxMatrix3Scale(mscxy, 1.0/scx, 1.0/scy); dmtxMatrix3MultiplyBy(m, mscxy); dmtxMatrix3Shear(mshx, 0.0, -shx);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -