当前位置: 首页 > news >正文

图像拼接C++代码记录

一、介绍

   要实现图像拼接需要哪几步呢?简单来说有以下几步:

   Step1: 对每幅图进行特征点提取

   Step2: 对对特征点进行匹配

   Step3: 进行图像配准

   Step4: 把图像拷贝到另一幅图像的特定位置

   Step4: 对重叠边界进行特殊处理(融合)

二、代码

    基于Surf的特征点检测

#include "highgui/highgui.hpp"    
#include "opencv2/nonfree/nonfree.hpp"    
#include "opencv2/legacy/legacy.hpp"   
#include <iostream>  using namespace cv;
using namespace std;void OptimizeSeam(Mat& img1, Mat& trans, Mat& dst);Rect CalculateRoI(Mat& imageTransform, Mat& image02);typedef struct
{Point2f left_top;Point2f left_bottom;Point2f right_top;Point2f right_bottom;
}four_corners_t;four_corners_t corners;void CalcCorners(const Mat& H, const Mat& src)
{// 左上角(0,0,1)double v2[] = { 0, 0, 1 };         double v1[3];Mat V2 = Mat(3, 1, CV_64FC1, v2);Mat V1 = Mat(3, 1, CV_64FC1, v1);V1 = H * V2;corners.left_top.x = v1[0] / v1[2];corners.left_top.y = v1[1] / v1[2];// 左下角(0,src.rows,1)v2[0] = 0;v2[1] = src.rows;v2[2] = 1;V2 = Mat(3, 1, CV_64FC1, v2); V1 = Mat(3, 1, CV_64FC1, v1);V1 = H * V2;corners.left_bottom.x = v1[0] / v1[2];corners.left_bottom.y = v1[1] / v1[2];// 右上角(src.cols,0,1)v2[0] = src.cols;v2[1] = 0;v2[2] = 1;V2 = Mat(3, 1, CV_64FC1, v2); V1 = Mat(3, 1, CV_64FC1, v1); V1 = H * V2;corners.right_top.x = v1[0] / v1[2];corners.right_top.y = v1[1] / v1[2];// 右下角(src.cols,src.rows,1)v2[0] = src.cols;v2[1] = src.rows;v2[2] = 1;V2 = Mat(3, 1, CV_64FC1, v2);V1 = Mat(3, 1, CV_64FC1, v1);V1 = H * V2;corners.right_bottom.x = v1[0] / v1[2];corners.right_bottom.y = v1[1] / v1[2];
}int main()
{Mat image01 = imread("imgs//right01.png", 1);  //右图Mat image02 = imread("imgs//left01.png", 1);  //左图imshow("p1", image01);imshow("p2", image02);//灰度图转换  Mat gray1, gray2;cvtColor(image01, gray1, CV_RGB2GRAY);cvtColor(image02, gray2, CV_RGB2GRAY);//提取特征点    SurfFeatureDetector Detector(200);vector<KeyPoint> keyPoint1, keyPoint2;Detector.detect(gray1, keyPoint1);Detector.detect(gray2, keyPoint2);//特征点描述 SurfDescriptorExtractor Descriptor;Mat imageDesc1, imageDesc2;Descriptor.compute(gray1, keyPoint1, imageDesc1);Descriptor.compute(gray2, keyPoint2, imageDesc2);//特征点匹配FlannBasedMatcher matcher;vector<vector<DMatch> > matchePoints;vector<Mat> train_desc(1, imageDesc1);matcher.add(train_desc);matcher.train();matcher.knnMatch(imageDesc2, matchePoints, 2);cout << "total match points: " << matchePoints.size() << endl;// Lowe's algorithm,获取优秀匹配点vector<DMatch> GoodMatchePoints;for (int i = 0; i < matchePoints.size(); i++){if (matchePoints[i][0].distance < 0.4 * matchePoints[i][1].distance){GoodMatchePoints.push_back(matchePoints[i][0]);}}Mat first_match;drawMatches(image02, keyPoint2, image01, keyPoint1, GoodMatchePoints, first_match);imshow("first_match", first_match);imwrite("first_match.jpg", first_match);vector<Point2f> imagePoints1, imagePoints2;for (int i = 0; i < GoodMatchePoints.size(); i++){imagePoints2.push_back(keyPoint2[GoodMatchePoints[i].queryIdx].pt);imagePoints1.push_back(keyPoint1[GoodMatchePoints[i].trainIdx].pt);}// 获取图像1到图像2的投影映射矩阵,尺寸为3*3  Mat homo = findHomography(imagePoints1, imagePoints2, CV_RANSAC);for (int i = 0; i < homo.rows; i++){cout << "投影映射矩阵第" << i << "行: "<< static_cast<unsigned>(homo.at<uchar>(i, 0)) << " "<< static_cast<unsigned>(homo.at<uchar>(i, 1)) << " "<< static_cast<unsigned>(homo.at<uchar>(i, 2)) << endl;}// 计算配准图的四个顶点坐标CalcCorners(homo, image01);cout << "left_top:    " << corners.left_top << endl;cout << "left_bottom: " << corners.left_bottom << endl;cout << "right_top:   " << corners.right_top << endl;cout << "right_bottom:" << corners.right_bottom << endl;// 图像配准  Mat imageTransform;warpPerspective(image01, imageTransform, homo, Size(MAX(corners.right_top.x, corners.right_bottom.x), image02.rows));imshow("trans1", imageTransform);imwrite("trans1.jpg", imageTransform);// 计算裁剪ROIRect RoI = CalculateRoI(imageTransform, image02);// 创建拼接后的图,需提前计算图的大小int dst_width = imageTransform.cols;int dst_height = imageTransform.rows;// 直接拼接Mat dst(dst_height, dst_width, CV_8UC3);dst.setTo(0);imageTransform.copyTo(dst(Rect(0, 0, imageTransform.cols, imageTransform.rows)));image02.copyTo(dst(Rect(0, 0, image02.cols, image02.rows)));imshow("b_dst", dst);imwrite("b_dst.jpg", dst);// 拼接处融合+边缘裁剪处理OptimizeSeam(image02, imageTransform, dst);Mat dst_crop = dst(RoI);imshow("dst_crop", dst_crop);imwrite("dst_crop.jpg", dst_crop);waitKey();return 0;
}void OptimizeSeam(Mat& img1, Mat& trans, Mat& dst)
{int rows = img1.rows;int cols = img1.cols;int start = MIN(corners.left_top.x, corners.left_bottom.x);double processWidth = cols - start;double alpha = 1;for (int i = 0; i < rows; i++){uchar* p = img1.ptr<uchar>(i);uchar* t = trans.ptr<uchar>(i);uchar* d = dst.ptr<uchar>(i);int label = 0;int begin = start;double wid = processWidth;for (int j = start; j < cols; j++){if (t[j * 3] == 0 && t[j * 3 + 1] == 0 && t[j * 3 + 2] == 0){// 如果遇到图像trans中无像素的黑点,则完全拷贝img1中的数据alpha = 1;}else{// 找到trans的每行第一个非0的位置if (label == 0){label = 1;begin = j;wid = cols - begin;}// img1中像素的权重,与当前处理点距重叠区域左边界的距离成正比// alpha = (processWidth - j + start) / processWidth;alpha = (wid - j + begin) / wid;}d[j * 3] = p[j * 3] * alpha + t[j * 3] * (1 - alpha);d[j * 3 + 1] = p[j * 3 + 1] * alpha + t[j * 3 + 1] * (1 - alpha);d[j * 3 + 2] = p[j * 3 + 2] * alpha + t[j * 3 + 2] * (1 - alpha);}}
}Rect CalculateRoI(Mat & imageTransform, Mat & image02)
{// 计算变换投影图像的有效区域Mat maskROI = Mat::zeros(imageTransform.size(), CV_8UC1);vector<Point> poly;poly.push_back(corners.left_top);poly.push_back(corners.right_top);poly.push_back(corners.right_bottom);poly.push_back(corners.left_bottom);vector<vector<Point>> polys;polys.push_back(poly);fillPoly(maskROI, polys, Scalar(255));// 计算拼接后图像的有效RoIMat maskROI2 = Mat::zeros(imageTransform.size(), CV_8UC1);maskROI2(Rect(0, 0, image02.cols, image02.rows)) = 255;bitwise_or(maskROI2, maskROI, maskROI2);bitwise_not(maskROI2, maskROI2);vector<vector<Point>> conts1;findContours(maskROI2, conts1, cv::RETR_TREE, cv::CHAIN_APPROX_SIMPLE);Rect RoI = Rect(0, 0, maskROI2.cols, maskROI2.rows);for (int i = 0; i < conts1.size(); i++){Rect rect = boundingRect(conts1[i]);cout << rect.x << " " << rect.y << " " << rect.x + rect.width << " " << rect.y + rect.height << endl;if (rect.width < rect.height){RoI.width = rect.x - 1;}else if (rect.y <= 1){RoI.y = rect.height + rect.y + 1;RoI.height -= rect.height + rect.y + 1;}else{RoI.height -= rect.height;}}return RoI;
}

    基于Sift的特征点检测

int main()
{Mat image01 = imread("1.png", 1);  // 右边Mat image02 = imread("2.png", 1);  // 左边imshow("p1", image01);imshow("p2", image02);Mat image1, image2;cvtColor(image01, image1, CV_RGB2GRAY);cvtColor(image02, image2, CV_RGB2GRAY);// 提取特征点    SiftFeatureDetector siftDetector(800);  // 海塞矩阵阈值,调整精度,值越大点越少,越精准 vector<KeyPoint> keyPoint1, keyPoint2;siftDetector.detect(image1, keyPoint1);siftDetector.detect(image2, keyPoint2);// 特征点描述 SiftDescriptorExtractor SiftDescriptor;Mat imageDesc1, imageDesc2;SiftDescriptor.compute(image1, keyPoint1, imageDesc1);SiftDescriptor.compute(image2, keyPoint2, imageDesc2);// 特征点匹配FlannBasedMatcher matcher;vector<vector<DMatch> > matchePoints;vector<Mat> train_desc(1, imageDesc1);matcher.add(train_desc);matcher.train();matcher.knnMatch(imageDesc2, matchePoints, 2);cout << "total match points: " << matchePoints.size() << endl;// Lowe's algorithm,获取优秀匹配点vector<DMatch> GoodMatchePoints;for (int i = 0; i < matchePoints.size(); i++){if (matchePoints[i][0].distance < 0.6 * matchePoints[i][1].distance){GoodMatchePoints.push_back(matchePoints[i][0]);}}Mat first_match;drawMatches(image02, keyPoint2, image01, keyPoint1, GoodMatchePoints, first_match);imshow("first_match ", first_match);imwrite("first_match.jpg", first_match);return 0;
}

    基于Orb的特征点检测

int main()
{Mat image01 = imread("1.png", 1);  //右图Mat image02 = imread("2.png", 1);  //左图imshow("p1", image01);imshow("p2", image02);Mat image1, image2;cvtColor(image01, image1, CV_RGB2GRAY);cvtColor(image02, image2, CV_RGB2GRAY);// 提取特征点    OrbFeatureDetector OrbDetector(3000);vector<KeyPoint> keyPoint1, keyPoint2;OrbDetector.detect(image1, keyPoint1);OrbDetector.detect(image2, keyPoint2);// 特征点描述  OrbDescriptorExtractor OrbDescriptor;Mat imageDesc1, imageDesc2;OrbDescriptor.compute(image1, keyPoint1, imageDesc1);OrbDescriptor.compute(image2, keyPoint2, imageDesc2);flann::Index flannIndex(imageDesc1, flann::LshIndexParams(12, 20, 2), cvflann::FLANN_DIST_HAMMING);Mat macthIndex(imageDesc2.rows, 2, CV_32SC1), matchDistance(imageDesc2.rows, 2, CV_32FC1);flannIndex.knnSearch(imageDesc2, macthIndex, matchDistance, 2, flann::SearchParams());// Lowe's algorithm, 获取优秀匹配点vector<DMatch> GoodMatchePoints;for (int i = 0; i < matchDistance.rows; i++){if (matchDistance.at<float>(i, 0) < 0.4 * matchDistance.at<float>(i, 1)){DMatch dmatches(i, macthIndex.at<int>(i, 0), matchDistance.at<float>(i, 0));GoodMatchePoints.push_back(dmatches);}}Mat first_match;drawMatches(image02, keyPoint2, image01, keyPoint1, GoodMatchePoints, first_match);imshow("first_match ", first_match);imwrite("match.jpg", first_match);return 0;
}

    基于Fast的特征点检测

int main()
{Mat image01 = imread("1.png", 1);  // 右边Mat image02 = imread("2.png", 1);  // 左边imshow("p1", image01);imshow("p2", image02);// 灰度图转换  Mat image1, image2;cvtColor(image01, image1, CV_RGB2GRAY);cvtColor(image02, image2, CV_RGB2GRAY);// 提取特征点    FastFeatureDetector Detector(50);  // 阈值 vector<KeyPoint> keyPoint1, keyPoint2;Detector.detect(image1, keyPoint1);Detector.detect(image2, keyPoint2);// 特征点描述   SiftDescriptorExtractor Descriptor;Mat imageDesc1, imageDesc2;Descriptor.compute(image1, keyPoint1, imageDesc1);Descriptor.compute(image2, keyPoint2, imageDesc2);// 特征点匹配BruteForceMatcher< L2<float> > matcher;vector<vector<DMatch> > matchePoints;vector<Mat> train_desc(1, imageDesc1);matcher.add(train_desc);matcher.train();matcher.knnMatch(imageDesc2, matchePoints, 2);cout << "total match points: " << matchePoints.size() << endl;// Lowe's algorithm,获取优秀匹配点vector<DMatch> GoodMatchePoints;for (int i = 0; i < matchePoints.size(); i++){if (matchePoints[i][0].distance < 0.6 * matchePoints[i][1].distance){GoodMatchePoints.push_back(matchePoints[i][0]);}}Mat first_match;drawMatches(image02, keyPoint2, image01, keyPoint1, GoodMatchePoints, first_match);imshow("first_match ", first_match);imwrite("first_match.jpg", first_match);return 0;
}

基于Stitch的图像拼接

#include "opencv2/imgcodecs.hpp"
#include "opencv2/highgui.hpp"
#include "opencv2/stitching.hpp"
#include <iostream>using namespace std;
using namespace cv;int main()
{Mat img1 = imread("left1.png", cv::IMREAD_COLOR);Mat img2 = imread("right1.png", cv::IMREAD_COLOR);vector<Mat> imgs;imgs.push_back(img1);imgs.push_back(img2);Mat pano;Ptr<Stitcher> stitcher = Stitcher::create(Stitcher::PANORAMA);Stitcher::Status status = stitcher->stitch(imgs, pano);if (status != Stitcher::OK){cout << "Can't stitch images, error code = " << int(status) << endl;return EXIT_FAILURE;}string result_name = "result1.jpg";imwrite(result_name, pano);cout << "stitching completed successfully\n" << result_name << " saved!";return EXIT_SUCCESS;
}


http://www.mrgr.cn/news/25139.html

相关文章:

  • Sqlserver常用sql
  • Python画笔案例-045 绘制渐变圆盘
  • Openpose
  • 语义分割数据集|河流湖泊分割|水灾预警
  • Acrel-7000企业能源管控平台通用设备“源荷” 联动
  • Python 常用的GIS库
  • C++当中的多态(二)
  • 程易科技AI OS:赋能开发者,构建智慧未来
  • 技术接口:日志程序2
  • 在 Debian 12 上安装中文五笔输入法
  • 【LLM大模型】大模型架构:layer\_normalization
  • C++的类和对象(下)
  • Android SPN/PLMN 显示逻辑简介
  • Python 调用手机摄像头
  • 定制型制造企业数字化转型记:借力无代码开创商业新版图
  • Tensorflow 兼容性测试-opencloudos
  • Mongoose OverwriteModelError: Cannot overwrite `note` model once compiled.
  • 使用API有效率地管理Dynadot域名,查看域名服务器(NS)信息
  • 量化交易backtrader实践(一)_数据获取篇(3)_爬取数据
  • wifiip地址可以随便改吗?wifi的ip地址怎么改变