Opencv图像识别从零到精通(30)---重映射,仿射变换

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_20823641/article/details/52232046

一、序言

       面对图像处理的时候,我们会旋转缩放图像,例如前面所提高的resize 插值改变,也是几何变换

       几何运算需要空间变换和灰度级差值两个步骤的算法,像素通过变换映射到新的坐标位置,新的位置可能是在几个像素之间,即不一定为整数坐标。这时就需要灰度级差值将映射的新坐标匹配到输出像素之间。最简单的插值方法是最近邻插值,就是令输出像素的灰度值等于映射最近的位置像素,该方法可能会产生锯齿。这种方法也叫零阶插值,相应比较复杂的还有一阶和高阶插值。

      除了插值算法感觉只要了解就可以了,图像处理中比较需要理解的还是空间变换

空间变换对应矩阵的仿射变换。一个坐标通过函数变换的新的坐标位置:


所以在程序中我们可以使用一个2*3的数组结构来存储变换矩阵:


以最简单的平移变换为例,平移(b1,b2)坐标可以表示为:


因此,平移变换的变换矩阵及逆矩阵记为:


缩放变换:将图像横坐标放大(或缩小)sx倍,纵坐标放大(或缩小)sy倍,变换矩阵及逆矩阵为:


选择变换:图像绕原点逆时针旋转a角,其变换矩阵及逆矩阵(顺时针选择)为:

二、重映射

重映射:

把一个图像中一个位置的像素放置到另一个图片指定位置的过程.

为了完成映射过程, 有必要获得一些插值为非整数像素坐标,因为源图像与目标图像的像素坐标不是一一对应的.

简单的说就是改变图片的位置(左,右,上,下,颠倒)

<span style="font-size:18px;">C++: void remap(InputArray src, OutputArraydst, InputArray map1, InputArray map2, int interpolation, intborderMode=BORDER_CONSTANT
, const Scalar& borderValue=Scalar())  </span>

    • 第一个参数,InputArray类型的src,输入图像,即源图像,填Mat类的对象即可,且需为单通道8位或者浮点型图像。
    • 第二个参数,OutputArray类型的dst,函数调用后的运算结果存在这里,即这个参数用于存放函数调用后的输出结果,需和源图片有一样的尺寸和类型。
    • 第三个参数,InputArray类型的map1,它有两种可能的表示对象。
  • 表示点(x,y)的第一个映射。
  • 表示CV_16SC2 , CV_32FC1 或CV_32FC2类型的X值。
    • 第四个参数,InputArray类型的map2,同样,它也有两种可能的表示对象,而且他是根据map1来确定表示那种对象。
  • 若map1表示点(x,y)时。这个参数不代表任何值。
  • 表示CV_16UC1 , CV_32FC1类型的Y值(第二个值)。

  • 第五个参数,int类型的interpolation,插值方式,之前的resize( )函数中有讲到,需要注意,resize( )函数中提到的INTER_AREA插值方式在这里是不支持的,所以可选的插值方式如下:
  • INTER_NEAREST - 最近邻插值
  • INTER_LINEAR – 双线性插值(默认值)
  • INTER_CUBIC – 双三次样条插值(逾4×4像素邻域内的双三次插值)
  • INTER_LANCZOS4 -Lanczos插值(逾8×8像素邻域的Lanczos插值)

  • 第六个参数,int类型的borderMode,边界模式,有默认值BORDER_CONSTANT,表示目标图像中“离群点(outliers)”的像素值不会被此函数修改。
  • 第七个参数,const Scalar&类型的borderValue,当有常数边界时使用的值,其有默认值Scalar( ),即默认值为0。
其中要变换的模式如下


三、仿射变换

 仿射变换(Affine Transformation)是空间直角坐标系的变换,从一个二维坐标变换到另一个二维坐标,仿射变换是一个线性变换,他保持了图像的平行性平直性,即图像中原来的直线和平行线,变换后仍然保持原来的直线和平行线,仿射变换比较常用的特殊变换有平移(Translation)、缩放(Scale)、翻转(Flip)、旋转(Rotation)和剪切(Shear)

其中,点1, 2 和 3 (在图一中形成一个三角形) 与图二中三个点是一一映射的关系, 且他们仍然形成三角形, 但形状已经和之前不一样了。我们能通过这样两组三点求出仿射变换 (可以选择自己喜欢的点), 接着就可以把仿射变换应用到图像中去。

而我们通常使用2 x 3的矩阵来表示仿射变换。


 

 

考虑到我们要使用矩阵 A 和 B 对二维向量 做变换, 所以也能表示为下列形式:


  或者     


 

即:       

也可以理解是坐标系的旋转和缩放、平移


