计算两幅图像的重叠区域

问题描述:已知两幅图像Image1和Image2,计算出两幅图像的重叠区域,并在Image1和Image2标识出重叠区域。

算法思想:

若两幅图像存在重叠区域,则进行图像匹配后,会得到一张完整的全景图,因而可以转换成图像匹配问题。

图像匹配问题,可以融合两幅图像,得到全景图,但无法标识出在原图像的重叠区域。

将两幅图像都理解为多边形,则其重叠区域的计算,相当于求多边形的交集。

通过多边形求交,获取重叠区域的点集,然后利用单应矩阵还原在原始图像的点集信息,从而标识出重叠区域。

算法步骤:

1.图像匹配计算,获取单应矩阵。

2.根据单应矩阵,计算图像2的顶点转换后的点集。

3.由图像1的顶点集合和图像2的转换点集,计算多边形交集。

4.根据单应矩阵的逆,计算多边形的交集在图像2中的原始点集。

代码实现如下所示:

bool ImageOverlap(cv::Mat &img1,cv::Mat &img2,std::vector<cv::Point> &vPtsImg1,std::vector<cv::Point> &vPtsImg2)
{
    cv::Mat g1(img1,Rect(0,0,img1.cols,img1.rows));
    cv::Mat g2(img2,Rect(0,0,img2.cols,img2.rows));

    cv::cvtColor(g1,g1,CV_BGR2GRAY);
    cv::cvtColor(g2,g2,CV_BGR2GRAY);

    std::vector<cv::KeyPoint> keypoints_roi, keypoints_img;  /* keypoints found using SIFT */
    cv::Mat descriptor_roi, descriptor_img;             /* Descriptors for SIFT */
    cv::FlannBasedMatcher matcher;                      /* FLANN based matcher to match keypoints */
    std::vector<cv::DMatch> matches, good_matches;
    cv::SIFT sift;
    int i, dist=80;

    sift(g1, Mat(), keypoints_roi, descriptor_roi);      /* get keypoints of ROI image */
    sift(g2, Mat(), keypoints_img, descriptor_img);         /* get keypoints of the image */
    matcher.match(descriptor_roi, descriptor_img, matches);

    double max_dist = 0; double min_dist = 1000;

    //-- Quick calculation of max and min distances between keypoints
    for( int i = 0; i < descriptor_roi.rows; i++ )
    { 
        double dist = matches[i].distance;
        if( dist < min_dist ) min_dist = dist;
        if( dist > max_dist ) max_dist = dist;
    }

    for (i=0; i < descriptor_roi.rows; i++)
    {
        if (matches[i].distance < 3*min_dist)
        {
            good_matches.push_back(matches[i]);
        }
    }

    //printf("%ld no. of matched keypoints in right image\n", good_matches.size());
    /* Draw matched keypoints */

    //Mat img_matches;
    //drawMatches(img1, keypoints_roi, img2, keypoints_img, 
    //    good_matches, img_matches, Scalar::all(-1), 
    //    Scalar::all(-1), vector<char>(), 
    //    DrawMatchesFlags::NOT_DRAW_SINGLE_POINTS);
    //imshow("matches",img_matches);

    vector<Point2f> keypoints1, keypoints2; 
    for (i=0; i<good_matches.size(); i++)
    {
        keypoints1.push_back(keypoints_img[good_matches[i].trainIdx].pt);
        keypoints2.push_back(keypoints_roi[good_matches[i].queryIdx].pt);
    }
    //计算单应矩阵
    Mat H = findHomography( keypoints1, keypoints2, CV_RANSAC );

    //show stitchImage
    // cv::Mat stitchedImage;
    // int mRows = img2.rows;
    // if (img1.rows> img2.rows)
    // {
        // mRows = img1.rows;
    // }
    // stitchedImage = Mat::zeros(img2.cols+img1.cols, mRows, CV_8UC3);
    // warpPerspective(img2,stitchedImage,H,Size(img2.cols+img1.cols,mRows));
    // Mat half(stitchedImage,Rect(0,0,img1.cols,img1.rows));
    // img1.copyTo(half);
    // imshow("stitchedImage",stitchedImage);

    std::vector<cv::Point> vSrcPtsImg1;
    std::vector<cv::Point> vSrcPtsImg2;

    vSrcPtsImg1.push_back(cv::Point(0,0));
    vSrcPtsImg1.push_back(cv::Point(0,img1.rows));
    vSrcPtsImg1.push_back(cv::Point(img1.cols,img1.rows));
    vSrcPtsImg1.push_back(cv::Point(img1.cols,0));

    vSrcPtsImg2.push_back(cv::Point(0,0));
    vSrcPtsImg2.push_back(cv::Point(0,img2.rows));
    vSrcPtsImg2.push_back(cv::Point(img2.cols,img2.rows));
    vSrcPtsImg2.push_back(cv::Point(img2.cols,0));

    //计算图像2在图像1中对应坐标信息
    std::vector<cv::Point> vWarpPtsImg2;
    for(int i = 0;i < vSrcPtsImg2.size();i++ )
    {
        cv::Mat srcMat = Mat::zeros(3,1,CV_64FC1);
        srcMat.at<double>(0,0) = vSrcPtsImg2[i].x;
        srcMat.at<double>(1,0) = vSrcPtsImg2[i].y;
        srcMat.at<double>(2,0) = 1.0;
        
        cv::Mat warpMat = H * srcMat;
        cv::Point warpPt;
        warpPt.x = cvRound(warpMat.at<double>(0,0)/warpMat.at<double>(2,0));
        warpPt.y = cvRound(warpMat.at<double>(1,0)/warpMat.at<double>(2,0));

        vWarpPtsImg2.push_back(warpPt);
    }
    //计算图像1和转换后的图像2的交点
    if(!PolygonClip(vSrcPtsImg1,vWarpPtsImg2,vPtsImg1))
        return false;

    for (int i = 0;i < vPtsImg1.size();i++)
    {
        cv::Mat srcMat = Mat::zeros(3,1,CV_64FC1);
        srcMat.at<double>(0,0) = vPtsImg1[i].x;
        srcMat.at<double>(1,0) = vPtsImg1[i].y;
        srcMat.at<double>(2,0) = 1.0;

        cv::Mat warpMat = H.inv() * srcMat;
        cv::Point warpPt;
        warpPt.x = cvRound(warpMat.at<double>(0,0)/warpMat.at<double>(2,0));
        warpPt.y = cvRound(warpMat.at<double>(1,0)/warpMat.at<double>(2,0));
        vPtsImg2.push_back(warpPt);
    }
    return true;
}

其中,多边形求交集可参考:http://www.cnblogs.com/dwdxdy/p/3232110.html

最终,程序运行的示意图如下:

计算两幅图像的重叠区域