Skip to content

[GSOC] New camera model for stitching pipeline#6933

Merged
alalek merged 65 commits intoopencv:masterfrom
hrnr:gsoc_all
Oct 22, 2016
Merged

[GSOC] New camera model for stitching pipeline#6933
alalek merged 65 commits intoopencv:masterfrom
hrnr:gsoc_all

Conversation

@hrnr
Copy link
Copy Markdown
Contributor

@hrnr hrnr commented Jul 17, 2016

Merge with extra: opencv/opencv_extra#303

This PR contains all work for New camera model for stitching pipeline GSoC 2016 project.

GSoC Proposal

Stitching pipeline is a well established code in OpenCV. It provides good results for creating panoramas from camera captured images. Main limitation of stitching pipeline is its expected camera model (perspective transformation). Although this model is fine for many applications working with camera captured images, there are applications which aren't covered by current stitching pipeline.

New camera model

Due to physical constraints it is possible for some applications to expect much simpler transform with less degrees of freedom. Those are situations when input data are not subject to perspective transform. The transformation can be much simpler, such as affine transformation. Datasets considered here includes images captured by special hardware (such as book scanners[0] that tries hard to eliminate perspective), maps from laser scanning (produced from different starting points), preprocessed images (where perspective was compensated by other robust means, taking advantage of physical situation, e.g. for book scanners we would use data from calibration to compensate remaining perspective). In all those situations we would like to obtain image mosaic under affine transformation.

I'd like to introduce new camera model based on affine transformation to stitching pipeline. This would include:

  • New Matcher using affine transformation (cv::estimateRigidTransform) to estimate H
  • New Estimator aware of affine model.
  • Defining and documenting this new model for CameraParams (e.g. now translation is always expected to be zero, this might not be true for affine transformation)
  • Integration works in compositing part of pipeline (there might be changes necessary depending how we would decide to represent affine model in CameraParams)
  • New options for high-level API to be able to use affine model instead of current one simply
  • Producing new sample code for stitching pipeline
  • Improving current documentation (current documentation does not mention details about current camera model, this might need some clarification)

I used approach based on affine transformation to merge maps produced by multiple robots [1] for my robotics project. It shows a good results. However, as mentioned earlier applications for this model are much broader than that.

Parallelism for FeaturesFinder

To make usage of stitching pipeline more comfortable and performant for large number of images, I’d like also to improve FeaturesFinder to allow finding features in parallel. All camera models and other users of FeaturesFinder may take benefit from that. The API could be similar to FeaturesMatcher::operator ()(features, pairwise_matches, mask).

This could be with TBB in similar manner as mentioned method in FeaturesMatcher, which is already being used in stitching pipeline so there would be almost no additional overhead in starting new threads in typical scenarios, because these threads are there already for FeaturesMatcher. This change would be fully integrated into high level stitching interface.

There might be some changes necessary in finders to ensure thread-safety. Where thread-safety can’t be ensured or it does not make sense (GPU finders), parallelization would be disabled and all images would be processed in serial manner so this method would be always safe to use regardless of underlying finder. This approach is also similar to FeaturesMatcher.

Benefits to OpenCV

  • New transform options for stitching pipeline
  • Performance improvements through parallel processing of image features

implemented goals (all + extras)

new camera model

  • affine matcher
  • affine estimator
  • affine warper
  • affine bundle adjusters
  • tests for affine stitching
    • basic affine stitching integration test
    • tests for affine matcher
    • integration tests on real-word scans
    • affine warper tests
    • affine bundle adjusters tests
  • integrating with high level API (Stitcher)
  • stitching_detailed sample
  • stitching simple sample

parallel feature finding

  • parallel API in feature finder
  • tests (incl. perf tests)
  • integrating with Stitcher

implemented extras

  • robust fuctions for affine transform estimations
    • add support for least median robust method
    • tests for LMEDS
    • Levenberg–Marquardt algorithm-based refining for affine estimation functions
    • tests and docs
    • perf tests
  • stitching tutorial for high level API
    • add examples of running the samples on testdata in opencv_extra
  • fix existing stitching tests with SURF (SURF was disabled)

video

short video presenting this project

other work

During this GSoC I have also coded some related work, that is not going to be included (mostly because we has chosen different approach or the work has been merged under this PR). It is listed here for completeness.

PRs:

commits:

@hrnr
Copy link
Copy Markdown
Contributor Author

hrnr commented Jul 21, 2016

ORB is apparently thread-unsafe when running with OpenCL.

I have restarted the build once. Last time it passed 1 OCL stitching tests, this time it went through 2 of them. There seems to be a race condition.

