【The magician of pixels】影像的幾何變換_使用opencv實踐影像的180度垂直翻轉x平移

影像的幾何變換




是以在不改變影像像素值為前提情況下
對影像的pixel進行空間幾何變換

你可能開始會問了
有哪些屬於幾何變換呢??


常見的幾何變換有以下幾種

坐標映射
平移
鏡像
旋轉
縮放
仿射變化

等等


影像的幾何變換是影像處理&分析的基礎
可應用到字符校正、車牌區域水平與垂直校正及圖像特徵分析與識別喔!!!


【坐標映射】

影像的座標映射主要是通過原影像與目標影像之間建立一種映射關係


這種映射關係可細分成兩種


(1)計算原影像任意像素在映射後的圖像座標位置


(2)計算變換後的圖像任意像素反映射在原圖像的坐標位置



正映射  :   由原影像映射到目標圖像稱作正映射

VS

反映射 : 由目標影像通過映射關係得到原圖像稱為反映射


一般影像處理及分析的研發工程師高手
會採取的是通過 反映射方式保證輸出目標影像的每個pixel都可以通過映射關係
在原圖像中找到唯一的對應像素

主要原因是因為正映射時常會映射不完全以及出現映射重複現象!!!!!


opencv中提供重映射的操作

重映射 講白話一點就是指

把一個影像中一個位置的pixel通過映射關係轉換到另一個影像的指定位置


對於輸出的原影像我們以 函數    f(x,y)   來作表示

目標影像則以  g(x,y)     映射關係為  T


滿足下列這個式子




需要注意的是 ,  通過映射關係T 實踐得到的目標影像可能存在目標影像像素值
屬於非整數的情況


一般可考慮  插值  或者  向上取整


void remap(InputArray src,                               // 輸入矩陣(影像)
                   OutputArray dst,                             //輸出(目標)矩陣(影像)
                   InputArray map1,                           // x坐標
                   InputArray map2,                           //y坐標
                   int interpolation,                              //插值方法
                   int borderMode=BORDER_CONSTANT, //邊界插值類型
                   const Scalar& borderValue=Scalar() );          //插值數值


【remap使用方式-程式碼】



/*Create by The magician of pixels 周冠羽
    remap  示範
*/

#include<opencv2/highgui/highgui.hpp>
#include<opencv2/imgproc/imgproc.hpp>
#include<opencv2/core/core.hpp>

#include<iostream>

using namespace std;
using namespace cv;

int main()
{
    Mat srcImg;
    srcImg = imread("C:\\img_res\\boy.jpg");

    if (!srcImg.data)
        return -1;
    
    //定義輸出矩陣
    Mat resultImg(srcImg.size(), srcImg.type());
    //x方向矩陣  及   y方向矩陣  之 定義
    Mat xMapImg(srcImg.size() , CV_32FC1 );
    Mat yMapImg(srcImg.size(), CV_32FC1);

    //獲得影像的寬、高
    int rows = srcImg.rows;
    int    cols = srcImg.cols;

    for (int i = 0; i < rows;i++)
        for (int j = 0; j < cols; j++)
        {
            //  x  和  y  進行反轉
            xMapImg.at<float>(i,j) = cols - j;
            yMapImg.at<float>(i, j) = rows - i;
        }
    //重映射 操作
    remap(srcImg , resultImg , xMapImg , yMapImg , CV_INTER_LINEAR , cv::BORDER_CONSTANT , Scalar(0,0,0) );

    //輸出
    namedWindow("原本的影像" , 0);
    imshow("原本的影像" , srcImg);
    namedWindow("進行重映射後的結果" , 0);
    imshow("進行重映射後的結果",resultImg);


    waitKey(0);
    return 0;
}

【效果呈現】

垂直鏡像



你可以清楚看到是一個上下顛倒(180度垂直翻轉)的效果

當然我們也可以做到像是左右顛倒轉向的效果



我所選用的影像尺寸為480*359



opencv官網的說明連結:
http://docs.opencv.org/2.4/doc/tutorials/imgproc/imgtrans/remap/remap.html








【平移】

影像的平移操作是將影像中所有像素座標進行
水平或垂直方向移動


換言之,所有像素點會按照你所給定的偏移量在水平方向上沿著x軸、垂直方向y軸移動。


就是將圖像所有的像素坐標分別加上指定的水平偏移量和垂直偏移量。

平移變換根據是否改變圖像大小分為兩種


以下方我測試的這張照片為例


平移的兩種類型




第一種.向前映射
影像原始信息部分可能會遺失(影像資訊不完整會有遮住部分)
這是向前映射,即將原圖像的坐標映射到變換後的圖像上

