Opencv圖像識別從零到精通(30)---重映射,仿射變換

一、序言

       面對圖像處理的時候,我們會旋轉縮放圖像,例如前面所提高的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,歡迎圖像識別與圖像算法,共同學習與交流

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章