質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.35%
OpenCV

OpenCV(オープンソースコンピュータービジョン)は、1999年にインテルが開発・公開したオープンソースのコンピュータビジョン向けのクロスプラットフォームライブラリです。

C++

C++はC言語をもとにしてつくられた最もよく使われるマルチパラダイムプログラミング言語の1つです。オブジェクト指向、ジェネリック、命令型など広く対応しており、多目的に使用されています。

Q&A

解決済

1回答

3106閲覧

OpenCVの鳥瞰図取得で取得できた画像が全面真っ黒になってしまいます

koomint

総合スコア3

OpenCV

OpenCV(オープンソースコンピュータービジョン)は、1999年にインテルが開発・公開したオープンソースのコンピュータビジョン向けのクロスプラットフォームライブラリです。

C++

C++はC言語をもとにしてつくられた最もよく使われるマルチパラダイムプログラミング言語の1つです。オブジェクト指向、ジェネリック、命令型など広く対応しており、多目的に使用されています。

0グッド

0クリップ

投稿2021/08/11 04:24

鳥瞰図を取得したいです

初心者です。
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の高さをキーで変えていくと、左上を基準に小さくなったり大きくなったりしています。

中心に表示されないのと、インデックスを元通りにすると画像自体出てこないこと、画像の上側が真っ黒で塗りつぶされてしまうことを解決したいです。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答1

0

ベストアンサー

コードをざっと眺めた結果,
チェスボード認識結果を示している画像は cv::undistort(image0, image, intrinsic, distortion, intrinsic); で歪みを補正したものであるように見えますが,
周辺視野が異様に歪んでおり,カメラの内部パラメータの校正がうまくできていないのではないか?と想像します.

(前処理で得られたカメラ内部パラメータの妥当性はどうのようにチェックされているのでしょうか?)

投稿2021/08/11 04:56

fana

総合スコア11996

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

koomint

2021/08/11 12:13

確かにCheckers.imgで周囲の壁が歪んでいるのは引っ掛かっていました。 カメラ内部パラメータの妥当性については特に調べたりしておらず、一つ目のプログラムを実行したときにうまくいってそうだったのでパラメータが正しく求められたと思い込んでいました。 鳥瞰図を得る前にカメラ内部パラメータの妥当性を他の方法で調べてみるべきですね。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.35%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問