利用C++中的opencv进行图像拼接

这篇文章依旧是记录采用C++复现图像拼接过程解决遇到的问题。因为自己没有学过C++,大学学的C考完试立马还给老师了,Python也是现学的,只会一点点MATLAB,所以遇到的问题和解决都很基础,目的是自己做一些记录,并分享给与我相同的图像入门小白,若有错误还希望大佬们指正。

一、拼接方法介绍

拼接的主要参考文献:

OpenCV常用图像拼接方法(二):基于模板匹配拼接_Color Space的博客-CSDN博客_图像拼接素材

图像拼接的程序的目的是将imgL(1)与imgR(2)拼接起来,方法是通过截取imgL中的一小部分特征区域与imgR的图像进行比对,找到最相似的区域坐标,以此作为拼接的联系坐标,将两幅图拼接起来(大白话就是将两图最相似的区域对齐叠起来达到拼接效果。)

利用C++中的opencv进行图像拼接
  Mat imgL = imread("A.jpg");
  Mat imgR = imread("B.jpg");
  double start = getTickCount();
  Mat grayL, grayR;
  cvtColor(imgL, grayL, COLOR_BGR2GRAY);
  cvtColor(imgR, grayR, COLOR_BGR2GRAY);

  Rect rectCut = Rect(372, 122, 128, 360);
  Rect rectMatched = Rect(0, 0, imgR.cols / 2, imgR.rows);
  Mat imgTemp = grayL(Rect(rectCut));
  Mat imgMatched = grayR(Rect(rectMatched));

  int width = imgMatched.cols - imgTemp.cols + 1;
  int height = imgMatched.rows - imgTemp.rows + 1;
  Mat matchResult(height, width, CV_32FC1);
  matchTemplate(imgMatched, imgTemp, matchResult, TM_CCORR_NORMED);
  normalize(matchResult, matchResult, 0, 1, NORM_MINMAX, -1);  //归一化到0--1范围

  double minValue, maxValue;
  Point minLoc, maxLoc;
  minMaxLoc(matchResult, &minValue, &maxValue, &minLoc, &maxLoc);

  Mat dstImg(imgL.rows, imgR.cols + rectCut.x - maxLoc.x, CV_8UC3, Scalar::all(0));
  Mat roiLeft = dstImg(Rect(0, 0, imgL.cols, imgL.rows));
  imgL.copyTo(roiLeft);

  Mat debugImg = imgR.clone();
  rectangle(debugImg, Rect(maxLoc.x, maxLoc.y, imgTemp.cols, imgTemp.rows), Scalar(0, 255, 0), 2, 8);
  imwrite("match.jpg", debugImg);

  Mat roiMatched = imgR(Rect(maxLoc.x, maxLoc.y - rectCut.y, imgR.cols - maxLoc.x, imgR.rows - 1 - (maxLoc.y - rectCut.y)));
  Mat roiRight = dstImg(Rect(rectCut.x, 0, roiMatched.cols, roiMatched.rows));

  roiMatched.copyTo(roiRight);

  double end = getTickCount();
  double useTime = (end - start) / getTickFrequency();
  cout << "use-time : " << useTime << "s" << endl;

  imwrite("dst.jpg", dstImg);
  cout << "Done!" << endl;

基础背景说明

1、首先这个程序是 C++程序,不是Python程序,我在Python中跑半天是不是很令人震惊。

我在Visual studio中创建一个C++的空项目进行复现(VS如何配置opencv环境以及创建空项目我后面估计会再分享一个博客记录说明,因为我自己也老忘,好麻烦呀,反观MATLAB好香)

写好啦:Visual Studio中设置opencv环境_日拱一卒不慌忙的博客-CSDN博客

2、其次上面这个函数是一个子函数的内容,直接复制到VS中是不能运行的,需要加入一些头文件,并把函数放进主函数中才能运行。

#include <windows.h>
#include <opencv2\opencv.hpp>
#include <iostream>
using namespace std;
using namespace cv;

