一、介紹
? ? ?圖像拼接.
二、分步實(shí)現(xiàn)
? ? ?要實(shí)現(xiàn)圖像拼接,簡(jiǎn)單來說有以下幾步:
- 對(duì)每幅圖進(jìn)行特征點(diǎn)提取
- 對(duì)對(duì)特征點(diǎn)進(jìn)行匹配
- 進(jìn)行圖像配準(zhǔn)
- 把圖像拷貝到另一幅圖像的特定位置
- 對(duì)重疊邊界進(jìn)行特殊處理
? ? ?PS:需要使用低版本的opencv,否則無法使用特征角點(diǎn)提取算子。
#include "highgui/highgui.hpp"
#include "opencv2/nonfree/nonfree.hpp"
#include "opencv2/legacy/legacy.hpp"
#include <iostream>
using namespace cv;
using namespace std;
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[3] = { 0, 0, 1 };
double v1[3] = { 0 };
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];
}
void OptimizeSeam(Mat& img1, Mat& trans, Mat& dst)
{
int start = MIN(corners.left_top.x, corners.left_bottom.x);//開始位置,即重疊區(qū)域的左邊界
double processWidth = img1.cols - start; // 重疊區(qū)域的寬度
int rows = dst.rows;
int cols = img1.cols; // 注意,是列數(shù)*通道數(shù)
double alpha = 1; // img1中像素的權(quán)重
for (int i = 0; i < rows; i++)
{
uchar* p = img1.ptr<uchar>(i); // 獲取第i行的首地址
uchar* t = trans.ptr<uchar>(i);
uchar* d = dst.ptr<uchar>(i);
for (int j = start; j < cols; j++)
{
// 如果遇到圖像trans中無像素的黑點(diǎn),則完全拷貝img1中的數(shù)據(jù)
if (t[j * 3] == 0 && t[j * 3 + 1] == 0 && t[j * 3 + 2] == 0)
{
alpha = 1;
}
else
{
// img1中像素的權(quán)重,與當(dāng)前處理點(diǎn)距重疊區(qū)域左邊界的距離成正比,實(shí)驗(yàn)證明,這種方法確實(shí)好
alpha = (processWidth - (j - start)) / processWidth;
}
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);
}
}
}
int main(int argc, char* argv[])
{
Mat image01 = imread("image2.png", 1); //右圖
Mat image02 = imread("image1.png", 1); //左圖
imshow("p2", image01);
imshow("p1", image02);
// 灰度圖轉(zhuǎn)換
Mat image1, image2;
cvtColor(image01, image1, CV_RGB2GRAY);
cvtColor(image02, image2, CV_RGB2GRAY);
// 提取特征點(diǎn)
SurfFeatureDetector Detector(2000);
vector<KeyPoint> keyPoint1, keyPoint2;
Detector.detect(image1, keyPoint1);
Detector.detect(image2, keyPoint2);
// 特征點(diǎn)描述
SurfDescriptorExtractor Descriptor;
Mat imageDesc1, imageDesc2;
Descriptor.compute(image1, keyPoint1, imageDesc1);
Descriptor.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,獲取優(yōu)秀匹配點(diǎn)
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]);
}
}
// draw match
Mat first_match;
drawMatches(image02, keyPoint2, image01, keyPoint1, GoodMatchePoints, first_match);
imshow("first_match ", 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);
cout << "變換矩陣為:\n" << homo << endl << endl; // 輸出映射矩陣
// 計(jì)算配準(zhǔn)圖的四個(gè)頂點(diǎn)坐標(biāo)
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;
// 圖像配準(zhǔn)
Mat imageTransform1, imageTransform2;
warpPerspective(image01, imageTransform1, homo, Size(MAX(corners.right_top.x, corners.right_bottom.x), image02.rows));
// warpPerspective(image01, imageTransform2, adjustMat*homo, Size(image02.cols*1.3, image02.rows*1.8));
imshow("直接經(jīng)過透視矩陣變換", imageTransform1);
// 創(chuàng)建拼接后的圖,需提前計(jì)算圖的大小
int dst_width = imageTransform1.cols; // 取最右點(diǎn)的長(zhǎng)度為拼接圖的長(zhǎng)度
int dst_height = image02.rows;
Mat dst(dst_height, dst_width, CV_8UC3);
dst.setTo(0);
imageTransform1.copyTo(dst(Rect(0, 0, imageTransform1.cols, imageTransform1.rows)));
image02.copyTo(dst(Rect(0, 0, image02.cols, image02.rows)));
imshow("b_dst", dst);
// 優(yōu)化拼接處
OptimizeSeam(image02, imageTransform1, dst);
imshow("dst", dst);
waitKey();
return 0;
}
??
?文章來源:http://www.zghlxwxcb.cn/news/detail-689796.html
三、利用stitch實(shí)現(xiàn)
#include "opencv2/imgcodecs.hpp"
#include "opencv2/highgui.hpp"
#include "opencv2/stitching.hpp"
#include <iostream>
using namespace std;
using namespace cv;
int main(int argc, char* argv[])
{
Mat img1 = imread("image1.png", cv::IMREAD_COLOR);
Mat img2 = imread("image2.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.zghlxwxcb.cn/news/detail-689796.html
到了這里,關(guān)于利用OpenCV實(shí)現(xiàn)圖像拼接的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!