web-dev-qa-db-ja.com

得られたホモグラフィマトリックスが良いかどうかを確認するにはどうすればよいですか?

この質問はすでに 質問 でしたが、まだわかりません。一連の点からcv::findHomographyを呼び出すことにより、ホモグラフィ行列を取得します。関連性があるかどうかを確認する必要があります。
提案された方法は、インライアの最大再投影誤差を計算し、それをしきい値と比較することです。しかし、そのようなフィルタリングの後、私はオブジェクトの境界ボックスがほぼ直線またはいくつかの奇妙な非凸四角形に、自己交差などで変形するという非常識な変形を続けています。
ホモグラフィ行列自体が適切かどうかを確認するために使用できる制約は何ですか?

23
lizarisk

あなたの質問は数学です。 3x3の行列が与えられた場合、それが適切な剛体変換を表すかどうかを判断します。何が「良い」かを定義するのは難しいですが、ここにあなたを助けることができるいくつかの手がかりがあります

  1. ホモグラフィでは、ポリゴンポイントの方向を保持する必要があります。簡単なテストを設計します。ポイント(0,0)、(imwidth、0)、(width、height)、(0、height)は、時計回りに配置されたポイントを持つ四角形を表します。それらのポイントにホモグラフィを適用し、反時計回りになった場合にそれらがまだ時計回りに配置されているかどうかを確認します。ホモグラフィは画像を反転(ミラーリング)していますが、それでも問題ない場合があります。しかし、あなたのポイントが「悪いホモグラフィ」を持っているよりも故障している場合
  2. ホモグラフィは、オブジェクトのスケールをあまり変更しません。たとえば、画像を最大X倍に縮小または拡大することが予想される場合は、このルールを確認してください。 4点(0,0)、(imwidth、0)、(width-1、height)、(0、height)をホモグラフィで変換し、比率が次の場合に四辺形の面積を計算します(ポリゴンの面積を計算するopencvメソッド)エリアの数が大きすぎる(または小さすぎる)場合は、エラーが発生している可能性があります。
  3. 優れたホモグラフィは通常、低い値の透視性を使用します。通常、画像のサイズが約1000x1000ピクセルの場合、これらの値は約0.005〜0.001になります。高い透視性は、おそらくエラーである巨大な歪みを引き起こします。これらの値がどこにあるかわからない場合は、私の投稿を読んでください: アフィン変換を理解しようとしています 。アフィン変換の計算を説明し、他の2つの値は遠近法のパラメーターです。

上記の3つの条件(条件2が最も重要)を確認すれば、ほとんどの問題を検出できると思います。幸運を

34
DanielHsH

編集:この回答は質問とは無関係ですが、私が行ったように、一致する結果を認識に使用しようとする人にとっては、ディスカッションが役立つ場合があります。

これは誰かを助けるかもしれません:

Point2f[] objCorners = { new Point2f(0, 0),
    new Point2f(img1.Cols, 0),
    new Point2f(img1.Cols, img1.Rows),
    new Point2f(0, img1.Rows) };

Point2d[] sceneCorners = MyPerspectiveTransform3(objCorners, homography);
double marginH = img2.Width * 0.1d;
double marginV = img2.Height * 0.1d;
bool homographyOK = isInside(-marginH, -marginV, img2.Width + marginH, img2.Height + marginV, sceneCorners);
if (homographyOK)
    for (int i = 1; i < sceneCorners.Length; i++)
        if (sceneCorners[i - 1].DistanceTo(sceneCorners[i]) < 1)
        {
            homographyOK = false;
            break;
        }
if (homographyOK)
    homographyOK = isConvex(sceneCorners);
if (homographyOK)
    homographyOK = minAngleCheck(sceneCorners, 20d);




     private static bool isInside(dynamic minX, dynamic minY, dynamic maxX, dynamic maxY, dynamic coors)
        {
            foreach (var c in coors)
                if ((c.X < minX) || (c.Y < minY) || (c.X > maxX) || (c.Y > maxY))
                    return false;
            return true;
        }      
        private static bool isLeft(dynamic a, dynamic b, dynamic c)
        {
            return ((b.X - a.X) * (c.Y - a.Y) - (b.Y - a.Y) * (c.X - a.X)) > 0;
        }
        private static bool isConvex<T>(IEnumerable<T> points)
        {
            var lst = points.ToList();
            if (lst.Count > 2)
            {
                bool left = isLeft(lst[0], lst[1], lst[2]);
                lst.Add(lst.First());
                for (int i = 3; i < lst.Count; i++)
                    if (isLeft(lst[i - 2], lst[i - 1], lst[i]) != left)
                        return false;
                return true;
            }
            else
                return false;
        }
        private static bool minAngleCheck<T>(IEnumerable<T> points, double angle_InDegrees)
        {
            //20d * Math.PI / 180d
            var lst = points.ToList();
            if (lst.Count > 2)
            {                
                lst.Add(lst.First());
                for (int i = 2; i < lst.Count; i++)
                {
                    double a1 = angleInDegrees(lst[i - 2], lst[i-1]);
                    double a2 = angleInDegrees(lst[i], lst[i - 1]);
                    double d = Math.Abs(a1 - a2) % 180d;

                    if ((d < angle_InDegrees) || ((180d - d) < angle_InDegrees))
                        return false;
                }
                return true;
            }
            else
                return false;
        }
        private static double angleInDegrees(dynamic v1, dynamic v2)
        {
            return (radianToDegree(Math.Atan2(v1.Y - v2.Y, v1.X - v2.X))) % 360d;
        }
        private static double radianToDegree(double radian)
        {
            var degree = radian * (180d / Math.PI);
            if (degree < 0d)
                degree = 360d + degree;

            return degree;
        }
        static Point2d[] MyPerspectiveTransform3(Point2f[] yourData, Mat transformationMatrix)
        {
            Point2f[] ret = Cv2.PerspectiveTransform(yourData, transformationMatrix);
            return ret.Select(point2fToPoint2d).ToArray();
        }  

enter image description here

0
Koray