19

私はオブジェクトをモデル画像で言いましょう。モデル イメージ上のオブジェクトとターゲット イメージ上のオブジェクト間の変換 (変位、スケール、回転) を計算したいと考えています。オブジェクトは 2D として扱うことができるので、2D 変換のみを計算する必要があると仮定したいと思います。

まず、手動で支援したいです。ユーザーは、モデル イメージ上のベース ポイントを選択し、次にターゲット イメージ上のターゲット ポイントを選択します。ポイント数はユーザーが定義する必要があります (ただし、最低 2 ~ 3 ポイント以上)。ポイントが異なる情報を提供する場合、変換を平均化する必要があります。たとえば、これからマッチングの品質を計算できます。

したがって、質問は2セットのポイントの変換を計算することですが、画像でそれを実行したいので、画像処理タグを追加しました。

特に歓迎されるのは、いくつかのコードまたは疑似コードに関する参照とアドバイスです。

2 つのポイントを使用すると、非常に簡単な問題になります。線の回転、スケール、および変位のみを取得する必要がありますが、より多くのポイントを使用してそれを平均化し、いくつかの品質係数を計算する方法です。

現在の解決策は次のとおりです。

void transformFnc(std::vector<PointF> basePoints, std::vector<PointF> targetPoints,
                  PointF& offset, double rotation, double scale)
{
    std::vector<Line> basePointsLines;
    std::vector<Line> targetPointsLines;
    assert(basePoints.size() == targetPoints.size());
    int pointsNumber = basePoints.size();

    for(int i = 0; i < pointsNumber; i++)
    {
         for(int j = i + 1; j < pointsNumber; j++)
         {
             basePointsLines.push_back(Line(basePoints[i], basePoints[j]));
             targetPointsLines.push_back(Line(targetPoints[i], targetPoints[j]));
         }
    }
    std::vector<double> scalesVector;
    std::vector<double> rotationsVector;
    double baseCenterX = 0, baseCenterY = 0, targetCenterX = 0, targetCenterY = 0;
    for(std::vector<Line>::iterator it = basePointsLines.begin(), i = targetPointsLines.begin();
        it != basePointsLines.end(), i != targetPointsLines.end(); it++, i++)
    {
        scalesVector.push_back((*i).length()/(*it).length());
        baseCenterX += (*it).pointAt(0.5).x(); 
        baseCenterY += (*it).pointAt(0.5).y();
        targetCenterX += (*i).pointAt(0.5).x();
        targetCenterY += (*i).pointAt(0.5).y();
        double rotation;
        rotation = (*i).angleTo((*it));
        rotationsVector.push_back(rotation);
    }
    baseCenterX = baseCenterX / pointsNumber;
    baseCenterY = baseCenterY / pointsNumber;
    targetCenterX = targetCenterX / pointsNumber;
    targetCenterY = targetCenterY / pointsNumber;

    offset = PointF(targetCenterX - baseCenterX, targetCenterY - baseCenterY);
    scale = sum(scalesVector) / scalesVector.size();
    rotation = sum(rotationsVector) / rotationsVector.size();
}

このコードで見つけることができる唯一の最適化は、スケールと回転から、残りの値と大きく異なる値を削除することです。

ソリューション命題のコードまたは疑似コードを探しています。また、一部のコードへの参照になることもあります。

これまでのところ、私が知っている答えは次のとおりです。

  • RANSAC アルゴリズムを使用できます
  • 最小二乗法でのアフィン変換計算アルゴリズムを探す必要があります
4

5 に答える 5

27

最初に、3x3 アフィン変換行列を使用した単純なアフィン変換で問題を一般化します。つまり、

[M11 M12 M13]
[M21 M22 M23]
[M31 M32 M33]

3 行目は常に [0 0 1] になることが既にわかっているので、単純に無視できます。

これで、問題を次の行列方程式として説明できます