int main()
{
    //1&#x3001;&#x8BFB;&#x53D6;&#x56FE;&#x50CF;&#xFF0C;&#x5E76;&#x5C06;RGB&#x56FE;&#x50CF;&#x8F6C;&#x6362;&#x4E3A;&#x7070;&#x5EA6;&#x56FE;
    Mat imgL = imread("C:\\Users\\AC\\Desktop\\A.jpg");
    Mat imgR = imread("C:\\Users\\AC\\Desktop\\B.jpg");
    double start = getTickCount();   //&#x8BA1;&#x65F6;&#x5F00;&#x59CB;
    Mat grayL, grayR;
    cvtColor(imgL, grayL, COLOR_BGR2GRAY);
    cvtColor(imgR, grayR, COLOR_BGR2GRAY);

    //2&#x3001;&#x5728;&#x5DE6;&#x56FE;&#x4E2D;&#x622A;&#x53D6;&#x4E00;&#x5C0F;&#x5757;&#x7279;&#x5F81;&#x533A;&#x57DF;&#x4F5C;&#x4E3A;&#x5339;&#x914D;&#x6A21;&#x677F;imgtemp
    Rect Rectcut = Rect(150, 150, 50, 50);
    int widthR = Rectcut.x;
    int heightR = Rectcut.y;
    Rect Rectmatched = Rect(0, 0, imgR.cols / 2, imgR.rows);
    Mat imgtemp = grayL(Rect(Rectcut));
    Mat imgmatched = grayR(Rect(Rectmatched));

    //3&#x3001;&#x91C7;&#x7528;&#x7279;&#x5F81;&#x6A21;&#x677F;&#x5728;&#x53F3;&#x56FE;&#x4E2D;&#x8FDB;&#x884C;&#x76F8;&#x4F3C;&#x6BD4;&#x5BF9;&#xFF0C;&#x627E;&#x5230;&#x6700;&#x76F8;&#x4F3C;&#x7684;&#x533A;&#x57DF;&#x4F4D;&#x7F6E;&#x5750;&#x6807;maxLoc
    int width = imgmatched.cols - imgtemp.cols + 1;
    int height = imgmatched.rows - imgtemp.rows + 1;
    Mat matchResult(height, width, CV_32FC1);  //CV_32 32&#x6BD4;&#x7279;&#xFF0C;F&#x4EE3;&#x8868;&#x5355;&#x7CBE;&#x5EA6;&#x6D6E;&#x70B9;&#x578B;&#xFF0C;C1&#x5355;&#x901A;&#x9053;&#x56FE;&#x50CF;&#xFF0C;2&#x4E09;&#x901A;&#x9053;&#xFF0C;3&#x56DB;&#x901A;&#x9053;
    matchTemplate(imgmatched, imgtemp, matchResult, TM_CCORR_NORMED);
    normalize(matchResult, matchResult, 0, 1, NORM_MINMAX, -1);  //&#x5F52;&#x4E00;&#x5316;&#x5230;0--1&#x8303;&#x56F4;
    double minValue, maxValue;
    Point minLoc, maxLoc;
    minMaxLoc(matchResult, &minValue, &maxValue, &minLoc, &maxLoc);

    //4&#x3001;&#x521B;&#x5EFA;&#x4E00;&#x4E2A;&#x5927;&#x7684;&#x7A7A;&#x56FE;&#xFF0C;&#x5148;&#x5C06;&#x5DE6;&#x56FE;&#x590D;&#x5236;&#x4E0A;&#x53BB;&#xFF0C;&#x518D;&#x5C06;&#x53F3;&#x56FE;&#x6839;&#x636E;&#x91CD;&#x53E0;&#x533A;&#x57DF;&#x4F4D;&#x7F6E;&#x5750;&#x6807;&#x622A;&#x5207;&#x540E;&#x590D;&#x5236;&#x5230;&#x5927;&#x56FE;&#x4E2D;&#xFF0C;&#x5F62;&#x6210;&#x6700;&#x7EC8;&#x7684;&#x62FC;&#x63A5;&#x56FE;&#x50CF;dstImg
    Mat dstImg(imgL.rows, imgR.cols + Rectcut.x - maxLoc.x, CV_8UC3, Scalar::all(0));
    Mat roiLeft = dstImg(Rect(0, 0, imgL.cols, imgL.rows));
    Mat debugImg = imgR.clone();
    rectangle(debugImg, Rect(maxLoc.x, maxLoc.y, imgtemp.cols, imgtemp.rows), Scalar(0, 255, 0), 2, 8);
    imwrite("match.jpg", debugImg);

    Mat roiMatched = imgR(Rect(maxLoc.x, maxLoc.y - Rectcut.y, imgR.cols - maxLoc.x, imgR.rows - 1 - (maxLoc.y - Rectcut.y)));
    Mat roiRight = dstImg(Rect(Rectcut.x, 0, roiMatched.cols, roiMatched.rows));
    roiMatched.copyTo(roiRight);

    //5&#x3001;&#x5B58;&#x56FE;&#xFF0C;&#x8FD8;&#x6709;&#x4EEA;&#x5F0F;&#x611F;
    double end = getTickCount();    //&#x8BA1;&#x65F6;&#x7ED3;&#x675F;
    double useTime = (end - start) / getTickFrequency();
    cout << "use-time : " << useTime << "s" << endl;

    imshow("&#x56FE;&#x50CF;", dstImg);
    waitKey(0);
    cout << "Done!" << endl;
    return 0;
}
</iostream></opencv2\opencv.hpp></windows.h>

