图像几何变换

来源:互联网 发布:手机知乎怎么回答问题 编辑:程序博客网 时间:2024/05/16 08:00

图像的集合变换


一、放射变换

最为常用的几何变换都是线性变换,这包括旋转、缩放、切变、反射以及正投影。在二维空间中,线性变换可以用 2×2 的变换矩阵表示。

1.旋转变换

 

绕原点逆时针旋转 θ 度角的变换公式是 x' = x \cos \theta - y \sin \theta 与 y' = x \sin \theta + y \cos \theta,用矩阵表示为:

\begin{pmatrix} x' \\ y' \end{pmatrix} = \begin{pmatrix} \cos \theta & - \sin\theta \\ \sin \theta & \cos \theta \end{pmatrix} \begin{pmatrix} x \\ y \end{pmatrix}

 

2.伸缩变换

 

缩放公式为 x' = s_x \cdot x 与 y' = s_y \cdot y,用矩阵表示为:

\begin{pmatrix} x' \\ y' \end{pmatrix} = \begin{pmatrix} s_x & 0 \\ 0 & s_y \end{pmatrix} \begin{pmatrix} x \\ y \end{pmatrix}

 

3.错切变换

 

错切变换公式:      x = x0 + b*y0;      y = d*x0 + y0;   b,d分别是x,y平移的分量

Shear-Table

 

用矩阵乘法表示变换为:       Shear-Matrix

4.平移变换

 

矩阵乘法表示变换。 x' = x + t_xy' = y + t_y 变为

\begin{pmatrix} x' \\ y' \\ 1 \end{pmatrix} = \begin{pmatrix} 1 & 0 & t_x \\ 0 & 1 & t_y \\ 0 & 0 & 1 \end{pmatrix} \begin{pmatrix} x \\ y \\ 1 \end{pmatrix}

 

二、图像插值放大

 

1.最邻近插值(近邻取样法):
  最临近插值的的思想很简单。对于通过反向变换得到的的一个浮点坐标,对其进行简单的取整,得到一个整数型坐标,这个整数型坐标对应的像素值就是目的像素的像素值,也就是说,取浮点坐标最邻近的左上角点对应的像素值。可见,最邻近插值简单且直观,但得到的图像质量不高。



2.双线性内插值:
  对于一个目的像素,设置坐标通过反向变换得到的浮点坐标为(i+u,j+v),其中i、j均为非负整数,u、v为[0,1)区间的浮点数,则这个像素得值 f(i+u,j+v) 可由原图像中坐标为 (i,j)、(i+1,j)、(i,j+1)、(i+1,j+1)所对应的周围四个像素的值决定,即:


    f(i+u,j+v) = (1-u)(1-v)f(i,j) + (1-u)vf(i,j+1) + u(1-v)f(i+1,j) + uvf(i+1,j+1)


其中f(i,j)表示源图像(i,j)处的像素值,以此类推。
  这就是双线性内插值法。双线性内插值法计算量大,但缩放后图像质量高,不会出现像素值不连续的的情况。由于双线性插值具有低通滤波器的性质,使高频分量受损,所以可能会使图像轮廓在一定程度上变得模糊。

3.三次卷积法
能够克服以上两种算法的不足,计算精度高,但计算亮大,他考虑一个浮点坐标(i+u,j+v)周围的16个邻点,目的像素值f(i+u,j+v)可由如下插值公式得到:


    f(i+u,j+v) = [A] * [B] * [C]


[A]=[ S(u + 1) S(u + 0) S(u - 1) S(u - 2) ]


  ┏ f(i-1, j-1) f(i-1, j+0) f(i-1, j+1) f(i-1, j+2) ┓
[B]=┃ f(i+0, j-1) f(i+0, j+0) f(i+0, j+1) f(i+0, j+2) ┃
  ┃ f(i+1, j-1) f(i+1, j+0) f(i+1, j+1) f(i+1, j+2) ┃
  ┗ f(i+2, j-1) f(i+2, j+0) f(i+2, j+1) f(i+2, j+2) ┛


  ┏ S(v + 1) ┓
[C]=┃ S(v + 0) ┃
  ┃ S(v - 1) ┃
  ┗ S(v - 2) ┛


   ┏ 1-2*Abs(x)^2+Abs(x)^3      , 0<=Abs(x)<1