[xp0]     [x0 y0 1  0  0  0 ]
[yp0]     [0  0  0  x0 y0 1 ]     [M11]
[xp1]     [x1 y1 1  0  0  0 ]     [M12]
[yp1]  =  [0  0  0  x1 y1 1 ]  *  [M13]
[xp2]     [x2 y2 1  0  0  0 ]     [M21]
[yp2]     [0  0  0  x2 y2 1 ]     [M22]
[xp3]     [x3 y3 1  0  0  0 ]     [M23]
[yp3]     [0  0  0  x3 y3 1 ]

ここで、xp と yp は投影された座標で、x と y は元の座標です。

これを呼びましょう

proj = M * trans

次に、変換に適合する最小二乗を次のように計算できます。

trans = pinv(M) * proj

ここで、pinv は擬​​似逆数です。

これにより、最小二乗法で与えられた点に最もよく適合するアフィン変換が得られます。

明らかに、これにより、シアー、座標反転、および望ましくない不均一なスケーリングも発生するため、シアーを回避するために何らかの方法でアフィン変換を制限する必要があります。これは非常に簡単であることがわかります。単一のベクトルを使用して、回転 (ベクトルの方向) とスケーリング (ベクトルの大きさ) を記述できます。もう一方のベクトルは単純にそれに直交します。これにより、自由度が 2 つ減少します。

M21 = -M12
M22 = M11

だからに減らす

[xp0]     [x0  y0 1 0]
[yp0]     [y0 -x0 0 1]
[xp1]     [x1  y1 1 0]     [M11]
[yp1]  =  [y1 -x1 0 1]  *  [M12]
[xp2]     [x2  y2 1 0]     [M13]
[yp2]     [y2 -x2 0 1]     [M23]
[xp3]     [x3  y3 1 0]
[yp3]     [y3 -x3 0 1]

上記の行列方程式を解いた後、M12 と M11 から M21 と M22 を計算します。

于 2013-01-11T03:27:35.010 に答える
6

反復最近点アルゴリズムを試してみます。

ここでは、スケーリングを使用した実装を見つけます。(SICP)

別の便利なリンク

于 2013-01-11T07:52:40.600 に答える
2

簡単にするために、入力x1,...,xnと出力y1,...,ynが複素数であるとします。

  1. を計算することで平均変位を計算avg(y) - avg(x)できます。これが完了すると、両方の平均を差し引いてxyそれらが 0 付近に集中するようにすることができます。

  2. 次に、回転とスケールを見つけます。両方を 1 つの複素数として表すことができるzため、x*zできるだけ に近づける必要がありますy。しかし、x*zは:の (実) 座標のR線形関数であるため、古典的な線形代数を使用して、最小二乗の意味で にできるだけ近いものを解くことができます。(zx,zy)zzx*zy

すべてをまとめると、最小二乗法で最適な変換が得られます。

于 2012-07-27T13:47:57.397 に答える
1

変換はアフィン変換であり、3*3 行列で記述できます。したがって、問題は基本的に、ある点のセットから他の点への最小平均二乗誤差アフィン変換を計算することです。

この問題は、一般的な計算幾何学の文献では非常に簡単に解決されています。良い古典的な本はこれです: http://www.robots.ox.ac.uk/~vgg/hzbook/hzbook1.html (広告はありません。ほとんどの人にとって単なる参考書です)。2D および 3D ジオメトリに関するすべての情報が見つかります。「アフィン変換 LMSE」などの単語で簡単にグーグルすると、情報とおそらくコードが得られます。

さらに、RANSAC などの他のタイプの堅牢なアルゴリズムを使用することもできます。アプリケーションによっては、その方向にさらに進むことが興味深い場合があります。

于 2012-07-27T13:38:18.483 に答える
0

変換を行うことができるMatlab のよりシンプルで明確なコード。

さらに複雑な C++ コード (VXL lib を使用)とpython および matlab ラッパーが含まれています。

または、ノイズに対して堅牢な修正された ICP (反復最近点) アルゴリズムを使用することもできます。

于 2013-04-29T05:58:42.447 に答える