Just a Computer Graphics Studio & My Life

透視變換相當值得探討,之前稍微研究了透視投影透視縮短,還在計算機圖學課上寫了透視投影的結果,如今會了OpenCV函式庫,直接呼叫來玩玩更加有趣~

程式碼其實不會很難,只要有提供API,就先不用管裡頭怎麼實做,當然我要的就是效果如何囉:P

/**
	Theme: Perspective Transform
	compiler: Visual C++ 2010 with OpenCV 2.4.3
	Date: 102/03/22
	Author: HappyMan
	Blog: https://cg2010studio.wordpress.com/
*/
#include <iostream>
#include <opencv2/core/core.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/highgui/highgui.hpp>
const int nOffset=200; // 設定位移幅度

int main(int argc, char *argv[]){
  cv::Mat src_img = cv::imread("happyman.jpg", 1);
  if(src_img.empty()) return -1;
  // 設定變換[之前]與[之後]的坐標 (左上,左下,右下,右上)
  cv::Point2f pts1[] = {cv::Point2f(0,0),cv::Point2f(0,src_img.rows),cv::Point2f(src_img.cols,src_img.rows),cv::Point2f(src_img.cols,0)};
  cv::Point2f pts2[] = {cv::Point2f(0,0),cv::Point2f(0+nOffset,src_img.rows),cv::Point2f(src_img.cols-nOffset,src_img.rows),cv::Point2f(src_img.cols,0)};
  // 透視變換行列計算
  cv::Mat perspective_matrix = cv::getPerspectiveTransform(pts1, pts2);
  cv::Mat dst_img;
  // 變換
  cv::warpPerspective(src_img, dst_img, perspective_matrix, src_img.size(), cv::INTER_LINEAR);
  // 繪製坐標變換[之前]與[之後]的示意圖
  cv::line(src_img, pts1[0], pts1[1], cv::Scalar(255,255,0), 2, CV_AA);
  cv::line(src_img, pts1[1], pts1[2], cv::Scalar(255,255,0), 2, CV_AA);
  cv::line(src_img, pts1[2], pts1[3], cv::Scalar(255,255,0), 2, CV_AA);
  cv::line(src_img, pts1[3], pts1[0], cv::Scalar(255,255,0), 2, CV_AA);
  cv::line(src_img, pts2[0], pts2[1], cv::Scalar(255,0,255), 2, CV_AA);
  cv::line(src_img, pts2[1], pts2[2], cv::Scalar(255,0,255), 2, CV_AA);
  cv::line(src_img, pts2[2], pts2[3], cv::Scalar(255,0,255), 2, CV_AA);
  cv::line(src_img, pts2[3], pts2[0], cv::Scalar(255,0,255), 2, CV_AA);

  cv::namedWindow("src", CV_WINDOW_AUTOSIZE|CV_WINDOW_FREERATIO);
  cv::namedWindow("dst", CV_WINDOW_AUTOSIZE|CV_WINDOW_FREERATIO);
  cv::imshow("src", src_img);
  cv::imshow("dst", dst_img);
  cv::waitKey(0);
}

執行結果顯示兩張影像:

透視變換 Perspective Transform

第一張是原始圖加上變換示意圖。

透視變換 Perspective Transform (1)

第二張是透視變換後的結果圖。

關鍵函式:warpPerspective~Applies a perspective transformation to an image.

Parameters:

  1. src – input image.
  2. dst – output image that has the size dsize and the same type as src .
  3. M – 3×3 transformation matrix.
  4. dsize – size of the output image.
  5. flags – combination of interpolation methods (INTER_LINEAR or INTER_NEAREST) and the optional flag WARP_INVERSE_MAP, that sets M as the inverse transformation (src -> dst).
  6. borderMode – pixel extrapolation method (BORDER_CONSTANT or BORDER_REPLICATE).
  7. borderValue – value used in case of a constant border; by default, it equals 0.
  • 沒有回傳值。

對我來說,最重要的會是它所使用的內插法,當然想要結果影像品質較好的話,所花費的計算量就相對較高,也許執行單一影像看不出來,然而當有一連串影像就會非常明顯!特別是針對影片而言~

interpolation

  • INTER_NEAREST – a nearest-neighbor interpolation
  • INTER_LINEAR – a bilinear interpolation (used by default)
  • INTER_CUBIC – a bicubic interpolation over 4×4 pixel neighborhood
  • INTER_AREA – resampling using pixel area relation. It may be a preferred method for image decimation, as it gives moire’-free results. But when the image is zoomed, it is similar to the INTER_NEAREST method.
  • INTER_LANCZOS4 – a Lanczos interpolation over 8×8 pixel neighborhood