二、拼接程序分析

程序基础准备

首先我们拿到一个程序需要清楚程序的目的(在做什么),程序的流程(怎么做的),对应到程序各部分的功能(每一个part到底在做啥,我附注在上面每段的程序上了),有背景基础才能有助于对新的语言所表达的内容进行理解,要不然就陷入到一堆字母中啊?啊?啊?放弃X,我就是放弃了好多遍又捡回来的(讲道理的时候说得可好听啦…)。

各部分基础说明

1、 M at imgL=imread(“路径( 双斜杠 )”)

M要大写,路径间隔要是双斜杠或反斜杠,每句后要有小写的;

否则你会喜提一个感叹号,杠杠的!

利用C++中的opencv进行图像拼接

创建一个名称为imgL的空的mat数据矩阵存放读取进的图像,图像的每个像素的灰度值会被记录在矩阵的每个对应位置上,同理Mat grayL, grayR,也是创建了两个名称为grayL, grayR的空的数据矩阵存放一会RGB转换为灰度图的图像,cvtcolor是一个转换函数,具体细节百度一下。

    Mat imgL = imread("C:\\Users\\AC\\Desktop\\A.jpg");
    Mat imgR = imread("C:\\Users\\AC\\Desktop\\B.jpg");
    Mat grayL, grayR;
    cvtColor(imgL, grayL, COLOR_BGR2GRAY);
    cvtColor(imgR, grayR, COLOR_BGR2GRAY);

2、Rect类

Rect Rectcut = Rect(150, 150, 50, 50);// 定义一个名称为Rectcut的矩形,Rect括号里面分别表示这个矩形的左上角坐标,宽和高,同理下面的Rectmatched,不要试图翻译矩 矩切,矩 矩匹配,爱它没结果,要 注重函数的构成形式,名称不重要,意义也不重要,重要的是 理解函数实现了一个什么样功能的操作

3、int类,double类

哈哈哈,终于碰到我眼熟的了,这是定义一个名称为widthR的整型,将Rectcut.x的值或是计算的结果存储到widthR中。

int widthR = Rectcut.x

1. OpenCV 数据结构与基本绘图_zcx_xxx的博客-CSDN博客

    Rect Rectcut = Rect(150, 150, 50, 50);
    int widthR = Rectcut.x;
    int heightR = Rectcut.y;
    Rect Rectmatched = Rect(0, 0, imgR.cols / 2, imgR.rows);
    Mat imgtemp = grayL(Rect(Rectcut));
    Mat imgmatched = grayR(Rect(Rectmatched));

4、匹配函数matchTemplate

matchTemplate是函数的名称,()里面是其需要输入的参数变量。

首先需要理解,函数的功能,其次需要知道自己想要正确使用这个函数需要输入的正确对应参数。

譬如 MatchTemplate函数,是在一幅图像中寻找与另一幅模板图像最匹配(相似)部分。

利用C++中的opencv进行图像拼接

参数详解

MatchTemplate(InputArray image, InputArray templ, OutputArray result, int method);

image:输入一个待匹配的图像,支持8U或者32F。

templ:输入一个模板图像,与image相同类型。

result:输出保存结果的矩阵,32F类型。

method:要使用的数据比较方法。

MatchTemplate函数参考下面这个博文
【 OpenCV 】MatchTemplate函数参数详解及原理分析_Nick大帅仔的博客-CSDN博客_matchtemplate

同理normalize这个函数是用来归一化的,minMaxloc是用来寻找最大值和最小值的坐标位置(&a代表取到a的地址)。

归一化函数normalize详解 – 百度文库