Is ORB expected to be thread-unsafe with OCL or should this be fixed? I can disable my parallel feature finding changes with OCL, but I don't know if this is not an actual bug in ORB.

@hrnr
Copy link
Copy Markdown
Contributor Author

hrnr commented Jul 22, 2016

I have disabled parallel feature finding when running with OpenCL. There is not much benefit, because it needs to wait for the device.

This solves the issues with ORB for me, but I'm still not sure if this should be opened as bug or not.

@hrnr
Copy link
Copy Markdown
Contributor Author

hrnr commented Jul 23, 2016

rebase to catch latest changes in master (especially #6962)

@hrnr
Copy link
Copy Markdown
Contributor Author

hrnr commented Jul 25, 2016

FYI: The build is not failing, there is only a warning that patch to opencv_extra is too big (~1MB). Is that a problem? I'd like to add more images.

@alalek
Copy link
Copy Markdown
Member

alalek commented Jul 25, 2016

I believe this is not a problem in this case.
When PR will ready please squash all commits into one to keep patch changes clean.

@mself
Copy link
Copy Markdown
Contributor

mself commented Jul 28, 2016

Jiri, I have some comments/suggestions that might be easier to discuss offline. Can you shoot me an email at dmz@mself.com to connect? I did some work to create variants of findHomography() that estimate constrained transformations that have 3 or 4 degrees of freedom rather than the 8 DOF of a full homography. These correspond to (3D) rotations only, and rotations plus uniform scaling. This is similar to your 4 DOF variant. I'd be interested in adding a 3 DOF version that only allows a (2D) rotation plus translation, for example. --Matthew

double* Bdata = B.ptr<double>();
A = Scalar::all(0);

for( int i = 0; i < (N/2); i++ )
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't runKernel() include all of the points in _m1 and _m2 in the estimate rather than just the first 3? That's what HomographyEstimatorCallback.runKernel() does. I presume that RANSAC will call this with sets of 3 points to determine the consensus set, but does it also call it at the end to refine the estimate with all of the points in the consensus set?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Or, to reverse the question, why does findHomography() do it the way it does?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess this is for improving initial guess for LM refining. I did some experiments with estimateAffine2D* functions and the transformation produced by RANSAC seems to be good enough initial guess. LM needs only 3 iterations in most cases and produced transformation is reasonable.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I think you are right. I did some tests on findHomography() to see what effect the final call to cb->runKernel has (it is run on all of the consensus points before starting the LM refinement. Basically, if you have either that final call to cb->runKernel or the LM step you get the same results as if you have both. My tests were with fairly stable transforms (adjacent video frames), but perhaps this additional step helps with stability on more extreme transitions. I think your approach is fine.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hm this is getting interesting. If I remove cb->runKernel LM seems to converge just fine, using juste about 5 iterations in most cases I tested. From POV cb->runKernel does not seem to be necessary.

git blames @vpisarev probably he could know more.

M.copyTo(_model);
return 1;
}
};
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You could also override checkSubset with a version that always returns true, since calling the parent class's version will always return true when count is 2. For the partial affine case, any 2 points will always produce a valid estimate. (Unless perhaps if they are too close to each other, but the parent class's function doesn't check for that in any case?)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice catch. Thank you.

I also though about too close points when I was implementing it (because estimateRigidTransform checks that), but neither estimateAffine3D nor findHomography does that, so I think it's ok to assume that this is in fact not causing too much problems.

@hrnr
Copy link
Copy Markdown
Contributor Author

hrnr commented Aug 11, 2016

ok. I think I have solved the the issue with coincident points. haveCollinearPoints also in fact check coincidence because when coincident, the check will essentialy be 0 <= FLT_EPSILON * (abs(dx2)+abs(dy2)) so it should report coincidence correctly.

I have reworked checkSubset for estimateAffine*, so that there is no duplicite code. Afterall functions should just be more robust.

@hrnr
Copy link
Copy Markdown
Contributor Author

hrnr commented Aug 11, 2016

I have experimented with solving the system in affinePartial callback analytically. In my experiments it surprisingly runs slower than SVD version.

SVD:


calib3d_posix_x64_5074d25_20160811-122223.xml

                     Name of Test                           Number of     Number of   Min     Median  Geometric mean   Mean   Standard deviation
                                                        collected samples outliers                                                              
