接住上一节,我们先看算法:
CulcMultiViewOffset(MY_FIRSTRESULT&reFirst, float&resX, float&resY, float&resAng, float* pLenghtUp, float* pLenghtDn , void* Points, unsigned int count, float Xr, float Yr, MyOffset Offset, int flag )
{
float DnToXAngle=0,array_angle[4];
float DnbaAngle=0;
float x=0,y=0;
int j=0;
unsigned int i=0;
if( Points == NULL )
return -1;
float sumX=0, sumY=0, sumA=0;
Vector* pPoints = (Vector*) Points;
Vector* VectUp = new Vector[count]; //跌偿娩秖ボ,
Vector* VectDn = new Vector[count]; //跌偿娩秖ボ
float* deltaA = new float[count]; //璸衡眔癸莱娩Жà
Vector rotatecenter(Xr, Yr); //跌偿臂锣いみ
Vector* VectDnRt = new Vector[count]; //跌偿郴翴癸莱跌偿臂锣いみ秖
Vector* UpCorner = new Vector[count]; //跌偿畒夹玒畒夹
Vector* DnCorner = new Vector[count]; //跌偿畒夹玒畒夹
//眔跌偿畒夹
for( i=0; i<count; i++ )
{
UpCorner[i] = *( pPoints+i );
DnCorner[i] = *( pPoints+count+i );
}
DnToXAngle=0;
for(i=0; i<count; i++)
{
j=(i+1)%count;
if (i%2==0)
array_angle[i]=atan((UpCorner[j].a-UpCorner[i].a)/(UpCorner[j].b-UpCorner[i].b))*180/PI;
else
array_angle[i]=abs(abs(atan((UpCorner[j].a-UpCorner[i].a)/(UpCorner[j].b-UpCorner[i].b))*180/PI)-90);
DnToXAngle+=array_angle[i];
}
DnToXAngle/=count;
Offset.dy=-Offset.dy;
//X=a*cos-b*sin;
//Y=a*sin+b*cos;
x=Offset.dx*cos(DnToXAngle*PI/180)-Offset.dy*sin(DnToXAngle*PI/180);
y=-(Offset.dx*sin(DnToXAngle*PI/180)+Offset.dy*cos(DnToXAngle*PI/180));
reFirst.UpX=x;
reFirst.UpY=y;
for(i=0; i<count; i++)
{
UpCorner[i].a+=x;
UpCorner[i].b+=y;
}
//ぃノσ納翴丁竚闽玒
for( i=0; i<count; i++ )
{
int j=(i+1)%count;
VectUp[i] = UpCorner[j] - UpCorner[i]; //超娩娩秖, 跌偿
VectDn[i] = DnCorner[j] - DnCorner[i]; //超娩娩秖, 跌偿
//璸衡`跌偿癸莱娩Жà, 眖跌偿秖臂锣跌偿癸莱秖Жà
deltaA[i] = /*pow(-1.0,i)**/VectDn[i].angle( VectUp[i] );//癴皐タ,抖皐璽
*(pLenghtUp+i) = VectUp[i].length();
*(pLenghtDn+i) = VectDn[i].length();
sumA += deltaA[i];
}
resAng = ((sumA/count)*180.00)/PI;
resAng = resAng - Offset.dang;//
if (!(abs(resAng)>=0))
{
i=0;
}
//臂锣畒夹, 沮璸衡à臂锣秖臂锣穝秖, 盢跌偿い郴翴瞅露臂锣いみ锣笆.
for( i=0; i<count; i++ )
{
VectDnRt[i] = *(pPoints+count+i) - rotatecenter;
//秖臂锣
VectDnRt[i] = VectDnRt[i].rotate( (resAng*PI)/180.00 );
//穝臂锣跌偿郴翴畒夹
DnCorner[i] = VectDnRt[i] + rotatecenter;
if( flag == 1)
{ //盢臂锣畒夹挡狦硄筁pPoint把计
*( pPoints+count+i ) = DnCorner[i];
}
//璸衡臂锣熬畉
sumX += (UpCorner[i]-DnCorner[i]).a;
sumY += (UpCorner[i]-DnCorner[i]).b;
}
resX = sumX/count;
resY = sumY/count;
delete[] VectUp;
delete[] VectDn;
delete[] deltaA;
delete[] VectDnRt;
delete[] DnCorner;
delete[] UpCorner;
return 0;
}
看上去参数一大堆,挺吓人的,有时候就会被这种纸老虎吓住。
我们该怎样学?最好是在现场,因为现场生产是正常的,所以,里头整几个断点,看看 m_WorldPoint[2][4]进入这个算法,最后怎样了,现在看来,是很好说清楚了:
第一, //X=a*cos-b*sin;
//Y=a*sin+b*cos;看到这个公式,显然是坐标旋转了一个角度的计算方法
第二,我们看是什么坐标用到了这个公式,m_WorldPoint[2][4]就是算法中的pPoints,counts=4,所以,UpCorner就是上面矩形四个点,DnCorner就是下面矩形四个点。UpCorner四个点构成四条线,然后每相邻线求角度,明明是矩形为什么要算四个角的和,然后还除以4,不就是90度吗?这一点还是很困惑的,实际是产品看上去很完美的是矩形,但实际不是,你要是还在几何矩形概念里,就着上了!
多年以后,再次看这一段坐标变换的代码,尴尬的是,又看不懂了,不过轻微还有点印象是,这里的(x,y)=(0,0),当年好像设断点监控过,所以这一段代码就是迷魂汤,是个陷阱,为什么这样呢?难道与设备状态有关,自从我们接手后,这种状态就无用了?或许就是
UpCorner[i].a+=x;//0
UpCorner[i].b+=y;//0
好在我正常运行的改进对角线四点程序和这个出入不大,显然已经没有这一段,说明,我的记忆还行,虽然研究这个公式花了时间,没用了,但如果你从头写视觉软件,一刻也离不开这个公式.
第三,关键来了, VectUp[i] = UpCorner[j] - UpCorner[i];
VectDn[i] = DnCorner[j] - DnCorner[i]; 这个把8个坐标变成了八个向量,有方向和长度,
这八个向量,分成上下一一对应两组,做了什么呢?
第四,求一一对应两个向量角度偏差,一共四组,求和后,取了均值角度偏差。
第五,以下矩形中心,和这个均值角度偏差,旋转下矩形四条边(四个向量),然后找出上下矩形平均x的偏差,平均y偏差
那么最后,就是以上矩形为标准,下矩形旋转和平移(均值角度偏差resAng,平均x的偏差resX ,平均y偏差resY ),这些数据都已经算出来,做最后的贴合。
这个算法中干扰很多,但影响不大,这就是现场的算法,多人经手,没人敢动,就一直保留下来了,这就是他们现在说的代码“屎山”。我们项目主题中的算法粗框架已经讲完了,如果你去看算法外的细节,又是一堆和现场机器运行状态相关的许多不敢轻易下手改动的代码。
算法相对代码“屎山”还是简单,起码他是数学可以保证的。需要强调的是,只说算法,实际
//X=a*cos-b*sin;
//Y=a*sin+b*cos;与
VectDnRt[i] = VectDnRt[i].rotate( (resAng*PI)/180.00 );在数学上是无差别的
这两个公式在我写megauging中一直在保驾护航,或许是走入社会开始学习的先入为主吧!
下一节,我们学习一下工程中图像处理类对ni的封装,待续