You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
2237 lines
88 KiB
2237 lines
88 KiB
/*
|
|
DataMatrix.Net
|
|
|
|
DataMatrix.Net - .net library for decoding DataMatrix codes.
|
|
Copyright (C) 2009/2010 Michael Faschinger
|
|
|
|
This library is free software; you can redistribute it and/or
|
|
modify it under the terms of the GNU General Public
|
|
License as published by the Free Software Foundation; either
|
|
version 3.0 of the License, or (at your option) any later version.
|
|
You can also redistribute and/or modify it under the terms of the
|
|
GNU Lesser General Public License as published by the Free Software
|
|
Foundation; either version 3.0 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 of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
General Public License or the GNU Lesser General Public License
|
|
for more details.
|
|
|
|
You should have received a copy of the GNU General Public
|
|
License and the GNU Lesser General Public License along with this
|
|
library; if not, write to the Free Software Foundation, Inc.,
|
|
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
|
|
Contact: Michael Faschinger - michfasch@gmx.at
|
|
|
|
*/
|
|
|
|
using System;
|
|
using System.Collections.Generic;
|
|
|
|
namespace DataMatrix.net
|
|
{
|
|
internal class DmtxDecode
|
|
{
|
|
#region Fields
|
|
int _edgeMin;
|
|
int _edgeMax;
|
|
int _scanGap;
|
|
double _squareDevn;
|
|
DmtxSymbolSize _sizeIdxExpected;
|
|
int _edgeThresh;
|
|
|
|
/* Image modifiers */
|
|
int _xMin;
|
|
int _yMin;
|
|
int _xMax;
|
|
int _yMax;
|
|
int _scale;
|
|
/* Internals */
|
|
byte[] _cache;
|
|
DmtxImage _image;
|
|
DmtxScanGrid _grid;
|
|
#endregion
|
|
|
|
#region Constructors
|
|
internal DmtxDecode(DmtxImage img, int scale)
|
|
{
|
|
|
|
int width = img.Width / scale;
|
|
int height = img.Height / scale;
|
|
|
|
this._edgeMin = DmtxConstants.DmtxUndefined;
|
|
this._edgeMax = DmtxConstants.DmtxUndefined;
|
|
this._scanGap = 1;
|
|
this._squareDevn = Math.Cos(50.0 * (Math.PI / 180.0));
|
|
this._sizeIdxExpected = DmtxSymbolSize.DmtxSymbolShapeAuto;
|
|
this._edgeThresh = 10;
|
|
|
|
this._xMin = 0;
|
|
this._xMax = width - 1;
|
|
this._yMin = 0;
|
|
this._yMax = height - 1;
|
|
this._scale = scale;
|
|
|
|
this._cache = new byte[width * height];
|
|
|
|
this._image = img;
|
|
ValidateSettingsAndInitScanGrid();
|
|
}
|
|
#endregion
|
|
|
|
#region Methods
|
|
private void ValidateSettingsAndInitScanGrid()
|
|
{
|
|
if (this._squareDevn <= 0.0 || this._squareDevn >= 1.0)
|
|
{
|
|
throw new ArgumentException("Invalid decode settings!");
|
|
}
|
|
|
|
if (this._scanGap < 1)
|
|
{
|
|
throw new ArgumentException("Invalid decode settings!");
|
|
}
|
|
|
|
if (this._edgeThresh < 1 || this._edgeThresh > 100)
|
|
{
|
|
throw new ArgumentException("Invalid decode settings!");
|
|
}
|
|
|
|
/* Reinitialize scangrid in case any inputs changed */
|
|
this._grid = new DmtxScanGrid(this);
|
|
}
|
|
|
|
internal int GetCacheIndex(int x, int y)
|
|
{
|
|
if (x < 0 || x >= Width || y < 0 || y >= Height)
|
|
{
|
|
throw new ArgumentException("Error: x and/or y outside cache size");
|
|
}
|
|
|
|
return y * Width + x;
|
|
}
|
|
|
|
bool GetPixelValue(int x, int y, int channel, ref int value)
|
|
{
|
|
int xUnscaled = x * this._scale;
|
|
int yUnscaled = y * this._scale;
|
|
|
|
return this._image.GetPixelValue(xUnscaled, yUnscaled, channel, ref value);
|
|
|
|
}
|
|
|
|
internal void CacheFillQuad(DmtxPixelLoc p0, DmtxPixelLoc p1, DmtxPixelLoc p2, DmtxPixelLoc p3)
|
|
{
|
|
DmtxBresLine[] lines = new DmtxBresLine[4];
|
|
DmtxPixelLoc pEmpty = new DmtxPixelLoc { X = 0, Y = 0 };
|
|
int posY;
|
|
int i, idx;
|
|
|
|
lines[0] = new DmtxBresLine(p0, p1, pEmpty);
|
|
lines[1] = new DmtxBresLine(p1, p2, pEmpty);
|
|
lines[2] = new DmtxBresLine(p2, p3, pEmpty);
|
|
lines[3] = new DmtxBresLine(p3, p0, pEmpty);
|
|
|
|
int minY = this._yMax;
|
|
int maxY = 0;
|
|
|
|
minY = DmtxCommon.Min(minY, p0.Y);
|
|
maxY = DmtxCommon.Max(maxY, p0.Y);
|
|
minY = DmtxCommon.Min(minY, p1.Y);
|
|
maxY = DmtxCommon.Max(maxY, p1.Y);
|
|
minY = DmtxCommon.Min(minY, p2.Y);
|
|
maxY = DmtxCommon.Max(maxY, p2.Y);
|
|
minY = DmtxCommon.Min(minY, p3.Y);
|
|
maxY = DmtxCommon.Max(maxY, p3.Y);
|
|
|
|
int sizeY = maxY - minY + 1;
|
|
|
|
int[] scanlineMin = new int[sizeY];
|
|
int[] scanlineMax = new int[sizeY];
|
|
|
|
for (i = 0; i < sizeY; i++)
|
|
{
|
|
scanlineMin[i] = this._xMax;
|
|
}
|
|
|
|
for (i = 0; i < 4; i++)
|
|
{
|
|
while (lines[i].Loc.X != lines[i].Loc1.X || lines[i].Loc.Y != lines[i].Loc1.Y)
|
|
{
|
|
idx = lines[i].Loc.Y - minY;
|
|
scanlineMin[idx] = DmtxCommon.Min(scanlineMin[idx], lines[i].Loc.X);
|
|
scanlineMax[idx] = DmtxCommon.Max(scanlineMax[idx], lines[i].Loc.X);
|
|
lines[i].Step(1, 0);
|
|
}
|
|
}
|
|
|
|
for (posY = minY; posY < maxY && posY < this._yMax; posY++)
|
|
{
|
|
idx = posY - minY;
|
|
int posX;
|
|
for (posX = scanlineMin[idx]; posX < scanlineMax[idx] && posX < this._xMax; posX++)
|
|
{
|
|
if (posX < 0 || posY < 0)
|
|
{
|
|
continue;
|
|
}
|
|
try
|
|
{
|
|
int cacheIndex = GetCacheIndex(posX, posY);
|
|
this._cache[cacheIndex] |= 0x80;
|
|
}
|
|
catch
|
|
{
|
|
// FIXXXME: log here as soon as there is a logger
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
internal DmtxMessage MosaicRegion(DmtxRegion reg, int fix)
|
|
{
|
|
int colorPlane = reg.FlowBegin.Plane;
|
|
|
|
reg.FlowBegin.Plane = 0; /* kind of a hack */
|
|
DmtxMessage rMsg = MatrixRegion(reg, fix);
|
|
|
|
reg.FlowBegin.Plane = 1; /* kind of a hack */
|
|
DmtxMessage gMsg = MatrixRegion(reg, fix);
|
|
|
|
reg.FlowBegin.Plane = 2; /* kind of a hack */
|
|
DmtxMessage bMsg = MatrixRegion(reg, fix);
|
|
|
|
reg.FlowBegin.Plane = colorPlane;
|
|
|
|
DmtxMessage oMsg = new DmtxMessage(reg.SizeIdx, DmtxFormat.Mosaic);
|
|
|
|
List<byte> totalMessage = new List<byte>();
|
|
for (int i = 0; i < bMsg.OutputSize; i++)
|
|
{
|
|
if (bMsg.Output[i] == 0)
|
|
{
|
|
break;
|
|
}
|
|
totalMessage.Add(bMsg.Output[i]);
|
|
}
|
|
for (int i = 0; i < gMsg.OutputSize; i++)
|
|
{
|
|
if (gMsg.Output[i] == 0)
|
|
{
|
|
break;
|
|
}
|
|
totalMessage.Add(gMsg.Output[i]);
|
|
}
|
|
for (int i = 0; i < rMsg.OutputSize; i++)
|
|
{
|
|
if (rMsg.Output[i] == 0)
|
|
{
|
|
break;
|
|
}
|
|
totalMessage.Add(rMsg.Output[i]);
|
|
}
|
|
totalMessage.Add(0);
|
|
oMsg.Output = totalMessage.ToArray();
|
|
return oMsg;
|
|
}
|
|
|
|
internal DmtxMessage MatrixRegion(DmtxRegion reg, int fix)
|
|
{
|
|
DmtxMessage result = new DmtxMessage(reg.SizeIdx, DmtxFormat.Matrix);
|
|
DmtxVector2 topLeft = new DmtxVector2();
|
|
DmtxVector2 topRight = new DmtxVector2();
|
|
DmtxVector2 bottomLeft = new DmtxVector2();
|
|
DmtxVector2 bottomRight = new DmtxVector2();
|
|
DmtxPixelLoc pxTopLeft = new DmtxPixelLoc();
|
|
DmtxPixelLoc pxTopRight = new DmtxPixelLoc();
|
|
DmtxPixelLoc pxBottomLeft = new DmtxPixelLoc();
|
|
DmtxPixelLoc pxBottomRight = new DmtxPixelLoc();
|
|
|
|
if (!PopulateArrayFromMatrix(reg, result))
|
|
{
|
|
throw new Exception("Populating Array from matrix failed!");
|
|
}
|
|
|
|
/* maybe place remaining logic into new dmtxDecodePopulatedArray()
|
|
function so other people can pass in their own arrays */
|
|
|
|
ModulePlacementEcc200(result.Array, result.Code,
|
|
reg.SizeIdx, DmtxConstants.DmtxModuleOnRed | DmtxConstants.DmtxModuleOnGreen | DmtxConstants.DmtxModuleOnBlue);
|
|
|
|
if (DmtxCommon.DecodeCheckErrors(result.Code, 0, reg.SizeIdx, fix) != true)
|
|
{
|
|
return null;
|
|
}
|
|
|
|
topLeft.X = bottomLeft.X = topLeft.Y = topRight.Y = -0.1;
|
|
topRight.X = bottomRight.X = bottomLeft.Y = bottomRight.Y = 1.1;
|
|
|
|
topLeft *= reg.Fit2Raw;
|
|
topRight *= reg.Fit2Raw;
|
|
bottomLeft *= reg.Fit2Raw;
|
|
bottomRight *= reg.Fit2Raw;
|
|
|
|
pxTopLeft.X = (int)(0.5 + topLeft.X);
|
|
pxTopLeft.Y = (int)(0.5 + topLeft.Y);
|
|
pxBottomLeft.X = (int)(0.5 + bottomLeft.X);
|
|
pxBottomLeft.Y = (int)(0.5 + bottomLeft.Y);
|
|
pxTopRight.X = (int)(0.5 + topRight.X);
|
|
pxTopRight.Y = (int)(0.5 + topRight.Y);
|
|
pxBottomRight.X = (int)(0.5 + bottomRight.X);
|
|
pxBottomRight.Y = (int)(0.5 + bottomRight.Y);
|
|
|
|
CacheFillQuad(pxTopLeft, pxTopRight, pxBottomRight, pxBottomLeft);
|
|
|
|
result.DecodeDataStream(reg.SizeIdx, null);
|
|
|
|
return result;
|
|
}
|
|
|
|
private bool PopulateArrayFromMatrix(DmtxRegion reg, DmtxMessage msg)
|
|
{
|
|
/* Capture number of regions present in barcode */
|
|
int xRegionTotal = DmtxCommon.GetSymbolAttribute(DmtxSymAttribute.DmtxSymAttribHorizDataRegions, reg.SizeIdx);
|
|
int yRegionTotal = DmtxCommon.GetSymbolAttribute(DmtxSymAttribute.DmtxSymAttribVertDataRegions, reg.SizeIdx);
|
|
|
|
/* Capture region dimensions (not including border modules) */
|
|
int mapWidth = DmtxCommon.GetSymbolAttribute(DmtxSymAttribute.DmtxSymAttribDataRegionCols, reg.SizeIdx);
|
|
int mapHeight = DmtxCommon.GetSymbolAttribute(DmtxSymAttribute.DmtxSymAttribDataRegionRows, reg.SizeIdx);
|
|
|
|
int weightFactor = 2 * (mapHeight + mapWidth + 2);
|
|
if (weightFactor <= 0)
|
|
{
|
|
throw new ArgumentException("PopulateArrayFromMatrix error: Weight Factor must be greater 0");
|
|
}
|
|
|
|
/* Tally module changes for each region in each direction */
|
|
for (int yRegionCount = 0; yRegionCount < yRegionTotal; yRegionCount++)
|
|
{
|
|
|
|
/* Y location of mapping region origin in symbol coordinates */
|
|
int yOrigin = yRegionCount * (mapHeight + 2) + 1;
|
|
|
|
for (int xRegionCount = 0; xRegionCount < xRegionTotal; xRegionCount++)
|
|
{
|
|
int[,] tally = new int[24, 24]; /* Large enough to map largest single region */
|
|
/* X location of mapping region origin in symbol coordinates */
|
|
int xOrigin = xRegionCount * (mapWidth + 2) + 1;
|
|
|
|
for (int i = 0; i < 24; i++)
|
|
{
|
|
for (int j = 0; j < 24; j++)
|
|
{
|
|
tally[i, j] = 0;
|
|
}
|
|
}
|
|
TallyModuleJumps(reg, tally, xOrigin, yOrigin, mapWidth, mapHeight, DmtxDirection.DmtxDirUp);
|
|
TallyModuleJumps(reg, tally, xOrigin, yOrigin, mapWidth, mapHeight, DmtxDirection.DmtxDirLeft);
|
|
TallyModuleJumps(reg, tally, xOrigin, yOrigin, mapWidth, mapHeight, DmtxDirection.DmtxDirDown);
|
|
TallyModuleJumps(reg, tally, xOrigin, yOrigin, mapWidth, mapHeight, DmtxDirection.DmtxDirRight);
|
|
|
|
/* Decide module status based on final tallies */
|
|
for (int mapRow = 0; mapRow < mapHeight; mapRow++)
|
|
{
|
|
for (int mapCol = 0; mapCol < mapWidth; mapCol++)
|
|
{
|
|
|
|
int rowTmp = (yRegionCount * mapHeight) + mapRow;
|
|
rowTmp = yRegionTotal * mapHeight - rowTmp - 1;
|
|
int colTmp = (xRegionCount * mapWidth) + mapCol;
|
|
int idx = (rowTmp * xRegionTotal * mapWidth) + colTmp;
|
|
|
|
if (tally[mapRow, mapCol] / (double)weightFactor >= 0.5)
|
|
msg.Array[idx] = (byte)DmtxConstants.DmtxModuleOnRGB;
|
|
else
|
|
msg.Array[idx] = (byte)DmtxConstants.DmtxModuleOff;
|
|
|
|
msg.Array[idx] |= (byte)DmtxConstants.DmtxModuleAssigned;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
private void TallyModuleJumps(DmtxRegion reg, int[,] tally, int xOrigin, int yOrigin, int mapWidth, int mapHeight, DmtxDirection dir)
|
|
{
|
|
int extent;
|
|
int lineStart, lineStop;
|
|
int travelStart, travelStop;
|
|
int line;
|
|
|
|
if (!(dir == DmtxDirection.DmtxDirUp || dir == DmtxDirection.DmtxDirLeft || dir == DmtxDirection.DmtxDirDown || dir == DmtxDirection.DmtxDirRight))
|
|
{
|
|
throw new ArgumentException("Only orthogonal directions are allowed in tally module jumps!");
|
|
}
|
|
|
|
int travelStep = (dir == DmtxDirection.DmtxDirUp || dir == DmtxDirection.DmtxDirRight) ? 1 : -1;
|
|
|
|
/* Abstract row and column progress using pointers to allow grid
|
|
traversal in all 4 directions using same logic */
|
|
bool horizontal = false;
|
|
if ((dir & DmtxDirection.DmtxDirHorizontal) != 0x00)
|
|
{
|
|
horizontal = true;
|
|
extent = mapWidth;
|
|
lineStart = yOrigin;
|
|
lineStop = yOrigin + mapHeight;
|
|
travelStart = (travelStep == 1) ? xOrigin - 1 : xOrigin + mapWidth;
|
|
travelStop = (travelStep == 1) ? xOrigin + mapWidth : xOrigin - 1;
|
|
}
|
|
else
|
|
{
|
|
extent = mapHeight;
|
|
lineStart = xOrigin;
|
|
lineStop = xOrigin + mapWidth;
|
|
travelStart = (travelStep == 1) ? yOrigin - 1 : yOrigin + mapHeight;
|
|
travelStop = (travelStep == 1) ? yOrigin + mapHeight : yOrigin - 1;
|
|
}
|
|
|
|
bool darkOnLight = (reg.OffColor > reg.OnColor);
|
|
int jumpThreshold = Math.Abs((int)(0.4 * (reg.OffColor - reg.OnColor) + 0.5));
|
|
|
|
if (jumpThreshold < 0)
|
|
{
|
|
throw new Exception("Negative jump threshold is not allowed in tally module jumps");
|
|
}
|
|
|
|
|
|
for (line = lineStart; line < lineStop; line++)
|
|
{
|
|
|
|
/* Capture tModule for each leading border module as normal but
|
|
decide status based on predictable barcode border pattern */
|
|
|
|
int travel = travelStart;
|
|
int color = horizontal ? this.ReadModuleColor(reg, line, travel, reg.SizeIdx, reg.FlowBegin.Plane)
|
|
: this.ReadModuleColor(reg, travel, line, reg.SizeIdx, reg.FlowBegin.Plane);
|
|
int tModule = (darkOnLight) ? reg.OffColor - color : color - reg.OffColor;
|
|
|
|
int statusModule = (travelStep == 1 || (line & 0x01) == 0) ? DmtxConstants.DmtxModuleOnRGB : DmtxConstants.DmtxModuleOff;
|
|
|
|
int weight = extent;
|
|
|
|
while ((travel += travelStep) != travelStop)
|
|
{
|
|
|
|
int tPrev = tModule;
|
|
int statusPrev = statusModule;
|
|
|
|
/* For normal data-bearing modules capture color and decide
|
|
module status based on comparison to previous "known" module */
|
|
|
|
color = horizontal ? this.ReadModuleColor(reg, line, travel, reg.SizeIdx, reg.FlowBegin.Plane)
|
|
: this.ReadModuleColor(reg, travel, line, reg.SizeIdx, reg.FlowBegin.Plane);
|
|
tModule = (darkOnLight) ? reg.OffColor - color : color - reg.OffColor;
|
|
|
|
if (statusPrev == DmtxConstants.DmtxModuleOnRGB)
|
|
{
|
|
statusModule = tModule < tPrev - jumpThreshold ? DmtxConstants.DmtxModuleOff : DmtxConstants.DmtxModuleOnRGB;
|
|
}
|
|
else if (statusPrev == DmtxConstants.DmtxModuleOff)
|
|
{
|
|
statusModule = tModule > tPrev + jumpThreshold ? DmtxConstants.DmtxModuleOnRGB : DmtxConstants.DmtxModuleOff;
|
|
}
|
|
int mapCol;
|
|
int mapRow;
|
|
if (horizontal)
|
|
{
|
|
mapRow = line - yOrigin;
|
|
mapCol = travel - xOrigin;
|
|
}
|
|
else
|
|
{
|
|
mapRow = travel - yOrigin;
|
|
mapCol = line - xOrigin;
|
|
}
|
|
if (!(mapRow < 24 && mapCol < 24))
|
|
{
|
|
throw new Exception("Tally module mump failed, index out of range!");
|
|
}
|
|
|
|
if (statusModule == DmtxConstants.DmtxModuleOnRGB)
|
|
{
|
|
tally[mapRow, mapCol] += (2 * weight);
|
|
}
|
|
|
|
weight--;
|
|
}
|
|
|
|
if (weight != 0)
|
|
{
|
|
throw new Exception("Tally module jump failed, weight <> 0!");
|
|
}
|
|
}
|
|
}
|
|
|
|
private int ReadModuleColor(DmtxRegion reg, int symbolRow, int symbolCol, DmtxSymbolSize sizeIdx, int colorPlane)
|
|
{
|
|
int i;
|
|
int color;
|
|
double[] sampleX = { 0.5, 0.4, 0.5, 0.6, 0.5 };
|
|
double[] sampleY = { 0.5, 0.5, 0.4, 0.5, 0.6 };
|
|
DmtxVector2 p = new DmtxVector2();
|
|
|
|
int symbolRows = DmtxCommon.GetSymbolAttribute(DmtxSymAttribute.DmtxSymAttribSymbolRows, sizeIdx);
|
|
int symbolCols = DmtxCommon.GetSymbolAttribute(DmtxSymAttribute.DmtxSymAttribSymbolCols, sizeIdx);
|
|
|
|
int colorTmp = color = 0;
|
|
for (i = 0; i < 5; i++)
|
|
{
|
|
|
|
p.X = (1.0 / symbolCols) * (symbolCol + sampleX[i]);
|
|
p.Y = (1.0 / symbolRows) * (symbolRow + sampleY[i]);
|
|
|
|
p *= reg.Fit2Raw;
|
|
|
|
GetPixelValue((int)(p.X + 0.5), (int)(p.Y + 0.5), colorPlane, ref colorTmp);
|
|
color += colorTmp;
|
|
}
|
|
|
|
return color / 5;
|
|
}
|
|
|
|
internal static int ModulePlacementEcc200(byte[] modules, byte[] codewords, DmtxSymbolSize sizeIdx, int moduleOnColor)
|
|
{
|
|
if ((moduleOnColor & (DmtxConstants.DmtxModuleOnRed | DmtxConstants.DmtxModuleOnGreen | DmtxConstants.DmtxModuleOnBlue)) == 0)
|
|
{
|
|
throw new Exception("Error with module placement ECC 200");
|
|
}
|
|
|
|
int mappingRows = DmtxCommon.GetSymbolAttribute(DmtxSymAttribute.DmtxSymAttribMappingMatrixRows, sizeIdx);
|
|
int mappingCols = DmtxCommon.GetSymbolAttribute(DmtxSymAttribute.DmtxSymAttribMappingMatrixCols, sizeIdx);
|
|
|
|
/* Start in the nominal location for the 8th bit of the first character */
|
|
int chr = 0;
|
|
int row = 4;
|
|
int col = 0;
|
|
|
|
do
|
|
{
|
|
/* Repeatedly first check for one of the special corner cases */
|
|
if ((row == mappingRows) && (col == 0))
|
|
PatternShapeSpecial1(modules, mappingRows, mappingCols, codewords, chr++, moduleOnColor);
|
|
else if ((row == mappingRows - 2) && (col == 0) && (mappingCols % 4 != 0))
|
|
PatternShapeSpecial2(modules, mappingRows, mappingCols, codewords, chr++, moduleOnColor);
|
|
else if ((row == mappingRows - 2) && (col == 0) && (mappingCols % 8 == 4))
|
|
PatternShapeSpecial3(modules, mappingRows, mappingCols, codewords, chr++, moduleOnColor);
|
|
else if ((row == mappingRows + 4) && (col == 2) && (mappingCols % 8 == 0))
|
|
PatternShapeSpecial4(modules, mappingRows, mappingCols, codewords, chr++, moduleOnColor);
|
|
|
|
/* Sweep upward diagonally, inserting successive characters */
|
|
do
|
|
{
|
|
if ((row < mappingRows) && (col >= 0) && (modules[row * mappingCols + col] & DmtxConstants.DmtxModuleVisited) == 0)
|
|
PatternShapeStandard(modules, mappingRows, mappingCols, row, col, codewords, chr++, moduleOnColor);
|
|
row -= 2;
|
|
col += 2;
|
|
} while ((row >= 0) && (col < mappingCols));
|
|
row += 1;
|
|
col += 3;
|
|
|
|
/* Sweep downward diagonally, inserting successive characters */
|
|
do
|
|
{
|
|
if ((row >= 0) && (col < mappingCols) && (modules[row * mappingCols + col] & DmtxConstants.DmtxModuleVisited) == 0)
|
|
PatternShapeStandard(modules, mappingRows, mappingCols, row, col, codewords, chr++, moduleOnColor);
|
|
row += 2;
|
|
col -= 2;
|
|
} while ((row < mappingRows) && (col >= 0));
|
|
row += 3;
|
|
col += 1;
|
|
/* ... until the entire modules array is scanned */
|
|
} while ((row < mappingRows) || (col < mappingCols));
|
|
|
|
/* If lower righthand corner is untouched then fill in the fixed pattern */
|
|
if ((modules[mappingRows * mappingCols - 1] &
|
|
DmtxConstants.DmtxModuleVisited) == 0)
|
|
{
|
|
|
|
modules[mappingRows * mappingCols - 1] |= (byte)moduleOnColor;
|
|
modules[(mappingRows * mappingCols) - mappingCols - 2] |= (byte)moduleOnColor;
|
|
} /* XXX should this fixed pattern also be used in reading somehow? */
|
|
|
|
/* XXX compare that chr == region->dataSize here */
|
|
return chr; /* XXX number of codewords read off */
|
|
}
|
|
|
|
internal static void PatternShapeStandard(byte[] modules, int mappingRows, int mappingCols, int row, int col, byte[] codeword, int codeWordIndex, int moduleOnColor)
|
|
{
|
|
PlaceModule(modules, mappingRows, mappingCols, row - 2, col - 2, codeword, codeWordIndex, DmtxMaskBit.DmtxMaskBit1, moduleOnColor);
|
|
PlaceModule(modules, mappingRows, mappingCols, row - 2, col - 1, codeword, codeWordIndex, DmtxMaskBit.DmtxMaskBit2, moduleOnColor);
|
|
PlaceModule(modules, mappingRows, mappingCols, row - 1, col - 2, codeword, codeWordIndex, DmtxMaskBit.DmtxMaskBit3, moduleOnColor);
|
|
PlaceModule(modules, mappingRows, mappingCols, row - 1, col - 1, codeword, codeWordIndex, DmtxMaskBit.DmtxMaskBit4, moduleOnColor);
|
|
PlaceModule(modules, mappingRows, mappingCols, row - 1, col, codeword, codeWordIndex, DmtxMaskBit.DmtxMaskBit5, moduleOnColor);
|
|
PlaceModule(modules, mappingRows, mappingCols, row, col - 2, codeword, codeWordIndex, DmtxMaskBit.DmtxMaskBit6, moduleOnColor);
|
|
PlaceModule(modules, mappingRows, mappingCols, row, col - 1, codeword, codeWordIndex, DmtxMaskBit.DmtxMaskBit7, moduleOnColor);
|
|
PlaceModule(modules, mappingRows, mappingCols, row, col, codeword, codeWordIndex, DmtxMaskBit.DmtxMaskBit8, moduleOnColor);
|
|
}
|
|
|
|
internal static void PatternShapeSpecial1(byte[] modules, int mappingRows, int mappingCols, byte[] codeword, int codeWordIndex, int moduleOnColor)
|
|
{
|
|
PlaceModule(modules, mappingRows, mappingCols, mappingRows - 1, 0, codeword, codeWordIndex, DmtxMaskBit.DmtxMaskBit1, moduleOnColor);
|
|
PlaceModule(modules, mappingRows, mappingCols, mappingRows - 1, 1, codeword, codeWordIndex, DmtxMaskBit.DmtxMaskBit2, moduleOnColor);
|
|
PlaceModule(modules, mappingRows, mappingCols, mappingRows - 1, 2, codeword, codeWordIndex, DmtxMaskBit.DmtxMaskBit3, moduleOnColor);
|
|
PlaceModule(modules, mappingRows, mappingCols, 0, mappingCols - 2, codeword, codeWordIndex, DmtxMaskBit.DmtxMaskBit4, moduleOnColor);
|
|
PlaceModule(modules, mappingRows, mappingCols, 0, mappingCols - 1, codeword, codeWordIndex, DmtxMaskBit.DmtxMaskBit5, moduleOnColor);
|
|
PlaceModule(modules, mappingRows, mappingCols, 1, mappingCols - 1, codeword, codeWordIndex, DmtxMaskBit.DmtxMaskBit6, moduleOnColor);
|
|
PlaceModule(modules, mappingRows, mappingCols, 2, mappingCols - 1, codeword, codeWordIndex, DmtxMaskBit.DmtxMaskBit7, moduleOnColor);
|
|
PlaceModule(modules, mappingRows, mappingCols, 3, mappingCols - 1, codeword, codeWordIndex, DmtxMaskBit.DmtxMaskBit8, moduleOnColor);
|
|
}
|
|
|
|
internal static void PatternShapeSpecial2(byte[] modules, int mappingRows, int mappingCols, byte[] codeword, int codeWordIndex, int moduleOnColor)
|
|
{
|
|
PlaceModule(modules, mappingRows, mappingCols, mappingRows - 3, 0, codeword, codeWordIndex, DmtxMaskBit.DmtxMaskBit1, moduleOnColor);
|
|
PlaceModule(modules, mappingRows, mappingCols, mappingRows - 2, 0, codeword, codeWordIndex, DmtxMaskBit.DmtxMaskBit2, moduleOnColor);
|
|
PlaceModule(modules, mappingRows, mappingCols, mappingRows - 1, 0, codeword, codeWordIndex, DmtxMaskBit.DmtxMaskBit3, moduleOnColor);
|
|
PlaceModule(modules, mappingRows, mappingCols, 0, mappingCols - 4, codeword, codeWordIndex, DmtxMaskBit.DmtxMaskBit4, moduleOnColor);
|
|
PlaceModule(modules, mappingRows, mappingCols, 0, mappingCols - 3, codeword, codeWordIndex, DmtxMaskBit.DmtxMaskBit5, moduleOnColor);
|
|
PlaceModule(modules, mappingRows, mappingCols, 0, mappingCols - 2, codeword, codeWordIndex, DmtxMaskBit.DmtxMaskBit6, moduleOnColor);
|
|
PlaceModule(modules, mappingRows, mappingCols, 0, mappingCols - 1, codeword, codeWordIndex, DmtxMaskBit.DmtxMaskBit7, moduleOnColor);
|
|
PlaceModule(modules, mappingRows, mappingCols, 1, mappingCols - 1, codeword, codeWordIndex, DmtxMaskBit.DmtxMaskBit8, moduleOnColor);
|
|
}
|
|
|
|
internal static void PatternShapeSpecial3(byte[] modules, int mappingRows, int mappingCols, byte[] codeword, int codeWordIndex, int moduleOnColor)
|
|
{
|
|
PlaceModule(modules, mappingRows, mappingCols, mappingRows - 3, 0, codeword, codeWordIndex, DmtxMaskBit.DmtxMaskBit1, moduleOnColor);
|
|
PlaceModule(modules, mappingRows, mappingCols, mappingRows - 2, 0, codeword, codeWordIndex, DmtxMaskBit.DmtxMaskBit2, moduleOnColor);
|
|
PlaceModule(modules, mappingRows, mappingCols, mappingRows - 1, 0, codeword, codeWordIndex, DmtxMaskBit.DmtxMaskBit3, moduleOnColor);
|
|
PlaceModule(modules, mappingRows, mappingCols, 0, mappingCols - 2, codeword, codeWordIndex, DmtxMaskBit.DmtxMaskBit4, moduleOnColor);
|
|
PlaceModule(modules, mappingRows, mappingCols, 0, mappingCols - 1, codeword, codeWordIndex, DmtxMaskBit.DmtxMaskBit5, moduleOnColor);
|
|
PlaceModule(modules, mappingRows, mappingCols, 1, mappingCols - 1, codeword, codeWordIndex, DmtxMaskBit.DmtxMaskBit6, moduleOnColor);
|
|
PlaceModule(modules, mappingRows, mappingCols, 2, mappingCols - 1, codeword, codeWordIndex, DmtxMaskBit.DmtxMaskBit7, moduleOnColor);
|
|
PlaceModule(modules, mappingRows, mappingCols, 3, mappingCols - 1, codeword, codeWordIndex, DmtxMaskBit.DmtxMaskBit8, moduleOnColor);
|
|
}
|
|
|
|
internal static void PatternShapeSpecial4(byte[] modules, int mappingRows, int mappingCols, byte[] codeword, int codeWordIndex, int moduleOnColor)
|
|
{
|
|
PlaceModule(modules, mappingRows, mappingCols, mappingRows - 1, 0, codeword, codeWordIndex, DmtxMaskBit.DmtxMaskBit1, moduleOnColor);
|
|
PlaceModule(modules, mappingRows, mappingCols, mappingRows - 1, mappingCols - 1, codeword, codeWordIndex, DmtxMaskBit.DmtxMaskBit2, moduleOnColor);
|
|
PlaceModule(modules, mappingRows, mappingCols, 0, mappingCols - 3, codeword, codeWordIndex, DmtxMaskBit.DmtxMaskBit3, moduleOnColor);
|
|
PlaceModule(modules, mappingRows, mappingCols, 0, mappingCols - 2, codeword, codeWordIndex, DmtxMaskBit.DmtxMaskBit4, moduleOnColor);
|
|
PlaceModule(modules, mappingRows, mappingCols, 0, mappingCols - 1, codeword, codeWordIndex, DmtxMaskBit.DmtxMaskBit5, moduleOnColor);
|
|
PlaceModule(modules, mappingRows, mappingCols, 1, mappingCols - 3, codeword, codeWordIndex, DmtxMaskBit.DmtxMaskBit6, moduleOnColor);
|
|
PlaceModule(modules, mappingRows, mappingCols, 1, mappingCols - 2, codeword, codeWordIndex, DmtxMaskBit.DmtxMaskBit7, moduleOnColor);
|
|
PlaceModule(modules, mappingRows, mappingCols, 1, mappingCols - 1, codeword, codeWordIndex, DmtxMaskBit.DmtxMaskBit8, moduleOnColor);
|
|
}
|
|
|
|
internal static void PlaceModule(byte[] modules, int mappingRows, int mappingCols, int row, int col, byte[] codeword, int codeWordIndex, DmtxMaskBit mask, int moduleOnColor)
|
|
{
|
|
if (row < 0)
|
|
{
|
|
row += mappingRows;
|
|
col += 4 - ((mappingRows + 4) % 8);
|
|
}
|
|
if (col < 0)
|
|
{
|
|
col += mappingCols;
|
|
row += 4 - ((mappingCols + 4) % 8);
|
|
}
|
|
|
|
/* If module has already been assigned then we are decoding the pattern into codewords */
|
|
if ((modules[row * mappingCols + col] & DmtxConstants.DmtxModuleAssigned) != 0)
|
|
{
|
|
if ((modules[row * mappingCols + col] & moduleOnColor) != 0)
|
|
codeword[codeWordIndex] |= (byte)mask;
|
|
else
|
|
codeword[codeWordIndex] &= (byte)(0xff ^ (int)mask);
|
|
}
|
|
/* Otherwise we are encoding the codewords into a pattern */
|
|
else
|
|
{
|
|
if ((codeword[codeWordIndex] & (byte)mask) != 0x00)
|
|
modules[row * mappingCols + col] |= (byte)moduleOnColor;
|
|
|
|
modules[row * mappingCols + col] |= (byte)DmtxConstants.DmtxModuleAssigned;
|
|
}
|
|
|
|
modules[row * mappingCols + col] |= (byte)DmtxConstants.DmtxModuleVisited;
|
|
}
|
|
|
|
internal DmtxRegion RegionFindNext(TimeSpan timeout)
|
|
{
|
|
DmtxPixelLoc loc = new DmtxPixelLoc();
|
|
DateTime startTime = DateTime.Now;
|
|
/* Continue until we find a region or run out of chances */
|
|
for (; ; )
|
|
{
|
|
DmtxRange locStatus = this._grid.PopGridLocation(ref loc);
|
|
if (locStatus == DmtxRange.DmtxRangeEnd)
|
|
break;
|
|
|
|
/* Scan location for presence of valid barcode region */
|
|
DmtxRegion reg = RegionScanPixel(loc.X, loc.Y);
|
|
if (reg != null)
|
|
return reg;
|
|
|
|
/* Ran out of time? */
|
|
if (DateTime.Now - startTime > timeout)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
DmtxRegion RegionScanPixel(int x, int y)
|
|
{
|
|
DmtxRegion reg = new DmtxRegion();
|
|
DmtxPixelLoc loc = new DmtxPixelLoc {X = x, Y = y};
|
|
|
|
|
|
int cacheIndex = this.DecodeGetCache(loc.X, loc.Y);
|
|
if (cacheIndex == -1)
|
|
return null;
|
|
|
|
if (this._cache[cacheIndex] != 0x00)
|
|
return null;
|
|
|
|
/* Test for presence of any reasonable edge at this location */
|
|
DmtxPointFlow flowBegin = this.MatrixRegionSeekEdge(loc);
|
|
if (flowBegin.Mag < (int)(this._edgeThresh * 7.65 + 0.5))
|
|
return null;
|
|
|
|
/* Determine barcode orientation */
|
|
if (MatrixRegionOrientation(reg, flowBegin) == false)
|
|
return null;
|
|
if (RegionUpdateXfrms(reg) == false)
|
|
return null;
|
|
|
|
/* Define top edge */
|
|
if (MatrixRegionAlignCalibEdge(reg, DmtxEdge.DmtxEdgeTop) == false)
|
|
return null;
|
|
if (RegionUpdateXfrms(reg) == false)
|
|
return null;
|
|
|
|
/* Define right edge */
|
|
if (MatrixRegionAlignCalibEdge(reg, DmtxEdge.DmtxEdgeRight) == false)
|
|
return null;
|
|
if (RegionUpdateXfrms(reg) == false)
|
|
return null;
|
|
|
|
//CALLBACK_MATRIX(®);
|
|
|
|
/* Calculate the best fitting symbol size */
|
|
if (MatrixRegionFindSize(reg) == false)
|
|
return null;
|
|
|
|
/* Found a valid matrix region */
|
|
return new DmtxRegion(reg);
|
|
}
|
|
|
|
int DecodeGetCache(int x, int y)
|
|
{
|
|
int width = this.Width;
|
|
int height = this.Height;
|
|
|
|
if (x < 0 || x >= width || y < 0 || y >= height)
|
|
return DmtxConstants.DmtxUndefined;
|
|
return y * width + x;
|
|
}
|
|
|
|
DmtxPointFlow MatrixRegionSeekEdge(DmtxPixelLoc loc)
|
|
{
|
|
DmtxPointFlow[] flowPlane = new DmtxPointFlow[3];
|
|
|
|
int channelCount = _image.ChannelCount;
|
|
|
|
/* Find whether red, green, or blue shows the strongest edge */
|
|
int strongIdx = 0;
|
|
for (int i = 0; i < channelCount; i++)
|
|
{
|
|
flowPlane[i] = GetPointFlow(i, loc, DmtxConstants.DmtxNeighborNone);
|
|
if (i > 0 && flowPlane[i].Mag > flowPlane[strongIdx].Mag)
|
|
strongIdx = i;
|
|
}
|
|
|
|
if (flowPlane[strongIdx].Mag < 10)
|
|
return DmtxConstants.DmtxBlankEdge;
|
|
|
|
DmtxPointFlow flow = flowPlane[strongIdx];
|
|
|
|
DmtxPointFlow flowPos = this.FindStrongestNeighbor(flow, +1);
|
|
DmtxPointFlow flowNeg = this.FindStrongestNeighbor(flow, -1);
|
|
if (flowPos.Mag != 0 && flowNeg.Mag != 0)
|
|
{
|
|
DmtxPointFlow flowPosBack = FindStrongestNeighbor(flowPos, -1);
|
|
DmtxPointFlow flowNegBack = FindStrongestNeighbor(flowNeg, +1);
|
|
if (flowPos.Arrive == (flowPosBack.Arrive + 4) % 8 &&
|
|
flowNeg.Arrive == (flowNegBack.Arrive + 4) % 8)
|
|
{
|
|
flow.Arrive = DmtxConstants.DmtxNeighborNone;
|
|
//CALLBACK_POINT_PLOT(flow.Loc, 1, 1, 1);
|
|
return flow;
|
|
}
|
|
}
|
|
|
|
return DmtxConstants.DmtxBlankEdge;
|
|
}
|
|
|
|
DmtxPointFlow FindStrongestNeighbor(DmtxPointFlow center, int sign)
|
|
{
|
|
DmtxPixelLoc loc = new DmtxPixelLoc();
|
|
DmtxPointFlow[] flow = new DmtxPointFlow[8];
|
|
|
|
int attempt = (sign < 0) ? center.Depart : (center.Depart + 4) % 8;
|
|
|
|
int occupied = 0;
|
|
int strongIdx = DmtxConstants.DmtxUndefined;
|
|
for (int i = 0; i < 8; i++)
|
|
{
|
|
|
|
loc.X = center.Loc.X + DmtxConstants.DmtxPatternX[i];
|
|
loc.Y = center.Loc.Y + DmtxConstants.DmtxPatternY[i];
|
|
int cacheIndex = DecodeGetCache(loc.X, loc.Y);
|
|
if (cacheIndex == DmtxConstants.DmtxUndefined)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if ((this._cache[cacheIndex] & 0x80) != 0x00)
|
|
{
|
|
if (++occupied > 2)
|
|
return DmtxConstants.DmtxBlankEdge;
|
|
continue;
|
|
}
|
|
|
|
int attemptDiff = Math.Abs(attempt - i);
|
|
if (attemptDiff > 4)
|
|
attemptDiff = 8 - attemptDiff;
|
|
if (attemptDiff > 1)
|
|
continue;
|
|
|
|
flow[i] = GetPointFlow(center.Plane, loc, i);
|
|
|
|
if (strongIdx == DmtxConstants.DmtxUndefined || flow[i].Mag > flow[strongIdx].Mag ||
|
|
(flow[i].Mag == flow[strongIdx].Mag && ((i & 0x01) != 0)))
|
|
{
|
|
strongIdx = i;
|
|
}
|
|
}
|
|
|
|
return (strongIdx == DmtxConstants.DmtxUndefined) ? DmtxConstants.DmtxBlankEdge : flow[strongIdx];
|
|
}
|
|
|
|
DmtxPointFlow GetPointFlow(int colorPlane, DmtxPixelLoc loc, int arrive)
|
|
{
|
|
int[] coefficient = new[] { 0, 1, 2, 1, 0, -1, -2, -1 };
|
|
int patternIdx;
|
|
int compass;
|
|
int[] mag = new int[4];
|
|
int[] colorPattern = new int[8];
|
|
DmtxPointFlow flow = new DmtxPointFlow();
|
|
|
|
for (patternIdx = 0; patternIdx < 8; patternIdx++)
|
|
{
|
|
int xAdjust = loc.X + DmtxConstants.DmtxPatternX[patternIdx];
|
|
int yAdjust = loc.Y + DmtxConstants.DmtxPatternY[patternIdx];
|
|
bool err = GetPixelValue(xAdjust, yAdjust, colorPlane, ref colorPattern[patternIdx]);
|
|
if (err == false)
|
|
{
|
|
return DmtxConstants.DmtxBlankEdge;
|
|
}
|
|
}
|
|
|
|
/* Calculate this pixel's flow intensity for each direction (-45, 0, 45, 90) */
|
|
int compassMax = 0;
|
|
for (compass = 0; compass < 4; compass++)
|
|
{
|
|
|
|
/* Add portion from each position in the convolution matrix pattern */
|
|
for (patternIdx = 0; patternIdx < 8; patternIdx++)
|
|
{
|
|
|
|
int coefficientIdx = (patternIdx - compass + 8) % 8;
|
|
if (coefficient[coefficientIdx] == 0)
|
|
continue;
|
|
|
|
int color = colorPattern[patternIdx];
|
|
|
|
switch (coefficient[coefficientIdx])
|
|
{
|
|
case 2:
|
|
mag[compass] += 2 * color;
|
|
break;
|
|
case 1:
|
|
mag[compass] += color;
|
|
break;
|
|
case -2:
|
|
mag[compass] -= 2 * color;
|
|
break;
|
|
case -1:
|
|
mag[compass] -= color;
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* Identify strongest compass flow */
|
|
if (compass != 0 && Math.Abs(mag[compass]) > Math.Abs(mag[compassMax]))
|
|
compassMax = compass;
|
|
}
|
|
|
|
/* Convert signed compass direction into unique flow directions (0-7) */
|
|
flow.Plane = colorPlane;
|
|
flow.Arrive = arrive;
|
|
flow.Depart = (mag[compassMax] > 0) ? compassMax + 4 : compassMax;
|
|
flow.Mag = Math.Abs(mag[compassMax]);
|
|
flow.Loc = loc;
|
|
|
|
return flow;
|
|
}
|
|
|
|
bool MatrixRegionFindSize(DmtxRegion reg)
|
|
{
|
|
DmtxSymbolSize sizeIdxBeg, sizeIdxEnd;
|
|
DmtxSymbolSize sizeIdx;
|
|
int bestColorOffAvg;
|
|
|
|
DmtxSymbolSize bestSizeIdx = DmtxSymbolSize.DmtxSymbolShapeAuto;
|
|
int bestContrast = 0;
|
|
int bestColorOnAvg = bestColorOffAvg = 0;
|
|
|
|
if (this._sizeIdxExpected == DmtxSymbolSize.DmtxSymbolShapeAuto)
|
|
{
|
|
sizeIdxBeg = 0;
|
|
sizeIdxEnd = (DmtxSymbolSize)(DmtxConstants.DmtxSymbolSquareCount + DmtxConstants.DmtxSymbolRectCount);
|
|
}
|
|
else if (this._sizeIdxExpected == DmtxSymbolSize.DmtxSymbolSquareAuto)
|
|
{
|
|
sizeIdxBeg = 0;
|
|
sizeIdxEnd = (DmtxSymbolSize)DmtxConstants.DmtxSymbolSquareCount;
|
|
}
|
|
else if (this._sizeIdxExpected == DmtxSymbolSize.DmtxSymbolRectAuto)
|
|
{
|
|
sizeIdxBeg = (DmtxSymbolSize)DmtxConstants.DmtxSymbolSquareCount;
|
|
sizeIdxEnd = (DmtxSymbolSize)(DmtxConstants.DmtxSymbolSquareCount + DmtxConstants.DmtxSymbolRectCount);
|
|
}
|
|
else
|
|
{
|
|
sizeIdxBeg = this._sizeIdxExpected;
|
|
sizeIdxEnd = this._sizeIdxExpected + 1;
|
|
}
|
|
|
|
/* Test each barcode size to find best contrast in calibration modules */
|
|
for (sizeIdx = sizeIdxBeg; sizeIdx < sizeIdxEnd; sizeIdx++)
|
|
{
|
|
|
|
int symbolRows = DmtxCommon.GetSymbolAttribute(DmtxSymAttribute.DmtxSymAttribSymbolRows, sizeIdx);
|
|
int symbolCols = DmtxCommon.GetSymbolAttribute(DmtxSymAttribute.DmtxSymAttribSymbolCols, sizeIdx);
|
|
int colorOffAvg;
|
|
int colorOnAvg = colorOffAvg = 0;
|
|
|
|
/* Sum module colors along horizontal calibration bar */
|
|
int row = symbolRows - 1;
|
|
int col;
|
|
int color;
|
|
for (col = 0; col < symbolCols; col++)
|
|
{
|
|
color = ReadModuleColor(reg, row, col, sizeIdx, reg.FlowBegin.Plane);
|
|
if ((col & 0x01) != 0x00)
|
|
colorOffAvg += color;
|
|
else
|
|
colorOnAvg += color;
|
|
}
|
|
|
|
/* Sum module colors along vertical calibration bar */
|
|
col = symbolCols - 1;
|
|
for (row = 0; row < symbolRows; row++)
|
|
{
|
|
color = ReadModuleColor(reg, row, col, sizeIdx, reg.FlowBegin.Plane);
|
|
if ((row & 0x01) != 0x00)
|
|
colorOffAvg += color;
|
|
else
|
|
colorOnAvg += color;
|
|
}
|
|
|
|
colorOnAvg = (colorOnAvg * 2) / (symbolRows + symbolCols);
|
|
colorOffAvg = (colorOffAvg * 2) / (symbolRows + symbolCols);
|
|
|
|
int contrast = Math.Abs(colorOnAvg - colorOffAvg);
|
|
if (contrast < 20)
|
|
continue;
|
|
|
|
if (contrast > bestContrast)
|
|
{
|
|
bestContrast = contrast;
|
|
bestSizeIdx = sizeIdx;
|
|
bestColorOnAvg = colorOnAvg;
|
|
bestColorOffAvg = colorOffAvg;
|
|
}
|
|
}
|
|
|
|
/* If no sizes produced acceptable contrast then call it quits */
|
|
if (bestSizeIdx == DmtxSymbolSize.DmtxSymbolShapeAuto || bestContrast < 20)
|
|
return false;
|
|
|
|
reg.SizeIdx = bestSizeIdx;
|
|
reg.OnColor = bestColorOnAvg;
|
|
reg.OffColor = bestColorOffAvg;
|
|
|
|
reg.SymbolRows = DmtxCommon.GetSymbolAttribute(DmtxSymAttribute.DmtxSymAttribSymbolRows, reg.SizeIdx);
|
|
reg.SymbolCols = DmtxCommon.GetSymbolAttribute(DmtxSymAttribute.DmtxSymAttribSymbolCols, reg.SizeIdx);
|
|
reg.MappingRows = DmtxCommon.GetSymbolAttribute(DmtxSymAttribute.DmtxSymAttribMappingMatrixRows, reg.SizeIdx);
|
|
reg.MappingCols = DmtxCommon.GetSymbolAttribute(DmtxSymAttribute.DmtxSymAttribMappingMatrixCols, reg.SizeIdx);
|
|
|
|
/* Tally jumps on horizontal calibration bar to verify sizeIdx */
|
|
int jumpCount = this.CountJumpTally(reg, 0, reg.SymbolRows - 1, DmtxDirection.DmtxDirRight);
|
|
int errors = Math.Abs(1 + jumpCount - reg.SymbolCols);
|
|
if (jumpCount < 0 || errors > 2)
|
|
return false;
|
|
|
|
/* Tally jumps on vertical calibration bar to verify sizeIdx */
|
|
jumpCount = CountJumpTally(reg, reg.SymbolCols - 1, 0, DmtxDirection.DmtxDirUp);
|
|
errors = Math.Abs(1 + jumpCount - reg.SymbolRows);
|
|
if (jumpCount < 0 || errors > 2)
|
|
return false;
|
|
|
|
/* Tally jumps on horizontal finder bar to verify sizeIdx */
|
|
errors = CountJumpTally(reg, 0, 0, DmtxDirection.DmtxDirRight);
|
|
if (jumpCount < 0 || errors > 2)
|
|
return false;
|
|
|
|
/* Tally jumps on vertical finder bar to verify sizeIdx */
|
|
errors = CountJumpTally(reg, 0, 0, DmtxDirection.DmtxDirUp);
|
|
if (errors < 0 || errors > 2)
|
|
return false;
|
|
|
|
/* Tally jumps on surrounding whitespace, else fail */
|
|
errors = CountJumpTally(reg, 0, -1, DmtxDirection.DmtxDirRight);
|
|
if (errors < 0 || errors > 2)
|
|
return false;
|
|
|
|
errors = CountJumpTally(reg, -1, 0, DmtxDirection.DmtxDirUp);
|
|
if (errors < 0 || errors > 2)
|
|
return false;
|
|
|
|
errors = CountJumpTally(reg, 0, reg.SymbolRows, DmtxDirection.DmtxDirRight);
|
|
if (errors < 0 || errors > 2)
|
|
return false;
|
|
|
|
errors = CountJumpTally(reg, reg.SymbolCols, 0, DmtxDirection.DmtxDirUp);
|
|
if (errors < 0 || errors > 2)
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
int CountJumpTally(DmtxRegion reg, int xStart, int yStart, DmtxDirection dir)
|
|
{
|
|
int x, xInc = 0;
|
|
int y, yInc = 0;
|
|
int state = DmtxConstants.DmtxModuleOn;
|
|
int jumpCount = 0;
|
|
|
|
if (xStart != 0 && yStart != 0)
|
|
{
|
|
throw new Exception("CountJumpTally failed, xStart or yStart must be zero!");
|
|
}
|
|
|
|
if (dir == DmtxDirection.DmtxDirRight)
|
|
{
|
|
xInc = 1;
|
|
}
|
|
else
|
|
{
|
|
yInc = 1;
|
|
}
|
|
|
|
if (xStart == -1 || xStart == reg.SymbolCols ||
|
|
yStart == -1 || yStart == reg.SymbolRows)
|
|
{
|
|
state = DmtxConstants.DmtxModuleOff;
|
|
}
|
|
|
|
bool darkOnLight = (reg.OffColor > reg.OnColor);
|
|
int jumpThreshold = Math.Abs((int)(0.4 * (reg.OnColor - reg.OffColor) + 0.5));
|
|
int color = this.ReadModuleColor(reg, yStart, xStart, reg.SizeIdx, reg.FlowBegin.Plane);
|
|
int tModule = (darkOnLight) ? reg.OffColor - color : color - reg.OffColor;
|
|
|
|
for (x = xStart + xInc, y = yStart + yInc;
|
|
(dir == DmtxDirection.DmtxDirRight && x < reg.SymbolCols) ||
|
|
(dir == DmtxDirection.DmtxDirUp && y < reg.SymbolRows);
|
|
x += xInc, y += yInc)
|
|
{
|
|
|
|
int tPrev = tModule;
|
|
color = ReadModuleColor(reg, y, x, reg.SizeIdx, reg.FlowBegin.Plane);
|
|
tModule = (darkOnLight) ? reg.OffColor - color : color - reg.OffColor;
|
|
|
|
if (state == DmtxConstants.DmtxModuleOff)
|
|
{
|
|
if (tModule > tPrev + jumpThreshold)
|
|
{
|
|
jumpCount++;
|
|
state = DmtxConstants.DmtxModuleOn;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (tModule < tPrev - jumpThreshold)
|
|
{
|
|
jumpCount++;
|
|
state = DmtxConstants.DmtxModuleOff;
|
|
}
|
|
}
|
|
}
|
|
|
|
return jumpCount;
|
|
}
|
|
|
|
bool MatrixRegionOrientation(DmtxRegion reg, DmtxPointFlow begin)
|
|
{
|
|
int cross;
|
|
DmtxSymbolSize symbolShape;
|
|
int maxDiagonal;
|
|
DmtxBestLine line2X;
|
|
|
|
if (this._sizeIdxExpected == DmtxSymbolSize.DmtxSymbolSquareAuto ||
|
|
(this._sizeIdxExpected >= DmtxSymbolSize.DmtxSymbol10x10 &&
|
|
this._sizeIdxExpected <= DmtxSymbolSize.DmtxSymbol144x144))
|
|
symbolShape = DmtxSymbolSize.DmtxSymbolSquareAuto;
|
|
else if (this._sizeIdxExpected == DmtxSymbolSize.DmtxSymbolRectAuto ||
|
|
(this._sizeIdxExpected >= DmtxSymbolSize.DmtxSymbol8x18 &&
|
|
this._sizeIdxExpected <= DmtxSymbolSize.DmtxSymbol16x48))
|
|
symbolShape = DmtxSymbolSize.DmtxSymbolRectAuto;
|
|
else
|
|
symbolShape = DmtxSymbolSize.DmtxSymbolShapeAuto;
|
|
|
|
if (_edgeMax != DmtxConstants.DmtxUndefined)
|
|
{
|
|
if (symbolShape == DmtxSymbolSize.DmtxSymbolRectAuto)
|
|
maxDiagonal = (int)(1.23 * _edgeMax + 0.5); /* sqrt(5/4) + 10% */
|
|
else
|
|
maxDiagonal = (int)(1.56 * _edgeMax + 0.5); /* sqrt(2) + 10% */
|
|
}
|
|
else
|
|
{
|
|
maxDiagonal = DmtxConstants.DmtxUndefined;
|
|
}
|
|
|
|
/* Follow to end in both directions */
|
|
bool err = this.TrailBlazeContinuous(reg, begin, maxDiagonal);
|
|
if (err == false || reg.StepsTotal < 40)
|
|
{
|
|
TrailClear(reg, 0x40);
|
|
return false;
|
|
}
|
|
|
|
/* Filter out region candidates that are smaller than expected */
|
|
if (this._edgeMin != DmtxConstants.DmtxUndefined)
|
|
{
|
|
int scale = _scale;
|
|
|
|
int minArea;
|
|
if (symbolShape == DmtxSymbolSize.DmtxSymbolSquareAuto)
|
|
minArea = (this._edgeMin * this._edgeMin) / (scale * scale);
|
|
else
|
|
minArea = (2 * this._edgeMin * this._edgeMin) / (scale * scale);
|
|
|
|
if ((reg.BoundMax.X - reg.BoundMin.X) * (reg.BoundMax.Y - reg.BoundMin.Y) < minArea)
|
|
{
|
|
TrailClear(reg, 0x40);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
DmtxBestLine line1X = this.FindBestSolidLine(reg, 0, 0, 1, DmtxConstants.DmtxUndefined);
|
|
if (line1X.Mag < 5)
|
|
{
|
|
TrailClear(reg, 0x40);
|
|
return false;
|
|
}
|
|
|
|
this.FindTravelLimits(reg, ref line1X);
|
|
if (line1X.DistSq < 100 || line1X.Devn * 10 >= Math.Sqrt(line1X.DistSq))
|
|
{
|
|
TrailClear(reg, 0x40);
|
|
return false;
|
|
}
|
|
if (!(line1X.StepPos >= line1X.StepNeg))
|
|
{
|
|
throw new Exception("Error calculating matrix region orientation");
|
|
}
|
|
|
|
DmtxFollow fTmp = this.FollowSeek(reg, line1X.StepPos + 5);
|
|
DmtxBestLine line2P = this.FindBestSolidLine(reg, fTmp.Step, line1X.StepNeg, 1, line1X.Angle);
|
|
|
|
fTmp = FollowSeek(reg, line1X.StepNeg - 5);
|
|
DmtxBestLine line2N = this.FindBestSolidLine(reg, fTmp.Step, line1X.StepPos, -1, line1X.Angle);
|
|
if (DmtxCommon.Max(line2P.Mag, line2N.Mag) < 5)
|
|
return false;
|
|
|
|
if (line2P.Mag > line2N.Mag)
|
|
{
|
|
line2X = line2P;
|
|
this.FindTravelLimits(reg, ref line2X);
|
|
if (line2X.DistSq < 100 || line2X.Devn * 10 >= Math.Sqrt(line2X.DistSq))
|
|
return false;
|
|
|
|
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;
|
|
this.FindTravelLimits(reg, ref line2X);
|
|
if (line2X.DistSq < 100 || line2X.Devn / Math.Sqrt(line2X.DistSq) >= 0.1)
|
|
return false;
|
|
|
|
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;
|
|
}
|
|
}
|
|
|
|
reg.LeftKnown = reg.BottomKnown = 1;
|
|
|
|
return true;
|
|
}
|
|
|
|
DmtxBestLine FindBestSolidLine(DmtxRegion reg, int step0, int step1, int streamDir, int houghAvoid)
|
|
{
|
|
int[,] hough = new int[3, DmtxConstants.DmtxHoughRes];
|
|
char[] houghTest = new char[DmtxConstants.DmtxHoughRes];
|
|
int i;
|
|
int step;
|
|
int sign = 0;
|
|
int tripSteps = 0;
|
|
DmtxBestLine line = new DmtxBestLine();
|
|
|
|
int angleBest = 0;
|
|
int hOffsetBest = 0;
|
|
|
|
/* Always follow path flowing away from the trail start */
|
|
if (step0 != 0)
|
|
{
|
|
if (step0 > 0)
|
|
{
|
|
sign = +1;
|
|
tripSteps = (step1 - step0 + reg.StepsTotal) % reg.StepsTotal;
|
|
}
|
|
else
|
|
{
|
|
sign = -1;
|
|
tripSteps = (step0 - step1 + reg.StepsTotal) % reg.StepsTotal;
|
|
}
|
|
if (tripSteps == 0)
|
|
tripSteps = reg.StepsTotal;
|
|
}
|
|
else if (step1 != 0)
|
|
{
|
|
sign = (step1 > 0) ? +1 : -1;
|
|
tripSteps = Math.Abs(step1);
|
|
}
|
|
else if (step1 == 0)
|
|
{
|
|
sign = +1;
|
|
tripSteps = reg.StepsTotal;
|
|
}
|
|
if (sign != streamDir)
|
|
{
|
|
throw new Exception("Sign must equal stream direction!");
|
|
}
|
|
|
|
DmtxFollow follow = this.FollowSeek(reg, step0);
|
|
DmtxPixelLoc rHp = follow.Loc;
|
|
|
|
line.StepBeg = line.StepPos = line.StepNeg = step0;
|
|
line.LocBeg = follow.Loc;
|
|
line.LocPos = follow.Loc;
|
|
line.LocNeg = follow.Loc;
|
|
|
|
/* Predetermine which angles to test */
|
|
for (i = 0; i < DmtxConstants.DmtxHoughRes; i++)
|
|
{
|
|
if (houghAvoid == DmtxConstants.DmtxUndefined)
|
|
{
|
|
houghTest[i] = (char)1;
|
|
}
|
|
else
|
|
{
|
|
int houghMin = (houghAvoid + DmtxConstants.DmtxHoughRes / 6) % DmtxConstants.DmtxHoughRes;
|
|
int houghMax = (houghAvoid - DmtxConstants.DmtxHoughRes / 6 + DmtxConstants.DmtxHoughRes) % DmtxConstants.DmtxHoughRes;
|
|
if (houghMin > houghMax)
|
|
houghTest[i] = (i > houghMin || i < houghMax) ? (char)1 : (char)0;
|
|
else
|
|
houghTest[i] = (i > houghMin && i < houghMax) ? (char)1 : (char)0;
|
|
}
|
|
}
|
|
|
|
/* Test each angle for steps along path */
|
|
for (step = 0; step < tripSteps; step++)
|
|
{
|
|
|
|
int xDiff = follow.Loc.X - rHp.X;
|
|
int yDiff = follow.Loc.Y - rHp.Y;
|
|
|
|
/* Increment Hough accumulator */
|
|
for (i = 0; i < DmtxConstants.DmtxHoughRes; i++)
|
|
{
|
|
|
|
if (houghTest[i] == 0)
|
|
continue;
|
|
|
|
int dH = (DmtxConstants.rHvX[i] * yDiff) - (DmtxConstants.rHvY[i] * xDiff);
|
|
if (dH >= -384 && dH <= 384)
|
|
{
|
|
int hOffset;
|
|
if (dH > 128)
|
|
hOffset = 2;
|
|
else if (dH >= -128)
|
|
hOffset = 1;
|
|
else
|
|
hOffset = 0;
|
|
|
|
hough[hOffset, i]++;
|
|
|
|
/* New angle takes over lead */
|
|
if (hough[hOffset, i] > hough[hOffsetBest, angleBest])
|
|
{
|
|
angleBest = i;
|
|
hOffsetBest = hOffset;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* CALLBACK_POINT_PLOT(follow.loc, (sign > 1) ? 4 : 3, 1, 2); */
|
|
|
|
follow = FollowStep(reg, follow, sign);
|
|
}
|
|
|
|
line.Angle = angleBest;
|
|
line.HOffset = hOffsetBest;
|
|
line.Mag = hough[hOffsetBest, angleBest];
|
|
|
|
return line;
|
|
}
|
|
|
|
DmtxFollow FollowSeek(DmtxRegion reg, int seek)
|
|
{
|
|
int i;
|
|
DmtxFollow follow = new DmtxFollow {Loc = reg.FlowBegin.Loc, Step = 0, Ptr = this._cache};
|
|
|
|
follow.PtrIndex = DecodeGetCache(follow.Loc.X, follow.Loc.Y);
|
|
|
|
int sign = (seek > 0) ? +1 : -1;
|
|
for (i = 0; i != seek; i += sign)
|
|
{
|
|
follow = FollowStep(reg, follow, sign);
|
|
if (Math.Abs(follow.Step) > reg.StepsTotal)
|
|
{
|
|
throw new Exception("Follow step count larger total step count!");
|
|
}
|
|
}
|
|
return follow;
|
|
}
|
|
|
|
|
|
bool TrailBlazeContinuous(DmtxRegion reg, DmtxPointFlow flowBegin, int maxDiagonal)
|
|
{
|
|
int negAssigns;
|
|
int sign;
|
|
DmtxPixelLoc boundMax;
|
|
|
|
DmtxPixelLoc boundMin = boundMax = flowBegin.Loc;
|
|
int cacheBegIndex = DecodeGetCache(flowBegin.Loc.X, flowBegin.Loc.Y);
|
|
this._cache[cacheBegIndex] = 0x80 | 0x40;
|
|
|
|
reg.FlowBegin = flowBegin;
|
|
|
|
int posAssigns = negAssigns = 0;
|
|
for (sign = 1; sign >= -1; sign -= 2)
|
|
{
|
|
|
|
DmtxPointFlow flow = flowBegin;
|
|
int cacheIndex = cacheBegIndex;
|
|
|
|
int steps;
|
|
for (steps = 0; ; steps++)
|
|
{
|
|
|
|
if (maxDiagonal != DmtxConstants.DmtxUndefined && (boundMax.X - boundMin.X > maxDiagonal ||
|
|
boundMax.Y - boundMin.Y > maxDiagonal))
|
|
break;
|
|
|
|
/* Find the strongest eligible neighbor */
|
|
DmtxPointFlow flowNext = FindStrongestNeighbor(flow, sign);
|
|
if (flowNext.Mag < 50)
|
|
break;
|
|
|
|
/* Get the neighbor's cache location */
|
|
int cacheNextIndex = DecodeGetCache(flowNext.Loc.X, flowNext.Loc.Y);
|
|
if ((this._cache[cacheNextIndex] & 0x80) != 0)
|
|
{
|
|
throw new Exception("Error creating Trail Blaze");
|
|
}
|
|
|
|
/* Mark departure from current location. If flowing downstream
|
|
* (sign < 0) then departure vector here is the arrival vector
|
|
* of the next location. Upstream flow uses the opposite rule. */
|
|
this._cache[cacheIndex] |= (sign < 0) ? (byte)(flowNext.Arrive) : (byte)(flowNext.Arrive << 3);
|
|
|
|
/* Mark known direction for next location */
|
|
/* If testing downstream (sign < 0) then next upstream is opposite of next arrival */
|
|
/* If testing upstream (sign > 0) then next downstream is opposite of next arrival */
|
|
this._cache[cacheNextIndex] = (sign < 0) ? (byte)(((flowNext.Arrive + 4) % 8) << 3) : (byte)((flowNext.Arrive + 4) % 8);
|
|
this._cache[cacheNextIndex] |= (0x80 | 0x40); /* Mark location as visited and assigned */
|
|
|
|
if (sign > 0)
|
|
posAssigns++;
|
|
else
|
|
negAssigns++;
|
|
cacheIndex = cacheNextIndex;
|
|
flow = flowNext;
|
|
|
|
if (flow.Loc.X > boundMax.X)
|
|
boundMax.X = flow.Loc.X;
|
|
else if (flow.Loc.X < boundMin.X)
|
|
boundMin.X = flow.Loc.X;
|
|
if (flow.Loc.Y > boundMax.Y)
|
|
boundMax.Y = flow.Loc.Y;
|
|
else if (flow.Loc.Y < boundMin.Y)
|
|
boundMin.Y = flow.Loc.Y;
|
|
|
|
/* CALLBACK_POINT_PLOT(flow.loc, (sign > 0) ? 2 : 3, 1, 2); */
|
|
}
|
|
|
|
if (sign > 0)
|
|
{
|
|
reg.FinalPos = flow.Loc;
|
|
reg.JumpToNeg = steps;
|
|
}
|
|
else
|
|
{
|
|
reg.FinalNeg = flow.Loc;
|
|
reg.JumpToPos = steps;
|
|
}
|
|
}
|
|
reg.StepsTotal = reg.JumpToPos + reg.JumpToNeg;
|
|
reg.BoundMin = boundMin;
|
|
reg.BoundMax = boundMax;
|
|
|
|
/* Clear "visited" bit from trail */
|
|
int clears = this.TrailClear(reg, 0x80);
|
|
if (posAssigns + negAssigns != clears - 1)
|
|
{
|
|
throw new Exception("Error cleaning after trail blaze continuous");
|
|
}
|
|
|
|
/* XXX clean this up ... redundant test above */
|
|
if (maxDiagonal != DmtxConstants.DmtxUndefined && (boundMax.X - boundMin.X > maxDiagonal ||
|
|
boundMax.Y - boundMin.Y > maxDiagonal))
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
int TrailClear(DmtxRegion reg, int clearMask)
|
|
{
|
|
if ((clearMask | 0xff) != 0xff)
|
|
{
|
|
throw new Exception("TrailClear mask is invalid!");
|
|
}
|
|
|
|
/* Clear "visited" bit from trail */
|
|
int clears = 0;
|
|
DmtxFollow follow = this.FollowSeek(reg, 0);
|
|
while (Math.Abs(follow.Step) <= reg.StepsTotal)
|
|
{
|
|
if ((follow.CurrentPtr & clearMask) == 0x00)
|
|
{
|
|
throw new Exception("Error performing TrailClear");
|
|
}
|
|
follow.CurrentPtr &= (byte)(clearMask ^ 0xff);
|
|
follow = FollowStep(reg, follow, +1);
|
|
clears++;
|
|
}
|
|
|
|
return clears;
|
|
}
|
|
|
|
DmtxFollow FollowStep(DmtxRegion reg, DmtxFollow followBeg, int sign)
|
|
{
|
|
int stepMod;
|
|
DmtxFollow follow = new DmtxFollow();
|
|
|
|
|
|
if (Math.Abs(sign) != 1)
|
|
{
|
|
throw new Exception("Invalid parameter 'sign', can only be -1 or +1");
|
|
}
|
|
|
|
int factor = reg.StepsTotal + 1;
|
|
if (sign > 0)
|
|
stepMod = (factor + (followBeg.Step % factor)) % factor;
|
|
else
|
|
stepMod = (factor - (followBeg.Step % factor)) % factor;
|
|
|
|
/* End of positive trail -- magic jump */
|
|
if (sign > 0 && stepMod == reg.JumpToNeg)
|
|
{
|
|
follow.Loc = reg.FinalNeg;
|
|
}
|
|
/* End of negative trail -- magic jump */
|
|
else if (sign < 0 && stepMod == reg.JumpToPos)
|
|
{
|
|
follow.Loc = reg.FinalPos;
|
|
}
|
|
/* Trail in progress -- normal jump */
|
|
else
|
|
{
|
|
int patternIdx = (sign < 0) ? followBeg.Neighbor & 0x07 : ((followBeg.Neighbor & 0x38) >> 3);
|
|
follow.Loc = new DmtxPixelLoc { X = followBeg.Loc.X + DmtxConstants.DmtxPatternX[patternIdx], Y = followBeg.Loc.Y + DmtxConstants.DmtxPatternY[patternIdx] };
|
|
}
|
|
|
|
follow.Step = followBeg.Step + sign;
|
|
follow.Ptr = this._cache;
|
|
follow.PtrIndex = DecodeGetCache(follow.Loc.X, follow.Loc.Y);
|
|
|
|
return follow;
|
|
}
|
|
|
|
void FindTravelLimits(DmtxRegion reg, ref DmtxBestLine line)
|
|
{
|
|
int i;
|
|
int negTravel;
|
|
int posWanderMin, posWanderMax, posWanderMinLock, posWanderMaxLock;
|
|
int negWanderMin, negWanderMax, negWanderMinLock, negWanderMaxLock;
|
|
DmtxFollow followNeg;
|
|
DmtxPixelLoc negMax;
|
|
|
|
/* line->stepBeg is already known to sit on the best Hough line */
|
|
DmtxFollow followPos = followNeg = this.FollowSeek(reg, line.StepBeg);
|
|
DmtxPixelLoc loc0 = followPos.Loc;
|
|
|
|
int cosAngle = DmtxConstants.rHvX[line.Angle];
|
|
int sinAngle = DmtxConstants.rHvY[line.Angle];
|
|
|
|
int distSqMax = 0;
|
|
DmtxPixelLoc posMax = negMax = followPos.Loc;
|
|
|
|
int posTravel = negTravel = 0;
|
|
int posWander = posWanderMin = posWanderMax = posWanderMinLock = posWanderMaxLock = 0;
|
|
int negWander = negWanderMin = negWanderMax = negWanderMinLock = negWanderMaxLock = 0;
|
|
|
|
for (i = 0; i < reg.StepsTotal / 2; i++)
|
|
{
|
|
|
|
bool posRunning = (i < 10 || Math.Abs(posWander) < Math.Abs(posTravel));
|
|
bool negRunning = (i < 10 || Math.Abs(negWander) < Math.Abs(negTravel));
|
|
|
|
int distSq;
|
|
int xDiff;
|
|
int yDiff;
|
|
if (posRunning)
|
|
{
|
|
xDiff = followPos.Loc.X - loc0.X;
|
|
yDiff = followPos.Loc.Y - loc0.Y;
|
|
posTravel = (cosAngle * xDiff) + (sinAngle * yDiff);
|
|
posWander = (cosAngle * yDiff) - (sinAngle * xDiff);
|
|
|
|
if (posWander >= -3 * 256 && posWander <= 3 * 256)
|
|
{
|
|
distSq = DistanceSquared(followPos.Loc, negMax);
|
|
if (distSq > distSqMax)
|
|
{
|
|
posMax = followPos.Loc;
|
|
distSqMax = distSq;
|
|
line.StepPos = followPos.Step;
|
|
line.LocPos = followPos.Loc;
|
|
posWanderMinLock = posWanderMin;
|
|
posWanderMaxLock = posWanderMax;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
posWanderMin = DmtxCommon.Min(posWanderMin, posWander);
|
|
posWanderMax = DmtxCommon.Max(posWanderMax, posWander);
|
|
}
|
|
}
|
|
else if (!negRunning)
|
|
{
|
|
break;
|
|
}
|
|
|
|
if (negRunning)
|
|
{
|
|
xDiff = followNeg.Loc.X - loc0.X;
|
|
yDiff = followNeg.Loc.Y - loc0.Y;
|
|
negTravel = (cosAngle * xDiff) + (sinAngle * yDiff);
|
|
negWander = (cosAngle * yDiff) - (sinAngle * xDiff);
|
|
|
|
if (negWander >= -3 * 256 && negWander < 3 * 256)
|
|
{
|
|
distSq = DistanceSquared(followNeg.Loc, posMax);
|
|
if (distSq > distSqMax)
|
|
{
|
|
negMax = followNeg.Loc;
|
|
distSqMax = distSq;
|
|
line.StepNeg = followNeg.Step;
|
|
line.LocNeg = followNeg.Loc;
|
|
negWanderMinLock = negWanderMin;
|
|
negWanderMaxLock = negWanderMax;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
negWanderMin = DmtxCommon.Min(negWanderMin, negWander);
|
|
negWanderMax = DmtxCommon.Max(negWanderMax, negWander);
|
|
}
|
|
}
|
|
|
|
followPos = FollowStep(reg, followPos, +1);
|
|
followNeg = FollowStep(reg, followNeg, -1);
|
|
}
|
|
line.Devn = DmtxCommon.Max(posWanderMaxLock - posWanderMinLock, negWanderMaxLock - negWanderMinLock) / 256;
|
|
line.DistSq = distSqMax;
|
|
|
|
return;
|
|
}
|
|
|
|
int DistanceSquared(DmtxPixelLoc a, DmtxPixelLoc b)
|
|
{
|
|
int xDelta = a.X - b.X;
|
|
int yDelta = a.Y - b.Y;
|
|
|
|
return (xDelta * xDelta) + (yDelta * yDelta);
|
|
}
|
|
|
|
bool RegionUpdateXfrms(DmtxRegion reg)
|
|
{
|
|
DmtxRay2 rLeft = new DmtxRay2();
|
|
DmtxRay2 rBottom = new DmtxRay2();
|
|
DmtxRay2 rTop = new DmtxRay2();
|
|
DmtxRay2 rRight = new DmtxRay2();
|
|
DmtxVector2 p00 = new DmtxVector2();
|
|
DmtxVector2 p10 = new DmtxVector2();
|
|
DmtxVector2 p11 = new DmtxVector2();
|
|
DmtxVector2 p01 = new DmtxVector2();
|
|
|
|
if (!(reg.LeftKnown != 0 && reg.BottomKnown != 0))
|
|
{
|
|
throw new ArgumentException("Error updating Xfrms!");
|
|
}
|
|
|
|
/* Build ray representing left edge */
|
|
rLeft.P.X = reg.LeftLoc.X;
|
|
rLeft.P.Y = reg.LeftLoc.Y;
|
|
double radians = reg.LeftAngle * (Math.PI / DmtxConstants.DmtxHoughRes);
|
|
rLeft.V.X = Math.Cos(radians);
|
|
rLeft.V.Y = Math.Sin(radians);
|
|
rLeft.TMin = 0.0;
|
|
rLeft.TMax = rLeft.V.Norm();
|
|
|
|
/* Build ray representing bottom edge */
|
|
rBottom.P.X = reg.BottomLoc.X;
|
|
rBottom.P.Y = reg.BottomLoc.Y;
|
|
radians = reg.BottomAngle * (Math.PI / DmtxConstants.DmtxHoughRes);
|
|
rBottom.V.X = Math.Cos(radians);
|
|
rBottom.V.Y = Math.Sin(radians);
|
|
rBottom.TMin = 0.0;
|
|
rBottom.TMax = rBottom.V.Norm();
|
|
|
|
/* Build ray representing top edge */
|
|
if (reg.TopKnown != 0)
|
|
{
|
|
rTop.P.X = reg.TopLoc.X;
|
|
rTop.P.Y = reg.TopLoc.Y;
|
|
radians = reg.TopAngle * (Math.PI / DmtxConstants.DmtxHoughRes);
|
|
rTop.V.X = Math.Cos(radians);
|
|
rTop.V.Y = Math.Sin(radians);
|
|
rTop.TMin = 0.0;
|
|
rTop.TMax = rTop.V.Norm();
|
|
}
|
|
else
|
|
{
|
|
rTop.P.X = reg.LocT.X;
|
|
rTop.P.Y = reg.LocT.Y;
|
|
radians = reg.BottomAngle * (Math.PI / DmtxConstants.DmtxHoughRes);
|
|
rTop.V.X = Math.Cos(radians);
|
|
rTop.V.Y = Math.Sin(radians);
|
|
rTop.TMin = 0.0;
|
|
rTop.TMax = rBottom.TMax;
|
|
}
|
|
|
|
/* Build ray representing right edge */
|
|
if (reg.RightKnown != 0)
|
|
{
|
|
rRight.P.X = reg.RightLoc.X;
|
|
rRight.P.Y = reg.RightLoc.Y;
|
|
radians = reg.RightAngle * (Math.PI / DmtxConstants.DmtxHoughRes);
|
|
rRight.V.X = Math.Cos(radians);
|
|
rRight.V.Y = Math.Sin(radians);
|
|
rRight.TMin = 0.0;
|
|
rRight.TMax = rRight.V.Norm();
|
|
}
|
|
else
|
|
{
|
|
rRight.P.X = reg.LocR.X;
|
|
rRight.P.Y = reg.LocR.Y;
|
|
radians = reg.LeftAngle * (Math.PI / DmtxConstants.DmtxHoughRes);
|
|
rRight.V.X = Math.Cos(radians);
|
|
rRight.V.Y = Math.Sin(radians);
|
|
rRight.TMin = 0.0;
|
|
rRight.TMax = rLeft.TMax;
|
|
}
|
|
|
|
/* Calculate 4 corners, real or imagined */
|
|
if (!p00.Intersect(rLeft, rBottom))
|
|
return false;
|
|
|
|
if (!p10.Intersect(rBottom, rRight))
|
|
return false;
|
|
|
|
if (!p11.Intersect(rRight, rTop))
|
|
return false;
|
|
|
|
if (!p01.Intersect(rTop, rLeft))
|
|
return false;
|
|
|
|
if (!RegionUpdateCorners(reg, p00, p10, p11, p01))
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
bool RegionUpdateCorners(DmtxRegion reg, DmtxVector2 p00,
|
|
DmtxVector2 p10, DmtxVector2 p11, DmtxVector2 p01)
|
|
{
|
|
double xMax = this.Width - 1;
|
|
double yMax = this.Height - 1;
|
|
|
|
if (p00.X < 0.0 || p00.Y < 0.0 || p00.X > xMax || p00.Y > yMax ||
|
|
p01.X < 0.0 || p01.Y < 0.0 || p01.X > xMax || p01.Y > yMax ||
|
|
p10.X < 0.0 || p10.Y < 0.0 || p10.X > xMax || p10.Y > yMax)
|
|
return false;
|
|
|
|
DmtxVector2 vOT = p01 - p00;
|
|
DmtxVector2 vOR = p10 - p00;
|
|
DmtxVector2 vTX = p11 - p01;
|
|
DmtxVector2 vRX = p11 - p10;
|
|
double dimOT = vOT.Mag();
|
|
double dimOR = vOR.Mag();
|
|
double dimTX = vTX.Mag();
|
|
double dimRX = vRX.Mag();
|
|
|
|
/* Verify that sides are reasonably long */
|
|
if (dimOT <= 8.0 || dimOR <= 8.0 || dimTX <= 8.0 || dimRX <= 8.0)
|
|
return false;
|
|
|
|
/* Verify that the 4 corners define a reasonably fat quadrilateral */
|
|
double ratio = dimOT / dimRX;
|
|
if (ratio <= 0.5 || ratio >= 2.0)
|
|
return false;
|
|
|
|
ratio = dimOR / dimTX;
|
|
if (ratio <= 0.5 || ratio >= 2.0)
|
|
return false;
|
|
|
|
/* Verify this is not a bowtie shape */
|
|
if (vOR.Cross(vRX) <= 0.0 || vOT.Cross(vTX) >= 0.0)
|
|
return false;
|
|
|
|
if (DmtxCommon.RightAngleTrueness(p00, p10, p11, Math.PI / 2.0) <= this._squareDevn)
|
|
return false;
|
|
if (DmtxCommon.RightAngleTrueness(p10, p11, p01, Math.PI / 2.0) <= this._squareDevn)
|
|
return false;
|
|
|
|
/* Calculate values needed for transformations */
|
|
double tx = -1 * p00.X;
|
|
double ty = -1 * p00.Y;
|
|
DmtxMatrix3 mtxy = DmtxMatrix3.Translate(tx, ty);
|
|
|
|
double phi = Math.Atan2(vOT.X, vOT.Y);
|
|
DmtxMatrix3 mphi = DmtxMatrix3.Rotate(phi);
|
|
DmtxMatrix3 m = mtxy * mphi;
|
|
|
|
DmtxVector2 vTmp = p10 * m;
|
|
double shx = -vTmp.Y / vTmp.X;
|
|
DmtxMatrix3 mshx = DmtxMatrix3.Shear(0.0, shx);
|
|
m *= mshx;
|
|
|
|
double scx = 1.0 / vTmp.X;
|
|
DmtxMatrix3 mscx = DmtxMatrix3.Scale(scx, 1.0);
|
|
m *= mscx;
|
|
vTmp = p11 * m;
|
|
|
|
double scy = 1.0 / vTmp.Y;
|
|
DmtxMatrix3 mscy = DmtxMatrix3.Scale(1.0, scy);
|
|
m *= mscy;
|
|
|
|
vTmp = p11 * m;
|
|
double skx = vTmp.X;
|
|
DmtxMatrix3 mskx = DmtxMatrix3.LineSkewSide(1.0, skx, 1.0);
|
|
m *= mskx;
|
|
|
|
vTmp = p01 * m;
|
|
double sky = vTmp.Y;
|
|
DmtxMatrix3 msky = DmtxMatrix3.LineSkewTop(sky, 1.0, 1.0);
|
|
reg.Raw2Fit = m * msky;
|
|
|
|
/* Create inverse matrix by reverse (avoid straight matrix inversion) */
|
|
msky = DmtxMatrix3.LineSkewTopInv(sky, 1.0, 1.0);
|
|
mskx = DmtxMatrix3.LineSkewSideInv(1.0, skx, 1.0);
|
|
m = msky * mskx;
|
|
|
|
DmtxMatrix3 mscxy = DmtxMatrix3.Scale(1.0 / scx, 1.0 / scy);
|
|
m *= mscxy;
|
|
|
|
mshx = DmtxMatrix3.Shear(0.0, -shx);
|
|
m *= mshx;
|
|
|
|
mphi = DmtxMatrix3.Rotate(-phi);
|
|
m *= mphi;
|
|
|
|
mtxy = DmtxMatrix3.Translate(-tx, -ty);
|
|
reg.Fit2Raw = m * mtxy;
|
|
|
|
return true;
|
|
}
|
|
|
|
bool MatrixRegionAlignCalibEdge(DmtxRegion reg, DmtxEdge edgeLoc)
|
|
{
|
|
int streamDir;
|
|
int avoidAngle;
|
|
DmtxSymbolSize symbolShape;
|
|
DmtxVector2 pTmp = new DmtxVector2();
|
|
DmtxPixelLoc loc1 = new DmtxPixelLoc();
|
|
DmtxPixelLoc locOrigin = new DmtxPixelLoc();
|
|
DmtxFollow follow;
|
|
|
|
/* Determine pixel coordinates of origin */
|
|
pTmp.X = 0.0;
|
|
pTmp.Y = 0.0;
|
|
pTmp *= reg.Fit2Raw;
|
|
locOrigin.X = (int)(pTmp.X + 0.5);
|
|
locOrigin.Y = (int)(pTmp.Y + 0.5);
|
|
|
|
if (this._sizeIdxExpected == DmtxSymbolSize.DmtxSymbolSquareAuto ||
|
|
(this._sizeIdxExpected >= DmtxSymbolSize.DmtxSymbol10x10 &&
|
|
this._sizeIdxExpected <= DmtxSymbolSize.DmtxSymbol144x144))
|
|
symbolShape = DmtxSymbolSize.DmtxSymbolSquareAuto;
|
|
else if (this._sizeIdxExpected == DmtxSymbolSize.DmtxSymbolRectAuto ||
|
|
(this._sizeIdxExpected >= DmtxSymbolSize.DmtxSymbol8x18 &&
|
|
this._sizeIdxExpected <= DmtxSymbolSize.DmtxSymbol16x48))
|
|
symbolShape = DmtxSymbolSize.DmtxSymbolRectAuto;
|
|
else
|
|
symbolShape = DmtxSymbolSize.DmtxSymbolShapeAuto;
|
|
|
|
/* Determine end locations of test line */
|
|
if (edgeLoc == DmtxEdge.DmtxEdgeTop)
|
|
{
|
|
streamDir = reg.Polarity * -1;
|
|
avoidAngle = reg.LeftLine.Angle;
|
|
follow = FollowSeekLoc(reg.LocT);
|
|
pTmp.X = 0.8;
|
|
pTmp.Y = (symbolShape == DmtxSymbolSize.DmtxSymbolRectAuto) ? 0.2 : 0.6;
|
|
}
|
|
else
|
|
{
|
|
streamDir = reg.Polarity;
|
|
avoidAngle = reg.BottomLine.Angle;
|
|
follow = FollowSeekLoc(reg.LocR);
|
|
pTmp.X = (symbolShape == DmtxSymbolSize.DmtxSymbolSquareAuto) ? 0.7 : 0.9;
|
|
pTmp.Y = 0.8;
|
|
}
|
|
|
|
pTmp *= reg.Fit2Raw;
|
|
loc1.X = (int)(pTmp.X + 0.5);
|
|
loc1.Y = (int)(pTmp.Y + 0.5);
|
|
|
|
DmtxPixelLoc loc0 = follow.Loc;
|
|
DmtxBresLine line = new DmtxBresLine(loc0, loc1, locOrigin);
|
|
int steps = this.TrailBlazeGapped(reg, line, streamDir);
|
|
|
|
DmtxBestLine bestLine = this.FindBestSolidLine2(loc0, steps, streamDir, avoidAngle);
|
|
|
|
if (edgeLoc == DmtxEdge.DmtxEdgeTop)
|
|
{
|
|
reg.TopKnown = 1;
|
|
reg.TopAngle = bestLine.Angle;
|
|
reg.TopLoc = bestLine.LocBeg;
|
|
}
|
|
else
|
|
{
|
|
reg.RightKnown = 1;
|
|
reg.RightAngle = bestLine.Angle;
|
|
reg.RightLoc = bestLine.LocBeg;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
int TrailBlazeGapped(DmtxRegion reg, DmtxBresLine line, int streamDir)
|
|
{
|
|
int distSq;
|
|
int travel = 0;
|
|
int outward = 0;
|
|
int[] dirMap = { 0, 1, 2, 7, 8, 3, 6, 5, 4 };
|
|
|
|
DmtxPixelLoc loc0 = line.Loc;
|
|
DmtxPointFlow flow = this.GetPointFlow(reg.FlowBegin.Plane, loc0, DmtxConstants.DmtxNeighborNone);
|
|
int distSqMax = (line.XDelta * line.XDelta) + (line.YDelta * line.YDelta);
|
|
int steps = 0;
|
|
bool onEdge = true;
|
|
|
|
DmtxPixelLoc beforeStep = loc0;
|
|
int beforeCacheIndex = DecodeGetCache(loc0.X, loc0.Y);
|
|
if (beforeCacheIndex == -1)
|
|
return 0;
|
|
|
|
this._cache[beforeCacheIndex] = 0;
|
|
|
|
do
|
|
{
|
|
if (onEdge)
|
|
{
|
|
DmtxPointFlow flowNext = FindStrongestNeighbor(flow, streamDir);
|
|
if (flowNext.Mag == DmtxConstants.DmtxUndefined)
|
|
break;
|
|
|
|
(new DmtxBresLine(line)).GetStep(flowNext.Loc, ref travel, ref outward);
|
|
if (flowNext.Mag < 50 || outward < 0 || (outward == 0 && travel < 0))
|
|
{
|
|
onEdge = false;
|
|
}
|
|
else
|
|
{
|
|
line.Step(travel, outward);
|
|
flow = flowNext;
|
|
}
|
|
}
|
|
|
|
if (!onEdge)
|
|
{
|
|
line.Step(1, 0);
|
|
flow = GetPointFlow(reg.FlowBegin.Plane, line.Loc, DmtxConstants.DmtxNeighborNone);
|
|
if (flow.Mag > 50)
|
|
onEdge = true;
|
|
}
|
|
|
|
DmtxPixelLoc afterStep = line.Loc;
|
|
int afterCacheIndex = DecodeGetCache(afterStep.X, afterStep.Y);
|
|
if (afterCacheIndex == -1)
|
|
break;
|
|
|
|
/* Determine step direction using pure magic */
|
|
int xStep = afterStep.X - beforeStep.X;
|
|
int yStep = afterStep.Y - beforeStep.Y;
|
|
if (Math.Abs(xStep) > 1 || Math.Abs(yStep) > 1)
|
|
{
|
|
throw new Exception("Invalid step directions!");
|
|
}
|
|
int stepDir = dirMap[3 * yStep + xStep + 4];
|
|
|
|
if (stepDir == 8)
|
|
{
|
|
throw new Exception("Invalid step direction!");
|
|
}
|
|
if (streamDir < 0)
|
|
{
|
|
this._cache[beforeCacheIndex] |= (byte)(0x40 | stepDir);
|
|
this._cache[afterCacheIndex] = (byte)(((stepDir + 4) % 8) << 3);
|
|
}
|
|
else
|
|
{
|
|
this._cache[beforeCacheIndex] |= (byte)(0x40 | (stepDir << 3));
|
|
this._cache[afterCacheIndex] = (byte)((stepDir + 4) % 8);
|
|
}
|
|
|
|
/* Guaranteed to have taken one step since top of loop */
|
|
int xDiff = line.Loc.X - loc0.X;
|
|
int yDiff = line.Loc.Y - loc0.Y;
|
|
distSq = (xDiff * xDiff) + (yDiff * yDiff);
|
|
|
|
beforeStep = line.Loc;
|
|
beforeCacheIndex = afterCacheIndex;
|
|
steps++;
|
|
|
|
} while (distSq < distSqMax);
|
|
|
|
return steps;
|
|
}
|
|
|
|
DmtxBestLine FindBestSolidLine2(DmtxPixelLoc loc0, int tripSteps, int sign, int houghAvoid)
|
|
{
|
|
int[,] hough = new int[3, DmtxConstants.DmtxHoughRes];
|
|
char[] houghTest = new char[DmtxConstants.DmtxHoughRes];
|
|
int i;
|
|
int step;
|
|
DmtxBestLine line = new DmtxBestLine();
|
|
|
|
int angleBest = 0;
|
|
int hOffsetBest = 0;
|
|
|
|
DmtxFollow follow = this.FollowSeekLoc(loc0);
|
|
DmtxPixelLoc rHp = line.LocBeg = line.LocPos = line.LocNeg = follow.Loc;
|
|
line.StepBeg = line.StepPos = line.StepNeg = 0;
|
|
|
|
/* Predetermine which angles to test */
|
|
for (i = 0; i < DmtxConstants.DmtxHoughRes; i++)
|
|
{
|
|
if (houghAvoid == DmtxConstants.DmtxUndefined)
|
|
{
|
|
houghTest[i] = (char)1;
|
|
}
|
|
else
|
|
{
|
|
int houghMin = (houghAvoid + DmtxConstants.DmtxHoughRes / 6) % DmtxConstants.DmtxHoughRes;
|
|
int houghMax = (houghAvoid - DmtxConstants.DmtxHoughRes / 6 + DmtxConstants.DmtxHoughRes) % DmtxConstants.DmtxHoughRes;
|
|
if (houghMin > houghMax)
|
|
houghTest[i] = (i > houghMin || i < houghMax) ? (char)1 : (char)0;
|
|
else
|
|
houghTest[i] = (i > houghMin && i < houghMax) ? (char)1 : (char)0;
|
|
}
|
|
}
|
|
|
|
/* Test each angle for steps along path */
|
|
for (step = 0; step < tripSteps; step++)
|
|
{
|
|
|
|
int xDiff = follow.Loc.X - rHp.X;
|
|
int yDiff = follow.Loc.Y - rHp.Y;
|
|
|
|
/* Increment Hough accumulator */
|
|
for (i = 0; i < DmtxConstants.DmtxHoughRes; i++)
|
|
{
|
|
|
|
if (houghTest[i] == 0)
|
|
continue;
|
|
|
|
int dH = (DmtxConstants.rHvX[i] * yDiff) - (DmtxConstants.rHvY[i] * xDiff);
|
|
if (dH >= -384 && dH <= 384)
|
|
{
|
|
int hOffset;
|
|
if (dH > 128)
|
|
hOffset = 2;
|
|
else if (dH >= -128)
|
|
hOffset = 1;
|
|
else
|
|
hOffset = 0;
|
|
|
|
hough[hOffset, i]++;
|
|
|
|
/* New angle takes over lead */
|
|
if (hough[hOffset, i] > hough[hOffsetBest, angleBest])
|
|
{
|
|
angleBest = i;
|
|
hOffsetBest = hOffset;
|
|
}
|
|
}
|
|
}
|
|
follow = FollowStep2(follow, sign);
|
|
}
|
|
|
|
line.Angle = angleBest;
|
|
line.HOffset = hOffsetBest;
|
|
line.Mag = hough[hOffsetBest, angleBest];
|
|
|
|
return line;
|
|
}
|
|
|
|
DmtxFollow FollowStep2(DmtxFollow followBeg, int sign)
|
|
{
|
|
DmtxFollow follow = new DmtxFollow();
|
|
|
|
if (Math.Abs(sign) != 1)
|
|
{
|
|
throw new Exception("Invalid parameter 'sign', can only be -1 or +1");
|
|
}
|
|
if ((followBeg.Neighbor & 0x40) == 0x00)
|
|
{
|
|
throw new Exception("Invalid value for neighbor!");
|
|
}
|
|
|
|
int patternIdx = (sign < 0) ? followBeg.Neighbor & 0x07 : ((followBeg.Neighbor & 0x38) >> 3);
|
|
follow.Loc = new DmtxPixelLoc { X = followBeg.Loc.X + DmtxConstants.DmtxPatternX[patternIdx], Y = followBeg.Loc.Y + DmtxConstants.DmtxPatternY[patternIdx] };
|
|
|
|
follow.Step = followBeg.Step + sign;
|
|
follow.Ptr = this._cache;
|
|
follow.PtrIndex = DecodeGetCache(follow.Loc.X, follow.Loc.Y);
|
|
|
|
return follow;
|
|
}
|
|
|
|
DmtxFollow FollowSeekLoc(DmtxPixelLoc loc)
|
|
{
|
|
DmtxFollow follow = new DmtxFollow {Loc = loc, Step = 0, Ptr = this._cache};
|
|
|
|
follow.PtrIndex = DecodeGetCache(follow.Loc.X, follow.Loc.Y);
|
|
|
|
return follow;
|
|
}
|
|
#endregion
|
|
|
|
#region Properties
|
|
internal int EdgeMin
|
|
{
|
|
get { return _edgeMin; }
|
|
set
|
|
{
|
|
_edgeMin = value;
|
|
ValidateSettingsAndInitScanGrid();
|
|
}
|
|
}
|
|
|
|
internal int EdgeMax
|
|
{
|
|
get { return _edgeMax; }
|
|
set { _edgeMax = value; ValidateSettingsAndInitScanGrid(); }
|
|
}
|
|
|
|
internal int ScanGap
|
|
{
|
|
get { return _scanGap; }
|
|
set { _scanGap = value; ValidateSettingsAndInitScanGrid(); }
|
|
}
|
|
|
|
internal int SquareDevn
|
|
{
|
|
get { return (int)(Math.Acos(this._squareDevn) * 180.0 / Math.PI); }
|
|
set { _squareDevn = Math.Cos(value * (Math.PI / 180.0)); ValidateSettingsAndInitScanGrid(); }
|
|
}
|
|
|
|
internal DmtxSymbolSize SizeIdxExpected
|
|
{
|
|
get { return _sizeIdxExpected; }
|
|
set { _sizeIdxExpected = value; ValidateSettingsAndInitScanGrid(); }
|
|
}
|
|
|
|
internal int EdgeThresh
|
|
{
|
|
get { return _edgeThresh; }
|
|
set { _edgeThresh = value; ValidateSettingsAndInitScanGrid(); }
|
|
}
|
|
|
|
internal int XMin
|
|
{
|
|
get { return _xMin; }
|
|
set { _xMin = value; ValidateSettingsAndInitScanGrid(); }
|
|
}
|
|
|
|
internal int XMax
|
|
{
|
|
get { return _xMax; }
|
|
set { _xMax = value; ValidateSettingsAndInitScanGrid(); }
|
|
}
|
|
|
|
internal int YMin
|
|
{
|
|
get { return _yMin; }
|
|
set { _yMin = value; ValidateSettingsAndInitScanGrid(); }
|
|
}
|
|
|
|
internal int YMax
|
|
{
|
|
get { return _yMax; }
|
|
set { _yMax = value; ValidateSettingsAndInitScanGrid(); }
|
|
}
|
|
|
|
internal int Scale
|
|
{
|
|
get { return _scale; }
|
|
set { _scale = value; ValidateSettingsAndInitScanGrid(); }
|
|
}
|
|
|
|
internal byte[] Cache
|
|
{
|
|
get { return _cache; }
|
|
set { _cache = value; ValidateSettingsAndInitScanGrid(); }
|
|
}
|
|
|
|
internal DmtxImage Image
|
|
{
|
|
get { return _image; }
|
|
set { _image = value; ValidateSettingsAndInitScanGrid(); }
|
|
}
|
|
|
|
|
|
internal DmtxScanGrid Grid
|
|
{
|
|
get { return _grid; }
|
|
set { _grid = value; }
|
|
}
|
|
|
|
internal int Height
|
|
{
|
|
get
|
|
{
|
|
return _image.Height / _scale;
|
|
}
|
|
}
|
|
|
|
|
|
internal int Width
|
|
{
|
|
get
|
|
{
|
|
return _image.Width / _scale;
|
|
}
|
|
}
|
|
#endregion
|
|
}
|
|
}
|