• 【OpenCV图像处理】 图像拼接技术


    目录

    一:图像拼接技术 需求分析

    二:图像拼接技术 具体步骤

    三:图像拼接技术 代码实现

    四:多张图像拼接  Stitcher算法


    一:图像拼接技术 需求分析

    将下面两张图像进行拼接

     

    拼接得到一张完整的图像

    二:图像拼接技术 具体步骤

    1.选择特征点

    1. //1、选择特征点
    2. //左图 右图 识别特征点 是Mat对象 用c d保存
    3. surf->detectAndCompute(left,Mat(),key2,d);
    4. surf->detectAndCompute(right,Mat(),key1,c);
    5. //特征点对比,保存 特征点为中心点区域比对
    6. vector matches;
    7. matcher.match(d,c,matches);
    8. //排序从小到大 找到特征点连线
    9. sort(matches.begin(),matches.end());

    2.保存最优的特征点对象

    1. //2、保存最优的特征点对象
    2. vectorgood_matches;
    3. int ptrpoint = std::min(50,(int)(matches.size()*0.15));
    4. for (int i = 0;i < ptrpoint;i++)
    5. {
    6. good_matches.push_back(matches[i]);
    7. }
    8. //2-1、画线 最优的特征点对象连线
    9. Mat outimg;
    10. drawMatches(left,key2,right,key1,good_matches,outimg,
    11. Scalar::all(-1),Scalar::all(-1),
    12. vector<char>(),DrawMatchesFlags::NOT_DRAW_SINGLE_POINTS);
    13. //imshow("outimg",outimg);

    3.特征点匹配

    1. //3、特征点匹配
    2. vectorimagepoint1,imagepoint2;
    3. for (int i= 0 ;i < good_matches.size();i++)
    4. {
    5. //查找特征点可连接处 变形
    6. imagepoint1.push_back(key1[good_matches[i].trainIdx].pt);
    7. //查找特征点可连接处 查找基准线
    8. imagepoint2.push_back(key2[good_matches[i].queryIdx].pt);
    9. }

    4.透视转换 图像融合

    1. //4、透视转换 图形融合
    2. Mat homo = findHomography(imagepoint1,imagepoint2,CV_RANSAC);
    3. //imshow("homo",homo);
    4. //根据透视转换矩阵进行计算 四个坐标
    5. CalcCorners(homo,right);
    6. //接收透视转换结果
    7. Mat imageTransForm;
    8. //透视转换
    9. warpPerspective(right,imageTransForm,homo,
    10. Size(MAX(corners.right_top.x,corners.right_bottom.x),left.rows));
    11. //右图透视变换 由于本次图片材料是自己截图拼接的 因此看不出透视变换的明显特征
    12. //imshow("imageTransForm",imageTransForm);
    13. //结果进行整合
    14. int dst_width = imageTransForm.cols;
    15. int dst_height = left.rows;
    16. Mat dst(dst_height,dst_width,CV_8UC3);
    17. dst.setTo(0);
    18. imageTransForm.copyTo(dst(Rect(0,0,imageTransForm.cols,imageTransForm.rows)));
    19. left.copyTo(dst(Rect(0,0,left.cols,left.rows)));

    右图的透视转换,由于图像材料是自己截图拼接的,因此看不出透视变换的明显特征,但根据上图可知已经做出透视变换图像处理操作

    左图与右图的透视转换结果 拼接 【这里只是将窗口移动测试看下前面步骤是否正确】

    可以看出左图与右图的透视转换结果 是可以进行接下来的图像融合操作的

    5.优化图像 进行最终的结果展示

    1. //5、优化图像
    2. OptimizeSeam(left,imageTransForm,dst);
    3. //最终图像拼接结果
    4. imshow("dst",dst);

    可以看出 顺利完成 两张图像拼接的图像处理操作 

    三:图像拼接技术 代码实现

    1. #include
    2. #include
    3. #include //图像融合
    4. #include //拼接算法
    5. #include
    6. #include
    7. using namespace std;
    8. using namespace cv;
    9. using namespace cv::xfeatures2d;
    10. typedef struct
    11. {
    12. Point2f left_top;
    13. Point2f left_bottom;
    14. Point2f right_top;
    15. Point2f right_bottom;
    16. }four_corners_t;
    17. four_corners_t corners;
    18. void CalcCorners(const Mat& H, const Mat& src)
    19. {
    20. double v2[] = { 0, 0, 1 };//左上角
    21. double v1[3];//变换后的坐标值
    22. Mat V2 = Mat(3, 1, CV_64FC1, v2); //列向量
    23. Mat V1 = Mat(3, 1, CV_64FC1, v1); //列向量
    24. V1 = H * V2;
    25. //左上角(0,0,1)
    26. cout << "V2: " << V2 << endl;
    27. cout << "V1: " << V1 << endl;
    28. corners.left_top.x = v1[0] / v1[2];
    29. corners.left_top.y = v1[1] / v1[2];
    30. //左下角(0,src.rows,1)
    31. v2[0] = 0;
    32. v2[1] = src.rows;
    33. v2[2] = 1;
    34. V2 = Mat(3, 1, CV_64FC1, v2); //列向量
    35. V1 = Mat(3, 1, CV_64FC1, v1); //列向量
    36. V1 = H * V2;
    37. corners.left_bottom.x = v1[0] / v1[2];
    38. corners.left_bottom.y = v1[1] / v1[2];
    39. //右上角(src.cols,0,1)
    40. v2[0] = src.cols;
    41. v2[1] = 0;
    42. v2[2] = 1;
    43. V2 = Mat(3, 1, CV_64FC1, v2); //列向量
    44. V1 = Mat(3, 1, CV_64FC1, v1); //列向量
    45. V1 = H * V2;
    46. corners.right_top.x = v1[0] / v1[2];
    47. corners.right_top.y = v1[1] / v1[2];
    48. //右下角(src.cols,src.rows,1)
    49. v2[0] = src.cols;
    50. v2[1] = src.rows;
    51. v2[2] = 1;
    52. V2 = Mat(3, 1, CV_64FC1, v2); //列向量
    53. V1 = Mat(3, 1, CV_64FC1, v1); //列向量
    54. V1 = H * V2;
    55. corners.right_bottom.x = v1[0] / v1[2];
    56. corners.right_bottom.y = v1[1] / v1[2];
    57. }
    58. //图像融合的去裂缝处理操作
    59. void OptimizeSeam(Mat& img1, Mat& trans, Mat& dst)
    60. {
    61. int start = MIN(corners.left_top.x, corners.left_bottom.x);//开始位置,即重叠区域的左边界
    62. double processWidth = img1.cols - start;//重叠区域的宽度
    63. int rows = dst.rows;
    64. int cols = img1.cols; //注意,是列数*通道数
    65. double alpha = 1;//img1中像素的权重
    66. for (int i = 0; i < rows; i++)
    67. {
    68. uchar* p = img1.ptr(i); //获取第i行的首地址
    69. uchar* t = trans.ptr(i);
    70. uchar* d = dst.ptr(i);
    71. for (int j = start; j < cols; j++)
    72. {
    73. //如果遇到图像trans中无像素的黑点,则完全拷贝img1中的数据
    74. if (t[j * 3] == 0 && t[j * 3 + 1] == 0 && t[j * 3 + 2] == 0)
    75. {
    76. alpha = 1;
    77. }
    78. else
    79. {
    80. //img1中像素的权重,与当前处理点距重叠区域左边界的距离成正比,实验证明,这种方法确实好
    81. alpha = (processWidth - (j - start)) / processWidth;
    82. }
    83. d[j * 3] = p[j * 3] * alpha + t[j * 3] * (1 - alpha);
    84. d[j * 3 + 1] = p[j * 3 + 1] * alpha + t[j * 3 + 1] * (1 - alpha);
    85. d[j * 3 + 2] = p[j * 3 + 2] * alpha + t[j * 3 + 2] * (1 - alpha);
    86. }
    87. }
    88. }
    89. int main()
    90. {
    91. //左图
    92. Mat left = imread("D:/00000000000003jieduanshipincailliao/a1.png");
    93. //右图
    94. Mat right = imread("D:/00000000000003jieduanshipincailliao/a2.png");
    95. //左右图显示
    96. imshow("left",left);
    97. imshow("right",right);
    98. //创建SURF对象
    99. Ptr surf;
    100. //create 函数参数 海森矩阵阀值 800特征点以内
    101. surf = SURF::create(800);
    102. //创建一个暴力匹配器 用于特征点匹配
    103. BFMatcher matcher;
    104. //特征点容器 存放特征点KeyPoint
    105. vectorkey1,key2;
    106. //保存特征点
    107. Mat c,d;
    108. //1、选择特征点
    109. //左图 右图 识别特征点 是Mat对象 用c d保存
    110. surf->detectAndCompute(left,Mat(),key2,d);
    111. surf->detectAndCompute(right,Mat(),key1,c);
    112. //特征点对比,保存 特征点为中心点区域比对
    113. vector matches;
    114. matcher.match(d,c,matches);
    115. //排序从小到大 找到特征点连线
    116. sort(matches.begin(),matches.end());
    117. //2、保存最优的特征点对象
    118. vectorgood_matches;
    119. int ptrpoint = std::min(50,(int)(matches.size()*0.15));
    120. for (int i = 0;i < ptrpoint;i++)
    121. {
    122. good_matches.push_back(matches[i]);
    123. }
    124. //2-1、画线 最优的特征点对象连线
    125. Mat outimg;
    126. drawMatches(left,key2,right,key1,good_matches,outimg,
    127. Scalar::all(-1),Scalar::all(-1),
    128. vector<char>(),DrawMatchesFlags::NOT_DRAW_SINGLE_POINTS);
    129. //imshow("outimg",outimg);
    130. //3、特征点匹配
    131. vectorimagepoint1,imagepoint2;
    132. for (int i= 0 ;i < good_matches.size();i++)
    133. {
    134. //查找特征点可连接处 变形
    135. imagepoint1.push_back(key1[good_matches[i].trainIdx].pt);
    136. //查找特征点可连接处 查找基准线
    137. imagepoint2.push_back(key2[good_matches[i].queryIdx].pt);
    138. }
    139. //4、透视转换 图形融合
    140. Mat homo = findHomography(imagepoint1,imagepoint2,CV_RANSAC);
    141. //imshow("homo",homo);
    142. //根据透视转换矩阵进行计算 四个坐标
    143. CalcCorners(homo,right);
    144. //接收透视转换结果
    145. Mat imageTransForm;
    146. //透视转换
    147. warpPerspective(right,imageTransForm,homo,
    148. Size(MAX(corners.right_top.x,corners.right_bottom.x),left.rows));
    149. //右图透视变换 由于本次图片材料是自己截图拼接的 因此看不出透视变换的明显特征
    150. //imshow("imageTransForm",imageTransForm);
    151. //结果进行整合
    152. int dst_width = imageTransForm.cols;
    153. int dst_height = left.rows;
    154. Mat dst(dst_height,dst_width,CV_8UC3);
    155. dst.setTo(0);
    156. imageTransForm.copyTo(dst(Rect(0,0,imageTransForm.cols,imageTransForm.rows)));
    157. left.copyTo(dst(Rect(0,0,left.cols,left.rows)));
    158. //5、优化图像
    159. OptimizeSeam(left,imageTransForm,dst);
    160. //最终图像拼接结果
    161. imshow("dst",dst);
    162. waitKey(0);
    163. return 0;
    164. }

    四:多张图像拼接  Stitcher算法

    如下四张图片拼接,可使用Stitcher算法【多张图片拼接】

    完整代码如下:

    1. #include
    2. #include
    3. #include //图像融合
    4. #include //拼接算法
    5. #include
    6. #include
    7. using namespace std;
    8. using namespace cv;
    9. using namespace cv::xfeatures2d;
    10. void example()
    11. {
    12. Mat img1 = imread("D:/00000000000003jieduanshipincailliao/b1.png");
    13. Mat img2 = imread("D:/00000000000003jieduanshipincailliao/b2.png");
    14. Mat img3 = imread("D:/00000000000003jieduanshipincailliao/b3.png");
    15. Mat img4 = imread("D:/00000000000003jieduanshipincailliao/b4.png");
    16. imshow("img1",img1);
    17. imshow("img2",img2);
    18. imshow("img3",img3);
    19. imshow("img4",img4);
    20. //带顺序容器vector
    21. vectorimages;
    22. images.push_back(img1);
    23. images.push_back(img2);
    24. images.push_back(img3);
    25. images.push_back(img4);
    26. //用来保存最终拼接图
    27. Mat result;
    28. //false 不使用GPU加速
    29. Stitcher sti = Stitcher::createDefault(false);
    30. //将向量容器中所有的图片按照顺序进行拼接,结果保存在result中
    31. Stitcher::Status sta = sti.stitch(images,result);
    32. if(sta != Stitcher::OK)
    33. {
    34. cout<<"canot Stitcher"<
    35. }
    36. imshow("result",result);
    37. waitKey(0);
    38. }
    39. int main()
    40. {
    41. example();
    42. return 0;
    43. }

  • 相关阅读:
    阿里云 / 京东 / 百度等 20 多家大厂 Java 面试上千道内卷真题
    2.4 Sample Moments and Hypothesis Tests
    编程机器人的参数表怎么看
    【Reinforcement Learning】强化学习基础内容有哪些?
    常见的设计模式
    sourceTree中的git rebase变基操作
    LeetCode高频题:设计一个股票推荐系统,自动根据注册用户的关注情况进行推荐,询问时,会推荐那个人多少只他还没有关注的股票
    聊聊SpringBootTest的webEnvironment
    使用.Net6中的System.Text.Json遇到几个常见问题及解决方案
    免费低代码平台,助企业高效管理任务
  • 原文地址:https://blog.csdn.net/m0_56051805/article/details/126145826