DataMatrix的识别原理
前言
最近在做关于DataMatrixer二维码项目的识别,加上网络上的公开资料比较少,因此做的过程比较痛苦,这里稍微记录一下,当作对项目总结。本次原理的解析是针对开源库libdmtx的。
一、算法的整体流程
5.30新项目记录
1. 问题定位
{
1.修改barcode.c
2.修改dmtxregion.c 170行
3.assert调试
}
函数包含结构
dmtxScanExcute
{
1.img = dmtxImageCreate();
2.dec = dmtxDecodeCreate(img); \\此时dec中包含dec
3.reg = dmtxRegionFindNext(dec)
{//进行十字扫描,并以这些点依次为起点去搜索边界
for (;;)
{
1.locStatus = PopGridLocation(&(dec->grid), &loc);
2.reg = dmtxRegionScanPiexl(dec, loc.X, loc.Y, buf)
{
1.cache = dmtxDecodeGetCache(loc); //判断是否访问或者分配过
2.flowBegin = MatrixRegionSeekEdge;
{
1.flowPlane[i] = GetPointFlow(dec); //获取不同通道的,幅值小于10的会被过滤掉
{
1.flow.arrive = arrive; //下面的函数可知
2.flow.depart = (mag[compassMax] > 0) ? compassMax + 4 : compassMax;
//明显就是梯度下降的方向,方向如下图标识
//| 6 7 0 |depart
//| 5 8 1 |
//| 4 3 2 |
}
2.flowPosBack = FindStrongestNeighbor(dec, flowPos, -1);//来回验证是否该起点的能不能按照相同的位置原路返回
{
1.正方向搜索 - 45、0、45,sign = 1(正负只是规定值)
{
(depart + 4) % 8
//| 0 1 2 |dmtxPatternX,这个值也是arrive的值
//| 7 8 3 |
//| 6 5 4 |
1. flow[i] = GetPointFlow() //i值也是arrive的值,arrive是相对与上一个点位的相对位置
2. 选取8个方向上flow响应最大的,两个位置响应相同的时候选取上表中的奇数位置
}
2.反方向搜索 - 45、0、45,sign = -1
}
3.flowNegBack = FindStrongestNeighbor(dec, flowNeg, +1);
}
3.MatrixRegionOrientation(dec, ®, flowBegin); //比较复杂和关键的函数
{//该函数定位DataMatrix的L形区域,并给出码区相应的左、上、下的边界
1.TrailBlazeContinuous(dec, reg, begin, maxDiagonal);//寻找连续的点位
{
//0x80 v = visited bit
//0x40 a = assigned bit
//0x38 u = 3 bits points upstream 0 - 7
//0x07 d = 3 bits points downstream 0 - 7
1.循环正向、负向寻找连续边界。
{
1. 起始位置cache :mark location as visited and assigned 1100 0000
2. flowNext = FindStrongestNeighbor(dec, flow, sign);
cacheNext = dmtxDecodeGetCache()
3. 设置当前cache = 11-- - -- - ,
1.正向:
高三位为当前点下一个点位的dmtxPatternX值,
低三位为当前点上一个点位的dmtxPatternX值;
2.负向:相反
}
2.TrailClear(dec, reg, 0x80)
{//将已经搜索到的点位的cache的值始终设置为:01 --- ---
1.FollowSeek() //搜寻起始位置heighbor为cache的后6位
2.FollowStep()
//假设边界点的排列方式为正、begin、负:++...++...o...--...--,那么
//该函数会从起始点沿着正方向进行搜索,搜到最后一个postive点位的时
//候跳转到negtive的最后一个点位正向搜索,形成闭环
}
}
2.如果连续点寻找返回失败则运行函数TrailClear(dec, reg, 0x40)将已经分配的点位设置为:10 --- ---
3.过滤掉小的区域(但是我们的程序中没有设置)
4.line1x = FindBestSolidLine(dec, reg, 0, 0, +1, DmtxUndefined); //寻找第一条直线
{//+1 为 sign = 1
//这个函数中使用了hough变换,结果储存在hough[3][180]中,将求每个连续点与起始点的相对
//位置差diff,diff再叉乘1~180°的长度为256的向量,在每个方向上选取叉乘结果为-384~384
//(即在该方向上偏离起始点位-1、0、1个像素的点),
// 如下图(每个边界点都有这样的图,最后所有的点的图进行累加)
//________________________________
//| \ |-1像素|0像素|1像素| 注意:单个点的行向量的求和值为1
//| \叉乘值|<-128 |<128 |else |
//|_角度\_____|______|_____|_____|
//| 1° | 0 | 0 | 1 |
//| 2° | 0 | 1 | 0 |
//| 3° | 0 | 1 | 0 |
//| 4° | 0 | 1 | 0 |
//| ... | ... | ... | ... |
//| 180° | 0 | 1 | 0 |
//|__________|______|_____|_____|
// 将上面每个点的矩阵进行累加值后选取3*180矩阵幅值最大的位置作为最终直线的角度;
// 直线最大幅值(感觉会有一点点缺陷,只选取了该角度上-1、0、1中的一个幅值,
// 自己感觉应该全加起来)todo
}
5.TrailClear(dec, reg, 0x40);//通过幅值过滤直线,感觉阈值太小了todo
6.FindTravelLimits(dec, reg, &line1x); //计算第一条直线
{
1.直线的起点(也是边界搜索的起点)
2.for(总的搜索点的一半)
}
3.fTmp = FollowSeek(dec, reg, line1x.stepPos + 5); //第一条直线正向搜索另一条直线
line2p = FindBestSolidLine(dec, reg, fTmp.step, line1x.stepNeg, +1, line1x.angle);
4.fTmp = FollowSeek(dec, reg, line1x.stepNeg - 5); //第一条直线负向搜索另一条直线
line2n = FindBestSolidLine(dec, reg, fTmp.step, line1x.stepPos, -1, line1x.angle);
5.//通过正负两直线的magnitude即幅值判断第二条直线,这两条直线分别组成了DataMatrix的L形区域
6.FindTravelLimits(dec, reg, &line2x);//讨论第二条直线的延伸范围
}
4.dmtxRegionUpdateXfrms()
{//进行初次坐标变换,因为这次变换中知道的信息不全,因此初步定一个范围
主要函数如下
1.dmtxVector2Norm()//坐标的正则化,//大概定位码区的四个顶点
2.dmtxRay2Intersect//大概定位码区的四个顶点
3.dmtxRegionUpdateCorners()//验证四个顶点是否合理,畸变太大就放弃解码
}
5.MatrixRegionAlignCalibEdge()
{//定位上边界
主要函数
1.dmtxMatrix3VMultiplyBy()//三阶矩阵乘法
2.FollowSeekLoc //已经计算过的点的属性的查找
3.BresLineInit() //初始化不连续边界
4.TrailBlazeGapped()//一种特殊的不连续点的震荡扫描方式
5.FindBestSolidLine2() //寻找到顶边界
}
6.dmtxRegionUpdateXfrms()
7.MatrixRegionAlignCalibEdge()
8.dmtxRegionUpdateXfrms()//寻找到底边界
9.MatrixRegionFindSize(dec, ®)/* Calculate the best fitting symbol size */
{
dmtxGetSymbolAttribute
ReadModuleColor
CountJumpTally()//计算二维码的尺寸
}
//3. 4. 5. 6.都是失败就return,寻找下一个点位
10.return dmtxRegionCreate(®);//最终返回区域
}
3.退出条件为搜寻完所有点(感觉这个条件有一点点奇怪,随着网格细分后面的点搜索到的概率越小,徒增时间)
}
}
4.msg = dmtxDecodeMatrixRegion(dec, reg, DmtxUndefined, buf);//成功寻找区域之后进行码区的读取与解码
{
//该函数编写十分巧妙,数学原理运用的也很完美
//涉及到线性代数和数论的知识,后面会专门写一篇文章去介绍
1.msg = dmtxMessageCreate(reg->sizeIdx, DmtxFormatMatrix, buf);
2.PopulateArrayFromMatrix(dec, reg, msg);
{
1.dmtxGetSymbolAttribute();
2.TallyModuleJumps();//increment counters used to determine module values
{//这个函数中计算的值就是算出来的码的值,它通过四个方向对编码块进行灰度求和
1.ReadModuleColor()
{
1.dmtxGetSymbolAttribute()//读取行、列的数量
}
}
}
3.dmtxMatrix3MultiplyBy();
CacheFillQuad();
4.dmtxDecodePopulateArray();
{//Ripped out a part of dmtxDecodeMatrixRegion function to this one to parse own array
1.ModulePlacementEcc200()
2.RsDecode()
3.DecodeDataStream()
}
}
5.dmtxRegionDestroy(&img);
}
代码原理细节
{
1. dec->scale感觉式降采样的倍数;
2. dec->cache获取的内存大小为降采样scale后的大小
3. dec->scanGap会除以dec->scale
4. grid->starPos = grid->extent - 1
5. GetGridCoordinates()
{
在当前网格划分的情况下,
}
8.FindStrongestNeighbor((flow)center)
}
二、算法原理
\qquad 这里会详细介绍代码实现,更偏重整个代码的体系的解析,具体的算法原理可能会放到其他文章。
具体细节待更新,时间紧先挖个坑
确实需要解答可以私信或者留言。