OpenCVのfindFundamentalMat関数で使用されるモデル



Model Used Findfundamentalmat Function Opencv



ランダムサンプリングコンセンサス(RANSAC)アルゴリズムは、連続反復法を使用して、「外れ値」を含むデータセットのセットから最適なパラメーターモデルを見つけることができます。最適なモデルを満たさないポイントは、「外れ値」として定義されます。画像レジストレーションやステッチングに広く使用されています。この記事では、OpenCVのコーナーミスマッチペアの検出におけるRANSACアルゴリズムを分析します。








1.RANSACの原則

RANSACアルゴリズムは、OpenCVで不一致のペアを除外して、最適なホモグラフィ行列Hを見つけるために使用されます。行列のサイズは3×3です。 RANSACの目的は、行列を満たすデータポイントの数が最大になるように、最適なパラメーター行列を見つけることです。通常、行列を正規化するにはh33 = 1です。ホモグラフィ行列には8つの未知のパラメータがあるため、点の位置情報に対応して、少なくとも8つの線形方程式を解く必要があります。ポイントペアのセットは、2つの方程式をリストできます。少なくとも4セットのマッチングポイントペアが含まれています。




ここで、(x、y)はターゲット画像のコーナーポイントの位置を表し、(x '、y')はシーン画像のコーナーポイントの位置を表し、sはスケールパラメータです。

RANSACアルゴリズムは、一致するデータセットからランダムに4つのサンプルを選択し、4つのサンプルが同一直線上にないことを確認し、ホモグラフィマトリックスを計算し、このモデルを使用してすべてのデータをテストし、モデルに一致するデータポイントの数と射影を計算します。エラー(つまり、コスト関数)。このモデルが最適なモデルである場合、対応するコスト関数は最小です。


-------------------------------------------------- -------------------------------------------------- -------------

RANSACアルゴリズムの手順:

1.データセットからランダムに4つのサンプルを選択し(4つのサンプルを同一線上に配置することはできません)、変換行列Hを計算し、モデルMとして記録します。

2.データセットとモデルMのすべてのデータの射影誤差を計算します。誤差がしきい値よりも小さい場合は、内部点セットIを追加します。

3.現在の内部点セットIの要素数が最適な内部点セットI_bestより大きい場合は、I_best = Iを更新し、同時に反復回数kを更新します。

4.反復回数がkより大きい場合は終了し、反復回数を1増やして、上記の手順を繰り返します。

注意:反復回数kが最大反復回数以下の場合、固定ではなく常に更新されます。

その中で、pは信頼水準であり、通常0.995wは「内部点」の比率です。mはモデルの計算に必要なサンプルの最小数= 4です。

-------------------------------------------------- -------------------------------------------------- -------------


2.ルーチン

OpenCVのこの関数は、findHomography関数を呼び出すことによって呼び出されます。以下はルーチンです。