圖像平移的變換公式如下:

設dx為水平偏移量,dy為垂直偏移量,
(x0,y0)為原圖像坐標,
(x,y)為變換後圖像坐標,

則平移變換的坐標映射為

效果示意圖
第一種. 平移圖像大小不發生改變

所以會產生局部被遮蔽的效果



右邊的平移圖像大小沒有變化,故圖像右下角的部分被截除了。






第二種.向後映射
能確保影像平移的完整信息

向前映射的逆變換為 ,向後映射,即將變換後的圖像坐標映射到原圖像上。
在圖像的幾何變換中,一般使用向後映射優點在於能保留完整原影像資訊


dx為水平偏移量
dy為垂直偏移量,
(x0,y0)為原圖像坐標,
(x,y)為變換後圖像坐標

第二種. 平移圖像大小發生改變




右邊平移圖像的大小發生了,在保證圖像平移的同時,也保存了完整的圖像信息






對於  原影像  而言

正變換矩陣為:



對於   目標影像   而言

逆變換矩陣




對4*4圖像矩陣向右平移x軸一個單位向下平移y軸一個單位
而且移動過後圖像大小仍保持不變多餘部分填充為白色時

我們可以利用下方矩陣示意圖來表示之

===================================================================

對4*4圖像矩陣向左平移x軸一個單位向上平移y軸一個單位
而且移動過後圖像大小發生改變了!!!多餘部分填充為白色時




(陣列示意圖製作方式: office  word )
參考連結: http://otacorn.blogspot.com/2013/07/insert-higher-level-matrix-in-word-2010-2007.html



【圖像平移-程式碼】


/*Create by The magician of pixels 周冠羽
影像平移  示範
*/

#include <opencv2/imgproc/imgproc.hpp>  
#include <opencv2/core/core.hpp>        
#include <opencv2/highgui/highgui.hpp> 
#include <iostream> 

using namespace std;
using namespace cv;

// 平移操作 圖像大小不改變----->圖像局部被遮擋
Mat imageTranslation1(cv::Mat & srcImage, int xOffset, int yOffset)
{
    int nRows = srcImage.rows;
    int nCols = srcImage.cols;
    Mat resultImage(srcImage.size(),
        srcImage.type());
    
    for (int i = 0; i < nRows; ++i)
    {
        for (int j = 0; j < nCols; ++j)
        {
            // 映射變換
            int x = j - xOffset;
            int y = i - yOffset;
            // 邊界判斷
            if (x >= 0 && y >= 0 && x < nCols && y < nRows)
                resultImage.at<Vec3b>(i, j) = srcImage.ptr<Vec3b>(y)[x];
        }
    }
    return resultImage;
}
// 平移操作 圖像大小發生改變---->保留完整圖像資訊
Mat imageTranslation2(cv::Mat & srcImage, int xOffset, int yOffset)
{
    // 平移尺寸的設置
    int nRows = srcImage.rows + abs(yOffset);
    int nCols = srcImage.cols + abs(xOffset);
    Mat resultImage(nRows, nCols,
        srcImage.type());
    for (int i = 0; i < nRows; ++i)
    {
        for (int j = 0; j < nCols; ++j)
        {
            // 映射變換
            int x = j - xOffset;
            int y = i - yOffset;
            // 邊界判斷
            if (x >= 0 && y >= 0 && x < nCols && y < nRows)
                resultImage.at<Vec3b>(i, j) = srcImage.ptr<Vec3b>(y)[x];
        }
    }
    return resultImage;
}
int main()
{
    Mat srcImage = imread("C:\\img_res\\boy.jpg");
    if (!srcImage.data)
        return -1;

    imshow("srcImage", srcImage);
    
    //水平 、 垂直  偏移量設置
    int xOffset = 50;
    int    yOffset = 80;
    // 圖像左平移不改變大小
    Mat resultImage1 =
        imageTranslation1(srcImage, xOffset, yOffset);
    imshow("resultImage1", resultImage1);
    // 圖像左平移改變大小
    Mat resultImage2 =
        imageTranslation2(srcImage, xOffset, yOffset);
    imshow("resultImage2", resultImage2);
    


    waitKey(0);
    return 0;
}




留言

這個網誌中的熱門文章

何謂淨重(Net Weight)、皮重(Tare Weight)與毛重(Gross Weight)

Architecture(架構) 和 Framework(框架) 有何不同?_軟體設計前的事前規劃的藍圖概念

經得起原始碼資安弱點掃描的程式設計習慣培養(五)_Missing HSTS Header