EstimateAffine2D::EstimateAffine::(100, 0.9)                   38             3     0.15 ms  0.15 ms     0.15 ms     0.15 ms       0.00 ms      
EstimateAffine2D::EstimateAffine::(100, 0.95)                  38             3     0.17 ms  0.18 ms     0.18 ms     0.18 ms       0.00 ms      
EstimateAffine2D::EstimateAffine::(100, 0.99)                  36             2     0.25 ms  0.25 ms     0.25 ms     0.25 ms       0.01 ms      
EstimateAffine2D::EstimateAffine::(100000, 0.9)                10             0     31.49 ms 31.63 ms    31.65 ms    31.65 ms      0.15 ms      
EstimateAffine2D::EstimateAffine::(100000, 0.95)               10             0     34.32 ms 34.40 ms    34.47 ms    34.47 ms      0.25 ms      
EstimateAffine2D::EstimateAffine::(100000, 0.99)               10             0     41.12 ms 41.34 ms    41.63 ms    41.64 ms      0.81 ms      
EstimateAffine2D::EstimateAffine::(5000, 0.9)                  13             1     1.23 ms  1.23 ms     1.24 ms     1.24 ms       0.01 ms      
EstimateAffine2D::EstimateAffine::(5000, 0.95)                 13             1     1.39 ms  1.39 ms     1.40 ms     1.40 ms       0.01 ms      
EstimateAffine2D::EstimateAffine::(5000, 0.99)                 38             3     1.77 ms  1.78 ms     1.81 ms     1.81 ms       0.05 ms      
EstimateAffinePartial2D::EstimateAffine::(100, 0.9)            100            8     0.06 ms  0.06 ms     0.06 ms     0.06 ms       0.00 ms      
EstimateAffinePartial2D::EstimateAffine::(100, 0.95)           75             6     0.07 ms  0.07 ms     0.07 ms     0.07 ms       0.00 ms      
EstimateAffinePartial2D::EstimateAffine::(100, 0.99)           63             5     0.08 ms  0.08 ms     0.09 ms     0.09 ms       0.00 ms      
EstimateAffinePartial2D::EstimateAffine::(100000, 0.9)         10             0     17.02 ms 17.07 ms    17.10 ms    17.10 ms      0.08 ms      
EstimateAffinePartial2D::EstimateAffine::(100000, 0.95)        10             0     17.59 ms 17.72 ms    17.73 ms    17.73 ms      0.12 ms      
EstimateAffinePartial2D::EstimateAffine::(100000, 0.99)        10             0     20.04 ms 20.10 ms    20.19 ms    20.19 ms      0.22 ms      
EstimateAffinePartial2D::EstimateAffine::(5000, 0.9)           13             1     0.50 ms  0.50 ms     0.50 ms     0.50 ms       0.01 ms      
EstimateAffinePartial2D::EstimateAffine::(5000, 0.95)          13             1     0.58 ms  0.58 ms     0.58 ms     0.58 ms       0.01 ms      
EstimateAffinePartial2D::EstimateAffine::(5000, 0.99)          13             1     0.71 ms  0.71 ms     0.72 ms     0.72 ms       0.02 ms      

analytic:


calib3d_posix_x64_5074d25_20160811-161048.xml

                     Name of Test                           Number of     Number of   Min     Median  Geometric mean   Mean   Standard deviation
                                                        collected samples outliers                                                              
