OpenCV 5.0.0
Open Source Computer Vision
読み込み中...
検索中...
見つかりません
🤖 AIによる機械翻訳(非公式) — これは OpenCV 5.0.0 公式リファレンス(英語)を AI (Claude) で自動翻訳したものです。訳に誤りを含む場合があります。正確な情報は 公式英語版(原文) を参照してください。
Diamondマーカーの検出

前のチュートリアル: ChArUcoボードの検出
次のチュートリアル: ArUcoとChArUcoによるキャリブレーション
ChArUcoダイヤモンドマーカー(単にダイヤモンドマーカーとも呼ぶ)は、3x3個の正方形と、白い正方形の中に配置された4個のArUcoマーカーで構成されるチェスボードである。見た目はChArUcoボードに似ているが、概念的には異なるものである。

Diamond marker examples

ChArUcoボードとダイヤモンドマーカーは、いずれもその検出が事前に検出されたArUcoマーカーに基づいている。ChArUcoの場合、使用するマーカーはその識別子を直接参照して選択される。つまり、(ボードに含まれる)マーカーが画像中で見つかれば、自動的にそのボードに属するものとみなされる。さらに、あるマーカーボードが画像中で複数回見つかった場合、システムはどちらをボードに使うべきか判断できないため、曖昧さが生じる。

一方、ダイヤモンドマーカーの検出は識別子に基づかない。代わりに、その検出はマーカー同士の相対位置に基づく。その結果、マーカーの識別子は同一のダイヤモンド内や異なるダイヤモンド間で重複してもよく、曖昧さなく同時に検出できる。ただし、相対位置に基づいてマーカーを見つける処理は複雑であるため、ダイヤモンドマーカーは3x3個の正方形と4個のマーカーというサイズに制限される。

単一のArUcoマーカーと同様に、各ダイヤモンドマーカーは4個のコーナーと1個の識別子で構成される。4個のコーナーはマーカー内の4個のチェスボードコーナーに対応し、識別子は実際には4個の数値の配列であり、これらはダイヤモンド内部の4個のArUcoマーカーの識別子である。

ダイヤモンドマーカーは、マーカーの重複を許可したいシナリオで有用である。例えば、次のような場合である:

  • ダイヤモンド型マーカーをラベル付けに利用することで、単一マーカーの識別子の数を増やすことができる。これにより最大で N^4 通りの異なるidを扱えるようになる。ここで N は使用する辞書に含まれるマーカーの数である。
  • 4つのマーカーそれぞれに概念的な意味を持たせる。例えば、4つのマーカーidのうち1つをマーカーのスケール(すなわち正方形のサイズ)を示すために使うことができる。こうすれば、4つのマーカーのうち1つを変えるだけで、同じダイヤモンドを異なるサイズで環境中に配置でき、ユーザがそれぞれのスケールを手動で指定する必要がなくなる。このケースはモジュールのsamplesフォルダ内のdetect_diamonds.cppファイルに含まれている。

さらに、コーナーはチェスボードのコーナーであるため、正確な姿勢推定に利用できる。

ダイヤモンド機能は<opencv2/objdetect/charuco_detector.hpp>に含まれている

ChArUcoダイヤモンドの作成

ダイヤモンドマーカーの画像はcv::aruco::CharucoBoard::generateImage()関数を使って簡単に作成できる。例えば次のようにする:

vector<int> diamondIds = {ids[0], ids[1], ids[2], ids[3]};
aruco::CharucoBoard charucoBoard(Size(3, 3), (float)squareLength, (float)markerLength, dictionary, diamondIds);
Mat markerImg;
charucoBoard.generateImage(Size(3*squareLength + 2*margins, 3*squareLength + 2*margins), markerImg, margins, borderBits);

これにより、正方形サイズ200ピクセル、マーカーサイズ120ピクセルのダイヤモンドマーカー画像が作成される。マーカーidは2番目の引数としてcv::Vec4iオブジェクトで与える。ダイヤモンドレイアウトにおけるマーカーidの順序は標準的なChArUcoボードと同じで、すなわち上、左、右、下の順である。

生成される画像は次のようになる:

Diamond marker

完全な動作例はsamples/cpp/tutorial_code/objectDetection/内のcreate_diamond.cppに含まれている。

サンプルcreate_diamond.cppは現在、cv::CommandLineParserを介してコマンドラインから入力を受け取る。このファイルの例の引数は次のようになる:

"_path_/mydiamond.png" -sl=200 -ml=120 -d=10 -ids=0,1,2,3

ChArUcoダイヤモンドの検出

多くの場合と同様に、ダイヤモンドマーカーの検出には事前にArUcoマーカーの検出が必要である。マーカーを検出した後、cv::aruco::CharucoDetector::detectDiamonds()関数を使ってダイヤモンドを検出する:

vector<int> markerIds;
vector<Vec4i> diamondIds;
vector<vector<Point2f> > markerCorners, diamondCorners;
vector<Vec3d> rvecs, tvecs;
detector.detectDiamonds(image, diamondCorners, diamondIds, markerCorners, markerIds);

cv::aruco::CharucoDetector::detectDiamonds()関数は、元画像と、事前に検出されたマーカーのコーナーおよびidを受け取る。markerCornersとmarkerIdsが空の場合、この関数はarucoマーカーとidを検出する。入力画像はChArUcoコーナーのサブピクセル精緻化を行うために必要である。また、正方形サイズとマーカーサイズの比率も受け取る。この比率は、マーカーの相対位置からダイヤモンドを検出することと、ChArUcoコーナーを補間することの両方に必要である。

