Search code examples
iosopencvrotationcrophomography

OpenCV : wrapPerspective on whole image


I'm detecting markers on images captured by my iPad. Because of that I want to calculate translations and rotations between them, I want to change change perspective on images these image, so it would look like I'm capturing them directly above markers.

Right now I'm using

points2D.push_back(cv::Point2f(0, 0));
points2D.push_back(cv::Point2f(50, 0));
points2D.push_back(cv::Point2f(50, 50));
points2D.push_back(cv::Point2f(0, 50));

Mat perspectiveMat = cv::getPerspectiveTransform(points2D, imagePoints);
cv::warpPerspective(*_image, *_undistortedImage, M, cv::Size(_image->cols, _image->rows));

Which gives my these results (look at the right-bottom corner for result of warpPerspective):

photo 1 photo 2 photo 3

As you probably see result image contains recognized marker in left-top corner of the result image. My problem is that I want to capture whole image (without cropping) so I could detect other markers on that image later.

How can I do that? Maybe I should use rotation/translation vectors from solvePnP function?

EDIT:

Unfortunatelly changing size of warped image don't help much, because image is still translated so left-top corner of marker is in top-left corner of image.

For example when I've doubled size using:

cv::warpPerspective(*_image, *_undistortedImage, M, cv::Size(2*_image->cols, 2*_image->rows));

I've recieved these images:

photo 4 photo 5


Solution

  • Your code doesn't seem to be complete, so it is difficult to say what the problem is.

    In any case the warped image might have completely different dimensions compared to the input image so you will have to adjust the size paramter you are using for warpPerspective.

    For example try to double the size:

    cv::warpPerspective(*_image, *_undistortedImage, M, 2*cv::Size(_image->cols, _image->rows));
    

    Edit:

    To make sure the whole image is inside this image, all corners of your original image must be warped to be inside the resulting image. So simply calculate the warped destination for each of the corner points and adjust the destination points accordingly.

    To make it more clear some sample code:

    // calculate transformation
    cv::Matx33f M = cv::getPerspectiveTransform(points2D, imagePoints);
    
    // calculate warped position of all corners
    
    cv::Point3f a = M.inv() * cv::Point3f(0, 0, 1);
    a = a * (1.0/a.z);
    
    cv::Point3f b = M.inv() * cv::Point3f(0, _image->rows, 1);
    b = b * (1.0/b.z);
    
    cv::Point3f c = M.inv() * cv::Point3f(_image->cols, _image->rows, 1);
    c = c * (1.0/c.z);
    
    cv::Point3f d = M.inv() * cv::Point3f(_image->cols, 0, 1);
    d = d * (1.0/d.z);
    
    // to make sure all corners are in the image, every position must be > (0, 0)
    float x = ceil(abs(min(min(a.x, b.x), min(c.x, d.x))));
    float y = ceil(abs(min(min(a.y, b.y), min(c.y, d.y))));
    
    // and also < (width, height)
    float width = ceil(abs(max(max(a.x, b.x), max(c.x, d.x)))) + x;
    float height = ceil(abs(max(max(a.y, b.y), max(c.y, d.y)))) + y;
    
    // adjust target points accordingly
    for (int i=0; i<4; i++) {
        points2D[i] += cv::Point2f(x,y);
    }
    
    // recalculate transformation
    M = cv::getPerspectiveTransform(points2D, imagePoints);
    
    // get result
    cv::Mat result;
    cv::warpPerspective(*_image, result, M, cv::Size(width, height), cv::WARP_INVERSE_MAP);