EstimateAffine2D::EstimateAffine::(100, 0.9)                   63             5     0.15 ms  0.15 ms     0.15 ms     0.15 ms       0.00 ms      
EstimateAffine2D::EstimateAffine::(100, 0.95)                  38             3     0.18 ms  0.18 ms     0.18 ms     0.18 ms       0.00 ms      
EstimateAffine2D::EstimateAffine::(100, 0.99)                  33             2     0.25 ms  0.25 ms     0.26 ms     0.26 ms       0.01 ms      
EstimateAffine2D::EstimateAffine::(100000, 0.9)                10             0     31.36 ms 31.43 ms    31.56 ms    31.56 ms      0.32 ms      
EstimateAffine2D::EstimateAffine::(100000, 0.95)               10             0     34.16 ms 34.27 ms    34.39 ms    34.39 ms      0.35 ms      
EstimateAffine2D::EstimateAffine::(100000, 0.99)               10             0     41.20 ms 41.35 ms    42.09 ms    42.11 ms      1.15 ms      
EstimateAffine2D::EstimateAffine::(5000, 0.9)                  10             0     1.21 ms  1.22 ms     1.23 ms     1.24 ms       0.03 ms      
EstimateAffine2D::EstimateAffine::(5000, 0.95)                 10             0     1.36 ms  1.37 ms     1.38 ms     1.38 ms       0.03 ms      
EstimateAffine2D::EstimateAffine::(5000, 0.99)                 13             1     1.76 ms  1.77 ms     1.79 ms     1.79 ms       0.05 ms      
EstimateAffinePartial2D::EstimateAffine::(100, 0.9)            42             3     0.05 ms  0.05 ms     0.05 ms     0.05 ms       0.00 ms      
EstimateAffinePartial2D::EstimateAffine::(100, 0.95)           25             2     0.06 ms  0.06 ms     0.06 ms     0.06 ms       0.00 ms      
EstimateAffinePartial2D::EstimateAffine::(100, 0.99)           42             3     0.07 ms  0.07 ms     0.07 ms     0.07 ms       0.00 ms      
EstimateAffinePartial2D::EstimateAffine::(100000, 0.9)         10             0     17.07 ms 17.15 ms    17.19 ms    17.20 ms      0.15 ms      
EstimateAffinePartial2D::EstimateAffine::(100000, 0.95)        10             0     17.71 ms 17.84 ms    17.86 ms    17.86 ms      0.12 ms      
EstimateAffinePartial2D::EstimateAffine::(100000, 0.99)        10             0     20.26 ms 20.34 ms    20.51 ms    20.51 ms      0.46 ms      
EstimateAffinePartial2D::EstimateAffine::(5000, 0.9)           13             1     0.49 ms  0.49 ms     0.50 ms     0.50 ms       0.01 ms      
EstimateAffinePartial2D::EstimateAffine::(5000, 0.95)          11             0     0.57 ms  0.57 ms     0.58 ms     0.58 ms       0.02 ms      
EstimateAffinePartial2D::EstimateAffine::(5000, 0.99)          19             1     0.69 ms  0.70 ms     0.71 ms     0.71 ms       0.02 ms      

you can find the code at 57bf2d4

@mself
Copy link
Copy Markdown
Contributor

mself commented Aug 11, 2016

That's surprising! If you're up for more experiments, I realized that you can solve the entire kernel analytically without even a matrix multiply. This should be even faster. I can't see how SVD could be faster than this!

        double x1 = from[0].x;
        double y1 = from[0].y;
        double x2 = from[1].x;
        double y2 = from[1].y;

        double X1 = to[0].x;
        double Y1 = to[0].y;
        double X2 = to[1].x;
        double Y2 = to[1].y;

        double d = 1./((x1-x2)*(x1-x2) + (y1-y2)*(y1-y2));

        Xdata[0] = d * ( (X1-X2)*(x1-x2) + (Y1-Y2)*(y1-y2) );
        Xdata[1] = d * ( (Y1-Y2)*(x1-x2) - (X1-X2)*(y1-y2) );
        Xdata[2] = d * ( (Y1-Y2)*(x1*y2 - x2*y1) - (X1*y2 - X2*y1)*(y1-y2) - (X1*x2 - X2*x1)*(x1-x2) );
        Xdata[3] = d * (-(X1-X2)*(x1*y2 - x2*y1) - (Y1*x2 - Y2*x1)*(x1-x2) - (Y1*y2 - Y2*y1)*(y1-y2) );

The compiler should be able to optimize all of the common subexpressions and there are no function calls.

@hrnr
Copy link
Copy Markdown
Contributor Author

hrnr commented Aug 11, 2016

Yep, I was also surprised. I think that the kernel is not a bottle neck for the function. But I will try your version, that seems even better.

I will optimize copying inliers, which is currently quite ineficient, that could also speed something up.