<span style="font-size:18px;">C++: void warpAffine(InputArray src,OutputArray dst, InputArray M, Size dsize, int flags=INTER_LINEAR, intborderMode=BORDER_CONSTANT, const Scalar& borderValue=Scalar())  </span>

  • 第一个参数,InputArray类型的src,输入图像,即源图像,填Mat类的对象即可。
  • 第二个参数,OutputArray类型的dst,函数调用后的运算结果存在这里,需和源图片有一样的尺寸和类型。
  • 第三个参数,InputArray类型的M,2×3的变换矩阵。
  • 第四个参数,Size类型的dsize,表示输出图像的尺寸。
  • 第五个参数,int类型的flags,插值方法的标识符。此参数有默认值INTER_LINEAR(线性插值),可选的插值方式如下:
    • INTER_NEAREST - 最近邻插值
    • INTER_LINEAR - 线性插值(默认值)
    • INTER_AREA - 区域插值
    • INTER_CUBIC –三次样条插值
    • INTER_LANCZOS4 -Lanczos插值
    • CV_WARP_FILL_OUTLIERS - 填充所有输出图像的象素。如果部分象素落在输入图像的边界外,那么它们的值设定为 fillval.
    • CV_WARP_INVERSE_MAP –表示M为输出图像到输入图像的反变换,即 。因此可以直接用来做象素插值。否则, warpAffine函数从M矩阵得到反变换。
  • 第六个参数,int类型的borderMode,边界像素模式,默认值为BORDER_CONSTANT。
  • 第七个参数,const Scalar&类型的borderValue,在恒定的边界情况下取的值,默认值为Scalar(),即0。
四、例子

<span style="font-size:18px;">#include<opencv2/opencv.hpp>  
#include<iostream>  
#include<vector>  
using namespace cv;  
using namespace std;   
int main()  
{  
    Mat srcImage = imread("lena.jpg", 1);  
    imshow("【原图】", srcImage);  
    Mat grayImage;  
    cvtColor(srcImage, grayImage, CV_BGR2GRAY);  
    Mat XImage, YImage;  
    Mat dstImage;  
    dstImage.create(srcImage.size(), srcImage.type());  
    XImage.create(srcImage.size(), CV_32FC1);  
    YImage.create(srcImage.size(), CV_32FC1);  
    for (int i = 0; i < srcImage.rows; i++)  
    {  
        for (int j = 0; j < srcImage.cols; j++)  
        {  
            XImage.at<float>(i, j) = static_cast<float>(srcImage.cols - j);  
            YImage.at<float>(i, j) = static_cast<float>(i);  
        }  
    }  
    remap(srcImage, dstImage, XImage, YImage, CV_INTER_LINEAR, BORDER_CONSTANT, Scalar(0, 0, 0));  
    imshow("【重映射后】", dstImage);  
    waitKey(0);  
    return 0;  
}  </span>

<span style="font-size:18px;">#include "opencv2/highgui/highgui.hpp"
#include "opencv2/imgproc/imgproc.hpp"
#include <iostream>
#include <stdio.h>
using namespace cv;
using namespace std;
char* source_window = "Source image";
char* warp_window = "Warp";
char* warp_rotate_window = "Warp + Rotate";
 int main( int argc, char** argv )
 {
   Point2f srcTri[3];
   Point2f dstTri[3];
   Mat rot_mat( 2, 3, CV_32FC1 );
   Mat warp_mat( 2, 3, CV_32FC1 );
   Mat src, warp_dst, warp_rotate_dst;
   src = imread("lena.jpg", 1 );
   warp_dst = Mat::zeros( src.rows, src.cols, src.type() );
   srcTri[0] = Point2f( 0,0 );
   srcTri[1] = Point2f( src.cols - 1, 0 );
   srcTri[2] = Point2f( 0, src.rows - 1 );
   dstTri[0] = Point2f( src.cols*0.0, src.rows*0.33 );
   dstTri[1] = Point2f( src.cols*0.85, src.rows*0.25 );
   dstTri[2] = Point2f( src.cols*0.15, src.rows*0.7 );
   warp_mat = getAffineTransform( srcTri, dstTri );
   warpAffine( src, warp_dst, warp_mat, warp_dst.size() );
   Point center = Point( warp_dst.cols/2, warp_dst.rows/2 );
   double angle = -50.0;
   double scale = 0.6;
   rot_mat = getRotationMatrix2D( center, angle, scale );
   warpAffine( warp_dst, warp_rotate_dst, rot_mat, warp_dst.size() );
   namedWindow( source_window, CV_WINDOW_AUTOSIZE );
   imshow( source_window, src );
   namedWindow( warp_window, CV_WINDOW_AUTOSIZE );
   imshow( warp_window, warp_dst );
   namedWindow( warp_rotate_window, CV_WINDOW_AUTOSIZE );
   imshow( warp_rotate_window, warp_rotate_dst );
   waitKey(0);
   return 0;
  }</span>



五、matlab

f=imread('d:\lena.jpg');
tform=maketform('affine',[-1 0 0;0 1 0;0 0 1]);
ff=imtransform(f,tform);
imshow(f)
figure
imshow(ff)


图像识别算法交流 QQ群:145076161,欢迎图像识别与图像算法,共同学习与交流

展开阅读全文

没有更多推荐了,返回首页