OpenCV 找出图像中最小值最大值函数minMaxLoc的使用 – 灰信网(软件开发博客聚合)

    //3&#x3001;&#x91C7;&#x7528;&#x7279;&#x5F81;&#x6A21;&#x677F;&#x5728;&#x53F3;&#x56FE;&#x4E2D;&#x8FDB;&#x884C;&#x76F8;&#x4F3C;&#x6BD4;&#x5BF9;&#xFF0C;&#x627E;&#x5230;&#x6700;&#x76F8;&#x4F3C;&#x7684;&#x533A;&#x57DF;&#x4F4D;&#x7F6E;&#x5750;&#x6807;maxLoc
    int width = imgmatched.cols - imgtemp.cols + 1;
    int height = imgmatched.rows - imgtemp.rows + 1;
    Mat matchResult(height, width, CV_32FC1);  //CV_32 32&#x6BD4;&#x7279;&#xFF0C;F&#x4EE3;&#x8868;&#x5355;&#x7CBE;&#x5EA6;&#x6D6E;&#x70B9;&#x578B;&#xFF0C;C1&#x5355;&#x901A;&#x9053;&#x56FE;&#x50CF;&#xFF0C;2&#x4E09;&#x901A;&#x9053;&#xFF0C;3&#x56DB;&#x901A;&#x9053;
    matchTemplate(imgmatched, imgtemp, matchResult, TM_CCORR_NORMED);
    normalize(matchResult, matchResult, 0, 1, NORM_MINMAX, -1);  //&#x5F52;&#x4E00;&#x5316;&#x5230;0--1&#x8303;&#x56F4;
    double minValue, maxValue;
    Point minLoc, maxLoc;
    minMaxLoc(matchResult, &minValue, &maxValue, &minLoc, &maxLoc);

5、拼图(浅拷贝和深拷贝)

    //4&#x3001;&#x521B;&#x5EFA;&#x4E00;&#x4E2A;&#x5927;&#x7684;&#x7A7A;&#x56FE;&#xFF0C;&#x5148;&#x5C06;&#x5DE6;&#x56FE;&#x590D;&#x5236;&#x4E0A;&#x53BB;&#xFF0C;&#x518D;&#x5C06;&#x53F3;&#x56FE;&#x6839;&#x636E;&#x91CD;&#x53E0;&#x533A;&#x57DF;&#x4F4D;&#x7F6E;&#x5750;&#x6807;&#x622A;&#x5207;&#x540E;&#x590D;&#x5236;&#x5230;&#x5927;&#x56FE;&#x4E2D;&#xFF0C;&#x5F62;&#x6210;&#x6700;&#x7EC8;&#x7684;&#x62FC;&#x63A5;&#x56FE;&#x50CF;dstImg
    Mat dstImg(imgL.rows, imgR.cols + Rectcut.x - maxLoc.x, CV_8UC3, Scalar::all(0));
    Mat roiLeft = dstImg(Rect(0, 0, imgL.cols, imgL.rows));
    Mat debugImg = imgR.clone();
    rectangle(debugImg, Rect(maxLoc.x, maxLoc.y, imgtemp.cols, imgtemp.rows), Scalar(0, 255, 0), 2, 8);
    imwrite("match.jpg", debugImg);

    Mat roiMatched = imgR(Rect(maxLoc.x, maxLoc.y - Rectcut.y, imgR.cols - maxLoc.x, imgR.rows - 1 - (maxLoc.y - Rectcut.y)));
    Mat roiRight = dstImg(Rect(Rectcut.x, 0, roiMatched.cols, roiMatched.rows));
    roiMatched.copyTo(roiRight);

这里会涉及到拷贝的问题,就是我们要形成最后的拼接图像,必然要先从原来的两幅图中的信息提取出来,然后进行一些修剪,再将其组合起来。

首先数据传递和处理不能影响到原图imgL(深拷贝),其次分图roiLeft的数据的处理要体现在最后的大的拼接图dstImg中(浅拷贝)。

深拷贝:分配新内存的同时拷贝数据!当被赋值的容器被修改时,原始容器数据不会改变。
浅拷贝:仅拷贝数据!当被赋值容器修改时,原始容器数据也会做同样改变。(感觉和C++中引用同理)
深拷贝是 b = a.clone(); 和 a.copyTo(b);浅拷贝是 b = a;和 b(a);

OpenCV中cv::Mat的深拷贝 浅拷贝问题_verystory的博客-CSDN博客_cv::mat拷贝

三、个人心得

个人总结一点程序调试的心得,这部分还是希望有大佬们多多指点我。

我自己的方法是,首先清楚程序的目的,流程和各部分的功能后,调试程序用的是笨办法,就是从上到下逐步取消注释运行部分程序看有没有报错,前面的没有报错,代表的前面没有问题,后面新的问题一定是出在了新的取消注释的部分中,然后先根据错误列表中首先看格式有没有问题,格式没问题了,看函数是否使用合适,参数是否符合函数的输入参数要求,譬如最后拼接出问题就是由于坐标值计算结果为负导致报错。

暂时就这么多啦,感谢在解决问题过程中各位大佬的帮助,如果我的回答帮助到你,还麻烦给我点赞加油呀~~

Original: https://blog.csdn.net/MOZHOUH/article/details/124969002
Author: 日拱一卒不慌忙
Title: 利用C++中的opencv进行图像拼接

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/632445/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球