几何空间变换和图像配准
几何空间变换又称为橡皮膜变换,因为他可以看做是在一幅橡皮膜上印制图像,然后根据一定规则拉伸橡皮膜。由两个基本操作组成:
1)坐标的空间变换
2)灰度内插
最常用的是仿射变换一般形式如下:
[x,y,1] = [v,w,1]*T
[t11 t12 0]
= [v,w,1]*[t21 t22 0]
[t31 t32 0]
这一变换可以根据矩阵T中元素的值,对一组坐标点做尺度变换,旋转,平移,偏移。
我们有两种方法使用该式子,第一种是前向映射,它由扫描输入图像的像素,并在每一个位置(v,w)直接计算输出图像中像素的空间位置(x,y)组成。
第二种方法是反向映射,扫描输出图像的位置,并在每一个位置(x,y)使用(v,w) = T-1(x,y)反向计算输入图像相应的位置,然后进行灰度内插(一般使用最邻近,双线性,双三次内插)。
前向映射一个问题是,输入图像的的两个或多个像素会映射到输出图像的同一个位置,另一种可能是某些输出位置完全没有像素,因此反向映射比前向映射更加有效,被很多的商业公司选用,包括mathworks的 matlab使用的也是反向映射。
一、图像平移
x = v + tx
y = w + ty
原图:
平移后的图像:
二、图像旋转
x = v*cosA – w * sinA
y = v *sinA + w * cosA
进行图像旋转时,需要注意的是当图像的大小发生改变时此时若以左上角点为原点,那么原点会发生改变,一般会选定图像的中心作为原点,这样在图像进行坐标变换时中心不变。但在在变换时要记得加减一个常数,使原点相对应。
旋转后的图像:
观察旋转后的图像,会有很明显的锯齿现象。
若采用双线性插值或者三次立方插值那么锯齿依然难以消除。
三、图像的尺度变换
x = cx * v
y = cy * w
这个就不附上图了,比较简单
四、图像的偏移
垂直偏移变换:
x =v*sv + w
y = w
水平偏移变换:
x = v
y = sh*v+w
水平偏移后的图像:
仿射变换的源代码:
#include<cv.h>
#include<highgui.h>
#include <cmath>
#include <stdlib.h>
const double PI = 3.141592653;
int main(){
IplImage * image,*image2;
image = cvLoadImage("E:\\image\\base.jpg",0);
cvNamedWindow("image",CV_WINDOW_AUTOSIZE);
//cvSaveImage("E:\\image\\pollen.jpg",image,0);
cvShowImage("image",image);
//cvWaitKey(0);
//平移 dx dy
int dx = 10;
int dy = 20;
image2 = cvCreateImage(cvSize(image->width + dx, image->height + dy),image->depth,1);
unsigned char * ptr,*dst;
int i,j ,m,n;//i j为新图中坐标,m n 为原图中坐标
for(i = 0 ; i < image2->height;i++){
for(j = 0; j< image2->width;j++){
dst = (unsigned char *)image2->imageData + i*image2->widthStep + j;
n = j - dx;
m = i - dy;
if(m < 0 || n < 0 || m >= image->height || n >= image->width){
*dst = 0;
}
else {
ptr = (unsigned char * )(image->imageData + m * image->width + n);
*dst = *ptr;
}
}
}
cvNamedWindow("image2",1);
cvShowImage("image2",image2);
//cvWaitKey(0);
cvSaveImage("E:\\image\\basepingyi.jpg",image,0);
/**************************************************************************************/
//旋转图像 这里以逆时针为正 插值选用双线性插值 围绕左上角点进行旋转 双线性插值
IplImage* image3;
double angle = 15;
double Hangle = angle*PI/180*(-1);
int Width,Height;
//double r,s;
double r,s;
int temp[4];
double z1,z2;
//原点会发生移动
double fx,fy;
double Hcos = cos(Hangle);
double Hsin = sin(Hangle);
//x y 存放原图中四个点的位置,以中心为原点 左上角开始 顺时针数
int x[4];
int y[4];
int x1[4];
int y1[4];
x[0] = -(image->width-1)/2;
x[1] = -x[0];
x[2] = -x[0];
x[3] = x[0];
y[0] = -(image->height -1)/2;
y[1] = y[0];
y[2] = -y[0];
y[3] = -y[0];
//x1 y1 分别存放新图中图像的四个角点的位置 以中心为原点 左上角点开始 顺时针数
for (i = 0; i < 4 ; i++)
{
x1[i] = (int)(x[i]*Hcos - y[i]*Hsin + 0.5);
y1[i] = (int)(x[i]*Hsin + y[i]*Hcos + 0.5);
printf("x: %d \n",x[i] );
printf("y: %d\n",y[i]);
printf("x1: %d \n",x1[i] );
printf("y1: %d\n",y1[i]);
}
//确定新图像的长宽
if (abs(y1[2] - y1[0]) > abs(y1[3] - y1[1]))
{
Height = abs(y1[2] - y1[0]);
Width = abs(x1[3] - x1[1]);
}
else{
Height = abs(y1[3] - y1[1]);
Width = abs( x1[2] - x1[0]);
}
image3 = cvCreateImage(cvSize(Width,Height),image->depth,1);
//两个偏移常量
fx = -1*(Width -1)* Hcos*0.5 -(Height -1)*Hsin*0.5 + (image->width-1)/2;
fy = (Width -1)*Hsin*0.5 -(Height -1)*Hcos*0.5 + (image->height -1)/2;
for (i = 0 ; i< Height ; i++)
{
for (j =0 ; j< Width; j++)
{
dst = (unsigned char *)(image3->imageData + i*image3->widthStep + j);
r = (int)(i*Hcos- j * Hsin + fy + 0.5);
s = (int)(i*Hsin + j*Hcos + fx + 0.5 );
if (r <0 || s < 0 || r >= image->height || s >= image->width)
{
*dst = 0;
}
else{
ptr = (unsigned char *)(image->imageData + image->widthStep*(int)r + (int)s );
temp[0] = *ptr;
temp[1] = *(ptr+1);
ptr = (unsigned char*)(image->imageData + image->widthStep *(int)(r+1) + int (s));
temp[2] = *ptr;
temp[3] = *(ptr+1);
z1 = (temp[1] - temp[0])*(s - int(s)) + temp[0];
z2 = (temp[3] -temp[2])*(s- int(s)) + temp[2];
*dst = (int)(z1 + (z2 -z1)*(r- (int)r));
}
}
}
cvSaveImage("bldpingyi.bmp",image2);
/**************************************************************************************/
/**************************************************************************************/
/* //这里是采用最简单的取离映射点最近的那个点 另外 的方法是采用双线性插值的方式
//旋转图像 这里以逆时针为正 插值选用双线性插值 围绕左上角点进行旋转
IplImage* image3;
double angle = 15;
double Hangle = angle*PI/180*(-1);
int Width,Height;
//double r,s;
int r,s;
//原点会发生移动
double fx,fy;
double Hcos = cos(Hangle);
double Hsin = sin(Hangle);
//x y 存放原图中四个点的位置,以中心为原点 左上角开始 顺时针数
int x[4];
int y[4];
int x1[4];
int y1[4];
x[0] = -(image->width-1)/2;
x[1] = -x[0];
x[2] = -x[0];
x[3] = x[0];
y[0] = -(image->height -1)/2;
y[1] = y[0];
y[2] = -y[0];
y[3] = -y[0];
//x1 y1 分别存放新图中图像的四个角点的位置 以中心为原点 左上角点开始 顺时针数
for (i = 0; i < 4 ; i++)
{
x1[i] = (int)(x[i]*Hcos - y[i]*Hsin + 0.5);
y1[i] = (int)(x[i]*Hsin + y[i]*Hcos + 0.5);
printf("x: %d \n",x[i] );
printf("y: %d\n",y[i]);
printf("x1: %d \n",x1[i] );
printf("y1: %d\n",y1[i]);
}
//确定新图像的长宽
if (abs(y1[2] - y1[0]) > abs(y1[3] - y1[1]))
{
Height = abs(y1[2] - y1[0]);
Width = abs(x1[3] - x1[1]);
}
else{
Height = abs(y1[3] - y1[1]);
Width = abs( x1[2] - x1[0]);
}
image3 = cvCreateImage(cvSize(Width,Height),image->depth,1);
//两个偏移常量
fx = -1*(Width -1)* Hcos*0.5 -(Height -1)*Hsin*0.5 + (image->width-1)/2;
fy = (Width -1)*Hsin*0.5 -(Height -1)*Hcos*0.5 + (image->height -1)/2;
for (i = 0 ; i< Height ; i++)
{
for (j =0 ; j< Width; j++)
{
dst = (unsigned char *)(image3->imageData + i*image3->widthStep + j);
r = (int)(i*Hcos- j * Hsin + fy + 0.5);
s = (int)(i*Hsin + j*Hcos + fx + 0.5 );
if (r <0 || s < 0 || r >= image->height || s >= image->width)
{
*dst = 0;
}
else{
ptr = (unsigned char *)(image->imageData + image->widthStep*r + s );
*dst = *ptr;
}
}
}
*/
/**************************************************************************************/
cvNamedWindow("image3",1);
cvShowImage("image3",image3);
cvSaveImage("bldxuanzhuan2.bmp",image3);
//cvWaitKey(0);
//垂直偏移 x = v*Sv + w y = w
//水平偏移 x = v y = Sh*v + w
//这里实现水平偏移
double sh = 0.8;
//确定新图的长和宽
Width = (int)(image->width + sh* image->height);
Height = image->height;
int t;
IplImage * image4 = cvCreateImage(cvSize(Width , Height),image->depth,1);
for (i = 0 ; i< Height ; i++)
{
for (j =0 ; j< Width; j++)
{
dst = (unsigned char *)(image4->imageData + i*image4->widthStep + j);
//r = (int)(i*Hcos- j * Hsin + fy + 0.5);
//s = (int)(i*Hsin + j*Hcos + fx + 0.5 );
t = i;
s = j - sh*i;
if (t <0 || s < 0 || t >= image->height || s >= image->width)
{
*dst = 0;
}
else{
ptr = (unsigned char *)(image->imageData + image->widthStep*t + (int)s );
z1 = *ptr;
z2 = *(ptr+1);
*dst = (int)(z1 + (z2 -z1)*(s- (int)s));
}
}
}
cvNamedWindow("image4",1);
cvShowImage("image4",image4);
cvSaveImage("bldpianyi.bmp",image4);
cvWaitKey(0);
cvReleaseImage(&image);
cvReleaseImage(&image2);
cvReleaseImage(&image3);
cvDestroyAllWindows();
return 0;
}
//图像的仿射变换和图像配准
//平移、缩放、旋转、偏移
//若想变换之后的图像时可以恢复的,那么在进行变换的时候图像的大小应该可以变化,是为了保留更多的信息
//使用反向映射的方式
五、图片配准
图像配准,一般用于对齐两幅或多幅相同场景的图像。前面已经讲了所有的仿射变换的函数形式,更为复杂的是多种仿射变化的连续作用,显然可以得出,多种仿射变换的连续作用可以用如下式子概括:
x =c1*v + c2*w + c3*v*w + c4
y =c5*v + c6*w + c7*v*w + c8
方程组含有8个未知量,假设我们已知一对图像中相对应的4个点的坐标,那个既可以解出8个未知量,从而实现两张图片的配准。
使用photoshop打开一种图片,放上至少4个小方块,能够肉眼识别,但又不要太大,太大会使误差增大,对图像进行一定的仿射变换,并记下四对匹配点的坐标。
这里
image1中取四个点
47 44
433 39
47 430
462 444
image2中取四个点
140 40
461 106
55 305
395 390
采用matlab解矩阵,求出8个参数,然后根据该参数进行图像配准:
matlab程序:************************************
% 图像配准 首先是解方程组获取参数
d = ones(4,4);
d(1:2,:) = [47 433 47 462;
44, 39 ,430 ,444];
d(3,:) = d(1,:).*d(2,:)
x= [140 461 55 395]
C1 = x/d
%得 C1 = 0.8290 -0.2200 -0.0000 110.7284
%事实上这个图像当时因为只是进行了缩放和旋转,因此不存在偏移,C(3) = 0是合理的
y = [40 ;106 ;305 ;390];
C2 = y/d
%C2 = 0.1797 0.6863 0.0000 1.3466
%理由同上,事实上这个图像当时因为只是进行了缩放和旋转,因此不存在偏移,C(3) = 0是合理的
图像配准程序,也是基于反向映射
/*******************************************************************
已知两幅图像上的四个对应点,很据变换式,采用matlab求出二者之间的变换方程
根据变换式,假如需要将图片2变换到图片1,那么需要求出图片1到2 的变换式,反向映射,
假如映射区域在2区域之内,那么赋予该处的值,否者需要赋予0
************************************************************************/
//中值滤波和均值滤波
#include<cv.h>
#include<highgui.h>
int main(){
IplImage * image,*image2,*image3;
image = cvLoadImage("E:\\image\\base.jpg",0);//以灰度图像的形式读入图片
image2 = cvLoadImage("E:\\image\\base2.jpg",0);
cvNamedWindow("image",CV_WINDOW_AUTOSIZE);
cvNamedWindow("image2",CV_WINDOW_AUTOSIZE);
cvNamedWindow("image3",CV_WINDOW_AUTOSIZE);
cvShowImage("image",image);
image3 = cvCreateImage(cvGetSize(image),image->depth,1);
//cvWaitKey(0);
unsigned char * ptr,*dst;
int i,j;
double m, n;
int temp[4];
double C[8] = {0.8290, -0.2200, -0.0000, 110.7284,0.1797,0.6863,0.0000,1.3466};//图片1到2 的变换矩阵
double Z1,Z2;
for( i = 0 ; i < image3->height;i++){
for( j = 0; j< image3->width;j++){
dst = (unsigned char *)image3->imageData+ i*image3->widthStep+ j;
n = C[0]*j + C[1]*i + C[2]*i*j + C[3];
m = C[4]*j + C[5]*i + C[6]*i*j + C[7];
if (m < 0.0 || n < 0.0 || m >= image2->height || n >= image2->width)
{
*dst = 0;
}
else{
ptr = (unsigned char *)(image2->imageData + image2->widthStep * (int)m + (int)n);
temp[0] = *ptr;
temp[1] = *(ptr+1);
ptr = (unsigned char *)(image2->imageData + image2->widthStep * (int)(m +1) + (int)n);
temp[2] = *ptr;
temp[3] = *(ptr+1);
Z1 = (temp[1] - temp[0])*(m - int(m)) + temp[0];
Z2 = (temp[3] -temp[2])*(m- int(m)) + temp[2];
*dst = (int)(Z1 + (Z2 -Z1)*(n- (int)n));
}
}
}
cvShowImage("image2",image2);
cvShowImage("image3",image3);
cvSaveImage("bldpeizhun.bmp",image3);
//比较配准效果
IplImage* image4;
image4 = cvCreateImage(cvSize(image->width , image->height),image->depth , 1);
cvSub(image , image3 , image4,0);
cvNamedWindow("sub");
cvShowImage("sub", image4);
cvSaveImage("bldsub.bmp",image4);
cvWaitKey(0);
return 0;
}
配准的两个源图像:
图1:
图2
配准之后的图像:
配准图像和原图像的差值:
转载于:https://www.cnblogs.com/libing64/archive/2012/01/04/2878736.html