この関数は検出されたダイヤモンドを2つの引数で返す。1つ目の引数diamondCornersは、検出された各ダイヤモンドの4つのコーナーすべてを含む配列である。その形式はcv::aruco::ArucoDetector::detectMarkers()関数で検出されるコーナーと同様であり、各ダイヤモンドについてコーナーはArUcoマーカーと同じ順序、すなわち左上のコーナーから始まる時計回りの順序で表現される。2つ目の戻り値の引数diamondIdsは、diamondCorners内の返されたダイヤモンドコーナーのすべてのidを含む。各idは実際には4個の整数の配列であり、cv::Vec4iで表現できる。

検出されたダイヤモンドはcv::aruco::drawDetectedDiamonds()関数を使って可視化できる。この関数は単に画像とダイヤモンドのコーナーおよびidを受け取る:

if(diamondIds.size() > 0) {
aruco::drawDetectedDiamonds(imageCopy, diamondCorners, diamondIds);

結果はcv::aruco::drawDetectedMarkers()が生成するものと同じだが、ダイヤモンドの4つのidが表示される点が異なる:

Detected diamond markers

完全な動作例はsamples/cpp/tutorial_code/objectDetection/内のdetect_diamonds.cppに含まれている。

サンプルdetect_diamonds.cppは現在、cv::CommandLineParserを介してコマンドラインから入力を受け取る。このファイルの例の引数は次のようになる:

-dp=path_to_opencv/opencv/samples/cpp/tutorial_code/objectDetection/detector_params.yml -sl=0.4 -ml=0.25 -refine=3
-v=path_to_opencv/opencv/doc/tutorials/objdetect/charuco_diamond_detection/images/diamondmarkers.jpg
-cd=path_to_opencv/opencv/samples/cpp/tutorial_code/objectDetection/tutorial_dict.yml

ChArUcoダイヤモンドの姿勢推定

ChArUcoダイヤモンドは4つのコーナーで表現されるため、その姿勢は単一のArUcoマーカーの場合と同じ方法で、すなわち cv::solvePnP() 関数を用いて推定できる。例えば次のとおり:

// estimate diamond pose
size_t N = diamondIds.size();
if(estimatePose && N > 0) {
cv::Mat objPoints(4, 1, CV_32FC3);
rvecs.resize(N);
tvecs.resize(N);
if(!autoScale) {
// set coordinate system
objPoints.ptr<Vec3f>(0)[0] = Vec3f(-squareLength/2.f, squareLength/2.f, 0);
objPoints.ptr<Vec3f>(0)[1] = Vec3f(squareLength/2.f, squareLength/2.f, 0);
objPoints.ptr<Vec3f>(0)[2] = Vec3f(squareLength/2.f, -squareLength/2.f, 0);
objPoints.ptr<Vec3f>(0)[3] = Vec3f(-squareLength/2.f, -squareLength/2.f, 0);
// Calculate pose for each marker
for (size_t i = 0ull; i < N; i++)
solvePnP(objPoints, diamondCorners.at(i), camMatrix, distCoeffs, rvecs.at(i), tvecs.at(i));
if(estimatePose) {
for(size_t i = 0u; i < diamondIds.size(); i++)
cv::drawFrameAxes(imageCopy, camMatrix, distCoeffs, rvecs[i], tvecs[i], squareLength*1.1f);
}

この関数は各ダイヤモンドマーカーの回転ベクトルと並進ベクトルを取得し、rvecstvecsに格納する。ダイヤモンドのコーナーはチェスボードの正方形のコーナーであるため、姿勢推定にはマーカー長ではなく正方形の長さを与える必要があることに注意する。カメラキャリブレーションの引数も必要である。

最後に、drawFrameAxes()を使って軸を描画し、推定された姿勢が正しいか確認できる:

Detected diamond axis

ダイヤモンド姿勢の座標系は、単純なArUcoマーカーの姿勢推定と同様に、マーカーの中心に位置し、Z軸が外向きを指す。

サンプル動画:

ChArUcoダイヤモンドの姿勢はChArUcoボードとしても推定できる:

for (size_t i = 0ull; i < N; i++) { // estimate diamond pose as Charuco board
Mat objPoints_b, imgPoints;
// The coordinate system of the diamond is placed in the board plane centered in the bottom left corner
vector<int> charucoIds = {0, 1, 3, 2}; // if CCW order, Z axis pointing in the plane
// vector<int> charucoIds = {0, 2, 3, 1}; // if CW order, Z axis pointing out the plane
charucoBoard.matchImagePoints(diamondCorners[i], charucoIds, objPoints_b, imgPoints);
solvePnP(objPoints_b, imgPoints, camMatrix, distCoeffs, rvecs[i], tvecs[i]);
}

完全な動作例はsamples/cpp/tutorial_code/objectDetection/内のdetect_diamonds.cppに含まれている。

サンプルdetect_diamonds.cppは現在、cv::CommandLineParserを介してコマンドラインから入力を受け取る。このファイルの例の引数は次のようになる:

-dp=path_to_opencv/opencv/samples/cpp/tutorial_code/objectDetection/detector_params.yml -sl=0.4 -ml=0.25 -refine=3
-v=path_to_opencv/opencv/doc/tutorials/objdetect/charuco_diamond_detection/images/diamondmarkers.jpg
-cd=path_to_opencv/opencv/samples/cpp/tutorial_code/objectDetection/tutorial_dict.yml
-c=path_to_opencv/opencv/samples/cpp/tutorial_code/objectDetection/tutorial_camera_params.yml