2014-09-10 71 views
2

我假設的問題很簡單,但由於我在線性代數方面的經驗,前一段時間我仍然無法解決它。我讀過幾所大學出版的演講稿,但我似乎無法遵循這種不規範的記法。如果任何人有一個更好的例子,它將不勝感激...給出4個已知點的平面世界點的攝像頭像素

問題: 相機是傾斜下來面對地板。給定像素座標,我希望能夠在地板的平面上獲得相應的3D世界座標。

已知:在哪裏我知道(X,Y)座標的像素和相關聯的世界地板

  • 4點(X,Y,Z = 0)座標。
  • 相機的位置是固定的,我知道相機在X,Y,Z方向上的位移。

未知:

  • 相機的繞x,Y,Z軸的旋轉。主要是,相機旋轉幾乎繞X軸,Y軸爲最小旋轉角度,但我認爲應該考慮在內。
  • 失真係數,但是線條中圖像的最小彎曲,並且更願意不引入棋盤校準程序。由此產生的一些錯誤不是交易斷路器。

我已經看了什麼爲 一個驚人的例子是發現here.它在本質上是完全一樣的問題,但一些後續的問題:

SolvePnP看起來是我的朋友,但我不太確定如何處理相機矩陣或分辨率係數。是否有某種方法可以避免攝像機矩陣和距離係數校準步驟,我認爲這是用棋盤過程完成的(可能會犧牲一些準確性)?還是有一些簡單的方法來做到這一點?

非常感謝您的意見!

+0

計算從圖像到平面座標的單應性。如果相機是靜態的,不是你需要的嗎? – Micka 2014-09-11 13:29:23

回答

1

試試這個方法:

從4個對應計算單應,給你所有的信息圖像平面和地平面座標之間轉換。

這種方法的侷限性是它假設一個統一參數化的圖像平面(針孔相機),所以鏡頭畸變會給你錯誤,如我的例子所見。如果你能夠消除鏡頭失真的影響,你會很好地採用這種方法,我猜。 此外,你會得到一些錯誤的像素座標作爲你的對應關係,如果你提供更多的對應關係,你可以得到更穩定的值。

使用該輸入圖像

enter image description here

在我從一個圖像處理軟件,這將對應於你知道你的形象4分的事實,閱讀4個角一個國際象棋領域。我選擇這些點(標記爲綠色):

enter image description here

現在我已經做了兩兩件事:第一轉化棋盤圖案的座標圖像(0,0),(0,1)等這給出了一個良好的地圖質量視覺印象。第二我從形象轉變爲世界。讀取棋盤座標中對應於(0,0)的圖像位置(87,291)中的最左角位置。如果我轉換那個像素位置,你會期望(0,0)作爲結果。

cv::Point2f transformPoint(cv::Point2f current, cv::Mat transformation) 
{ 
    cv::Point2f transformedPoint; 
    transformedPoint.x = current.x * transformation.at<double>(0,0) + current.y * transformation.at<double>(0,1) + transformation.at<double>(0,2); 
    transformedPoint.y = current.x * transformation.at<double>(1,0) + current.y * transformation.at<double>(1,1) + transformation.at<double>(1,2); 
    float z = current.x * transformation.at<double>(2,0) + current.y * transformation.at<double>(2,1) + transformation.at<double>(2,2); 
    transformedPoint.x /= z; 
    transformedPoint.y /= z; 

    return transformedPoint; 
} 

int main() 
{ 
    // image from http://d20uzhn5szfhj2.cloudfront.net/media/catalog/product/cache/1/image/9df78eab33525d08d6e5fb8d27136e95/5/2/52440-chess-board.jpg 

    cv::Mat chessboard = cv::imread("../inputData/52440-chess-board.jpg"); 

    // known input: 
    // image locations/read pixel values 
    // 478,358 
    // 570, 325 
    // 615,382 
    // 522,417 

    std::vector<cv::Point2f> imageLocs; 
    imageLocs.push_back(cv::Point2f(478,358)); 
    imageLocs.push_back(cv::Point2f(570, 325)); 
    imageLocs.push_back(cv::Point2f(615,382)); 
    imageLocs.push_back(cv::Point2f(522,417)); 

    for(unsigned int i=0; i<imageLocs.size(); ++i) 
    { 
     cv::circle(chessboard, imageLocs[i], 5, cv::Scalar(0,0,255)); 
    } 
    cv::imwrite("../outputData/chessboard_4points.png", chessboard); 

    // known input: this is one field of the chessboard. you could enter any (corresponding) real world coordinates of the ground plane here. 
    // world location: 
    // 3,3 
    // 3,4 
    // 4,4 
    // 4,3 

    std::vector<cv::Point2f> worldLocs; 
    worldLocs.push_back(cv::Point2f(3,3)); 
    worldLocs.push_back(cv::Point2f(3,4)); 
    worldLocs.push_back(cv::Point2f(4,4)); 
    worldLocs.push_back(cv::Point2f(4,3)); 


    // for exactly 4 correspondences. for more you can use cv::findHomography 
    // this is the transformation from image coordinates to world coordinates: 
    cv::Mat image2World = cv::getPerspectiveTransform(imageLocs, worldLocs); 
    // the inverse is the transformation from world to image. 
    cv::Mat world2Image = image2World.inv(); 


    // create all known locations of the chessboard (0,0) (0,1) etc we will transform them and test how good the transformation is. 
    std::vector<cv::Point2f> worldLocations; 
    for(unsigned int i=0; i<9; ++i) 
     for(unsigned int j=0; j<9; ++j) 
     { 
      worldLocations.push_back(cv::Point2f(i,j)); 
     } 


    std::vector<cv::Point2f> imageLocations; 

    for(unsigned int i=0; i<worldLocations.size(); ++i) 
    { 
     // transform the point 
     cv::Point2f tpoint = transformPoint(worldLocations[i], world2Image); 
     // draw the transformed point 
     cv::circle(chessboard, tpoint, 5, cv::Scalar(255,255,0)); 
    } 

    // now test the other way: image => world 
    cv::Point2f imageOrigin = cv::Point2f(87,291); 
    // draw it to show which origin i mean 
    cv::circle(chessboard, imageOrigin, 10, cv::Scalar(255,255,255)); 
    // transform point and print result. expected result is "(0,0)" 
    std::cout << transformPoint(imageOrigin, image2World) << std::endl; 

    cv::imshow("chessboard", chessboard); 
    cv::imwrite("../outputData/chessboard.png", chessboard); 
    cv::waitKey(-1); 


} 

產生的圖像是:

enter image description here

,你可以看到有錯誤的數據一些大的量。正如我所說的,這是因爲像素座標稍微錯誤(作爲對應關係)(並且在一個小區域內!),並且由於鏡頭失真導致地平面在圖像上顯示爲真實平面。

成果轉化(87291)世界座標是:

[0.174595, 0.144853] 

預期/完美的結果會一直[0,0]

希望這會有所幫助。

+0

嘿,這是一個非常酷的主意。所以基本上我正在計算透視變換矩陣,然後就是這樣來回騎車。所以我更加註意讓鏡頭失真等等,而且看起來好像不太好。那個因素在哪裏?在圖片的第一次閱讀中,馬上可以不露面嗎? – user2891729 2014-09-11 23:08:04

+1

這工作完美。非常感激!!! – user2891729 2014-09-12 03:53:27

+0

是的,不失真的原始圖像(調整已知點的圖像位置),你就完成了 – Micka 2014-09-12 05:24:00

0

當然,您可以將cameraMatrix設置爲標識矩陣(eye(3))並將distCoefficients設置爲NULL,並且solvePNP會假定您擁有完美的相機。正如你所說,這會引入一些額外的不準確性,但你仍然會得到答案。

如果你發現你的結果不夠準確,相機校準真的沒什麼大不了的。

+0

嘿,我很欣賞這種迴應。杜,這應該是我想到的完美條件。在給出答案之前,讓我試試看。 – user2891729 2014-09-10 18:14:35

相關問題