Opencv C++ SIFT特征提取(单图像,多图像)+如何设置阈值+如何对文件夹进行批处理+如何设置掩膜裁剪影像

一、何谓SITF特征提取,它有什么作用?

SIFT(Scale-Invariant Feature Transform)是一种用于图像处理和计算机视觉的特征提取算法。由David Lowe于1999年首次提出,它是一种非常有效的局部特征描述符,具有尺度不变性、旋转不变性和对部分遮挡的鲁棒性。

SIFT特征提取的主要步骤包括:

  1. 尺度空间极值检测(Scale-Space Extrema Detection):通过不同尺度的高斯模糊图像,检测图像中的局部最小和最大值,形成尺度空间。

  2. 关键点定位(Key Point Localization):在尺度空间中,通过对极值点的局部区域进行拟合,找到关键点的准确位置。

  3. 关键点方向分配(Orientation Assignment):为每个关键点分配一个主方向,使得描述子具有旋转不变性。

  4. 描述子生成(Descriptor Generation):在关键点的周围区域计算局部梯度,生成具有尺度和旋转不变性的特征描述子。

SIFT特征提取的作用:

  1. 物体识别和图像匹配:SIFT特征可以用于在不同图像中寻找相似的局部特征,从而实现物体识别和图像匹配。

  2. 目标跟踪:SIFT特征对于目标在不同尺度和角度下的稳定性使其成为目标跟踪任务中的有力工具。

  3. 全景图像拼接:在全景图像拼接中,SIFT特征可以用于检测并匹配图像中的关键点,实现图像的自动对准和拼接。

  4. 图像检索:通过将图像中的SIFT特征转化为高维特征向量,可以用于图像检索,找到包含相似特征的图像。

  5. 图像配准:在医学图像处理等领域,SIFT特征可以用于图像配准,即将不同图像对齐以进行比较和分析。

总体而言,SIFT特征提取在计算机视觉领域中被广泛应用,特别是在需要处理具有尺度和旋转变化的图像时。

二、代码展示

1.必要的准备

建议先安装好OpenCV_contrib:

【一步到位】Visual Studio20xx+OpenCV4.5.1+opencv_contrib的安装与配置-CSDN博客

2.代码实例

在OpenCV 4.x版本中,SIFT算法已经从 xfeatures2d 模块移到了 cv 命名空间下。以下是一个基于C++和OpenCV 4.5.1的简单SIFT特征点提取的示例代码:

(1)——对单一图片进行特征点提取

该代码实现了两个功能,一是对输入的图像进行了特征点提取,二是统计了一共有多少个特征点。

#include <opencv2/opencv.hpp>

int main() {
	// 读取图像
	cv::Mat image = cv::imread("D://lena.jpg");

	if (image.empty()) {
		std::cerr << "Error: Could not read the image." << std::endl;
		return -1;
	}

	// 创建SIFT检测器
	cv::Ptr<cv::SIFT> detector = cv::SIFT::create();

	// 检测关键点和计算描述子
	std::vector<cv::KeyPoint> keypoints;
	cv::Mat descriptors;
	detector->detectAndCompute(image, cv::noArray(), keypoints, descriptors);

	// 在图像上绘制关键点
	cv::Mat outputImage;
	cv::drawKeypoints(image, keypoints, outputImage);

	// 保存结果图像
	cv::imwrite("sift_keypoints.jpg", outputImage);
	std::cout << "SIFT keypoint extraction completed. " << keypoints.size() << " keypoints detected." << std::endl;
	cv::namedWindow("原图");
	imshow("原图", image);
	cv::namedWindow("结果图");
	imshow("结果图", outputImage);
	cv::waitKey(0);
	

	return 0;
}

结果如图所示:

(2)——对两张图片进行特征点提取

step1:首先需要两张特征图像,如果没有的话,可以通过下列代码随机生成,或者是直接百度网盘下载:链接:https://pan.baidu.com/s/1sEY633fESQgxKUm2OlH1rw?pwd=1024 
提取码:1024 

--来自百度网盘超级会员V5的分享

#include <opencv2/opencv.hpp>

int main() {
	// 创建两张空白图像
	cv::Mat image1 = cv::Mat::zeros(300, 400, CV_8UC3);
	cv::Mat image2 = cv::Mat::zeros(300, 400, CV_8UC3);

	// 随机生成特征点的数量
	int numPoints = 50;

	// 随机生成特征点坐标并在图像上绘制
	for (int i = 0; i < numPoints; ++i) {
		cv::Point2f point1(rand() % image1.cols, rand() % image1.rows);
		cv::Point2f point2(rand() % image2.cols, rand() % image2.rows);

		// 在图像上绘制特征点
		cv::circle(image1, point1, 3, cv::Scalar(0, 255, 0), -1);
		cv::circle(image2, point2, 3, cv::Scalar(0, 255, 0), -1);
	}

	// 保存生成的图像
	cv::imwrite("random_image1.jpg", image1);
	cv::imwrite("random_image2.jpg", image2);

	// 显示生成的图像
	cv::imshow("Image 1", image1);
	cv::imshow("Image 2", image2);
	cv::waitKey(0);

	return 0;
}

两个图像的特征提取与匹配:使用Brute-Force匹配器匹配两个图像的特征点。

该代码目的:

  • 读取两个图像。
  • 使用SIFT检测器提取两个图像中的关键点和描述子。
  • 使用Brute-Force匹配器匹配两个图像的特征点。
  • 绘制匹配结果并保存图像。

#include <opencv2/opencv.hpp>

int main() {
	// 读取两个图像
	cv::Mat image1 = cv::imread("random_image1.jpg");
	cv::Mat image2 = cv::imread("random_image2.jpg");//如果这里你是选择下载的百度网盘图像,或者是自己生成的,请写具体地址。

	if (image1.empty() || image2.empty()) {
		std::cerr << "Error: Could not read the images." << std::endl;
		return -1;
	}

	// 创建SIFT检测器
	cv::Ptr<cv::SIFT> detector = cv::SIFT::create();

	// 检测关键点和计算描述子
	std::vector<cv::KeyPoint> keypoints1, keypoints2;
	cv::Mat descriptors1, descriptors2;
	detector->detectAndCompute(image1, cv::noArray(), keypoints1, descriptors1);
	detector->detectAndCompute(image2, cv::noArray(), keypoints2, descriptors2);

	// 使用Brute-Force匹配器进行匹配
	cv::BFMatcher matcher;
	std::vector<cv::DMatch> matches;
	matcher.match(descriptors1, descriptors2, matches);

	// 绘制匹配结果
	cv::Mat matchImage;
	cv::drawMatches(image1, keypoints1, image2, keypoints2, matches, matchImage);

	// 保存结果图像
	cv::imwrite("sift_matches.jpg", matchImage);

	std::cout << "SIFT feature matching completed. " << matches.size() << " matches found." << std::endl;
	cv::namedWindow("原图1");
	imshow("原图1", image1);
	cv::namedWindow("原图2");
	imshow("原图2", image2);
	cv::namedWindow("结果图");
	imshow("结果图", matchImage);
	cv::waitKey(0);
	return 0;
}

结果图:

函数及代码介绍:

创建SIFT检测器

cv::Ptr<cv::SIFT> detector = cv::SIFT::create();

检查关键点和计算描述子:

std::vector<cv::KeyPoint> keypoints1, keypoints2;
cv::Mat descriptors1, descriptors2;
detector->detectAndCompute(image1, cv::noArray(), keypoints1, descriptors1);
detector->detectAndCompute(image2, cv::noArray(), keypoints2, descriptors2);

使用Brute-Force匹配器进行匹配

cv::BFMatcher matcher;
std::vector<cv::DMatch> matches;
matcher.match(descriptors1, descriptors2, matches);

Brute-Force匹配器是一种简单直接的特征匹配方法,它基于一种朴素的原理:对于一个特征集合中的每个特征,在另一个特征集合中找到与之最相似的特征。这种匹配方法的简单性和直观性使得它在很多应用中仍然很有用。

具体来说,Brute-Force匹配器的原理如下:

  1. 特征提取:首先,对于两幅图像,使用某种特征提取算法(如SIFT、SURF等)从图像中提取关键点(keypoints)和对应的特征描述子(feature descriptors)。

  2. 特征匹配:对于第一幅图像中的每个特征,通过计算其特征描述子与第二幅图像中所有特征描述子的相似度(例如使用欧氏距离或汉明距离等),找到与之最匹配的特征。

  3. 建立匹配:通过设定一个阈值或者使用一些筛选方法,将相似度较高的特征匹配对保留下来,形成最终的匹配集合。

  4. 生成匹配结果:将保留下来的匹配对可视化或用于后续的图像处理任务。

Brute-Force匹配器的优点是简单、直观,容易理解和实现。然而,它的缺点在于计算成本较高,特别是当特征点数量较大时,需要进行大量的特征匹配计算,计算复杂度较高。因此,在大规模图像处理中,可能会采用一些加速方法,例如使用空间索引结构(KD-Tree、FLANN等)来加速匹配过程。

绘制匹配结果:

cv::Mat matchImage;
cv::drawMatches(image1, keypoints1, image2, keypoints2, matches, matchImage);

三、关于阈值问题

因为输出的结果图中有数百组特征点匹配,我们可以采用按照特征点的相似程度高低进行排序,然后选择最佳的五十组匹配,重新修改上述代码。

	// 使用Brute-Force匹配器进行匹配
	cv::BFMatcher matcher;
	std::vector<cv::DMatch> matches;
	matcher.match(descriptors1, descriptors2, matches);

	// 根据匹配结果的距离进行排序
	std::sort(matches.begin(), matches.end(), [](const cv::DMatch& a, const cv::DMatch& b) {
		return a.distance < b.distance;
		});

	// 选择最佳的50组匹配
	std::vector<cv::DMatch> bestMatches(matches.begin(), matches.begin() + 50);

	// 绘制匹配结果
	cv::Mat matchImage;
	cv::drawMatches(image1, keypoints1, image2, keypoints2, bestMatches, matchImage);

	// 保存结果图像
	cv::imwrite("sift_top50_matches.jpg", matchImage);

	std::cout << "SIFT feature matching completed. " << bestMatches.size() << " best matches found." << std::endl;
	cv::namedWindow("原图1");
	imshow("原图1", image1);
	cv::namedWindow("原图2");
	imshow("原图2", image2);
	cv::namedWindow("结果图");
	imshow("结果图", matchImage);
	cv::waitKey(0);
	return 0;
}

其最后结果如下图所示:

(图片与上述图片不同,该项为后期补充)

四、以文件夹形式进行批处理+对图片进行掩膜裁剪。

//这个代码实现了如下功能:1·读文件夹内的图片。2·对其中心做了一个圆形掩膜。3·通过将掩膜反转,裁剪获得原始图片上除了中心圆形以外的区域。4·而后将结果依次输出到指定的文件夹中。
#include <opencv2/opencv.hpp>
#include <iostream>
#include <filesystem>
using namespace cv;

int main() {
	std::string input_folder = "Z:\\Image";  // 输入图片所在文件夹路径
	std::string output_folder = "Z:\\cropped_image";  // 输出结果保存文件夹路径

	std::vector<cv::String> filenames;
	cv::glob(input_folder, filenames);

	for (size_t i = 0; i < filenames.size(); ++i) {
		
			// 读取两张图像
			cv::Mat image1 = cv::imread(filenames[i]);
			

			if (image1.empty()) {
				std::cerr << "Error: Could not read images " << filenames[i]<< std::endl;
				continue;
			}
			// 获取图像中心点
			Point center(image1.cols / 2, image1.rows / 2);

			// 创建一个和输入图像大小相同的黑色图像
			Mat mask = Mat::zeros(image1.size(), CV_8UC1);

			// 以中心为原点,画一个200像素半径的圆形掩膜
			circle(mask, center, 350, Scalar(255), -1);



			// 反转掩膜,将圆形区域变为黑色,外部区域变为白色
			bitwise_not(mask, mask);

			// 创建一个与输入图像相同大小的输出图像
			Mat result = Mat::zeros(image1.size(), image1.type());

			// 使用反转后的掩膜来裁剪图像
			image1.copyTo(result, mask);


			// 保存裁剪后的图像到文件
			imwrite("cropped_image.jpg", result);

			// 构建输出文件路径
			std::string output_name = "cropped_image_" + std::to_string(i) + "_"  + ".jpg";
			std::string output_path = output_folder + "/" + output_name;

			// 保存结果图像到指定文件夹
			cv::imwrite(output_path, result);

		
	}
	return 0;
}

其结果如下:

猜你喜欢

转载自blog.csdn.net/w2492602718/article/details/134325552