落地基于特征的图像拼接

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

#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;

}

}

}

}

相关推荐
骄傲的心别枯萎8 小时前
RV1126 NO.57:ROCKX+RV1126人脸识别推流项目之读取人脸图片并把特征值保存到sqlite3数据库
数据库·opencv·计算机视觉·sqlite·音视频·rv1126
中年程序员一枚11 小时前
cv2.sqrBoxFilter 是 OpenCV 中用于计算像素邻域平方和的盒式滤波函数
人工智能·opencv·计算机视觉
棒棒的皮皮11 小时前
【OpenCV】Python图像处理之平滑处理
图像处理·python·opencv·计算机视觉
棒棒的皮皮11 小时前
【OpenCV】Python图像处理之重映射
图像处理·python·opencv·计算机视觉
中年程序员一枚11 小时前
cv2.blur 是 OpenCV 中实现均值滤波(归一化盒式滤波) 的核心函数
人工智能·opencv·均值算法
技术净胜1 天前
MATLAB进行图像分割从基础阈值到高级分割
opencv·计算机视觉·matlab
骄傲的心别枯萎1 天前
RV1126 NO.56:ROCKX+RV1126人脸识别推流项目之VI模块和VENC模块讲解
人工智能·opencv·计算机视觉·音视频·rv1126
骄傲的心别枯萎1 天前
RV1126 NO.55:ROCKX+RV1126人脸识别推流项目讲解
opencv·计算机视觉·音视频·rv1126
码界奇点1 天前
Python与OpenCV集成海康威视工业相机从基础配置到高级应用的全方位指南
python·数码相机·opencv·相机·python3.11
棒棒的皮皮1 天前
【OpenCV】Python图像处理几何变换之透视
图像处理·python·opencv·计算机视觉