落地基于特征的图像拼接

针对一张图不同角度的两张图片进行拼接,首先思路进行特征点提取之后进行暴力匹配,将两幅图的特征点进行对比。

#include <opencv2/opencv.hpp>

#include <iostream>

#define RATIO 0.8

using namespace std;

using namespace cv;

void linspace(Mat& image, float begin, float finish, int number, Mat &mask);

void generate_mask(Mat &img, Mat &mask);

int main(int argc, char** argv) {

Mat left = imread("C:/newword/image/36.jpg");

Mat right = imread("C:/newword/image/37.jpg");

if (left.empty() || right.empty()) {

printf("could not load image...\n");

return -1;

}

// 提取特征点与描述子

vector<KeyPoint> keypoints_right, keypoints_left;

Mat descriptors_right, descriptors_left;

auto detector = AKAZE::create();

detector->detectAndCompute(left, Mat(), keypoints_left, descriptors_left);

detector->detectAndCompute(right, Mat(), keypoints_right, descriptors_right);

// 暴力匹配

vector<DMatch> matches;

auto matcher = DescriptorMatcher::create(DescriptorMatcher::BRUTEFORCE);

// 发现匹配

std::vector< std::vector<DMatch> > knn_matches;

matcher->knnMatch(descriptors_left, descriptors_right, knn_matches, 2);

const float ratio_thresh = 0.5f;

std::vector<DMatch> good_matches;

for (size_t i = 0; i < knn_matches.size(); i++)

{

if (knn_matches[i][0].distance < ratio_thresh * knn_matches[i][1].distance)//distance代表两点之间的距离度量。意思就是,第一匹配点的值小于第二匹配点的值乘以0.7f被看作为符合配对需求的对吗

{

good_matches.push_back(knn_matches[i][0]);

}

}

printf("total good match points : %d\n", good_matches.size());

Mat dst;

drawMatches(left, keypoints_left, right, keypoints_right, good_matches, dst);

imshow("output", dst);

imwrite("C:/newword/21.png", dst);



//-- Localize the object

std::vector<Point2f> left_pts;

std::vector<Point2f> right_pts;

for (size_t i = 0; i < good_matches.size(); i++)

{

// 收集所有好的匹配点

left_pts.push_back(keypoints_left[good_matches[i].queryIdx].pt);

right_pts.push_back(keypoints_right[good_matches[i].trainIdx].pt);

}

// 配准与对齐,对齐到第一张

Mat H = findHomography(right_pts, left_pts, RANSAC);

// 获取全景图大小

int h = max(left.rows, right.rows);

int w = left.cols + right.cols;

Mat panorama_01 = Mat::zeros(Size(w, h), CV_8UC3);

Rect roi;

roi.x = 0;

roi.y = 0;

roi.width = left.cols;

roi.height = left.rows;

// 获取左侧与右侧对齐图像

left.copyTo(panorama_01(roi));

imwrite("C:/newword/22.png", panorama_01);

Mat panorama_02;

warpPerspective(right, panorama_02, H, Size(w, h));//将侧面视角转换为正面视角

imwrite("C:/newword/23.png", panorama_02);

// 计算融合重叠区域mask

Mat mask = Mat::zeros(Size(w, h), CV_8UC1);

generate_mask(panorama_02, mask);//对转化后的图像进行掩码处理

// 创建遮罩层并根据mask完成权重初始化--两张图片平滑过渡

Mat mask1 = Mat::ones(Size(w, h), CV_32FC1);

Mat mask2 = Mat::ones(Size(w, h), CV_32FC1);

// left mask

linspace(mask1, 1, 0, left.cols, mask);//目的将函数左面图像从1渐变到0以便完成图像的融合函数linspace(mask1, 1, 0, left.cols, mask)中,在mask1矩阵上,从值1开始,以一定的步长递减到0,递减的步数由left.cols指定,mask可能是用于辅助计算或存储中间结果等。即通过这个函数调用,mask1自身会被修改为一个具有线性渐变值的矩阵,而不是将mask1渐变到mask上。

// right mask

linspace(mask2, 0, 1, left.cols, mask);目的将函数右面图像从0渐变到1以便完成图像的融合

imshow("mask1", mask1);

imshow("mask2", mask2);

// 左侧融合

Mat m1;

vector<Mat> mv;

mv.push_back(mask1); //线性渐变值的矩阵

mv.push_back(mask1);

mv.push_back(mask1);

merge(mv, m1);//使用merge函数将向量mv中的三个单通道掩码合并成一个三通道掩码M1

panorama_01.convertTo(panorama_01, CV_32F);// 这行代码将 panorama_01 的数据类型从 CV_8UC3(8 位无符号整数,三通道)转换为 CV_32F(32 位浮点数)。这是因为 multiply 函数要求参与乘法运算的两个矩阵数据类型一致,而 mask1 是 CV_32FC1 类型,扩展后的 m1 也是 CV_32F 类型,所以需要将 panorama_01 转换为相同的数据类型。

multiply(panorama_01, m1, panorama_01);// multiply 函数用于对 panorama_01 和 m1 进行逐元素乘法运算,并将结果存储回 panorama_01。由于 m1 是一个掩码,其中的值表示不同位置的权重,通过与 panorama_01 相乘,实现了对 panorama_01 图像不同位置的加权操作。在 mask1 中值较大的区域,panorama_01 相应位置的像素值在相乘后保留得更多;而在 mask1 中值较小的区域,panorama_01 相应位置的像素值在相乘后会被削弱。这样做的目的通常是为了在后续的图像融合过程中,控制左侧图像不同区域的贡献程度,以实现平滑的融合效果。

// 右侧融合

mv.clear();

mv.push_back(mask2);

mv.push_back(mask2);

mv.push_back(mask2);

Mat m2;

merge(mv, m2);

panorama_02.convertTo(panorama_02, CV_32F);

multiply(panorama_02, m2, panorama_02);

// 合并全景图

Mat panorama;

add(panorama_01, panorama_02, panorama);

panorama.convertTo(panorama, CV_8U);

imwrite("C:/newword/28.png", panorama);

waitKey(0);

return 0;

}

void generate_mask(Mat &img, Mat &mask) {

int w = img.cols;

int h = img.rows;

for (int row = 0; row < h; row++) {

for (int col = 0; col < w; col++) {

Vec3b p = img.at<Vec3b>(row, col);

int b = p[0];

int g = p[1];

int r = p[2];

if (b == g && g == r && r == 0) {

mask.at<uchar>(row, col) = 255;

}

}

}

imwrite("C:/newword/29.png", mask);

}

void linspace(Mat& image, float begin, float finish, int w1, Mat &mask) {

int offsetx = 0;

float interval = 0;

float delta = 0;

for (int i = 0; i < image.rows; i++) {

offsetx = 0;

interval = 0;

delta = 0;

for (int j = 0; j < image.cols; j++) {

int pv = mask.at<uchar>(i, j);

if (pv == 0 && offsetx == 0) {

offsetx = j;

delta = w1 - offsetx;

interval = (finish - begin) / (delta - 1);

image.at<float>(i, j) = begin + (j - offsetx)*interval;

}

else if (pv == 0 && offsetx > 0 && (j - offsetx) < delta) {

image.at<float>(i, j) = begin + (j - offsetx)*interval;

}

}

}

}

相关推荐
新知图书3 小时前
OpenCV键盘事件
人工智能·opencv·计算机外设
2201_7549184112 小时前
OpenCV--模板匹配
人工智能·opencv·计算机视觉
QQ_77813297415 小时前
基于OpenCV的指纹验证:从原理到实战的深度解析
opencv
bjxiaxueliang15 小时前
一文详解OpenCV环境搭建:Ubuntu20.4使用CLion配置OpenCV开发环境
人工智能·opencv·计算机视觉
断眉的派大星16 小时前
用opencv校正图片的方向
人工智能·opencv·计算机视觉
闭月之泪舞19 小时前
OpenCv高阶(一)——图像金字塔(上采样、下采样)
人工智能·opencv·计算机视觉
呼呼~²⁰¹⁷20 小时前
opencv无法设置禁用RGB转换问题
人工智能·opencv·计算机视觉
闭月之泪舞1 天前
OpenCv高阶(二)——图像的掩膜
人工智能·opencv·计算机视觉
bjxiaxueliang1 天前
一文详解OpenCV环境搭建:Windows使用CLion配置OpenCV开发环境
人工智能·windows·opencv
jndingxin1 天前
OpenCV 图形API(22)矩阵操作
人工智能·opencv