關鍵函式:getPerspectiveTransform~Calculates a perspective transform from four pairs of the corresponding points.

Parameters:

  1. src – Coordinates of quadrangle vertices in the source image.
  2. dst – Coordinates of the corresponding quadrangle vertices in the destination image.
  • 回傳3×3矩陣。

參考:透視変換を行うwarpPerspectivegetPerspectiveTransform

Advertisements

Comments on: "[OpenCV] 透視變換 (Perspective Transform)" (30)

  1. 我看了你說的縮放演算法跟我說的失真有些差距,可能我之前說的不清楚吧,我是使用逆透視轉換之後將圖片轉換成鳥瞰圖的形式,在鳥瞰圖的情況之下,在高度的部分會有被拉高的現象,我是想知道有沒有演算法可以解決這個問題,不過目前我還沒找到解決的方法。

    喜歡

  2. 哈囉,你好,最近我剛好也正在學習透視轉換與逆透視轉換,我有2個問題想要請教你。
    1. 不管是透視轉換或是逆透視轉換在圖片轉換之後都會有圖片失真的現象,在物體高度的部份會有拉長的情形,這是有辦法可以改善的嗎?
    2. 在使用透視轉換或是逆透視轉換之後,有什麼辦法可以得知你與物體的距離跟物體的高度呢?OpenCV裡面有現成的涵式可以使用的嗎?

    喜歡

  3. 哈哈 目前做到預定完成的進度了
    目前能利用Kinect透過openCV攝影並直接顯示道路的邊緣偵測逆透視影像
    只差一步驟就完成了
    目前卡在一個小問題
    我試著將霍夫轉換套入我的系統中
    但是由於整段都是利用CV::的寫法去寫
    所以套入出了一點問題
    以下是我要套入的Code
    #include
    #include
    #include

    int main(int argc, char** argv)
    {
    const char* filename = argc >= 2 ? argv[1] : “pic1.png";
    IplImage* src = cvLoadImage( filename, 0 );
    IplImage* dst;
    IplImage* color_dst;
    CvMemStorage* storage = cvCreateMemStorage(0);
    CvSeq* lines = 0;
    int i;

    if( !src )
    return -1;

    dst = cvCreateImage( cvGetSize(src), 8, 1 );
    color_dst = cvCreateImage( cvGetSize(src), 8, 3 );

    cvCanny( src, dst, 50, 200, 3 );
    cvCvtColor( dst, color_dst, CV_GRAY2BGR );
    #if 0
    lines = cvHoughLines2( dst, storage, CV_HOUGH_STANDARD, 1, CV_PI/180, 100, 0, 0 );

    for( i = 0; i total,100); i++ )
    {
    float* line = (float*)cvGetSeqElem(lines,i);
    float rho = line[0];
    float theta = line[1];
    CvPoint pt1, pt2;
    double a = cos(theta), b = sin(theta);
    double x0 = a*rho, y0 = b*rho;
    pt1.x = cvRound(x0 + 1000*(-b));
    pt1.y = cvRound(y0 + 1000*(a));
    pt2.x = cvRound(x0 – 1000*(-b));
    pt2.y = cvRound(y0 – 1000*(a));
    cvLine( color_dst, pt1, pt2, CV_RGB(255,0,0), 3, CV_AA, 0 );
    }
    #else
    lines = cvHoughLines2( dst, storage, CV_HOUGH_PROBABILISTIC, 1, CV_PI/180, 50, 50, 10 );
    for( i = 0; i total; i++ )
    {
    CvPoint* line = (CvPoint*)cvGetSeqElem(lines,i);
    cvLine( color_dst, line[0], line[1], CV_RGB(255,0,0), 3, CV_AA, 0 );
    }
    #endif
    cvNamedWindow( “Source", 1 );
    cvShowImage( “Source", src );

    cvNamedWindow( “Hough", 1 );
    cvShowImage( “Hough", color_dst );

    cvWaitKey(0);

    return 0;
    }
    有些地方小弟不太清楚該如何改成cv:: 請教版大該如何修改這邊的寫法呢?
    不好意思又再次請教您了~

    喜歡

    • 恭喜你的進度趕上了!
      呵~其實上網google一下就會有相關資料,
      原本也想幫你找一下,
      不過發現自己沒啥時間分心,
      若你已經解決了,
      盡情分享給我吧~ 😉

      喜歡

  4. 剛剛重新灌了一次 也找2008 + OPENCV2.3.0的教學文 成功了!!! 好感動~~
    真的很感謝你^^

    喜歡

    • 請問一下 cv::line(src_img, pts1[0], pts1[1], cv::Scalar(255,255,0), 2, CV_AA);
      這8行的寫法是在寫畫出邊緣的梯形嗎?
      如果我想讓梯形180度翻轉的圖 那這裡該如何修改
      不好意思一直麻煩您

      喜歡

  5. 我先嘗試用您的Code去Debug
    但是執行後會出現黑色視窗並瞬間結束
    我只有把(“happyman.jpg", 1)這裡的圖片換成我有的圖片名稱也有把圖片丟入資料夾內
    跑完後下面出現
    —————————————————————————————————
    錯誤 1 fatal error C1083: 無法開啟包含檔案: ‘opencv2/core/core.hpp’: No such file or directory c:\users\pinwei0989\documents\visual studio 2008\projects513test513test513test.cpp 2 0513test
    —————————————————————————————————
    請問是為什麼呢? 環境沒建立好嗎?

    喜歡

    • 你可以到該路徑底下去找該檔案,看它是否存在~
      你的檔名取得也真是奇怪呢~ 😛
      還有你的OpenCV是哪一版本?

      喜歡

      • VC2008 OPENCV2.1
        看來是路徑的問題~
        環境我重建看看^^

        喜歡

        • 先跟您說聲感謝!
          這篇文章幫我釐清一些對於透視法的觀念
          很有幫助~現在我的目標就是要把輸入的圖片預設為下面的圖
          在經由轉換變為上面那張
          這樣的方式就能將前方道路的影像轉換為逆透視法(鳥瞰圖)
          目前專題就只差這部份了
          我再繼續研究看看: )

          喜歡

          • 我詳細讀了Code後發現還蠻容易理解的
            只是Debug一直出問題…
            因為我是用2008 OPENCV2.1
            我試著去改Include的路徑
            發現imgproc.hpp 跟 core.hpp 都是OPENCV2.1裡面沒有的
            不知該如何解決…

            喜歡

          • 建議你可以安裝最新版本的OpenCV,我的部落格也有安裝教學,可讓你輕而易舉就完成開發環境喔!
            我文章中的code,前頭都會寫我用了什麼IDE和OpenCV版本,若你安裝順利的話,就能跟我一樣跑出成果喔~ 🙂

            喜歡

  6. 版主你好,
    小弟最近在做有關影像處理部分的專題
    因為找了很多資料還是不知該怎麼寫..
    想請問您
    如何在OpenCV寫出逆透視法轉換的code?
    這裡有參考資料:http://www.inf.cyut.edu.tw/AIT2010/ft_193.pdf
    裡面的3.2節 (3)(4)是轉換的公式
    因為專題做到這邊卡了很久,希望能跟您請教看看

    喜歡

    • 你有試著按照公式實做嗎?
      感覺上那公式就可以做到呢!
      如果想按照我上面的code來實現「逆透視」,
      你可以試著改第21行……
      cv::Mat perspective_matrix = cv::getPerspectiveTransform(pts1, pts2);
      將pts1和pts2調換,
      也許就是你想要的效果(結果)囉! 😀

      喜歡

  7. 原來是這樣…Thanks for ur reply
    (好像lag太久了(!? 現在才看到orz)

    喜歡

  8. 版主你好:
    個人在此有個可能與這篇主題比較無關的問題
    關於您的程式碼,有不少地方都有"cv::",怎麼沒有直接在一開始就使用namespace呢?
    還是說有其他用意?謝謝

    喜歡

    • 嗨,你好~
      如果在主程式main上外宣告namespace cv的話,
      就可以省略"cv::"這個前綴。
      如果你是寫C++為主的程式,
      我想多會宣告namespace std,
      像是cin和cout這一類的使用,
      就不必寫成std::cin和std::cout,
      也就是你可以省略"std::"前綴,
      只要很簡單地寫cin和cout即可作用喔~
      所以宣告namespace,
      是為了簡化程式的寫法,
      希望有回答到你的問題。 😀

      喜歡

      • 我知道,因為我自己寫code就是cv跟std兩個namespace同時都有使用
        我的問題主要就是:版主怎麼沒有使用他們?
        因為感覺用了他們後,程式看起來是會比較簡潔,而且不用每次都打cv::

        但我也不確定是不是有其他用意,所以提出此疑問
        Thanks

        喜歡

發表留言

在下方填入你的資料或按右方圖示以社群網站登入:

WordPress.com Logo

您的留言將使用 WordPress.com 帳號。 登出 / 變更 )

Twitter picture

您的留言將使用 Twitter 帳號。 登出 / 變更 )

Facebook照片

您的留言將使用 Facebook 帳號。 登出 / 變更 )

Google+ photo

您的留言將使用 Google+ 帳號。 登出 / 變更 )

連結到 %s

標籤雲

%d 位部落客按了讚: