鳥瞰図を取得したいです
初心者です。
OpenCVで鳥瞰図を取得するものを作っています。
参考にしたのは「詳解OpenCV3 コンピュータライブラリを使った画像処理・認識」という本のp670のコードです。
###起きていること
以下のような別のプログラムでまずカメラの内部行列と歪みパラメータを取得しています。このプログラムは問題ないです。
```C++ #include <iostream> #include <string> #include <sstream> #include <iomanip> #include <vector> #include <opencv2/opencv.hpp> int main(int argc, char *argv[]) { const int IMAGE_NUM = 12; // 読み込むチェッカーボードの画像数 const int PAT_ROW = 7; // チェッカーパターンの行数(一番外側を除く) const int PAT_COL = 7; // チェッカーパターンの列数(一番外側を除く) const int PAT_SIZE = PAT_ROW * PAT_COL; const int ALL_POINTS = IMAGE_NUM * PAT_SIZE; const double CHESS_SIZE = 80.0; // チェッカーマークの大きさ[mm] int corner_count, found; std::vector<int> p_count[IMAGE_NUM]; // cv::Mat src_img[IMAGE_NUM]; std::vector<cv::Mat> srcImages; cv::Size pattern_size = cv::Size2i(PAT_COL, PAT_ROW); std::vector<cv::Point2f> corners; std::vector<std::vector<cv::Point2f>> img_points; // STEP(1) キャリブレーション画像の読み込み std::cerr << "STEP(1) キャリブレーション画像の読み込み..."; for (int i = 0; i < IMAGE_NUM; i++) { std::ostringstream ostr; ostr << "../calib_img/" << "3-" << std::setfill('0') << std::setw(2) << i << ".png"; cv::Mat src = cv::imread(ostr.str()); if (src.empty()) { std::cerr << "cannot load image file : " << ostr.str() << std::endl; } else { srcImages.emplace_back(src); } } std::cerr << "done\n"; // STEP(2) 3次元空間座標の設定 std::cerr << "STEP(2) 3次元空間座標の設定..."; std::vector<cv::Point3f> object; for (int j = 0; j < PAT_ROW; j++) { for (int k = 0; k < PAT_COL; k++) { cv::Point3f p(j * CHESS_SIZE, k * CHESS_SIZE, 0.0); object.emplace_back(p); } } // 3次元の点を ALL_POINTS * 3 の行列(32Bit浮動小数点数:1チャンネル)に変換する std::vector<std::vector<cv::Point3f>> obj_points; for (int i = 0; i < IMAGE_NUM; i++) { obj_points.emplace_back(object); } std::cerr << "done\n"; // STEP(3) チェスボードのコーナー検出 std::cerr << "STEP(3) チェスボードのコーナー検出..."; int found_num = 0; cv::namedWindow("Calibration", cv::WINDOW_AUTOSIZE); for (int i = 0; i < IMAGE_NUM; i++) { auto found = cv::findChessboardCorners(srcImages[i], pattern_size, corners); if (found) { std::cout << std::setfill('0') << std::setw(2) << i << "... ok" << std::endl; found_num++; // STEP(4) コーナー位置をサブピクセル精度に修正,描画 cv::Mat src_gray = cv::Mat(srcImages[i].size(), CV_8UC1); cv::cvtColor(srcImages[i], src_gray, cv::COLOR_BGR2GRAY); cv::find4QuadCornerSubpix(src_gray, corners, cv::Size(3,3)); cv::drawChessboardCorners(srcImages[i], pattern_size, corners, found); img_points.emplace_back(corners); cv::imshow("Calibration", srcImages[i]); cv::waitKey(5); } else { std::cerr << std::setfill('0') << std::setw(2) << i << "... fail" << std::endl; } } cv::destroyWindow("Calibration"); if (found_num != IMAGE_NUM) { std::cerr << "Calibration Images are insufficient." << std::endl; return -1; } std::cerr << "done\n"; // STEP(5) 内部パラメータ,歪み係数の推定 std::cerr << "STEP(5) 内部パラメータ,歪み係数の推定..."; cv::Mat camera_matrix; // カメラ内部パラメータ行列 cv::Mat distortion_coefficients; // 歪み係数 std::vector<cv::Mat> rvecs, tvecs; // 各ビューの回転ベクトルと並進ベクトル cv::calibrateCamera( obj_points, img_points, srcImages[0].size(), camera_matrix, distortion_coefficients, rvecs, tvecs ); std::cerr << "done\n"; // STEP(6) XMLファイルへの書き出し std::cerr << "STEP(6) XMLファイルへの書き出し..."; cv::FileStorage fs("./../../../coyomi/apps/bird/intrinsics.xml", cv::FileStorage::WRITE); if(!fs.isOpened()) { std::cerr << "File can not be opened." << std::endl; return -1; } fs << "image_width" << srcImages[0].size().width << "image_height" << srcImages[0].size().height << "camera_matrix" << camera_matrix << "distortion_coefficients" << distortion_coefficients; fs.release(); std::cerr << "done.\n"; std::cerr << "Bye!\n"; return 0; }
その後、次のプログラムで鳥瞰図を取得します。
このコードは、カメラ内部行列と歪みパラメータ取得方法、入力値以外は、詳解OpenCV3のp671のコードそのままです。
このプログラムに問題があります。
```C++ #include <iostream> #include <fstream> #include <opencv2/opencv.hpp> #include <stdlib.h> //入力は[board_w] = 7, [board_h] = 7, [intinsics.xml] = intrinsics.xml, [checker_image] = image.png void help(char *argv[]){ std::cout << "Call: " << argv[0] << "[board_w] [board_h] [intrinsics.xml] [checker_image]" << std::endl; //std::cout << "Demonstrates Pyramid Lucas-Kanade optical flow." << std::endl; } //引数:[board_w], [board_h], [intrinsics.xml], [checker_image] //[board_w]:チェスボードの横のマス数 //[board_h]:チェスボードの盾のマス数 //[intrinsics.xml]:カメラ定数とかが入ってるファイル //[checker_image]:入力画像 int main(int argc, char *argv[]){ //argcはプログラムの引数の数 //./bird image.jpgで実行したとしたらargv[0] = bird,argv[1] = image.jpgでargcは2 if(argc != 5){ std::cout << "\nERROR: too few parameters\n"; help(argv); return -1; } //atoiは文字列を数値に変換 int board_w = atoi(argv[1]); int board_h = atoi(argv[2]); int board_n = board_w * board_h; cv::Size board_sz(board_w, board_h); //読み込みでファイルを開く cv::FileStorage fs(argv[3], cv::FileStorage::READ); cv::Mat intrinsic, distortion; fs["camera_matrix"] >> intrinsic; fs["distortion_coefficients"] >> distortion; if(!fs.isOpened() || intrinsic.empty() || distortion.empty()){ std::cout << "Error: Couldn't load intrinsic parameters from " << argv[3] << std::endl; return -1; } fs.release(); cv::Mat gray_image, image, image0 = cv::imread(argv[4], 1); if(image0.empty()){ std::cout << "Error: Couldn't load image" << argv[4] << std::endl; return -1; } //画像の歪みを補正する cv::undistort(image0, image, intrinsic, distortion, intrinsic); //入力画像、出力画像、グレーにする定数 cv::cvtColor(image, gray_image, cv::COLOR_BGR2GRAY); //その平面上のチェスボードを得る std::vector<cv::Point2f> corners; //チェスボードの内側コーナー位置を求める bool found = cv::findChessboardCorners( image, //内側コーナーの個数 board_sz, //コーナーの出力配列 corners, //白黒画像に変換する際に固定閾値の代わりに適応的閾値を利用 //輪郭抽出の際の誤った四角を除外するための基準追加 cv::CALIB_CB_ADAPTIVE_THRESH | cv::CALIB_CB_FILTER_QUADS ); if(!found){ std::cout << "Couldn't acquire checkerboard on " << argv[4] << ", only found " << corners.size() << " of " << board_n << " corners\n"; return -1; } //ここまでは多分問題ない //これらのコーナーをサブピクセルの精度で得る cv::cornerSubPix( gray_image, corners, //探索ウィンドウサイズ cv::Size(11, 11), //ゼロ領域(この場合使用しない) cv::Size(-1 ,-1), cv::TermCriteria( cv::TermCriteria::EPS | cv::TermCriteria::COUNT, 30, 0.1 ) ); //画像と物体の点を得る //物体の点(r,c) //(0, 0), (board_w-1, 0), (0, board_h-1), (board_w-1, board_h-1) //これはコーナーがcorners[r*board_w + c]であることを意味する cv::Point2f objPts[4], imgPts[4]; objPts[0].x = 0; objPts[0].y = 0; objPts[1].x = board_w-1; objPts[1].y = 0; objPts[2].x = 0; objPts[2].y = board_h-1; objPts[3].x = board_w-1; objPts[3].y = board_h-1; imgPts[0] = corners[0]; imgPts[1] = corners[board_w-1]; imgPts[2] = corners[(board_h-1) * board_w]; imgPts[3] = corners[(board_h-1) * board_w + board_w-1]; //点をB,G,R,YELLOWの順で描画する cv::circle(image, imgPts[0], 9, cv::Scalar(255, 0, 0), 3); cv::circle(image, imgPts[1], 9, cv::Scalar(0, 255, 0), 3); cv::circle(image, imgPts[2], 9, cv::Scalar(0, 0, 255), 3); cv::circle(image, imgPts[3], 9, cv::Scalar(0, 255, 255), 3); //発見したチェスボードを描画する cv::drawChessboardCorners(image, board_sz, corners, found); cv::imshow("Checkers", image); cv::imwrite("./apps/bird/Checkers.png", image); //ホモグラフィを求める cv::Mat H = cv::getPerspectiveTransform(objPts, imgPts); //このビューの高さzをユーザーが調整できるようにする double Z = 50; cv::Mat birds_image; //Escキーで止まる for(;;){ H.at<double>(2, 2) = Z; //ホモグラフィを用いて再マッピングする cv::warpPerspective( image, birds_image, //変換配列 H, //出力画像の配列 image.size(), cv::WARP_INVERSE_MAP | cv::INTER_LINEAR, cv::BORDER_CONSTANT, //境界を黒で埋める cv::Scalar::all(0) ); cv::imshow("Birds_Eye", birds_image); int key = cv::waitKey() & 255; if(key == 'u') Z += 0.5; if(key == 'd') Z -= 0.5; if(key == 27) break; } cv::imwrite("./apps/bird/Birds_Eye.png", birds_image); //回転と平行移動ベクトルを表示 std::vector<cv::Point2f> image_points; std::vector<cv::Point3f> object_points; for(int i = 0; i < 4; ++i){ image_points.push_back(imgPts[i]); object_points.push_back( cv::Point3f(objPts[i].x, objPts[i].y, 0) ); } cv::Mat rvec, tvec, rmat; cv::solvePnP( //オブジェクト座標系の3次元座標系 object_points, //画像座標系の2次元座標点 image_points, //カメラ行列 intrinsic, //最初に歪みを補正したので歪み係数はゼロになる cv::Mat(), //回転ベクトルを出力 rvec, //平行移動ベクトルを出力 tvec ); cv::Rodrigues(rvec, rmat); //出力と終了 std::cout << "rotation matrix: " << rmat << std::endl; std::cout << "translation vector: " << tvec << std::endl; std::cout << "homography matrix: " << H << std::endl; std::cout << "inverted homography matrix: " << H.inv() << std::endl; return 1; }
エラーは吐いていません。発見したチェスボード(Checkers.png)を描画したものはこのようになりました。
しかし、鳥瞰図は全体が真っ黒になってしまいます。
試したこと
Zの高さを変えたら、左上に少しだけ図が現れたので、表示はされているが表示位置に問題があるのかと思い、試しにobjPtsのインデックスを
0→2, 1→3, 2→0, 3→1
にしました。向きは当然おかしいですが、以下のように表示されました。
Zの高さをキーで変えていくと、左上を基準に小さくなったり大きくなったりしています。
中心に表示されないのと、インデックスを元通りにすると画像自体出てこないこと、画像の上側が真っ黒で塗りつぶされてしまうことを解決したいです。
回答1件
あなたの回答
tips
プレビュー
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2021/08/11 12:13