估算两张图像之间的仿射变换

5

我有一张示例图片:

在此输入图片描述

使用以下变换矩阵对图片进行仿射变换:

[[ 1.25  0.    -128  ]
 [ 0.    2.    -192  ]]

并从结果中裁剪出一个128x128的部分以获得输出图像:

enter image description here

现在,我想通过仅比较示例和输出图像来估计变形矩阵和裁剪大小/位置。 我使用SURF检测特征点,并通过暴力匹配它们:

enter image description here

有许多匹配,我保留了最佳的三个(按距离),因为这是估算仿射变换所需的数量。 然后,我使用这3个关键点使用getAffineTransform估计仿射变换。 但是,它返回的转换完全错误:

-0.00 1.87 -6959230028596648489132997794229911552.00 
0.00 -1.76 -0.00

我在做什么不对吗?下面是源代码。

执行仿射变换(Python):

"""Apply an affine transform to an image."""
import cv
import sys
import numpy as np
if len(sys.argv) != 10:
    print "usage: %s in.png out.png x1 y1 width height sx sy flip" % __file__
    sys.exit(-1)
source = cv.LoadImage(sys.argv[1])
x1, y1, width, height, sx, sy, flip = map(float, sys.argv[3:])
X, Y = cv.GetSize(source)
Xn, Yn = int(sx*(X-1)), int(sy*(Y-1))
if flip:
    arr = np.array([[-sx, 0, sx*(X-1)-x1], [0, sy, -y1]])
else:
    arr = np.array([[sx, 0, -x1], [0, sy, -y1]])
print arr
warp = cv.fromarray(arr)
cv.ShowImage("source", source)
dest = cv.CreateImage((Xn, Yn), source.depth, source.nChannels)
cv.WarpAffine(source, dest, warp)
cv.SetImageROI(dest, (0, 0, int(width), int(height)))
cv.ShowImage("dest", dest)
cv.SaveImage(sys.argv[2], dest)
cv.WaitKey(0)

从两个图像中估算仿射变换(C++):

#include <stdio.h>
#include <iostream>
#include <opencv2/core/core.hpp>
#include <opencv2/features2d/features2d.hpp>
#include <opencv2/calib3d/calib3d.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/nonfree/nonfree.hpp>
#include <opencv2/imgproc/imgproc.hpp>

#include <algorithm>

using namespace cv;

void readme();

bool cmpfun(DMatch a, DMatch b) { return a.distance < b.distance; }

/** @function main */
int main( int argc, char** argv )
{
    if( argc != 3 )
    {
        return -1;
    }

    Mat img_1 = imread( argv[1], CV_LOAD_IMAGE_GRAYSCALE );
    Mat img_2 = imread( argv[2], CV_LOAD_IMAGE_GRAYSCALE );

    if( !img_1.data || !img_2.data )
    {
        return -1;
    }

    //-- Step 1: Detect the keypoints using SURF Detector
    int minHessian = 400;

    SurfFeatureDetector detector( minHessian );

    std::vector<KeyPoint> keypoints_1, keypoints_2;

    detector.detect( img_1, keypoints_1 );
    detector.detect( img_2, keypoints_2 );

    //-- Step 2: Calculate descriptors (feature vectors)
    SurfDescriptorExtractor extractor;

    Mat descriptors_1, descriptors_2;

    extractor.compute( img_1, keypoints_1, descriptors_1 );
    extractor.compute( img_2, keypoints_2, descriptors_2 );

    //-- Step 3: Matching descriptor vectors with a brute force matcher
    BFMatcher matcher(NORM_L2, false);
    std::vector< DMatch > matches;
    matcher.match( descriptors_1, descriptors_2, matches );

    double max_dist = 0;
    double min_dist = 100;

    //-- Quick calculation of max and min distances between keypoints
    for( int i = 0; i < descriptors_1.rows; i++ )
    {   double dist = matches[i].distance;
        if( dist < min_dist ) min_dist = dist;
        if( dist > max_dist ) max_dist = dist;
    }
    printf("-- Max dist : %f \n", max_dist );
    printf("-- Min dist : %f \n", min_dist );

    //-- Draw only "good" matches (i.e. whose distance is less than 2*min_dist )
    //-- PS.- radiusMatch can also be used here.
    sort(matches.begin(), matches.end(), cmpfun);
    std::vector< DMatch > good_matches;
    vector<Point2f> match1, match2;
    for (int i = 0; i < 3; ++i)
    {
        good_matches.push_back( matches[i]);
        Point2f pt1 = keypoints_1[matches[i].queryIdx].pt;
        Point2f pt2 = keypoints_2[matches[i].trainIdx].pt;
        match1.push_back(pt1);
        match2.push_back(pt2);
        printf("%3d pt1: (%.2f, %.2f) pt2: (%.2f, %.2f)\n", i, pt1.x, pt1.y, pt2.x, pt2.y);
    }

    //-- Draw matches
    Mat img_matches;
    drawMatches( img_1, keypoints_1, img_2, keypoints_2, good_matches, img_matches,
                 Scalar::all(-1), Scalar::all(-1), vector<char>(), DrawMatchesFlags::NOT_DRAW_SINGLE_POINTS);

    //-- Show detected matches
    imshow("Matches", img_matches );
    imwrite("matches.png", img_matches);

    waitKey(0);

    Mat fun = getAffineTransform(match1, match2);
    for (int i = 0; i < fun.rows; ++i)
    {
        for (int j = 0; j < fun.cols; j++)
        {
            printf("%.2f ", fun.at<float>(i,j));
        }
        printf("\n");
    }

    return 0;
}

/** @function readme */
void readme()
{
    std::cout << " Usage: ./SURF_descriptor <img1> <img2>" << std::endl;
}

嗨,我正在使用OpenCV 2.4.3,并且我运行了您发布的两张图片的C++程序,但是它给出的结果与您发布的不同:http://daiw.de/share/Forum/estimating-an-affine-transform-between-two-images_matches_1.png 或者另一种方式 http://daiw.de/share/Forum/estimating-an-affine-transform-between-two-images_matches_2.png 我需要做些其他的事情才能重现您的问题吗? - Tobias Hermann
我使用2.4.4版本获取了图像。我不知道这是否相关。 - mpenkov
2.4.4将于2013年02月发布。http://code.opencv.org/projects/opencv/versions/11 - Tobias Hermann
@Dobi:我承认错误。我确实使用的是2.4.3版本(和你一样),但我不明白为什么你会得到不同的特征点。 - mpenkov
没问题,我认为这已经不重要了。 ;) 现在使用我的答案中的double类型是否有效? - Tobias Hermann
嗨,我认为你可以使用estimateRigidTransform(); 我不知道在你提问的时候它是否可用。 - Alejandro Silvestri
1个回答

1

cv::Mat getAffineTransform 返回的矩阵是由双精度浮点数构成的,而不是单精度浮点数。你得到的矩阵可能是正确的,你只需要在循环中更改printf命令即可。

printf("%.2f ", fun.at<double>(i,j));

或者更简单的方法:用以下内容替换此手动输出

std::cout << fun << std::endl;

这样更简短,而且您不必自己关心数据类型。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接