[cpp] プレーンビュー コピー
  1. #include
  2. #include'opencv2 / opencv.hpp '
  3. #include'opencv2 / core / core.hpp '
  4. #include'opencv2 / features2d / features2d.hpp '
  5. #include'opencv2 / highgui / highgui.hpp '
  6. を使用して 名前空間履歴書
  7. を使用して 名前空間時間
  8. intメイン(intargc、char** argv)
  9. {{
  10. マットobj = imread('F:\ Picture \ obj.jpg')。//ターゲット画像をロードします
  11. マットシーン= imread('F:\ Picture \ scene.jpg')。//シーン画像をロードします
  12. もし(obj.empty()|| scene.empty())
  13. {{
  14. 費用<<「画像を開くことができません! n」
  15. 戻る0
  16. }
  17. ベクトルobj_keypoints、scene_keypoints
  18. マットobj_descriptors、scene_descriptors
  19. ORB検出器// ORBアルゴリズムを使用して特徴点を抽出する
  20. detector.detect(obj、obj_keypoints)
  21. detector.detect(scene、scene_keypoints)
  22. detector.compute(obj、obj_keypoints、obj_descriptors)
  23. detector.compute(scene、scene_keypoints、scene_descriptors)
  24. BFMatcherの一致(NORM_HAMMING、true)。//類似性の尺度としてのハミング距離
  25. ベクトル一致
  26. matcher.match(obj_descriptors、scene_descriptors、matches)
  27. マットmatch_img
  28. drawMatches(obj、obj_keypoints、scene、scene_keypoints、matches、match_img)
  29. imshow(「誤った一致を除外する前に」、match_img)
  30. //一致するペア番号を保存します
  31. ベクター<int> queryIdxs(matches.size())、trainIdxs(matches.size())
  32. ために((size_ti = 0 i
  33. {{
  34. queryIdxs [i] = matches [i] .queryIdx
  35. trainIdxs [i] =マッチ[i] .trainIdx
  36. }
  37. マットH12//変換行列
  38. ベクトルpoints1KeyPoint :: convert(obj_keypoints、points1、queryIdxs)
  39. ベクトルpoints2KeyPoint :: convert(scene_keypoints、points2、trainIdxs)
  40. intransacReprojThreshold = 5//拒否のしきい値
  41. H12 = findHomography(Mat(points1)、Mat(points2)、CV_RANSAC、ransacReprojThreshold)
  42. ベクター<char> matchesMask(matches.size()、0)
  43. マットpoints1t
  44. spectiveTransform(Mat(points1)、points1t、H12)
  45. ために((size_ti1 = 0 i1//「内側のポイント」を保存します
  46. {{
  47. もし(norm(points2 [i1] --points1t.at((int)i1,0))<= ransacReprojThreshold ) //内側の点をマークします
  48. {{
  49. matchesMask [i1] = 1
  50. }
  51. }
  52. マットmatch_img2//「外部ポイント」を除外した後
  53. drawMatches(obj、obj_keypoints、scene、scene_keypoints、matches、match_img2、Scalar(0,0,255)、Scalar :: all(-1)、matchesMask)
  54. //目標位置を描画します
  55. std :: vector obj_corners(4)
  56. obj_corners [0] = cvPoint(0,0)obj_corners [1] = cvPoint(obj.cols、0)
  57. obj_corners [2] = cvPoint(obj.cols、obj.rows)obj_corners [3] = cvPoint(0、obj.rows)
  58. std :: vector scene_corners(4)
  59. spectiveTransform(obj_corners、scene_corners、H12)
  60. line(match_img2、scene_corners [0] + Point2f(static_cast<浮く>(obj.cols)、0)、
  61. scene_corners [1] + Point2f(static_cast<浮く>(obj.cols)、0)、スカラー(0,0,255)、2)
  62. line(match_img2、scene_corners [1] + Point2f(static_cast<浮く>(obj.cols)、0)、
  63. scene_corners [2] + Point2f(static_cast<浮く>(obj.cols)、0)、スカラー(0,0,255)、2)
  64. line(match_img2、scene_corners [2] + Point2f(static_cast<浮く>(obj.cols)、0)、
  65. scene_corners [3] + Point2f(static_cast<浮く>(obj.cols)、0)、スカラー(0,0,255)、2)
  66. line(match_img2、scene_corners [3] + Point2f(static_cast<浮く>(obj.cols)、0)、
  67. scene_corners [0] + Point2f(static_cast<浮く>(obj.cols)、0)、スカラー(0,0,255)、2)
  68. imshow(「誤った一致を除外した後」、match_img2)
  69. waitKey(0)
  70. 戻る0
  71. }

3.RANSACソースコード分析

(1)findHomographyはcvFindHomography関数を内部的に呼び出します

srcPoints:ターゲット画像ポイントセット

dstPoints:シーンイメージポイントセット

方法:最小中央値法、RANSAC法、最小二乗法

ransacReprojTheshold:投影エラーのしきい値

マスク:マスク

[cpp] プレーンビュー コピー
  1. cvFindHomography(constCvMat * objectPoints、constCvMat * imagePoints、
  2. CvMat * __H、int方法、ダブルransacReprojThreshold、
  3. CvMat *マスク)
  4. {{
  5. const ダブル信頼度= 0.995//信頼
  6. const intmaxIters = 2000//最大反復回数
  7. const ダブルdefaultRANSACReprojThreshold = 3//デフォルトの拒否しきい値
  8. ブール結果=false
  9. Ptr m、M、tempMask
  10. ダブルH [9]
  11. CvMat matH = cvMat(3、3、CV_64FC1、H)//変換行列
  12. intカウント
  13. CV_Assert(CV_IS_MAT(imagePoints)&& CV_IS_MAT(objectPoints))
  14. count = MAX(imagePoints-> cols、imagePoints-> rows)
  15. CV_Assert(カウント> = 4)//少なくとも4つのサンプル
  16. もし(ransacReprojThreshold<= 0 )
  17. ransacReprojThreshold = defaultRANSACReprojThreshold
  18. m = cvCreateMat(1、count、CV_64FC2)//同次座標に変換
  19. cvConvertPointsHomogeneous(imagePoints、m)
  20. M = cvCreateMat(1、カウント、CV_64FC2)//同次座標に変換
  21. cvConvertPointsHomogeneous(objectPoints、M)
  22. もし( マスク )
  23. マスク->列== 1)&&
  24. mask-> rows * mask-> cols == count)
  25. もし(マスク||カウント> 4)
  26. tempMask = cvCreateMat(1、count、CV_8U)
  27. もし(!tempMask.empty())
  28. cvSet(tempMask、cvScalarAll(1。))
  29. CvHomographyEstimator estimator(4)
  30. もし(カウント== 4)
  31. メソッド= 0
  32. もし(メソッド== CV_LMEDS)//最小中央値法
  33. 結果= estimator.runLMeDS(M、m、&matH、tempMask、confidence、maxIters)
  34. そうしないと もし(メソッド== CV_RANSAC)// RANSACアルゴリズムを使用
  35. 結果= estimator.runRANSAC(M、m、&matH、tempMask、ransacReprojThreshold、confidence、maxIters)//(2)分析
  36. そうしないと
  37. 結果= estimator.runKernel(M、m、&matH)> 0//最小二乗法
  38. もし(結果&&カウント> 4)
  39. {{
  40. icvCompressPoints((CvPoint2D64f *)M-> data.ptr、tempMask-> data.ptr、1、count)//内側のポイントセットを保存します
  41. count = icvCompressPoints((CvPoint2D64f *)m-> data.ptr、tempMask-> data.ptr、1、count)
  42. M-> cols = m-> cols =カウント
  43. もし(メソッド== CV_RANSAC)//
  44. estimator.runKernel(M、m、およびmatH)//内部点セットで最小二乗法を使用して変換行列を再推定します
  45. estimator.refine(M、m、&matH、10)//
  46. }
  47. もし(結果)
  48. cvConvert(&matH、__ H)//変換行列を保存します
  49. もし(マスク&& tempMask)
  50. {{
  51. もし(CV_ARE_SIZES_EQ(マスク、tempMask))
  52. cvCopy(tempMask、mask)
  53. そうしないと
  54. cvTranspose(tempMask、mask)
  55. }
  56. 戻る((int)結果
  57. }

(2)runRANSAC
maxIters:最大反復回数

m1、m2:データサンプル

モデル:変換行列

reprojThreshold:投影エラーのしきい値

自信:自信0.995

[cpp] プレーンビュー コピー
  1. ブールCvModelEstimator2 :: runRANSAC(constCvMat * m1、constCvMat * m2、CvMat *モデル、
  2. CvMat * mask0、ダブルreprojThreshold、
  3. ダブル信頼、intmaxIters)
  4. {{
  5. ブール結果=false
  6. cv :: Ptrマスク= cvCloneMat(mask0)
  7. cv :: Ptrモデル、エラー、tmask
  8. cv :: Ptr ms1、ms2
  9. intiter、niters = maxIters
  10. intcount = m1-> rows * m1-> cols、maxGoodCount = 0
  11. CV_Assert(CV_ARE_SIZES_EQ(m1、m2)&& CV_ARE_SIZES_EQ(m1、マスク))
  12. もし( カウント
  13. 戻る false
  14. models = cvCreateMat(modelSize.height * maxBasicSolutions、modelSize.width、CV_64FC1)
  15. err = cvCreateMat(1、count、CV_32FC1)//ポイントの各グループに対応する投影エラーを保存します
  16. tmask = cvCreateMat(1、count、CV_8UC1)
  17. もし(カウント> modelPoints)// 4ポイント以上
  18. {{
  19. ms1 = cvCreateMat(1、modelPoints、m1->タイプ)
  20. ms2 = cvCreateMat(1、modelPoints、m2->タイプ)
  21. }
  22. そうしないと // 4ポイントに等しい
  23. {{
  24. 硝石= 1// 1回繰り返す
  25. ms1 = cvCloneMat(m1)//毎回ランダムに見つかったサンプルポイントを保存します
  26. ms2 = cvCloneMat(m2)
  27. }
  28. ために(移動経路= 0//継続的な反復
  29. {{
  30. inti、goodCount、nmodels
  31. もし(カウント> modelPoints)
  32. {{
  33. //(3)でgetSubsetを分析します
  34. ブール見つかった= getSubset(m1、m2、ms1、ms2、300)// 4セットの点をランダムに選択し、3つの点は同一線上にない、(3)分析
  35. もし(!found)
  36. {{
  37. もし(iter == 0)
  38. 戻る false
  39. ブレーク
  40. }
  41. }
  42. nmodels = runKernel(ms1、ms2、models)//近似変換行列を推定し、1を返します
  43. もし(nmodels<= 0 )
  44. 継続する
  45. ために(i = 0 i// 1回実行
  46. {{
  47. CvMat model_i
  48. cvGetRows(モデル、&model_i、i * modelSize.height、(i + 1)* modelSize.height)// 3×3の行列要素を取得します
  49. goodCount = findInliers(m1、m2、&model_i、err、tmask、reprojThreshold)//内側の点を見つける、(4)分析
  50. もし(goodCount> MAX(maxGoodCount、modelPoints-1))//現在の内部ポイントセット要素の数が最適な内部ポイントセット要素の数よりも多い
  51. {{
  52. std :: swap(tmask、mask)
  53. cvCopy(&model_i、model)//最適なモデルを更新します
  54. maxGoodCount = goodCount
  55. //信頼度は0.995のデフォルトの信頼度であり、modelPointsは必要な最小サンプル数= 4、硝石反復回数です
  56. niters = cvRANSACUpdateNumIters(confidence、//反復回数を更新します、(5)分析
  57. ((ダブル)(count-goodCount)/ count、modelPoints、niters)
  58. }
  59. }
  60. }
  61. もし(maxGoodCount> 0)
  62. {{
  63. もし(マスク!= mask0)
  64. cvCopy(マスク、マスク0)
  65. 結果=true
  66. }
  67. 戻る結果
  68. }

第一歩硝石の初期値は2000で、これは初期RANSACアルゴリズムのサイクル数です。 getSubset()関数は、対応するシーケンスのグループから4つのグループをランダムに選択します(3X3行列を計算するため、少なくとも4つのグループに対応する座標)。m1とm2は入力シーケンスであり、ms1とms2は対応する4です。ランダムに選択された一致のグループ。

4セットの一致をランダムに選択した後、対応する行列はこれら4セットに基づいて計算する必要があるため、関数runKernel()は4セットの一致に基づいて行列を計算し、パラメーターのモデルは結果の行列になります。

ステップ2このマトリックスは単なる仮説です。この仮説を検証するには、他のポイントを使用して計算し、他のポイントが内部ポイントか外部ポイントかを確認する必要があります。

findInliers()関数は、内部ポイントを計算するために使用されます。上で得られた行列を使用して、すべてのシーケンスを取り込み、内側の点と外側の点を計算します。関数の戻り値はgoodCountです。これは、今回計算された内部ポイントの数です。

ステップ3関数にはmaxGoodCountの値もあります。各ループの内部ポイントの最大数がこの値に格納されます。推定された行列がより多くの内部点を持っている場合、その行列は正しい可能性が高くなります。

ステップ4したがって、内部ポイントの数を計算した後、goodCountとmaxGoodCountの間のサイズの関係をすぐに決定します。 goodCount> maxGoodCountの場合、goodCountをmaxGoodCountに割り当てます。割り当て後のコード行は非常に重要です。別々に取り出しましょう。コードは次のとおりです。


  1. niters = cvRANSACUpdateNumIters(confidence、
  2. ((ダブル)(count-goodCount)/ count、modelPoints、niters)

硝石は元々、反復の数、つまりループの数です。しかし、このコード行を通じて、各ループの後に硝石の値が更新されることがわかりました。つまり、ループの総数は各ループの後に変更されます。

cvRANSACUpdateNumIters()関数は、confidence(confidence)count(一致の総数)goodCount(内部ポイントの現在の数)niters(反復の現在の総数)のパラメーターを使用して、反復の総数のサイズを動的に変更します。この関数の中心的なアイデアは 内部点の割合が大きい場合、正しい推定値が見つかった可能性が高いため、時間を節約するために反復回数を適切に減らす必要があります。 。この反復回数の削減は指数関数的に削減されるため、時間の節約も大幅に削減されます。したがって、最初に設計された2000の反復回数は、数十回の反復にすぎない可能性があります。同様に、最初に反復回数を10000以上に設定すると、数回の反復の後、硝石は再び非常に小さくなります。したがって、最初の硝石をいくら大きく設定しても、最終的な実行時間には影響しません。

したがって、入力データが増加し、アルゴリズムの実行時間が増加しない理由を理解する必要があります。 openCVのRANSACアルゴリズムは、最初に反復回数を2000に設定し、次に反復プロセス中に反復の総数を動的に変更します。入力データの量に関係なく、反復の総数は増加せず、推定反復数は4回の一致によって計算されます。行列の時間は一定であり、内部点は行列を推定することによって計算され、この点で増加した時間オーバーヘッドは基本的に無視できます。したがって、最終的な結果は、入力ポイントがいくつあっても、計算時間はあまり変わらないということです。


上記は、RANSACアルゴリズムのコアコードです。その中で使用される機能のいくつかを以下に示します。


(3)getSubset

ms1、ms2:ランダムに見つかった4つのサンプルを保存します

maxAttempts:最大反復回数、300

[cpp] プレーンビュー コピー
  1. ブールCvModelEstimator2 :: getSubset(constCvMat * m1、constCvMat * m2、
  2. CvMat * ms1、CvMat * ms2、intmaxAttempts)
  3. {{
  4. cv :: AutoBuffer<int> _idx(modelPoints)// modelPoints必要なサンプルポイントの最小数
  5. int* idx = _idx
  6. inti = 0、j、k、idx_i、iters = 0
  7. inttype = CV_MAT_TYPE(m1-> type)、elemSize = CV_ELEM_SIZE(type)
  8. const int* m1ptr = m1-> data.i、* m2ptr = m2-> data.i
  9. int* ms1ptr = ms1-> data.i、* ms2ptr = ms2-> data.i
  10. intcount = m1-> cols * m1-> rows
  11. assert(CV_IS_MAT_CONT(m1-> type&m2-> type)&&(elemSize%のサイズ((int)== 0))
  12. elemSize / =のサイズ((int)。//各データが占めるバイト数
  13. ために(食べる人
  14. {{
  15. ために(i = 0 i
  16. {{
  17. idx [i] = idx_i = cvRandInt(&rng)%カウント//ポイントのセットをランダムに選択します
  18. ために(j = 0 d //繰り返し選択されているかどうかを検出します
  19. もし(idx_i == idx [j])
  20. ブレーク
  21. もし(j
  22. 継続する //再選択
  23. ために(k = 0 k//ポイントデータをコピーします
  24. {{
  25. ms1ptr [i * elemSize + k] = m1ptr [idx_i * elemSize + k]
  26. ms2ptr [i * elemSize + k] = m2ptr [idx_i * elemSize + k]
  27. }
  28. もし(checkPartialSubsets &&(!checkSubset(ms1、i + 1)||!checkSubset(ms2、i + 1)))//ポイントが同一線上にあるかどうかを確認します
  29. {{
  30. iters ++//同一線上にある場合は、別のグループを選択します
  31. 継続する
  32. }
  33. i ++
  34. }
  35. もし(!checkPartialSubsets && i == modelPoints &&
  36. (!checkSubset(ms1、i)||!checkSubset(ms2、i)))
  37. 継続する
  38. ブレーク
  39. }
  40. 戻るi == modelPoints && iters//見つかったサンプルポイントの数を返します
  41. }

(4)findInliers&computerReprojError

[cpp] プレーンビュー コピー
  1. intCvModelEstimator2 :: findInliers(constCvMat * m1、constCvMat * m2、
  2. constCvMat *モデル、CvMat * _err、
  3. CvMat * _mask、ダブルしきい値)
  4. {{
  5. inti、count = _err-> rows * _err-> cols、goodCount = 0
  6. const 浮く* err = _err-> data.fl
  7. uchar *マスク= _mask-> data.ptr
  8. computeReprojError(m1、m2、model、_err)//ポイントの各グループの投影誤差を計算します
  9. しきい値* =しきい値
  10. ために(i = 0 i
  11. goodCount + = mask [i] = err [i]<= threshold//エラーは制限された範囲内です。「内部ポイントセット」を追加してください
  12. 戻るgoodCount
  13. }
  14. // --------------------------------------
  15. ボイドCvHomographyEstimator :: computeReprojError(constCvMat * m1、constCvMat * m2、constCvMat *モデル、CvMat * _err)
  16. {{
  17. inti、カウント= m1-> rows * m1-> cols
  18. constCvPoint2D64f * M =(constCvPoint2D64f *)m1-> data.ptr
  19. constCvPoint2D64f * m =(constCvPoint2D64f *)m2-> data.ptr
  20. const ダブル* H = model-> data.db
  21. 浮く* err = _err-> data.fl
  22. ために(i = 0 i//上記の変換式に対応するポイントの各グループの投影エラーを保存します
  23. {{
  24. ダブルww = 1./(H[6]*M[i].x + H [7] * M [i] .y + 1.)
  25. ダブルdx =(H [0] * M [i] .x + H [1] * M [i] .y + H [2])* ww-m [i] .x
  26. ダブルdy =(H [3] * M [i] .x + H [4] * M [i] .y + H [5])* ww-m [i] .y
  27. err [i] =(浮く)(dx * dx + dy * dy)
  28. }
  29. }
(5)cvRANSACUpdateNumIters

上記kに対応する計算式

p:自信

ep:外点比

[cpp] プレーンビュー コピー
  1. cvRANSACUpdateNumIters(ダブルp、ダブルep、
  2. intmodel_points、intmax_iters)
  3. もし(model_points<= 0 )
  4. CV_Error(CV_StsOutOfRange、「モデルポイントの数は正でなければなりません」)。
  5. p = MAX(p、0。)
  6. p = MIN(p、1)
  7. ep = MAX(ep、0。)
  8. ep = MIN(ep、1)
  9. // infとnanを避けます
  10. ダブルnum = MAX(1。-p、DBL_MIN)// num = 1-p、分子を与える
  11. ダブルdenom = 1. --pow(1。--ep、model_points)//分母を行う
  12. もし(デノム
  13. 戻る0
  14. num = log(num)
  15. denom = log(denom)
  16. 戻るデノム> = 0

エピポーラジオメトリの知識は、ステレオ画像ペアを処理するときによく使用されます。また、基本行列を計算することも非常に一般的です。 OpenCVは、基本的なマトリックスアルゴリズムを実装しています。古いバージョンのCコードの場合、基本行列を計算するためのRANSACメソッドにバグがあり、反復回数が収束しないため、毎回計算されるサンプル数が最大制限に達する可能性があります。根本的な原因は、サンプリングの信頼性を計算するための式が正しくないことです。 、新しいバージョンのコードを注意深く読んでいません。このバグが修正されているかどうかわかりません。ただし、このバグは最終結果に大きな影響を与えませんが、計算の効率に影響を与えます。元々、このバグの影響下で、数個のサンプルが反復を終了する可能性があり、数百個のサンプルが必要になる場合があります。

新しいバージョンの基本的な行列計算関数にもいくつかの問題があります。 cv :: findFundamentalMat関数を見てみましょう。

//!基本行列を見つけるためのアルゴリズム
列挙型
{{
FM_7POINT = CV_FM_7POINT、//!<7-point algorithm
FM_8POINT = CV_FM_8POINT、//!<8-point algorithm
FM_LMEDS = CV_FM_LMEDS、//!FM_RANSAC = CV_FM_RANSAC //!}

//!対応する2D点のセットから基本行列を見つけます
CV_EXPORTSマットfindFundamentalMat(const Mat&points1、const Mat&points2、
CV_OUT vector&mask、int method = FM_RANSAC、
double param1 = 3.、double param2 = 0.99)

//!対応する2D点のセットから基本行列を見つけます
CV_EXPORTS_WマットfindFundamentalMat(const Mat&points1、const Mat&points2、
intメソッド= FM_RANSAC、
double param1 = 3.、double param2 = 0.99)

上記は、OpenCVが基本行列を計算するためのC ++インターフェースです。その内部実装は、呼び出されるCコード関数のままですが、上位層にのみカプセル化されます。インターネット上のいくつかの文書は、2つのパラメーターconst Mat&points1とpoints2をベクトルタイプで直接渡すことができると述べています。実際、これは間違っています。ベクトルで直接渡すと、コンパイル時にエラーが報告されない場合がありますが、実行時に直接クラッシュします。 cv :: Matのコンストラクターは、Point2fを2つの浮動小数点数に変換してMatの2つの要素に格納しませんが、Point2fをMatの1つの要素に格納するため、findFundamentalMatはこのMatを読み取ります。エラー。

したがって、Matpoints1とMatpoints2を正直に作成したほうがよいでしょう。行列の次元は2xNまたはNx2にすることができます。ここで、Nは特徴点の数です。注意すべきもう1つのポイントは、マトリックスのタイプをCV_64Fにすることはできないということです。これは、findFundamentalMatがfloatタイプに従ってpoints1とpoints2を内部的に解析することを意味します。 CV_64Fを設定すると、データの読み取りに失敗し、プログラムがクラッシュします。 CV_32Fとして設定することをお勧めします。以下は、一致した特徴点からFを計算するためのコード例です。

//ベクトルm_LeftKey
//ベクトルm_RightKey

//ベクトルm_Matches

//上記の3つの変数が計算されました。これらは、抽出されたキーポイントとそれらのマッチングです。以下では、Fを直接計算します

//スペースを割り当てます

int ptCount =(int)m_Matches.size()
マットp1(ptCount、2、CV_32F)
マットp2(ptCount、2、CV_32F)

//キーポイントをマットに変換します

Point2f pt
for(int i = 0 i{{
pt = m_LeftKey [m_Matches [i] .queryIdx] .pt
p1.at(i、0)= pt.x
p1.at(i、1)= pt.y

pt = m_RightKey [m_Matches [i] .trainIdx] .pt
p2.at(i、0)= pt.x
p2.at(i、1)= pt.y
}

// RANSACメソッドを使用してFを計算します

//マットm_Fundamental

//上記の変数は基本的な行列です

//ベクトルm_RANSACStatus

//上記の変数が定義され、RANSAC後の各ポイントの状態を格納するために使用されています

m_Fundamental = findFundamentalMat(p1、p2、m_RANSACStatus、FM_RANSAC)

//ワイルドポイントの数を計算します

int OutlinerCount = 0
for(int i = 0 i{{
If(m_RANSACStatus [i] == 0)//ステータスが0の場合、ワイルドポイントを意味します
{{
OutlinerCount ++
}
}

//内部の点を計算します

//ベクトルm_LeftInlier
//ベクトルm_RightInlier
//ベクトルm_InlierMatches

//上記の3つの変数は、内部ポイントと一致する関係を保存するために使用されます

int InlinerCount = ptCount-OutlinerCount
m_InlierMatches.resize(InlinerCount)
m_LeftInlier.resize(InlinerCount)
m_RightInlier.resize(InlinerCount)
InlinerCount = 0
for(int i = 0 i{{
if(m_RANSACStatus [i]!= 0)
{{
m_LeftInlier [InlinerCount] .x = p1.at(i、0)
m_LeftInlier [InlinerCount] .y = p1.at(i、1)
m_RightInlier [InlinerCount] .x = p2.at(i、0)
m_RightInlier [InlinerCount] .y = p2.at(i、1)
m_InlierMatches [InlinerCount] .queryIdx = InlinerCount
m_InlierMatches [InlinerCount] .trainIdx = InlinerCount
InlinerCount ++
}
}

//内側のポイントをdrawMatchesで使用できる形式に変換します

ベクトルkey1(InlinerCount)
ベクトルkey2(InlinerCount)
KeyPoint :: convert(m_LeftInlier、key1)
KeyPoint :: convert(m_RightInlier、key2)

// Fを計算した後、内部ポイントマッチングを表示します

//マットm_matLeftImage
//マットm_matRightImage

//上記の2つの変数は、左右の画像を保存します

マットOutImage
drawMatches(m_matLeftImage、key1、m_matRightImage、key2、m_InlierMatches、OutImage)
cvNamedWindow( '機能の一致'、1)
cvShowImage( 'Match features'、&(IplImage(OutImage)))
cvWaitKey(0)
cvDestroyWindow( '機能の一致')


ピンホールカメラのモデルと変形

このセクションの機能はすべてピンホールカメラモデルを使用します。つまり、ビューは、透視変換によって3次元空間内の点を画像平面に投影することです。射影式は次のとおりです。

s  cdot mまたは

s  cdot  begin {bmatrix} u \ v \ 1  end {bmatrix} =  begin {bmatrix} fx&0&cx \ 0&fy&cy \ 0&0&1  end {bmatrix}  cdot  begin {bmatrix} r_ {11}&r_ {12}&r_ {13}&t_ {1} \ r_ {21}&r_ {22}&r_ {23}&t_ {2} \ r_ {31}&r_ {32}&r_ {33}&t_ {3}  end {bmatrix}  cdot  begin {bmatrix} X \ Y \ Z \ 1  end {bmatrix}

ここで、(X、Y、Z)は点のワールド座標であり、(u、v)は画像平面に投影された点の座標(ピクセル単位)です。 Aは、カメラ行列または内部パラメータ行列と呼ばれます。 (cx、cy)は基準点(通常は画像の中央)、fx、fyはピクセル単位の焦点距離です。したがって、カメラからの画像が何らかの要因でアップサンプリングまたはダウンサンプリングされた場合、これらすべてのパラメーター(fx、fy、cx、およびcy)は同じスケールでスケーリング(乗算または除算)されます。内部パラメータマトリックスはシーンのビューに依存せず、一度計算されると、再利用できます(焦点距離が固定されている限り)。回転-平行移動行列[R | t]は、外部パラメータ行列と呼ばれ、固定シーンに対するカメラの動き、または逆に、カメラの周りのオブジェクトの剛体の動きを表すために使用されます。つまり、[R | t]は、点(X、Y、Z)の座標を、カメラに対して固定された座標系に変換します。上記の変換は、次の形式(z≠0)と同等です。

 begin {bmatrix} x \ y \ z  end {bmatrix} = R  cdot  begin {bmatrix} X \ Y \ Z  end {bmatrix} + t

バツ '= バツ /

Y '= Y /

u = fx  cdot x

v = fy  cdot y

実際のレンズには通常、ある程度の変形があり、主な変形は半径方向の変形であり、わずかな接線方向の変形があります。したがって、上記のモデルは次のように拡張できます。

 begin {bmatrix} x \ y \ z  end {bmatrix} = R  cdot  begin {bmatrix} X \ Y \ Z  end {bmatrix} + t

バツ '= バツ /

Y '= Y /

バツ

Y

ここに r = バツ '+ Y '

u = fx  cdot x

v = fy  cdot y

1 半径方向の変形係数は、 p 1 p 1接線方向の変形係数です。 OpenCVでは高次係数は考慮されません。変形係数は撮影シーンとは関係がないため、内部パラメータであり、撮影した画像の解像度とは関係ありません。

次の関数は、上記のモデルを使用して次のことを行います。

  • 内部パラメータと外部パラメータを指定して、3次元の点を画像平面に投影します。
  • 内部パラメータ、いくつかの3次元点座標、およびそれらに対応する画像座標が与えられた場合、外部パラメータを計算します。
  • 既知のキャリブレーションモードによれば、カメラの外部パラメータと内部パラメータは、いくつかの角度からの写真から計算されます(各角度には対応する3D-2Dポイントペアがいくつかあります)。


カメラのキャリブレーション

ProjectPoints2

プロジェクト3Dは画像平面を指します

/************************************************************************ * Copyright(c) 2016 Only Crazy * All rights reserved. * * Brief: FAST feature point extraction and FREAK descriptor image matching, based on OpenCV2.4.8 * Version: 1.0 * Author: Wei Crazy * Date: 2016/07/21 * Address: Guangzhou & Beijing ************************************************************************/ #include #include #include #include #include #include #include using namespace std using namespace cv int main() { Mat img1_src = imread('im5.jpg',0) Mat img2_src = imread('im6.jpg',0) //FastFeatureDetector fast(40) SurfFeatureDetector fast(2000,4) FREAK extractor vector keypoints1,keypoints2 Mat descriptor1,descriptor2 vector final_matches vector matches double t = (double)getTickCount() fast.detect(img1_src,keypoints1) fast.detect(img2_src,keypoints2) //drawKeypoints(img1_src,keypoints1,img1_src,Scalar(0,255,0)) //drawKeypoints(img2_src,keypoints2,img2_src,Scalar(0,255,0))//Here is the problem! ! ! I’m drunk, the problem here, I wasted 5 days, Oye, it’s a whole 5 days, because I want to see what the detected feature points are like here, I want to see it immediately with my parents. A newborn baby is in a mood. As a result, there was almost no correct match in feature matching, which made me think it was a problem with the descriptor FREAK. So I looked for questions, read papers, and visited forums. The last one was found inadvertently. The problem is: these two sentences are to draw the feature points in img1_src intact, that is, in the original image, and later when I describe the feature points, I describe it directly under the picture full of feature points. , Not the original picture! Not the original picture! It is a picture full of feature points! So when the match is carried out in the later stage, it is obvious that all kinds of random matches are like the puppy next door, and they want to do bad things when they see the cat. Ever since, I just commented out these two sentences! extractor.compute(img1_src,keypoints1,descriptor1) extractor.compute(img2_src,keypoints2,descriptor2) BFMatcher matcher(NORM_HAMMING,true)//Violence match and crosscheck, which means that the second parameter selects true. matcher.match(descriptor1,descriptor2,matches) final_matches=matches cout<<'number of total_matches : '<//Next is RANSAC to eliminate mismatches vector querymatches, trainmatches vector p1,p2 for(int i=0ifor(int i=0icout<1]<<' and '<1]<vector status Mat h = findHomography(querymatches,trainmatches,status,CV_FM_RANSAC,10) int index=0 vector super_final_matches for (int i=0icout<if (status[i] != 0) { super_final_matches.push_back(final_matches[i]) index++ } } cout<<'number of inlier_matches : '< 'imgMatch',imgMatch) t = ((double)getTickCount()-t)/getTickFrequency() cout<<' total time [s] : '<0) return 0 }
object_points
オブジェクトポイントの座標は、3xNまたはNx3マトリックスです。ここで、Nはビュー内のすべてのポイントの数です。
ローテーションベクトル
回転ベクトル、1x3または3x1。
translation_vector
平行移動ベクトル、1x3または3x1。
固有の行列
カメラのパラメータ行列A: s_i  begin {bmatrix} x
歪み係数
変形パラメータベクトル、4x1または1x4は、[ 1 p 1p ]。 NULLの場合、すべての変形係数は0に設定されます。
image_points
画像ポイントの座標を格納するための出力配列。サイズは2xNまたはNx2です。ここで、Nはビュー内のすべてのポイントの数です。
dpdrot
オプションのパラメータ、回転ベクトル部分の画像上の点の導関数、Nx3行列。
dpdt
オプションのパラメーター、平行移動ベクトル部分の画像上の点の導関数、Nx3行列。
dpdf
オプションのパラメーター、fxおよびfyの画像上の点の導関数、Nx2行列。
dpdc
オプションのパラメーター、cxおよびcyの画像上の点の導関数、Nx2行列。
dpddist
オプションのパラメータ、変形係数上のイメージポイントの導関数、Nx4行列。

関数cvProjectPoints2は、指定された内部パラメーターと外部パラメーターを使用して、2次元画像平面に投影された3次元点の座標を計算します。さらに、この関数は、射影パラメータに関して画像ポイントの偏導関数のヤコビ行列を計算できます。ヤコビ行列は、cvCalibrateCamera2およびcvFindExtrinsicCameraParams2関数の大域的最適化で使用できます。この関数は、内部および外部パラメーターの逆投影誤差を計算するためにも使用できます。内部パラメータおよび(または)外部パラメータを特定の値に設定すると、この関数を使用して外部変換(または内部変換)を計算できることに注意してください。

FindHomography


FindHomography

2つの平面間の透視変換を計算します

void cvProjectPoints2( const CvMat* object_points, const CvMat* rotation_vector, const CvMat* translation_vector, const CvMat* intrinsic_matrix, const CvMat* distortion_coeffs, CvMat* image_points, CvMat* dpdrot=NULL, CvMat* dpdt=NULL, CvMat* dpdf=NULL, CvMat* dpdc=NULL, CvMat* dpddist=NULL ) 
void cvFindHomography( const CvMat* src_points, const CvMat* dst_points, CvMat* homography )
src_points
dst_points
ターゲット平面の点座標サイズは、2xN、Nx2、3xN、またはNx3マトリックスです(後者の2つは同次座標を表します)。
ホモグラフィ
出力3x3ホモグラフィマトリックス。

関数cvFindHomographyは、ソース平面とターゲット平面の間の透視変換を計算します  sum_i((x

 theta  leftarrow norm(r)

逆投影エラーを最小限に抑えます。

r  leftarrow r /  theta

この関数は、初期内部パラメーターと外部パラメーター行列を計算するために使用できます。ホモグラフィ行列のスケールは可変であるため、次のように正規化されます h 33= 1

CalibrateCamera2

キャリブレーションを使用して、カメラの内部パラメーターと外部パラメーターを計算します
The point coordinates of the original plane, the size is 2xN, Nx2, 3xN or Nx3 matrix (the latter two represent homogeneous coordinates), where N represents the number of points.
object_points
キャリブレーションポイントのワールド座標は、3xNまたはNx3マトリックスです。ここで、Nはすべてのビューのポイントの総数です。
image_points
キャリブレーションポイントの画像座標は、2xNまたはNx2マトリックスです。ここで、Nはすべてのビューのポイントの総数です。
point_counts
ベクトル、異なるビューのポイントの数を指定します。1xMまたはMx1ベクトル、Mはビューの数です。
画像サイズ
画像サイズは、内部パラメータを初期化するときにのみ使用されます。
固有の行列
出力内部パラメータ行列(A) R =  cos( theta)I +(1-  cos( theta))rr ^ T +  sin( theta) begin {bmatrix} 0&-r_z&r_y \ r_z&0&-r_x \ -r_y&r_x&0  end { bmatrix}CV_CALIB_USE_INTRINSIC_GUESSおよび(または)CV_CALIB_FIX_ASPECT_RATIONを指定する場合は、fx、fy、cx、およびcyの一部またはすべてを初期化する必要があります。
歪み係数
出力サイズは4x1または1x4ベクトルで、変形パラメーター[k1、k2、p1、p2]が含まれています。
回転ベクトル
出力サイズは、回転ベクトルである3xMまたはMx3行列です(回転行列のコンパクトな表現。詳細については、関数cvRodrigues2を参照してください)。
translation_vectors
出力サイズは、平行移動ベクトルである3xMまたはMx3行列です。
フラグ
異なるフラグは、0または次の値の組み合わせにすることができます。
  • CV_CALIB_USE_INTRINSIC_GUESS-内部パラメータマトリックスには、fx、fy、cx、cyの初期値が含まれています。それ以外の場合、(cx、cy)は画像の中心に初期化され(ここでは画像サイズが使用されます)、焦点距離は最小二乗差法によって計算されます。内部パラメーターがわかっている場合は、この関数を使用する必要はなく、cvFindExtrinsicCameraParams2を使用するだけであることに注意してください。
  • CV_CALIB_FIX_PRINCIPAL_POINT-プライマリポイントは、グローバル最適化プロセス中、常に中央位置または他の指定された位置(CV_CALIB_USE_INTRINSIC_GUESSが設定されている場合)で変更されません。
  • CV_CALIB_FIX_ASPECT_RATIO-最適化プロセスでは、fxとfyには独立変数が1つだけあり、比率fx / fyは変更されないままであると見なされ、fx / fyの値は内部パラメーター行列がの場合の値と同じです。初期化されました。この場合、(fx、fy)の実際の初期値は、入力メモリマトリックス(CV_CALIB_USE_INTRINSIC_GUESSが指定されている場合)または推定値(後者の場合はfxおよびfyを任意の値に設定できます。比率のみ)から読み取られます。使用されている)。
  • CV_CALIB_ZERO_TANGENT_DIST –接線変形パラメーター(p1、p2)は0に設定され、最適化プロセス中、それらの値は0のままです。

関数cvCalibrateCamera2は、各ビューからカメラの内部パラメーターと外部パラメーターを推定します。 3Dオブジェクト上のポイントと、各ビューの対応する2D投影を指定する必要があります。これらは、検出が容易な既知のジオメトリと特徴点を持つオブジェクトを使用することで実現できます。このようなオブジェクトは、キャリブレーションデバイスまたはキャリブレーションモードと呼ばれます。 OpenCVには、チェス盤をキャリブレーションデバイスとして使用する方法が組み込まれています(cvFindChessboardCornersを参照)。現在、初期化のために渡される内部パラメータ(CV_CALIB_USE_INTRINSIC_GUESSが設定されていない場合)は、平面キャリブレーション機器のみをサポートします(オブジェクトポイントのZ座標はすべて0またはすべて1である必要があります)。ただし、3Dキャリブレーション装置を使用して、初期の内部パラメーターマトリックスを提供することはできます。内部および外部パラメーター行列の初期値が計算された後、それらは逆投影エラー(画像上の実際の座標とcvProjectPoints2によって計算された画像座標との差の2乗の合計)を減らすために最適化されます。

FindExtrinsicCameraParams2

指定されたビューの外部カメラパラメータを計算します

void cvCalibrateCamera2( const CvMat* object_points, const CvMat* image_points, const CvMat* point_counts, CvSize image_size, CvMat* intrinsic_matrix, CvMat* distortion_coeffs, CvMat* rotation_vectors=NULL, CvMat* translation_vectors=NULL, int flags=0 ) 
object_points
キャリブレーションポイントの座標は、3xNまたはNx3マトリックスです。ここで、Nはビュー内の数値です。
image_points
画像内のキャリブレーションポイントの座標は、2xNまたはNx2マトリックスです。ここで、Nはビュー内の数値です。
固有の行列
内部参照マトリックス(A)  sin( theta) begin {bmatrix} 0&-r_z&r_y \ r_z&0&-r_x \ -r_y&r_x&0  end {bmatrix} =  frac {R-R ^ T} {2}
歪み係数
変形パラメーターを持つサイズ4x1または1x4のベクトル[ 1 p 1p ]。 NULLの場合、すべての変形係数は0です。
ローテーションベクトル
出力サイズは、回転ベクトルである3x1または1x3行列です(回転行列のコンパクトな表現。詳細については、関数cvRodrigues2を参照してください)。
translation_vector
平行移動ベクトルを含む、サイズが3x1または1x3の行列。

関数cvFindExtrinsicCameraParams2は、既知の内部パラメーターと特定のビューの外部パラメーターを使用して、カメラの外部パラメーターを推定します。 3Dオブジェクトの点座標と対応する2D投影を指定する必要があります。この関数は、逆投影エラーを最小限に抑えるためにも使用できます。

ロドリゲス2

回転行列と回転ベクトルの間で変換する
void cvFindExtrinsicCameraParams2( const CvMat* object_points, const CvMat* image_points, const CvMat* intrinsic_matrix, const CvMat* distortion_coeffs, CvMat* rotation_vector, CvMat* translation_vector ) 
src
入力回転ベクトル(3x1または1x3)または回転行列(3x3)。
出力回転行列(3x3)または回転ベクトル(3x1または1x3)
ヤコビアン
オプションの出力ヤコビ行列(3x9または9x3)、入力部分に関する出力配列の偏導関数。

この関数は、回転ベクトルを回転行列に、またはその逆に変換します。回転ベクトルは、回転行列のコンパクトな表現です。回転ベクトルの方向は回転軸であり、ベクトルの長さは回転軸の周りの回転角です。回転行列Rとそれに対応する回転ベクトルrは、次の式で変換されます。

p_2 ^ T  cdot F  cdot p_1 = 0

l_2 = F  cdot p_1

l_1 = F ^ T  cdot p_2

逆変換は、次の式でも簡単に実現できます。

a  cdot x + b  cdot y + c = 0

回転ベクトルは、自由度が3つしかない回転​​行列の便利な表現です。この表現は、関数cvFindExtrinsicCameraParams2およびcvCalibrateCamera2のグローバル最適化で使用されます。

UnConstraint2

カメラのレンズによって引き起こされる画像の歪みを修正する
int cvRodrigues2( const CvMat* src, CvMat* dst, CvMat* jacobian=0 ) 
src
元の画像(変形された画像)。変換できるのは32fC1画像のみです。
結果の画像(修正された画像)。
固有の行列
カメラのパラメータマトリックス、形式は次のとおりです。 画像
歪み係数
4つの変形係数で構成されるベクトル、サイズは4x1または1x4、形式は[ 1 p 1p ]

関数cvUnConstraint2は、ラジアルレンズとタンジェンシャルレンズの歪みを相殺するように画像を変換します。カメラパラメータと変形パラメータは、関数cvCalibrateCamera2を介して取得できます。このセクションの冒頭で述べた式を使用して、入力画像内の各出力画像ピクセルの位置を計算し、次に出力画像のピクセル値を双一次補間によって計算します。画像の解像度がキャリブレーション中に使用された画像の解像度と異なる場合、変形が変化していないため、fx、fy、cx、およびcyを適宜調整する必要があります。

InitUnConstraintMap

変形画像と非変形画像の対応を計算する(マップ)
void cvUndistort2( const CvArr* src, CvArr* dst, const CvMat* intrinsic_matrix, const CvMat* distortion_coeffs ) 
固有の行列
カメラパラメータマトリックス(A)[fx 0 cx 0 fy cy 0 01]。
歪み係数
変形係数ベクトル[k1、k2、p1、p2]、サイズは4x1または1x4です。
mapx
x座標の対応する行列。
マップ
y座標の対応する行列。

関数cvInitUnConstraintMapは、変形されていない対応、つまり変形された画像内の正しい画像の各ピクセルの座標を事前に計算します。この対応は、(入力画像と出力画像とともに)cvRemap関数に渡すことができます。

FindChessboardCorners

ボードの内側の角の位置を見つける
void cvInitUndistortMap( const CvMat* intrinsic_matrix, const CvMat* distortion_coeffs, CvArr* mapx, CvArr* mapy ) 
画像
入力チェス盤は、8ビットのグレースケールまたはカラー画像である必要があります。
pattern_size
チェス盤の各行と列のコーナーポイントの数。
コーナー
コーナーが検出されました
corner_count
出力、コーナーポイントの数。 NULLでない場合、関数は検出されたコーナーポイントの数をこの変数に格納します。
フラグ
さまざまな操作フラグは、0または次の値の組み合わせにすることができます。
  • CV_CALIB_CB_ADAPTIVE_THRESH-適応しきい値(平均画像輝度で計算)を使用して、固定しきい値ではなく白黒に画像を変換します。
  • CV_CALIB_CB_NORMALIZE_IMAGE-cvNormalizeHistを使用して、固定しきい値または2値化の適応しきい値を使用する前に、画像の明るさを均等化します。
  • CV_CALIB_CB_FILTER_QUADS-他の基準(輪郭領域、周囲長、正方形の形状など)を使用して、輪郭検出段階で検出された誤った正方形を削除します。

関数cvFindChessboardCornersは、入力画像がチェス盤のパターンであるかどうかを判別し、コーナーポイントの位置を判別しようとします。すべてのコーナーポイントが検出され、それらがすべて特定の順序(行ごと、左から右への各行)に配置されている場合、関数はゼロ以外の値を返します。そうでない場合、関数はすべてのコーナーポイントを検索または記録できません。関数は0を返します。たとえば、通常のチェス盤チャートには、右側に8x8の正方形と7x7の内側の角があります。内側の角は、黒い四角が互いに連絡し合う場所です。この関数は、地面の座標がおおよその地面の値にすぎないことを検出します。それらの場所を正確に特定したい場合は、関数cvFindCornerSubPixを使用できます。

DrawChessBoardCorners

チェス盤の検出された角を描きます
int cvFindChessboardCorners( const void* image, CvSize pattern_size, CvPoint2D32f* corners, int* corner_count=NULL, int flags=CV_CALIB_CB_ADAPTIVE_THRESH ) 
画像
結果の画像は8ビットのカラー画像である必要があります。
pattern_size
各行と列のコーナーポイントの数。
コーナー
検出されたコーナーポイントの配列。
カウント
コーナーポイントの数。
pattern_was_found
ボード全体が見つかったか(≠0)、見つからなかったか(= 0)を示します。 cvFindChessboardCorners関数の戻り値を送信できます。

チェス盤が完全に検出されていない場合、関数cvDrawChessboardCornersは、チェス盤全体が検出された場合、チェス盤の検出された角を赤い円で描画します。すべての角は直線で接続されます。


姿勢推定

CreatePOSITObject

オブジェクト情報を含む構造を初期化します

void cvDrawChessboardCorners( CvArr* image, CvSize pattern_size, CvPoint2D32f* corners, int count, int pattern_was_found ) 
ポイント
3Dオブジェクトモデルへのポインタ
point_count
オブジェクトポイント

関数cvCreatePOSITObjectは、オブジェクト構造にメモリを割り当て、オブジェクトの逆行列を計算します。

前処理されたオブジェクトデータは構造体CvPOSITObjectに格納され、OpenCV内でのみ呼び出すことができます。つまり、ユーザーはデータ構造体を直接読み書きできません。ユーザーはこの構造を作成し、関数へのポインターを渡すことしかできません。

オブジェクトは、特定の座標系の一連のポイントのコレクションです。関数cvPOSITは、カメラ座標系の中心からターゲットポイントポイント[0]までのベクトルを計算します。

特定のオブジェクトに対するすべての操作が完了したら、関数cvReleasePOSITObjectを使用してメモリを解放する必要があります。

POSIT

POSITアルゴリズムを実行します
CvPOSITObject* cvCreatePOSITObject( CvPoint3D32f* points, int point_count ) 
posit_object
オブジェクト構造へのポインタ
image_points
ポインターは、2次元平面上のターゲットピクセルの投影を指します。
焦点距離
使用するカメラの焦点距離
基準
POSIT反復アルゴリズムプログラムの終了条件
ローテーションマトリックス
回転行列
translation_vector
変換行列。

関数cvPOSITは、POSITアルゴリズムを実行します。画像の座標はカメラの座標系で与えられます。焦点距離は、カメラのキャリブレーションによって取得できます。アルゴリズムの各反復は、推定位置での透視投影を再計算します。

2つの投影のパラダイムの違いは、対応するポイントの最大距離です。差が小さすぎる場合、パラメーターcriteria.epsilonはプログラムを終了します。

ReleasePOSITObject

3Dオブジェクト構造を解放します
void cvPOSIT( CvPOSITObject* posit_object, CvPoint2D32f* image_points, double focal_length, CvTermCriteria criteria, CvMatr32f rotation_matrix, CvVect32f translation_vector ) 
posit_object
CvPOSIT構造体へのポインターへのポインター。

関数cvReleasePOSITObjectは、関数cvCreatePOSITObjectによって割り当てられたメモリを解放します。

CalcImageHomography

長方形または楕円形の平面オブジェクト(腕など)のホモグラフィ行列を計算します
void cvReleasePOSITObject( CvPOSITObject** posit_object ) 
ライン
オブジェクトの主軸方向はベクトル(dx、dy、dz)です。
センター
オブジェクトの座標中心((cx、cy、cz))。
内在的
カメラの内部パラメーター(3x3マトリックス)。
ホモグラフィ
出力ホモグラフィ行列(3x3)。

関数cvCalcImageHomographyは、画像平面から画像平面への初期画像変化(3D長方形オブジェクトラインで定義)のホモグラフィ行列を計算します。


エピポーラジオメトリ(デュアルビュージオメトリ)

FindFundamentalMat

2つの画像の対応する点から基本行列を計算します

void cvCalcImageHomography( float* line, CvPoint3D32f* center, float* intrinsic, float* homography ) 
ポイント1
最初の画像ポイントの配列、サイズは2xN / Nx2または3xN / Nx3(Nポイントの数)、マルチチャネル1xNまたはNx1も可能です。ポイント座標は浮動小数点数(倍精度または単精度)である必要があります。 :
ポイント2
2番目の画像のポイント配列、形式、サイズは最初の画像と同じです。
Fundamental_matrix
出力の基本行列。サイズは3x3または9x3です(7ポイントメソッドは最大3つの行列を返すことができます)。
方法
基本行列の計算方法
  • CV_FM_7POINT – 7ポイントアルゴリズム、ポイント数= 7
  • CV_FM_8POINT – 8ポイントアルゴリズム、ポイント数> = 8
  • CV_FM_RANSAC – RANSACアルゴリズム、ポイント数> = 8
  • CV_FM_LMEDS-LMedSアルゴリズム、ポイント数> = 8
param1
このパラメーターは、メソッドRANSACまたはLMedSにのみ使用されます。これは、ある点からエピポーラ線までの最大距離です。この値を超えるポイントは破棄され、以降の計算には使用されません。通常、この値は0.5または1.0に設定されます。
param2
このパラメーターは、メソッドRANSACまたはLMedSにのみ使用されます。これは、行列が正しいという確信を表しています。たとえば、0.99に設定できます。
状態
N個の要素を持つ出力配列の場合、計算プロセスで破棄されるポイントがない場合、要素は1に設定され、それ以外の場合は0に設定されます。この配列は、メソッドRANSACおよびLMedSの場合にのみ使用できます。他のメソッドの場合、ステータスは常に1に設定されます。このパラメーターはオプションです。

エピポーラジオメトリは、次の式で表すことができます。

ここで、Fは基本行列です。 p 1 p それらは2つの写真の対応するポイントです。

関数FindFundamentalMatは、上記の4つのメソッドのいずれかを使用して基本行列を計算し、基本行列の値を返します。行列が見つからない場合は0を返し、1つの行列が見つかった場合は1を返します。複数の行列が見つかった場合は、3を返します。計算された基本行列を関数cvComputeCorrespondEpilinesに渡して、指定された点のエピポーラ線を計算できます。

int cvFindFundamentalMat( const CvMat* points1, const CvMat* points2, CvMat* fundamental_matrix, int method=CV_FM_RANSAC, double param1=1., double param2=0.99, CvMat* status=NULL) 

ComputeCorrespondEpilines

ある画像内の点について、別の画像内の対応するエピポーラ線を計算します。
Example 1: Use the RANSAC algorithm to estimate the fundamental matrix. int numPoints = 100 CvMat* points1 CvMat* points2 CvMat* status CvMat* fundMatr points1 = cvCreateMat(2,numPoints,CV_32F) points2 = cvCreateMat(2,numPoints,CV_32F) status = cvCreateMat(1,numPoints,CV_32F) /* Load the corresponding point data here... */ fundMatr = cvCreateMat(3,3,CV_32F) int num = cvFindFundamentalMat(points1,points2,fundMatr,CV_FM_RANSAC,1.0,0.99,status) if( num == 1 ) printf('Fundamental matrix was found
') else printf('Fundamental matrix was not found
') Example 2: The case of 7-point algorithm (3 matrices). CvMat* points1 CvMat* points2 CvMat* fundMatr points1 = cvCreateMat(2,7,CV_32F) points2 = cvCreateMat(2,7,CV_32F) /* Load the corresponding point data here... */ fundMatr = cvCreateMat(9,3,CV_32F) int num = cvFindFundamentalMat(points1,points2,fundMatr,CV_FM_7POINT,0,0,0) printf('Found %d matrixes
',num) 
ポイント
入力ポイントは2xNまたは3xN配列です(Nはポイントの数です)
which_image
ポイントを含む画像インデックス(1または2)
Fundamental_matrix
基本マトリックス
特派員_lines
極を計算する、3xN配列

関数ComputeCorrespondEpilinesは、外線ジオメトリの基本方程式に従って、各入力ポイントの対応する外線を計算します。ポイントが最初の画像(which_image = 1)にある場合、対応するエピポーラ線は次のように計算できます。

ここで、Fは基本行列です。 p 1最初の画像のポイントは、 l -2番目のフレームに対応する反対の線です。ポイントが2番目の画像(which_image = 2)にある場合、計算は次のようになります。

その中で p 2番目の画像のポイントは、 l 1最初の画像に対応するエピポーラ線です。各エピポーラ線は、3つの係数a、b、cで表すことができます。

正規化されたエピポーラ線係数は、correspondent_linesに格納されます。に


フリーク機能の説明+ BruteForceMatcherマッチング+誤ったマッチングを排除するためのRANSAC

時間はあっという間に過ぎ、数日前の広州への旅行は忘れられませんでした。暑い屋外のサウナのようでした。この期間は、画像から特定のターゲットを見つける方法を研究してきました。これは、局所的な特徴抽出と特徴マッチングに基づいて実現できます。に
現在、次のタイプのローカル機能記述子があります。

  1. 2004年に開発されたSIFT
  2. SURFは後で改善され、それらの記述子は高次元の整数です。
  3. 2010年に登場したBRIEFはバイナリ記述子です。この記述子は、計算速度を向上させるのに良いアイデアを提供しますが、同じ回転とスケールを持たず、ノイズに対しても敏感です(ただし、ノイズに抵抗するためにガウス平滑化を使用します)
  4. 2011年に登場したORBアルゴリズムもバイナリですが、回転不変性とノイズ感度の問題を解決しますが、スケール不変性はありません。
  5. 2011年には、一定の回転、一定のスケール、およびアンチノイズの問題を解決するために、BRISKアルゴリズムが同時に登場しました。インターネット上に動画があり、リアルタイムのパフォーマンスも良いです。まず、私は直感的に理解しています。リンク[( http://v.youku.com/v_show/id_XMTI5MzI3Mzk0OA==.html)]
  6. 2012年に提案されたFREAKアルゴリズムは、実際にはBRISKの改良版です。 BRISKの利点があります。この論文で、著者は、FREAKアルゴリズムが以前のすべての記述子よりも優れている可能性があると述べました。 (注:これは機能記述子です。このホワイトペーパーには特徴点の検出方法がなく、特徴点記述子のみが記載されているためです)
    レビューを読んだ後、私はそれを達成するためにFREAKを使用することにしました。 15日かかりました。今日、私は自分自身を励まし、他の人に奉仕するためにこの段落を書きました。に
    最初にコードを貼り付けます
void cvComputeCorrespondEpilines( const CvMat* points, int which_image, const CvMat* fundamental_matrix, CvMat* correspondent_lines) 
  • 1
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 十一
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 2. 3
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 3. 4
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • フォーファイブ
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83


1:
まず、特徴点の検出についてお話します。 fastやBriskの代わりにsurfを使用するのはなぜですか?私は自分でテストを行ったところ、高速で活発なものを使用し、その後フリークで説明すると、最終的なマッチング効果が良くないことがわかりました。これは、ふるいにかけるよりも優れています。論文では、フリークが全体的に最も効果的であるという効果を比較していますが、そのような結果は得られません。研究を続けていきたいです。あなたがアイデアを持っているなら、アイデアを交換することを歓迎します、ああ、いや、私にアドバイスを与えることを歓迎します!に
二:
RANSACのこのアルゴリズム、cvFindFundamentalMatの代わりにopencvライブラリのcvFindHomographyを使用してください、これら2つは同じではないため。文字通り、1つはホモグラフィ行列を見つけることであり、もう1つは基本行列を見つけることです。 2つの違いは何ですか? ? ?に
これについては、「コンピュータビジョンのマルチビュージオメトリ」で説明されています。

  1. 2Dホモグラフィ:2D画像の一連の点から別の画像の一連の点への射影変換。
  2. 基本行列:チームのすべての点を、x`Fx = 0が成り立つ特異行列Fにします。これは、ランク2の3 * 3の特異行列であり、元に戻すことはできません。これは、エピポーラハーネス制約を介した2次元画像上の点のマッピングであり、2次元から1次元へのマッピングです。

詳細については、本や百度を読むことができます、または私が自由になるまで待ってから、詳細に説明してください。実はあまり知らないハハハ!もう一度見てください!注意深く話してください!に
以前、自分でRANSACアルゴリズムを書いたことがありますが、その効果は特に満足できるものではありませんでした。 RANSACアルゴリズム自体も比較的時間がかかるため、さまざまな偉大な神々も多くの改善方法を提案しています。 opencvがこれらの改善されたアルゴリズムをフォローアップしたのだろうか?推奨総説 'ランダムのためのユニバーサルフレームワーク
その中で言及されているプロサックである「サンプルコンセンサス」は、ローランサックの速度が大幅に向上しました!誰もが自分で学んでいるので、今後はRANSACについてのブログ記事を書きたいと思います。に
三:
コードではBFMatcherクラスが使用され、オブジェクトパラメーターには2つのBFMatcher :: BFMatcher(int normType = NORM_L2、bool crossCheck = false)があります。最初のものは距離を指します。ふるい分けまたはサーフで記述されている場合は、NORM_L2およびNORM_L2 ORBを選択し、BRISK、FREAKおよびその他のバイナリ記述子はハミング距離を使用します。これはNORM_HAMMINGです。NORM_HAMMING2はORBとして使用されます。 2番目のパラメーターは、crossCheckを実行するかどうかです。 trueを選択するのが最善です。これは私の実験でも確認されています。 trueを選択することは、knnマッチングでkを1に設定することと同じです。つまり、複数の近傍ではなく、最良のマッチのセットのみが返されます。 (注:knnmatchを使用する場合は、crossCheckパラメーターにfalseを選択してください)。一致の最良のセットのみを返すこの方法は、比率テストの代わりに使用できます。実験により、falseが選択された場合、1対多の一致と多対1の一致が返されることが証明されています。後期にランサックを使用した場合の効果は、非常に不十分な場合があります。 crossCheckはクロスマッチングであり、不正な一致を排除します。紙のサポートがあります、興味があればそれを検索することができます。


参照ブログ投稿:https://blog.csdn.net/u011867581/article/details/43818183

https://blog.csdn.net/luoshixian099/article/details/50217655

https://blog.csdn.net/lcb_coconut/article/details/52145293?locationNum=4