{
Mat ms1 = _ms1.getMat(), ms2 = _ms2.getMat();
// check colinearity and also check that points are too close
return !(haveCollinearPoints(ms1, count) || haveCollinearPoints(ms2, count));
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you should only check _ms1 for collinear points and not _ms2. The estimator is only unstable when _ms1 has collinear points -- it doesn't matter if _ms2 does or not. Also, consider the case where the true affine transform is degenerate (i.e. maps all input points to a line). This case may not be common in CV, but it is valid and there is no reason not to produce an accurate estimate of the transform in this case. By checking _ms2, this case won't be able to be estimated and will error out instead. Plus it's faster to only check _ms1.

Copy link
Copy Markdown
Contributor

@mself mself Aug 11, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually, I think it might be faster to not do these checks at all. It seems like they may be more expensive than the work that they will (occasionally) save. Instead, checkSubset() could always return true and runKernel() can return 0 if the determinant is too small (< FLT_EPSILON). This isn't extra work since runKernel() has to compute the determinant in any case.

I think that for findHomography() the situation is different. In that case runKernel() is more expensive (Eigen decomp of an 8x8 matrix) and the geometric consistency check is quite cheap.

@hrnr
Copy link
Copy Markdown
Contributor Author

hrnr commented Aug 12, 2016

I have updated the perf test to use the new API. It seems that lot of time is spent in Levenberg-Marquart refining. RANSAC is only about 1/2 or even 1/3 runtime. LMEDS takes much longer, faster kernel makes more sense here. Here are current results with SVD-based kernels:


calib3d_posix_x64_d9a138e_20160812-152109.xml

                           Name of Test                                 Number of     Number of    Min     Median   Geometric mean   Mean    Standard deviation
                                                                    collected samples outliers                                                                 
EstimateAffine2D::EstimateAffine::(100, 0.9, LMEDS, 0)                     13             1      0.07 ms   0.07 ms     0.07 ms      0.07 ms       0.00 ms      
EstimateAffine2D::EstimateAffine::(100, 0.9, LMEDS, 10)                    73             5      0.10 ms   0.10 ms     0.10 ms      0.10 ms       0.00 ms      
EstimateAffine2D::EstimateAffine::(100, 0.9, RANSAC, 0)                    38             3      0.11 ms   0.11 ms     0.11 ms      0.11 ms       0.00 ms      
EstimateAffine2D::EstimateAffine::(100, 0.9, RANSAC, 10)                   13             1      0.15 ms   0.16 ms     0.16 ms      0.16 ms       0.00 ms      
EstimateAffine2D::EstimateAffine::(100, 0.95, LMEDS, 0)                    50             4      0.07 ms   0.08 ms     0.08 ms      0.08 ms       0.00 ms      
EstimateAffine2D::EstimateAffine::(100, 0.95, LMEDS, 10)                   63             5      0.10 ms   0.10 ms     0.10 ms      0.10 ms       0.00 ms      
EstimateAffine2D::EstimateAffine::(100, 0.95, RANSAC, 0)                   63             5      0.14 ms   0.14 ms     0.14 ms      0.14 ms       0.00 ms      
EstimateAffine2D::EstimateAffine::(100, 0.95, RANSAC, 10)                  25             2      0.17 ms   0.17 ms     0.17 ms      0.17 ms       0.00 ms      
EstimateAffine2D::EstimateAffine::(100, 0.99, LMEDS, 0)                    25             2      0.12 ms   0.12 ms     0.13 ms      0.13 ms       0.00 ms      
EstimateAffine2D::EstimateAffine::(100, 0.99, LMEDS, 10)                   25             2      0.16 ms   0.16 ms     0.16 ms      0.16 ms       0.00 ms      
EstimateAffine2D::EstimateAffine::(100, 0.99, RANSAC, 0)                   13             1      0.21 ms   0.21 ms     0.21 ms      0.21 ms       0.01 ms      
EstimateAffine2D::EstimateAffine::(100, 0.99, RANSAC, 10)                  35             2      0.24 ms   0.24 ms     0.25 ms      0.25 ms       0.01 ms      
EstimateAffine2D::EstimateAffine::(100000, 0.9, LMEDS, 0)                  10             0     79.69 ms  79.82 ms     79.93 ms    79.93 ms       0.36 ms      
EstimateAffine2D::EstimateAffine::(100000, 0.9, LMEDS, 10)                 10             0     137.52 ms 138.79 ms   138.58 ms    138.58 ms      0.54 ms      
EstimateAffine2D::EstimateAffine::(100000, 0.9, RANSAC, 0)                 10             0     10.13 ms  10.20 ms     10.25 ms    10.25 ms       0.17 ms      
EstimateAffine2D::EstimateAffine::(100000, 0.9, RANSAC, 10)                10             0     38.84 ms  39.01 ms     39.04 ms    39.04 ms       0.17 ms      
EstimateAffine2D::EstimateAffine::(100000, 0.95, LMEDS, 0)                 10             0     97.95 ms  98.12 ms     98.31 ms    98.31 ms       0.72 ms      
EstimateAffine2D::EstimateAffine::(100000, 0.95, LMEDS, 10)                10             0     156.45 ms 157.31 ms   157.32 ms    157.32 ms      0.81 ms      
EstimateAffine2D::EstimateAffine::(100000, 0.95, RANSAC, 0)                10             0     12.92 ms  13.03 ms     13.17 ms    13.17 ms       0.33 ms      
EstimateAffine2D::EstimateAffine::(100000, 0.95, RANSAC, 10)               10             0     41.73 ms  41.84 ms     42.12 ms    42.12 ms       0.44 ms      
EstimateAffine2D::EstimateAffine::(100000, 0.99, LMEDS, 0)                 10             0     152.33 ms 152.64 ms   153.70 ms    153.73 ms      2.95 ms      
EstimateAffine2D::EstimateAffine::(100000, 0.99, LMEDS, 10)                10             0     174.49 ms 174.91 ms   175.25 ms    175.25 ms      1.00 ms      
EstimateAffine2D::EstimateAffine::(100000, 0.99, RANSAC, 0)                10             0     20.06 ms  20.22 ms     20.33 ms    20.33 ms       0.29 ms      
EstimateAffine2D::EstimateAffine::(100000, 0.99, RANSAC, 10)               10             0     48.78 ms  49.49 ms     49.80 ms    49.82 ms       1.47 ms      
EstimateAffine2D::EstimateAffine::(5000, 0.9, LMEDS, 0)                    10             0      2.98 ms   2.99 ms     3.00 ms      3.00 ms       0.02 ms      
EstimateAffine2D::EstimateAffine::(5000, 0.9, LMEDS, 10)                   10             0      4.25 ms   4.37 ms     4.36 ms      4.36 ms       0.08 ms      
EstimateAffine2D::EstimateAffine::(5000, 0.9, RANSAC, 0)                   10             0      0.56 ms   0.56 ms     0.57 ms      0.57 ms       0.02 ms      
EstimateAffine2D::EstimateAffine::(5000, 0.9, RANSAC, 10)                  13             1      1.21 ms   1.21 ms     1.22 ms      1.22 ms       0.01 ms      
EstimateAffine2D::EstimateAffine::(5000, 0.95, LMEDS, 0)                   10             0      3.65 ms   3.67 ms     3.68 ms      3.68 ms       0.03 ms      
EstimateAffine2D::EstimateAffine::(5000, 0.95, LMEDS, 10)                  10             0      4.82 ms   4.86 ms     4.86 ms      4.86 ms       0.03 ms      
EstimateAffine2D::EstimateAffine::(5000, 0.95, RANSAC, 0)                  10             0      0.72 ms   0.72 ms     0.73 ms      0.73 ms       0.02 ms      
EstimateAffine2D::EstimateAffine::(5000, 0.95, RANSAC, 10)                 13             1      1.36 ms   1.37 ms     1.38 ms      1.38 ms       0.01 ms      
EstimateAffine2D::EstimateAffine::(5000, 0.99, LMEDS, 0)                   10             0      5.71 ms   5.72 ms     5.74 ms      5.74 ms       0.03 ms      
EstimateAffine2D::EstimateAffine::(5000, 0.99, LMEDS, 10)                  10             0      6.88 ms   6.92 ms     6.93 ms      6.93 ms       0.06 ms      
EstimateAffine2D::EstimateAffine::(5000, 0.99, RANSAC, 0)                  10             0      1.11 ms   1.11 ms     1.12 ms      1.12 ms       0.02 ms      
EstimateAffine2D::EstimateAffine::(5000, 0.99, RANSAC, 10)                 13             1      1.75 ms   1.76 ms     1.77 ms      1.77 ms       0.01 ms      
EstimateAffinePartial2D::EstimateAffine::(100, 0.9, LMEDS, 0)              13             1      0.02 ms   0.02 ms     0.03 ms      0.03 ms       0.00 ms      
EstimateAffinePartial2D::EstimateAffine::(100, 0.9, LMEDS, 10)             100            8      0.05 ms   0.06 ms     0.06 ms      0.06 ms       0.00 ms      
EstimateAffinePartial2D::EstimateAffine::(100, 0.9, RANSAC, 0)             13             1      0.03 ms   0.03 ms     0.03 ms      0.03 ms       0.00 ms      
EstimateAffinePartial2D::EstimateAffine::(100, 0.9, RANSAC, 10)            75             6      0.06 ms   0.06 ms     0.06 ms      0.06 ms       0.00 ms      
EstimateAffinePartial2D::EstimateAffine::(100, 0.95, LMEDS, 0)             25             2      0.03 ms   0.03 ms     0.03 ms      0.03 ms       0.00 ms      
EstimateAffinePartial2D::EstimateAffine::(100, 0.95, LMEDS, 10)            88             7      0.06 ms   0.06 ms     0.06 ms      0.06 ms       0.00 ms      
EstimateAffinePartial2D::EstimateAffine::(100, 0.95, RANSAC, 0)            63             5      0.04 ms   0.04 ms     0.04 ms      0.04 ms       0.00 ms      
EstimateAffinePartial2D::EstimateAffine::(100, 0.95, RANSAC, 10)           63             5      0.07 ms   0.07 ms     0.07 ms      0.07 ms       0.00 ms      
EstimateAffinePartial2D::EstimateAffine::(100, 0.99, LMEDS, 0)             13             1      0.05 ms   0.05 ms     0.05 ms      0.05 ms       0.00 ms      
EstimateAffinePartial2D::EstimateAffine::(100, 0.99, LMEDS, 10)            100            8      0.06 ms   0.07 ms     0.07 ms      0.07 ms       0.00 ms      
EstimateAffinePartial2D::EstimateAffine::(100, 0.99, RANSAC, 0)            38             3      0.05 ms   0.06 ms     0.06 ms      0.06 ms       0.00 ms      
EstimateAffinePartial2D::EstimateAffine::(100, 0.99, RANSAC, 10)           50             4      0.08 ms   0.08 ms     0.08 ms      0.08 ms       0.00 ms      
EstimateAffinePartial2D::EstimateAffine::(100000, 0.9, LMEDS, 0)           10             0     36.71 ms  36.79 ms     36.98 ms    36.99 ms       0.44 ms      
EstimateAffinePartial2D::EstimateAffine::(100000, 0.9, LMEDS, 10)          10             0     53.86 ms  53.94 ms     54.06 ms    54.06 ms       0.28 ms      
EstimateAffinePartial2D::EstimateAffine::(100000, 0.9, RANSAC, 0)          13             1      3.89 ms   3.92 ms     3.93 ms      3.93 ms       0.04 ms      
EstimateAffinePartial2D::EstimateAffine::(100000, 0.9, RANSAC, 10)         10             0     14.11 ms  14.25 ms     14.28 ms    14.28 ms       0.16 ms      
EstimateAffinePartial2D::EstimateAffine::(100000, 0.95, LMEDS, 0)          10             0     48.91 ms  48.98 ms     49.02 ms    49.02 ms       0.15 ms      
EstimateAffinePartial2D::EstimateAffine::(100000, 0.95, LMEDS, 10)         10             0     66.05 ms  66.24 ms     66.33 ms    66.33 ms       0.32 ms      
EstimateAffinePartial2D::EstimateAffine::(100000, 0.95, RANSAC, 0)         13             1      5.02 ms   5.05 ms     5.06 ms      5.06 ms       0.03 ms      
EstimateAffinePartial2D::EstimateAffine::(100000, 0.95, RANSAC, 10)        10             0     15.26 ms  15.36 ms     15.40 ms    15.41 ms       0.16 ms      
EstimateAffinePartial2D::EstimateAffine::(100000, 0.99, LMEDS, 0)          10             0     79.02 ms  79.15 ms     79.19 ms    79.19 ms       0.18 ms      
EstimateAffinePartial2D::EstimateAffine::(100000, 0.99, LMEDS, 10)         10             0     96.13 ms  96.40 ms     96.44 ms    96.44 ms       0.28 ms      
EstimateAffinePartial2D::EstimateAffine::(100000, 0.99, RANSAC, 0)         10             0      7.54 ms   7.59 ms     7.66 ms      7.66 ms       0.22 ms      
EstimateAffinePartial2D::EstimateAffine::(100000, 0.99, RANSAC, 10)        10             0     17.79 ms  18.22 ms     18.26 ms    18.27 ms       0.51 ms      
EstimateAffinePartial2D::EstimateAffine::(5000, 0.9, LMEDS, 0)             10             0      1.37 ms   1.38 ms     1.39 ms      1.39 ms       0.02 ms      
EstimateAffinePartial2D::EstimateAffine::(5000, 0.9, LMEDS, 10)            10             0      1.84 ms   1.85 ms     1.86 ms      1.86 ms       0.05 ms      
EstimateAffinePartial2D::EstimateAffine::(5000, 0.9, RANSAC, 0)            13             1      0.20 ms   0.20 ms     0.21 ms      0.21 ms       0.01 ms      
EstimateAffinePartial2D::EstimateAffine::(5000, 0.9, RANSAC, 10)           25             2      0.60 ms   0.60 ms     0.61 ms      0.61 ms       0.01 ms      
EstimateAffinePartial2D::EstimateAffine::(5000, 0.95, LMEDS, 0)            10             0      1.83 ms   1.90 ms     1.90 ms      1.90 ms       0.03 ms      
EstimateAffinePartial2D::EstimateAffine::(5000, 0.95, LMEDS, 10)           10             0      2.40 ms   2.45 ms     2.45 ms      2.45 ms       0.04 ms      
EstimateAffinePartial2D::EstimateAffine::(5000, 0.95, RANSAC, 0)           13             1      0.26 ms   0.26 ms     0.27 ms      0.27 ms       0.01 ms      
EstimateAffinePartial2D::EstimateAffine::(5000, 0.95, RANSAC, 10)          14             1      0.66 ms   0.66 ms     0.67 ms      0.67 ms       0.02 ms      
EstimateAffinePartial2D::EstimateAffine::(5000, 0.99, LMEDS, 0)            10             0      2.96 ms   3.01 ms     3.02 ms      3.03 ms       0.05 ms      
EstimateAffinePartial2D::EstimateAffine::(5000, 0.99, LMEDS, 10)           10             0      3.35 ms   3.47 ms     3.45 ms      3.45 ms       0.06 ms      
EstimateAffinePartial2D::EstimateAffine::(5000, 0.99, RANSAC, 0)           10             0      0.39 ms   0.40 ms     0.40 ms      0.40 ms       0.01 ms      
EstimateAffinePartial2D::EstimateAffine::(5000, 0.99, RANSAC, 10)          13             1      0.79 ms   0.80 ms     0.80 ms      0.80 ms       0.02 ms      

@mself
Copy link
Copy Markdown
Contributor

mself commented Aug 12, 2016

OK, cool. Perhaps the reason that findHomography() has the final runKernel() call that passes all of the consensus points is to improve the starting estimate for LM so that it takes fewer iterations. I noted that it took ~3 with the runKernel() call and ~5 without it. The results appeared to be virtually identical, so it may be about performance rather than accuracy. Or maybe it is about stability, since findHomography() is a lot less numerically stable.

@mself
Copy link
Copy Markdown
Contributor

mself commented Aug 12, 2016

Now that you've updated the APIs, it would be interesting to compare the performance of the analytic runKernel() with niters = 0 to remove the LM part.

hrnr added 23 commits October 21, 2016 17:53
reestimation step is not needed. estimateAffine2D* functions are running their own reestimation on inliers using the Levenberg-Marquardt algorithm, which is better than simply rerunning RANSAC on inliers.
bundle adjuster that expect affine transform with 4DOF. Refines parameters for all cameras together.

stitching: fix bug in BundleAdjusterAffinePartial

* use the invers properly
* use static buffer for invers to speed it up
* add support for using affine bundle adjuster with 4DOF
* improve logging of initial intristics
prevents spurious test failures on mac. values are still pretty fine.
* fix bug with AffineBestOf2NearestMatcher (we want to select affine partial mode)
* select right bundle adjuster
* this prevents failure on mac. tranformation is still ok.
* implements affine bundle adjuster that is using full affine transform
* existing test case modified to test both affinePartial an full affine bundle adjuster
* show basic usage of stitching api (Stitcher class)
* added new datasets to existing testcase
* removed unused include
* added comment to make that this also checks too close points
* use common function to check collinearity
* this also ensures that point will not be too close to each other
* more similar to `findHomography`, `findFundamentalMat`, `findEssentialMat` and similar
* follows standard recommended semantic INPUTS, OUTPUTS, FLAGS
* allows to disable refining
* supported LMEDS robust method (tests yet to come) along with RANSAC
* extended docs with some tips
* rewrite in googletest style
* parametrize to test both robust methods (RANSAC and LMEDS)
* get rid of boilerplate
* rework in googletest style
* add testing for LMEDS
* test for LMEDS speed
* test with/without Levenberg-Marquart
* remove sanity checking (this is covered by accuracy tests)
* test transformations in loop
* improves test by testing more potential transformations
* use analytical solution instead of SVD
* this version is faster especially for smaller amount of points
* avoid copying inliers
* avoid converting input points if not necessary
* check only `from` point for collinearity, as `to` does not affect stability of transform
* add some examples how to run stitcher sample code
* mention stitching_detailed.cpp
* do error computing in floats instead of doubles

this have required precision + we were storing the result in float anyway. This make code faster and allows auto-vectorization by smart compilers.
* refer to new functions on appropriate places
* prefer estimateAffine*2D over estimateRigidTransform
@hrnr
Copy link
Copy Markdown
Contributor Author

hrnr commented Oct 21, 2016

rebased again.

* mention camera models in module documentation to give user a better overview and reduce confusion
@hrnr
Copy link
Copy Markdown
Contributor Author

hrnr commented Oct 21, 2016

I have restarted the build due to github DNS issues.

@alalek alalek merged commit c17afe0 into opencv:master Oct 22, 2016
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants