关于图像处理中的插值和旋转
关于图像处理中的插值和旋转
在模板匹配问题中,涉及到角度插值和坐标插值(亚像素精度)问题。
角度插值:得到最佳匹配模板左右相邻的模板的信息,二次拟合(x为角度,y为得分)
坐标插值:得到最佳匹配点的3*3邻域点的匹配情况,三次拟合(x,y坐标,y为得分)
确定拟合模型(什么方程)——》找到需要的信息(多少个点及其对应的值)——》求极值
注意: 相邻元素的确定。在我遇到的实际问题中,要找到同一尺度下的相邻角度的模板,而所有模板在制作保存时用到了并行,故需要遍历搜索。以三个角度为x,以它们同一点(变量的控制)处的得分为y。
【将点一次带入得到方程组/最小二乘法列出方程组】
三次拟合示例:a0+a1x+a2y+a3x^2+a4y^2+a5xy=z
voidfittingCurve3D(double *x,double *y,double *z,intn,double *index)
{
//直接把点带入方程,把方程组写成矩阵形式,结果用伪逆表示[inv(A'A)]A'b
double *a =newdouble[n * 6];//已知点的信息矩阵,含xy
double *b =newdouble[n];//已知点的信息矩阵,含z
///////构造a的一维数组,n*6
for (int i = 0; i <n * 6; ++i)
{
if (i % 6 == 0)
{
a[i]= 1;
a[i+ 1] = x[i / 6];//x
a[i+ 2] = y[i / 6];//y
a[i+ 3] = x[i / 6] * x[i / 6];//x^2
a[i+ 4] = y[i / 6] * y[i / 6];//y^2
a[i+ 5] = x[i / 6] * y[i / 6];//x*y
}
}
CvMat AMat = cvMat(n, 6,CV_64FC1, a);//变成矩阵AMat
CvMat *ATransposedMat =cvCreateMat(AMat.cols, AMat.rows,CV_64FC1);
cvTranspose(&AMat,ATransposedMat);//转置ATransposedMat
///用于存储矩阵相乘结果,构造的数组n*n
double *c =newdouble[6 * 6];
for (int i = 0; i < 6 *6; ++i)
{
c[i]= 0;
}
CvMat invMat1 = cvMat(6,6,CV_64FC1, c);//变成矩阵invMat1
cvGEMM(ATransposedMat,&AMat, 1, NULL, 0,&invMat1, 0);// ATransposedMat(转置)*AMat=invMat1
cvInvert(&invMat1,&invMat1, 0);//求invMat1的逆矩阵,invMat1
///////构造b的一维数组,n*1
for (int i = 0; i <n; ++i)
{
b[i]= z[i];
}
CvMat BMat = cvMat(n, 1,CV_64FC1, b);//变成矩阵BMat
//用于存储矩阵相乘结果,构造的数组6*1
double *d =newdouble[6];
for (int i = 0; i < 6;++i)
{
d[i]= 0;
}
CvMat invMat2 = cvMat(6,1,CV_64FC1, d);//变成矩阵invMat2
cvGEMM(ATransposedMat,&BMat, 1, NULL, 0,&invMat2, 0);//AMat的转置矩阵 * BMat矩阵 = invMat2
CvMat indexMat = cvMat(6,1,CV_64FC1, index);
cvGEMM(&invMat1,&invMat2, 1, NULL, 0,&indexMat, 0);//invMat1 * invMat2 = indexMat
cvReleaseMat(&ATransposedMat);
delete[] a;
delete[] b;
delete[] c;
delete[] d;
}
各种曲面的拟合:如拟合球面 :x^2+ay^2+bz^2+cx+dy+ez+f=0
http://blog.****.net/hj199404182515/article/details/59480954
二.旋转
旋转算法原理:http://blog.****.net/liyuan02/article/details/6750828
正向映射://旋转后dst图像坐标系
double xtemp0 = c*x - s*y + (-0.5*c*(srcW - 1) + 0.5*s*(srcH - 1) +0.5*dstW);
double ytemp0 = s*x + c*y + (-0.5*s*(srcW - 1) -0.5*c*(srcH - 1) + 0.5*dstH);
逆向映射:(映射回原图的放大图temp)
double xtemp = c*i + s*j + (-c*(dstW - 1)*0.5 - s* (dstH - 1)*0.5+0.5*temp->width);
double ytemp = -s*i + c*j + (s*(dstW - 1)*0.5 - c* (dstH -1)*0.5+ 0.5*temp->height);
//将原图复制到临时图像temp中心 (保证原图可以任意角度旋转的最小尺寸 )
int tempLength =int( sqrt((double)width *width + (double)height *height) + 10);
int tempX = (tempLength + 1) / 2 - width /2;
int tempY = (tempLength + 1) / 2 - height /2;
IplImage* temp =cvCreateImage(cvSize(tempLength, tempLength), src->depth,src->nChannels);
memset(temp->imageData, 255, sizeof(char)*temp->imageSize);
cvSetImageROI(temp, cvRect(tempX, tempY,width, height));
cvCopy(src, temp, NULL);
cvResetImageROI(temp);
cvReleaseImage(&temp);
正向映射:输入图像上整数点坐标映射到输出图像之后,变成了非整数点坐标。因此,需要将其像素值按一定权重分配到其周围四个像素点上。遍历输出图像,对每个点,将每次映射分配而来的像素值叠加。
图像旋转中的插值:一般采用反向双线性插值
http://www.cnblogs.com/mlv5/archive/2012/02/02/2336321.html
示例:
intImgBilinearInter(floatfx,floatfy,IplImage * img) {
//参考函数cv::PicRotaryBilInear——双线性插值
int value;
int x = floor(fx);
int y = floor(fy);
cv::Point P0 = Pixels_Bound(img, x, y);
cv::Point P2 = Pixels_Bound(img, x + 1, y);
cv::Point P1 = Pixels_Bound(img, x, y + 1);
cv::Point P3 = Pixels_Bound(img, x + 1, y + 1);//边界饱和
float u =fx - x;
float v =fy - y;
float pm3 = u*v;
float pm2 = u*(1 - v);
float pm1 = v*(1 - u);
float pm0 = (1 - u)*(1 -v);
// 指向图像的指针
uchar* lp = (uchar *)img->imageData;
int step =img->widthStep /sizeof(uchar);
//value = (pm0*lp[P0.y*step + P0.x] + pm1*lp[P1.y *step +P1.x] + pm2*lp[P2.y *step + P2.x ] + pm3*lp[P3.y *step + P3.x ]);
float value_float =(pm0*lp[P0.y*step + P0.x] + pm1*lp[P1.y *step + P1.x] + pm2*lp[P2.y *step +P2.x] + pm3*lp[P3.y *step + P3.x]);
value= round(value_float);
return value;
}
cv::Point Pixels_Bound(IplImage *pic, intx0,inty0) {
if (x0<0) {x0 = 0; }
elseif (x0 >=pic->width) { x0 = pic->width - 1; }
if (y0<0) {y0 = 0; }
elseif (y0 >=pic->height) { y0 = pic->height - 1; }
cv::Point P(x0,y0);
return P;
}
双线性插值部分,边界元素的处理需注意。