S(x)={ 4-8*Abs(x)+5*Abs(x)^2-Abs(x)^3 , 1<=Abs(x)<2           其中 S(x)是对 Sin(x*Pi)/x 的逼近(Pi是圆周率——π) 
   ┗ 0                , Abs(x)>=2

最邻近插值(近邻取样法)、双线性内插值、三次卷积法 等插值算法对于旋转变换、错切变换、一般线性变换 和非线性变换 都适用。

 

 

三、编程实现

Java类库内置方法实现了图像几何变换有:旋转,缩放,错切和平移等变换,图像放大使用的算法是最临近插值方法,下面自己实现的算法进行实验,至于Java内置的方法通过调用Graphics2D相关函数设置参数就可以完成,可以查看相应文档。

1.旋转

 

旋转代码  收藏代码
  1. /****************************************************  
  2.  * 图像旋转  
  3.  * pix  --原图像像素数组  
  4.  * opix--输出图像像素数组   
  5.  * (x,y) --原图像坐标 0<=x<=w, 0<=y<=h  
  6.  * (i,j) --输出图像坐标0<=i,j<=owh     
  7.  * beta  --旋转角度  
  8.  ****************************************************/  
  9. public int[] imRotate(int[] pix, float beta, int iw, int ih, int owh)  
  10. {  
  11.     //1.旋转后的新图像最大最小包围盒宽高  
  12.     int[] opix = new int[owh * owh];  
  13.       
  14.     double t = beta / 180;             
  15.               
  16.     float cos_beta =  (float)Math.cos(t * Math.PI);//顺时针旋转  
  17.     float sin_beta =  (float)Math.sin(t * Math.PI);  
  18.       
  19.     //2.逆旋转变换, 计算输出图像点p(i,j)所对应的原图像的坐标(x,y)  
  20.     for(int i = 0;i < owh;i++)      
  21.     {  
  22.         for(int j = 0;j < owh;j++)  
  23.         {                 
  24.             //旋转变换的逆变换  
  25.             float u = (j-owh/2)*cos_beta+(i-owh/2)*sin_beta;  
  26.             float v = (i-owh/2)*cos_beta-(j-owh/2)*sin_beta;  
  27.           
  28.             //换成相对于原图像的绝对坐标  
  29.             u += iw/2;  
  30.             v += ih/2;  
  31.           
  32.             int x = (int)u;  
  33.             int y = (int)v;  
  34.   
  35.             int index = i * owh + j;  
  36.             //3.检验条件, 对满足条件的点(x,y),赋值F(i,j)=f(x,y)  
  37.             if((x >= 0) && (x <= iw - 1) && (y >= 0) && (y <= ih - 1))  
  38.                 opix[index] = pix[y * iw + x];                            
  39.         }  
  40.     }      
  41.     return opix;  
  42. }   
  43.     

 实验效果如下:


 

2.镜像变换

 

镜像变换代码  收藏代码
  1. //镜象变换    
  2.     public int[] imMirror(int[] pixs, int w, int h, int n)  
  3.     {  
  4.         int[] pix = new int[w * h];  
  5.                       
  6.         //根据镜象变换公式,计算(u,v)并赋值F(u,v)=f(i,j)  
  7.         for(int j = 0; j < h; j++)  
  8.         {  
  9.             for(int i = 0; i < w; i++)  
  10.             {  
  11.                 if(n == 0)     //水平  
  12.                 {                     
  13.                     int u = w - 1 - i;  
  14.                     int v = j;  
  15.                     pix[v*w+u] = pixs[j*w+i];   
  16.                 }  
  17.                 else if(n == 1)//垂直  
  18.                 {                     
  19.                     int u = i;  
  20.                     int v = h - 1 - j;  
  21.                     pix[v*w+u] = pixs[j*w+i];   
  22.                 }                 
  23.             }             
  24.         }  
  25.         return pix;  
  26.     }  
  27.       

 

 实验效果如下:


3.错切变换

 

错切变换代码  收藏代码
  1. * 错切变换算法  
  2.    * 1.计算图像四角点坐标  
  3.    * 2.计算包围图像的最小矩形,即包围盒的宽和高  
  4.    * 3.将处理包围盒内的所有像素设置为背景色  
  5.    * 4.根据错切变换公式,  
  6.    *   u = (int)(i + j * shx)  
  7.    *   v = (int)(i * shy + j)  
  8.    *  计算(u,v)并赋值 F(u,v) = f(i,j)        
  9.    **********************************************/  
  10.      
  11.   public int[] imShear(int[] pixs, double shx, double shy,   
  12.                        int w, int h, int ow, int oh)  
  13.   {   
  14.       int[] pix = new int[ow * oh];  
  15.               
  16.     //根据错切变换公式,计算(u,v)并赋值F(u,v)=f(i,j)  
  17.     for(int j = 0; j < h; j++)  
  18.     {  
  19.         for(int i = 0; i < w; i++)  
  20.         {  
  21.             int u = (int)(i + j * shx);  
  22.             int v = (int)(i * shy + j);  
  23.             pix[v*ow+u] = pixs[j*w+i];                
  24.         }             
  25.     }  
  26.     return pix;  
  27.   }  
  28.     

 实验效果如下:


 

4.平移变换

 

平移变换代码  收藏代码
  1. //平移变换算法  
  2.   public int[] imTrans(int[] pixs, int tx, int ty,   
  3.                        int w, int h, int ow, int oh)  
  4.   {   
  5.       int u, v;  
  6.       int[] pix = new int[ow * oh];  
  7.               
  8.     //根据错切变换公式,计算(u,v)并赋值F(u,v)=f(i,j)  
  9.     for(int j = 0; j < h; j++)  
  10.     {  
  11.         for(int i = 0; i < w; i++)  
  12.         {  
  13.             u = i + tx;  
  14.             v = j + ty;  
  15.             pix[v*ow+u] = pixs[j*w+i];                
  16.         }             
  17.     }  
  18.     return pix;  
  19.   }  
  20.     

 

 实验效果如下:


5.最邻近插值

 

最邻近插值代码  收藏代码
  1. //最邻近插值  
  2. public int[] nearNeighbor(int pixs[], int iw, int ih,   
  3.                           int ow, int oh, float p)  
  4.    {          
  5.        int opix[] = new int[ow * oh];//目标图像素数组  
  6.           
  7.     ColorModel cm = ColorModel.getRGBdefault();  
  8.     for(int i = 0; i < oh; i++)   
  9.     {  
  10.         int u = (int)(i/p);  
  11.         for(int j = 0; j < ow; j++)  
  12.         {  
  13.             int r, g, b;              
  14.             int v = (int)(j/p);               
  15.              
  16.             r = cm.getRed(pixs[u*iw+v]);  
  17.             g = cm.getGreen(pixs[u*iw+v]);  
  18.             b = cm.getBlue(pixs[u*iw+v]);  
  19.               
  20.             opix[i*ow+j] = 255 << 24|r << 16|g << 8|b;  
  21.         }  
  22.     }  
  23.     return opix;       
  24.    }  
  25.      

 实验效果如下:

 

6.双线性插值

 

双线性插值代码  收藏代码
  1. //双线性插值算法  
  2.    public int[] bilinear(int pixs[], int iw, int ih, int ow, int oh, float p)  
  3.    {      
  4.     int pixd[]=new int[ow * oh];//目标图像素数组  
  5.           
  6.     ColorModel cm = ColorModel.getRGBdefault();  
  7.     for(int i = 0; i < oh-1; i++)   
  8.     {  
  9.         float dy = i/p;  
  10.         int iy = (int)dy;  
  11.         if(iy > ih-2) iy = ih-2;  
  12.         float d_y = dy - iy;  
  13.           
  14.         for(int j = 0; j < ow-1; j++)   
  15.         {  
  16.             int a,r,g,b;  
  17.             float dx = j/p;           
  18.             int ix = (int)dx;  
  19.             if(ix > iw-2) ix = iw-2;  
  20.             float d_x= dx - ix;  
  21.               
  22.             //f(i+u,j+v)=(1-u)(1-v)f(i,j)+u(1-v)f(i+1,j)+  
  23.             //           (1-u)vf(i,j+1)+uvf(i+1,j+1)  
  24.             r = (int)((1-d_x)*(1-d_y)*cm.getRed(pixs[iy*iw+ix])+  
  25.                          d_x*(1-d_y)*cm.getRed(pixs[iy*iw+ix+1])+  
  26.                          (1-d_x)*d_y*cm.getRed(pixs[(iy+1)*iw+ix])+  
  27.                          d_x*d_y*cm.getRed(pixs[(iy+1)*iw+ix+1]));  
  28.                           
  29.             g = (int)((1-d_x)*(1-d_y)*cm.getGreen(pixs[iy*iw+ix])+  
  30.                          d_x*(1-d_y)*cm.getGreen(pixs[iy*iw+ix+1])+  
  31.                          (1-d_x)*d_y*cm.getGreen(pixs[(iy+1)*iw+ix])+  
  32.                          d_x*d_y*cm.getGreen(pixs[(iy+1)*iw+ix+1]));  
  33.                           
  34.             b = (int)((1-d_x)*(1-d_y)*cm.getBlue(pixs[iy*iw+ix])+  
  35.                          d_x*(1-d_y)*cm.getBlue(pixs[iy*iw+ix+1])+  
  36.                          (1-d_x)*d_y*cm.getBlue(pixs[(iy+1)*iw+ix])+  
  37.                          d_x*d_y*cm.getBlue(pixs[(iy+1)*iw+ix+1]));  
  38.                                               
  39.             pixd[i*ow+j] = 255 << 24|r << 16|g << 8|b;  
  40.         }  
  41.     }  
  42.     return pixd;        
  43.    }  
  44.      

 实验效果如下:


 

7.三次卷积插值

 

三次卷积插值代码  收藏代码
  1.  //对整个图像按给定的宽度和高度比例进行缩放  
  2. public int[] scale(int[] pix, int iw, int ih, int ow, int oh,   
  3.                    float scalex, float scaley)  
  4. {         
  5.     int pixelsSrc[]  = new int[iw * ih];  
  6.     int pixelsDest[] = new int[ow * oh];  
  7.       
  8.     //第三步,缩放图像  
  9.     this.scale(pix, 00, iw, ih, iw, scalex, scaley, pixelsDest);  
  10.     return (pixelsDest);  
  11. }  
  12.   
  13. /********************************************************  
  14.  * src 原图像的像素数据,  
  15.  * (x, y) 被缩放的区域在左上角的坐标,  
  16.  * (w, h) 缩放区域的宽度和高度  
  17.  * scansize 原图像的扫描宽度  
  18.  * (scalex, scaley) 水平和垂直方向上的缩放比  
  19.  * dst 缩放后的图像像素数据  
  20.  ********************************************************/    
  21. public void scale(int[] src, int x, int y, int w, int h, int scansize,  
  22.                   float scalex, float scaley, int[] dst)   
  23. {                 
  24.     //原图像的宽度  
  25.     int srcWidth = scansize;  
  26.       
  27.     //原图像可以扫描的总行数:即原图像的高度  
  28.     int srcHeight = src.length / scansize;  
  29.       
  30.     //width---height:处理区域的宽度和高度,  
  31.     //如果参数传递是合法的,那么它们就是w,h  
  32.     int width  = w;  
  33.     int height = h;  
  34.   
  35.     if((x + w) > scansize)  width  = scansize  - x;  
  36.     if((y + h) > srcHeight) height = srcHeight - y;  
  37.   
  38.     int dstWidth  = (int)( width * scalex + 0.5f);  
  39.     int dstHeight = (int)(height * scaley + 0.5f);  
  40.   
  41.     //进行反向变换  
  42.     //i--按行,j--按列  
  43.     for(int i = 0;i < dstHeight;i++)  
  44.     {             
  45.         //按反向变换法,获取第i行所对应的原图像的位置:行:yy  
  46.         float y_inverse_map = i / scaley;  
  47.           
  48.         int y_lt = (int)y_inverse_map;  
  49.           
  50.         //垂直方向偏移量  
  51.         float v = y_inverse_map - y_lt;  
  52.           
  53.         //左上角的y坐标  
  54.         y_lt += y;  
  55.           
  56.         int indexBase = i * dstWidth;  
  57.         for(int j = 0;j < dstWidth;j++)  
  58.         {  
  59.             float x_inverse_map = j / scalex;  
  60.           
  61.             int x_lt = (int)x_inverse_map;  
  62.           
  63.             //水平方向偏移量  
  64.             float u = x_inverse_map - x_lt;  
  65.           
  66.             //左上角的y坐标  
  67.             x_lt += x;  
  68.               
  69.             //通过计算获取变换后的点  
  70.             int index  = indexBase + j;  
  71.             dst[index] = interpolate(src, x_lt, y_lt, u, v,   
  72.                                      srcWidth, srcHeight);  
  73.         }  
  74.     }  
  75. }  
  76.   
  77. /**************************************************************   
  78.  * src:原图像的像素,  
  79.  * (x,y):经过反向变换后,与反向变换点最接近的原图像的左上角的点  
  80.  * (u,v):反向变换点相对(x,y)点的偏移量.  
  81.  * scanw:原图像的扫描宽度,scanh原图像的高度  
  82.  **************************************************************/  
  83. private int interpolate(int[] src, int x, int y, float u,   
  84.                         float v, int scanw, int scanh)  
  85. {         
  86.     ColorModel colorModel = ColorModel.getRGBdefault();  
  87.       
  88.     int r = 0;  
  89.     int g = 0;  
  90.     int b = 0;  
  91.       
  92.     //邻近区域的像素值  
  93.     int red[][]   = new int[4][4];  
  94.     int green[][] = new int[4][4];  
  95.     int blue[][]  = new int[4][4];  
  96.       
  97.     //邻近区域的坐标  
  98.     int xx[] = new int[4];  
  99.     int yy[] = new int[4];  
  100.       
  101.     xx[0] = x - 1; xx[1] = x; xx[2] = x + 1; xx[3] = x + 2;  
  102.     yy[0] = y - 1; yy[1] = y; yy[2] = y + 1; yy[3] = y + 2;  
  103.       
  104.     if(xx[0] < 0) xx[0] = 0;  
  105.     if(yy[0] < 0) yy[0] = 0;  
  106.     if(xx[2] > scanw - 1) xx[2] = scanw - 1;  
  107.     if(yy[2] > scanh - 1) yy[2] = scanh - 1;  
  108.     if(xx[3] > scanw - 1) xx[3] = scanw - 1;  
  109.     if(yy[3] > scanh - 1) yy[3] = scanh - 1;  
  110.   
  111.     //获取4*4区域的像素值  
  112.     int i = 0; int j = 0;  
  113.     for(i = 0; i < 4; i++)  
  114.     {  
  115.         int indexBase = yy[i] * scanw;  
  116.         //j处理像素行  
  117.         for(j = 0; j < 4; j++)  
  118.         {  
  119.             int index   = indexBase + xx[j];  
  120.             red[i][j]   = colorModel.getRed(src[index]);  
  121.             green[i][j] = colorModel.getGreen(src[index]);  
  122.             blue[i][j]  = colorModel.getBlue(src[index]);  
  123.         }  
  124.     }  
  125.       
  126.     float su[] = new float[4];  
  127.     float sv[] = new float[4];  
  128.       
  129.     su[0] = sinx_x(1.0f + u);  
  130.     su[1] = sinx_x(u);  
  131.     su[2] = sinx_x(1.0f - u);  
  132.     su[3] = sinx_x(2.0f - u);  
  133.   
  134.     sv[0] = sinx_x(1.0f + v);  
  135.     sv[1] = sinx_x(v);  
  136.     sv[2] = sinx_x(1.0f - v);  
  137.     sv[3] = sinx_x(2.0f - v);  
  138.       
  139.     //作矩阵乘积:sv * red,sv * green,sv * blue  
  140.     float sv_r[] = new float[4];   
  141.     float sv_g[] = new float[4];  
  142.     float sv_b[] = new float[4];  
  143.       
  144.     for(i = 0; i < 4; i++)  
  145.     {  
  146.         for(j = 0; j < 4; j++)  
  147.         {  
  148.             sv_r[i] += (sv[j] * red[j][i]);  
  149.             sv_g[i] += (sv[j] * green[j][i]);  
  150.             sv_b[i] += (sv[j] * blue[j][i]);  
  151.         }  
  152.     }  
  153.       
  154.     r = (int)(su[0] * sv_r[0] + su[1] * sv_r[1]   
  155.       + su[2] * sv_r[2] + su[3] * sv_r[3]);  
  156.     g = (int)(su[0] * sv_g[0] + su[1] * sv_g[1]   
  157.       + su[2] * sv_g[2] + su[3] * sv_g[3]);  
  158.     b = (int)(su[0] * sv_b[0] + su[1] * sv_b[1]   
  159.       + su[2] * sv_b[2] + su[3] * sv_b[3]);  
  160.       
  161.     r = (r < 0) ? 0 : ((r > 255) ? 255 : r);  
  162.     g = (g < 0) ? 0 : ((g > 255) ? 255 : g);  
  163.     b = (b < 0) ? 0 : ((b > 255) ? 255 : b);  
  164.     return (0xFF000000 | (( r << 16 ) | ( g << 8 ) | b ));  
  165. }  
  166.   
  167. //计算 sin(x)/x 的值,采用多项式展开  
  168. private float sinx_x(float x)  
  169. {         
  170.     float x_abs = Math.abs(x);  
  171.     float x_x   = x_abs * x_abs;  
  172.     float x_x_x = x_x * x_abs;  
  173.       
  174.     if(x_abs < 1.0f)  
  175.         return (1.0f - 2.0f * x_x + x_x_x);  
  176.     else if(x_abs < 2.0f)  
  177.         return (4.0f - 8.0f * x_abs + 5.0f * x_x - x_x_x);  
  178.     else   
  179.         return  0.0f;  
  180. }        

 实验效果如下:

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

0 0
原创粉丝点击