opencv学习 特征提取

news2024/11/15 15:51:11

内容来源于《opencv4应用开发入门、进阶与工程化实践》  

图像金字塔

拉普拉斯金字塔

对输入图像进行reduce操作会生成不同分辨率的图像,对这些图像进行expand操作,然后使用reduce减去expand之后的结果,就会得到拉普拉斯金字塔图像。

详情可查看https://zhuanlan.zhihu.com/p/80362140

图像金字塔融合

 拉普拉斯金字塔通过源图像减去先缩小再放大的图像构成,保留的是残差,为图像还原做准备。

根据拉普拉斯金字塔的定义可以知道,拉普拉斯金字塔的每一层都是一个高斯差分图像。:

原图 = 拉普拉斯金字塔图L0层 + expand(高斯金字塔G1层),也就是说,可以基于低分辨率的图像与它的高斯差分图像,重建生成一个高分辨率的图像。

详情参考https://zhuanlan.zhihu.com/p/454085730的图像融合部分,讲的很好。

步骤:

  1. 生成苹果、橘子的高斯金字塔G_{L}G_{R}
  2.  求苹果、橘子的的拉普拉斯金字塔L_{apple}L_{orange}
  3. 求mask的高斯金字塔G_{mask}
  4. 在每个尺度(分辨率)下,用G_{mask}拼接L_{apple}L_{orange},最终得到拼接的拉普拉斯金字塔L_{fused}
  5. 生成最低分辨率的起始图(都选取最低分辨率下的G_{L}G_{R} 根据同分辨率下G_{mask} 进行拼接,得到最低分辨率下的拼接结果 O_{min}
  6. O_{min}开始,利用L_{fused}得到最高分辨率的拼接结果

示例代码:

int level = 3;
Mat smallestLevel;
Mat blend(Mat &a, Mat &b, Mat &m) {
	int width = a.cols;
	int height = a.rows;
	Mat dst = Mat::zeros(a.size(), a.type());
	Vec3b rgb1;
	Vec3b rgb2;
	int r1 = 0, g1 = 0, b1 = 0;
	int r2 = 0, g2 = 0, b2 = 0;
	int red = 0, green = 0, blue = 0;
	int w = 0;
	float w1 = 0, w2 = 0;
	for (int row = 0; row<height; row++) {
		for (int col = 0; col<width; col++) {
			rgb1 = a.at<Vec3b>(row, col);
			rgb2 = b.at<Vec3b>(row, col);
			w = m.at<uchar>(row, col);
			w2 = w / 255.0f;
			w1 = 1.0f - w2;

			b1 = rgb1[0] & 0xff;
			g1 = rgb1[1] & 0xff;
			r1 = rgb1[2] & 0xff;

			b2 = rgb2[0] & 0xff;
			g2 = rgb2[1] & 0xff;
			r2 = rgb2[2] & 0xff;

			red = (int)(r1*w1 + r2*w2);
			green = (int)(g1*w1 + g2*w2);
			blue = (int)(b1*w1 + b2*w2);

			// output
			dst.at<Vec3b>(row, col)[0] = blue;
			dst.at<Vec3b>(row, col)[1] = green;
			dst.at<Vec3b>(row, col)[2] = red;
		}
	}
	return dst;
}

vector<Mat> buildGaussianPyramid(Mat &image) {
	vector<Mat> pyramid;
	Mat copy = image.clone();
	pyramid.push_back(image.clone());
	Mat dst;
	for (int i = 0; i<level; i++) {
		pyrDown(copy, dst, Size(copy.cols / 2, copy.rows / 2));
		dst.copyTo(copy);
		pyramid.push_back(dst.clone());
	}
	smallestLevel = dst;
	return pyramid;
}

vector<Mat> buildLapacianPyramid(Mat &image) {
	vector<Mat> lp;
	Mat temp;
	Mat copy = image.clone();
	Mat dst;
	for (int i = 0; i<level; i++) {
		pyrDown(copy, dst, Size(copy.cols / 2, copy.rows / 2));
		pyrUp(dst, temp, copy.size());
		Mat lapaian;
		subtract(copy, temp, lapaian);
		lp.push_back(lapaian);
		copy = dst.clone();
	}
	smallestLevel = dst;
	return lp;
}
void FeatureVectorOps::pyramid_blend_demo(Mat &apple, Mat &orange) {
	Mat mc = imread("D:/images/mask.png");
	if (apple.empty() || orange.empty()) {
		return;
	}
	imshow("苹果图像", apple);
	imshow("橘子图像", orange);

	vector<Mat> la = buildLapacianPyramid(apple);
	Mat leftsmallestLevel;
	smallestLevel.copyTo(leftsmallestLevel);

	vector<Mat> lb = buildLapacianPyramid(orange);
	Mat rightsmallestLevel;
	smallestLevel.copyTo(rightsmallestLevel);

	Mat mask;
	cvtColor(mc, mask, COLOR_BGR2GRAY);

	vector<Mat> maskPyramid = buildGaussianPyramid(mask);
	Mat samllmask;
	smallestLevel.copyTo(samllmask);

	Mat currentImage = blend(leftsmallestLevel, rightsmallestLevel, samllmask);
	imwrite("D:/samll.png", currentImage);
	// 重建拉普拉斯金字塔
	vector<Mat> ls;
	for (int i = 0; i<level; i++) {
		Mat a = la[i];
		Mat b = lb[i];
		Mat m = maskPyramid[i];
		ls.push_back(blend(a, b, m));
	}

	// 重建原图
	Mat temp;
	for (int i = level - 1; i >= 0; i--) {
		pyrUp(currentImage, temp, ls[i].size());
		add(temp, ls[i], currentImage);
	}
	imshow("高斯金子图像融合重建-图像", currentImage);
}

Harris角点检测

角点是图像中亮度变化最强的地方,反映了图像的本质特征。

图像的角点在各个方向上都有很强的梯度变化。

亚像素级别的角点检测

详细请参考https://www.cnblogs.com/qq21497936/p/13096048.html

大概理解是角点一般在边缘上,边缘的梯度与沿边缘方向的的向量正交,也就是内积为0,根据内积为零,角点周围能列出一个方程组,方程组的解就是角点坐标。

opencv亚像素级别定位函数API:

void cv::cornerSubPix(
    InputArray image
    InputOutputArray corners //输入整数角点坐标,输出浮点数角点坐标
    Size winSize //搜索窗口
    Size zeroZone 
    TermCriteria criteria //停止条件
)

 示例代码

void FeatureVectorOps::corners_sub_pixels_demo(Mat &image) {
	Mat gray;
	cvtColor(image, gray, COLOR_BGR2GRAY);
	int maxCorners = 400;
	double qualityLevel = 0.01;
	std::vector<Point2f> corners;
	goodFeaturesToTrack(gray, corners, maxCorners, qualityLevel, 5, Mat(), 3, false, 0.04);

	Size winSize = Size(5, 5);
	Size zeroZone = Size(-1, -1);
    //opencv迭代终止条件类
	TermCriteria criteria = TermCriteria(TermCriteria::EPS + TermCriteria::COUNT, 10, 0.001);

	cornerSubPix(gray, corners, winSize, zeroZone, criteria);
	for (size_t t = 0; t < corners.size(); t++) {
		printf("refined Corner: %d, x:%.2f, y:%.2f\n", t, corners[t].x, corners[t].y);
	}
}

HOG特征描述子

详细请参考:https://baijiahao.baidu.com/s?id=1646997581304332534&wfr=spider&for=pc&searchword=HOG%E7%89%B9%E5%BE%81%E6%8F%8F%E8%BF%B0%E5%AD%90

讲的很好。

大概就是以一种特殊的直方图来表示图像特征,直方图存储的是梯度的方向和幅值(x轴是方向,y轴是幅值且加权)。

示例代码:

virtual void cv::HOGDescriptor::compute(
    InputArray img
    std::vector<float> & descriptors
    Size winStride=Size()
    Size padding=Size()
    const std::vector<Point> &locations = std::vector<Point>()
)

void FeatureVectorOps::hog_feature_demo(Mat &image) {
	Mat gray;
	cvtColor(image, gray, COLOR_BGR2GRAY);
	HOGDescriptor hogDetector;
	std::vector<float> hog_descriptors;
	hogDetector.compute(gray, hog_descriptors, Size(8, 8), Size(0, 0));
	std::cout << hog_descriptors.size() << std::endl;
	for (size_t t = 0; t < hog_descriptors.size(); t++) {
		std::cout << hog_descriptors[t] << std::endl;
	}
}

HOG特征行人检测

opencv基于HOG行人特征描述子的检测函数:

void HOGDescriptor::detectMultiScale(

    InputArray img,
    vector<Rect>& foundLocations, 

    double hitThreshold=0, 
    Size winStride=Size(), 
    Size padding=Size(),
    double scale=1.05,
    double finalThreshold=2.0,
    bool useMeanshiftGrouping=false
)
//示例代码
void FeatureVectorOps::hog_detect_demo(Mat &image) {
	HOGDescriptor *hog = new HOGDescriptor();
	hog->setSVMDetector(hog->getDefaultPeopleDetector());
	vector<Rect> objects;
	hog->detectMultiScale(image, objects, 0.0, Size(4, 4), Size(8, 8), 1.25);
	for (int i = 0; i < objects.size(); i++) {
		rectangle(image, objects[i], Scalar(0, 0, 255), 2, 8, 0);
	}
	imshow("HOG行人检测", image);
}

ORB特征描述子

没看懂。

描述子匹配

暴力匹配:

再使用暴力匹配之前先创建暴力匹配器:

static Ptr<BFMatcher> cv::BFMatcher::create(
    int normType=NORM_L2 //计算描述子暴力匹配时采用的计算方法
    bool crossCheck=false //是否使用交叉验证
)

调用暴力匹配的匹配方法,有两种,最佳匹配和KNN匹配

void cv::DescriptorMatch::match(
    InputArray queryDescriptors
    InputArray trainDescriptors
    std::vector<DMatch> & matches
    InputArray mask=noArray
)

void cv::DescriptorMatch::knnMatch(
    InputArray queryDescriptors
    InputArray trainDescriptors
    std::vector<DMatch> & matches
    int k
    InputArray mask=noArray
    bool compactResult =false
)
FLANN匹配:
cv::FlannBasedMatcher::FlannBasedMatcher(
    const Ptr<flann::IndexParams> & indexParams=makePtr<flann::KDTreeIndexParams>()
    const Ptr<flann::SearchParams> & searchParams=makePtr<flann::SearchParams>()
)

示例代码:

void FeatureVectorOps::orb_match_demo(Mat &box, Mat &box_in_scene) {
	// ORB特征提取
	auto orb_detector = ORB::create();
	std::vector<KeyPoint> box_kpts;
	std::vector<KeyPoint> scene_kpts;
	Mat box_descriptors, scene_descriptors;
	orb_detector->detectAndCompute(box, Mat(), box_kpts, box_descriptors);
	orb_detector->detectAndCompute(box_in_scene, Mat(), scene_kpts, scene_descriptors);

	// 暴力匹配
	auto bfMatcher = BFMatcher::create(NORM_HAMMING, false);
	std::vector<DMatch> matches;
	bfMatcher->match(box_descriptors, scene_descriptors, matches);
	Mat img_orb_matches;
	drawMatches(box, box_kpts, box_in_scene, scene_kpts, matches, img_orb_matches);
	imshow("ORB暴力匹配演示", img_orb_matches);

	// FLANN匹配
	auto flannMatcher = FlannBasedMatcher(new flann::LshIndexParams(6, 12, 2));
	flannMatcher.match(box_descriptors, scene_descriptors, matches);
	Mat img_flann_matches;
	drawMatches(box, box_kpts, box_in_scene, scene_kpts, matches, img_flann_matches);
	namedWindow("FLANN匹配演示", WINDOW_FREERATIO);
	cv::namedWindow("FLANN匹配演示", cv::WINDOW_NORMAL);
	imshow("FLANN匹配演示", img_flann_matches);
}

基于特征的对象检测

特征描述子匹配之后,可以根据返回的各个DMatch中的索引得到关键点对,然后拟合生成从对象到场景的变换矩阵H。根据矩阵H可以求得对象在场景中的位置,从而完成基于特征的对象检测。

opencv中求得单应性矩阵的API:

Mat cv::findHomograph(
    InputArray srcPoints
    OutputArray dstPoints
    int method=0
    double ransacReprojThreshold=3
    OutputArray mask=noArray()
    const int maxIters=2000;
    const double confidence=0.995
)

有了变换矩阵H ,可以运用透视变换函数求得场景中对象的四个点坐标并绘制出来。

透视变换函数:

void cv::perspectiveTransform(
    InputArray src
    OutputArray dst
    InputArray m
)

示例代码:

void FeatureVectorOps::find_known_object(Mat &book, Mat &book_on_desk) {
	// ORB特征提取
	auto orb_detector = ORB::create();
	std::vector<KeyPoint> box_kpts;
	std::vector<KeyPoint> scene_kpts;
	Mat box_descriptors, scene_descriptors;
	orb_detector->detectAndCompute(book, Mat(), box_kpts, box_descriptors);
	orb_detector->detectAndCompute(book_on_desk, Mat(), scene_kpts, scene_descriptors);

	// 暴力匹配
	auto bfMatcher = BFMatcher::create(NORM_HAMMING, false);
	std::vector<DMatch> matches;
	bfMatcher->match(box_descriptors, scene_descriptors, matches);
	
	// 好的匹配
	std::sort(matches.begin(), matches.end());
	const int numGoodMatches = matches.size() * 0.15;
	matches.erase(matches.begin() + numGoodMatches, matches.end());
	Mat img_bf_matches;
	drawMatches(book, box_kpts, book_on_desk, scene_kpts, matches, img_bf_matches);
	imshow("ORB暴力匹配演示", img_bf_matches);

	// 单应性求H
	std::vector<Point2f> obj_pts;
	std::vector<Point2f> scene_pts;
	for (size_t i = 0; i < matches.size(); i++)
	{
		//-- Get the keypoints from the good matches
		obj_pts.push_back(box_kpts[matches[i].queryIdx].pt);
		scene_pts.push_back(scene_kpts[matches[i].trainIdx].pt);
	}

	Mat H = findHomography(obj_pts, scene_pts, RANSAC);
	std::cout << "RANSAC estimation parameters: \n" << H << std::endl;
	std::cout << std::endl;
	H = findHomography(obj_pts, scene_pts, RHO);
	std::cout << "RHO estimation parameters: \n" << H << std::endl;
	std::cout << std::endl;
	H = findHomography(obj_pts, scene_pts, LMEDS);
	std::cout << "LMEDS estimation parameters: \n" << H << std::endl;

	// 变换矩阵得到目标点
	std::vector<Point2f> obj_corners(4);
	obj_corners[0] = Point(0, 0); obj_corners[1] = Point(book.cols, 0);
	obj_corners[2] = Point(book.cols, book.rows); obj_corners[3] = Point(0, book.rows);
	std::vector<Point2f> scene_corners(4);
	perspectiveTransform(obj_corners, scene_corners, H);

	// 绘制结果
	Mat dst;
	line(img_bf_matches, scene_corners[0] + Point2f(book.cols, 0), scene_corners[1] + Point2f(book.cols, 0), Scalar(0, 255, 0), 4);
	line(img_bf_matches, scene_corners[1] + Point2f(book.cols, 0), scene_corners[2] + Point2f(book.cols, 0), Scalar(0, 255, 0), 4);
	line(img_bf_matches, scene_corners[2] + Point2f(book.cols, 0), scene_corners[3] + Point2f(book.cols, 0), Scalar(0, 255, 0), 4);
	line(img_bf_matches, scene_corners[3] + Point2f(book.cols, 0), scene_corners[0] + Point2f(book.cols, 0), Scalar(0, 255, 0), 4);

	//-- Show detected matches
	namedWindow("基于特征的对象检测", cv::WINDOW_NORMAL);
	imshow("基于特征的对象检测", img_bf_matches);
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1421600.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

6.2 静态内存分配模式

Bruce Powel Douglass大师介绍-CSDN博客https://blog.csdn.net/ChatCoding/article/details/134665868 嵌入式软件开发从小工到专家-CSDN博客https://blog.csdn.net/ChatCoding/article/details/135297955 C嵌入式编程设计模式源码-CSDN博客https://blog.csdn.net/ChatCoding…

STM32单片机基本原理与应用(四)

直流电机驱动控制原理 1、电机正反转控制 在STM32中&#xff0c;直流电机的正反转控制主要通过改变电机输入电源的极性来实现。当电机的电压极性发生变化时&#xff0c;电机的旋转方向也会相应改变。在硬件电路中&#xff0c;可以通过继电器或晶体管等电子开关来切换电机的电源…

消息中间件RabbitMQ介绍

一、基础知识 1. 什么是RabbitMQ RabbitMQ是2007年发布&#xff0c;是一个在AMQP(高级消息队列协议)基础上完成的&#xff0c;简称MQ全称为Message Queue, 消息队列&#xff08;MQ&#xff09;是一种应用程序对应用程序的通信方法&#xff0c;由Erlang&#xff08;专门针对于大…

【Tomcat与网络4】Tomcat的连接器设计

目录 1 如何设计一个灵活可靠的连接器 2 主要组件介绍 在上一篇&#xff0c;我们介绍了Tomcat提供服务的整体结构&#xff0c;本文我们一起来看一下Tomcat的连接器的设计。 在前面我们提到Tomcat主要完成两个功能&#xff1a; 处理 Socket 连接&#xff0c;负责网络字节流与…

中国的茶文化:现代生活中的茶文化

中国的茶文化&#xff1a;现代生活中的茶文化 引言 在现代社会的快节奏生活中&#xff0c;茶文化并未随时间流逝而褪色&#xff0c;反而以其独特的方式融入了全球各地人们的日常生活。它超越了饮品本身的范畴&#xff0c;成为一种连接历史、人文与现代生活方式的艺术形式。本文…

VRRP负载平衡

1.构图 2.实验 2.1如图提示配置pc1-pc4 掩码:255.255.255.0网关均设置为192.168.1(\2).254 2.2配置sw1-sw2 [sw1]port-group 1 //创建接口组&#xff0c;组号是1[sw1-port-group 1]group-member gigabitethernet 0/0/1 to gigabitethernet 0/0/3 //添加组成员&#xff0c;从…

Unity 设置鼠标

前言 本章主要对鼠标图标样式还有鼠标显隐进行设置 图标样式的设置 代码控制 有时候需要有改变鼠标样式的需求可以使用如下代码 Cursor.SetCursor(this.mouseTexture, Vector2.zero, CursorMode.Auto); 传入的要替换的图标偏移量允许您在支持的平台上使用硬件光标&#xff0…

【Javaweb】【C00157】基于SSM的宠物护理预定系统(论文+PPT)

基于SSM的宠物护理预定系统&#xff08;论文PPT&#xff09; 项目简介项目获取开发环境项目技术运行截图 项目简介 这是一个基于ssm的宠物护理预订系统 本系统分为前台系统模块、后台管理员模块以及后台会员用户模块 其中前台系统模块&#xff1a;当游客打开系统的网址后&…

《Lua程序设计》-- 学习9

迭代器和泛型for 迭代器和闭包 迭代器&#xff08;iterator&#xff09;是一种可以让我们遍历一个集合中所有元素的代码结构。在Lua语言中&#xff0c;通常使用函数表示迭代器&#xff1a;每一次调用函数时&#xff0c;函数会返回集合中的“下一个”元素。 一个闭包就是一个…

万户 ezOFFICE SendFileCheckTemplateEdit.jsp SQL注入漏洞

0x01 产品简介 万户OA ezoffice是万户网络协同办公产品多年来一直将主要精力致力于中高端市场的一款OA协同办公软件产品,统一的基础管理平台,实现用户数据统一管理、权限统一分配、身份统一认证。统一规划门户网站群和协同办公平台,将外网信息维护、客户服务、互动交流和日…

C++/MFC:在窗体Form(Dialog)中多个编辑框时,在输入时将回车解释为TAB键,将输入焦点移到下一个编辑框的方法

很多时候&#xff0c;为了输入方便&#xff0c;常用的做法&#xff0c;就是将回车键解释为将输入焦点移动到下一个编辑框中。就像是我的VxTerm中的快速连接输入一样&#xff1a; VxTerm是一个国产化替代的SSH工具&#xff0c;可以从本站的资源中免费下载并且免费使用&#xff…

移动端基础:rem适配布局

rem单位 rem是相对单位&#xff0c;类似于em 但rem的基准是相对于HTML元素的字体大小 rem的优点是可以通过修改HTML文字大小改变页面元素大小&#xff0c;做到整体控制 媒体查询 使用media查询&#xff0c;可以针对不同的媒体类型定义不同的样式 可以针对不同的屏幕尺寸设…

大模型重塑车载语音交互:赛道巨头如何引领新周期?

车载语音交互赛道正进入新一轮竞争周期。 高工智能汽车注意到&#xff0c;传统车载语音交互赛道当前基本已进入成熟期&#xff0c;主要为任务型助手&#xff0c;包括从单轮对话到多轮对话&#xff0c;单音区到多音区&#xff0c;从单一的导航、多媒体娱乐等座舱功能扩展智能驾…

[嵌入式系统-6]:龙芯1B 开发学习套件 -3-软件层次架构

目录 一、龙芯软件架构 1.1 通用软件架构 1.2 龙芯软件架构 1.3 龙芯各种应用程序 1.4 龙芯SOC芯片硬件&#xff1a;龙芯1B 1.5 PMON软件 1.6 龙芯IDE管辖的软件 &#xff08;1&#xff09;CPU Core驱动程序 &#xff08;2&#xff09;SOC芯片外设驱动程序 &#xff…

人工智能(pytorch)搭建模型23-pytorch搭建生成对抗网络(GAN):手写数字生成的项目应用

大家好&#xff0c;我是微学AI&#xff0c;今天给大家介绍一下人工智能(pytorch)搭建模型23-pytorch搭建生成对抗网络(GAN):手写数字生成的项目应用。生成对抗网络&#xff08;GAN&#xff09;是一种强大的生成模型&#xff0c;在手写数字生成方面具有广泛的应用前景。通过生成…

Adobe Camera Raw forMac/win:掌控原始之美的秘密武器

Adobe Camera Raw&#xff0c;这款由Adobe开发的插件&#xff0c;已经成为摄影师和设计师们的必备工具。对于那些追求完美、渴望探索更多创意可能性的专业人士来说&#xff0c;它不仅仅是一个插件&#xff0c;更是一个能够释放无尽创造力的平台。 在数字摄影时代&#xff0c;R…

数据结构——栈和队列(C语言)

栈种常见的数据结构&#xff0c;它用来解决一些数据类型的问题&#xff0c;那么好&#xff0c;我来带着大家来学习一下栈 文章目录 栈对栈的认识栈的模拟实现栈的练习方法一方法二 栈 对栈的认识 栈&#xff08;stack&#xff09;是限定只能在表的一端进行插入删除操作的线性…

苍穹外卖项目可以写的简历和如何优化简历

文章目录 重点写中规写添加自己个性的项目面试会问道的问题 我是一名双非大二计算机本科生&#xff0c;希望我的分享对你有帮助&#xff0c;点赞关注不迷路。 简历编写一直是很多人求职人的心病&#xff0c;我自己上学期有一门课程是去校内企业面试&#xff0c;当时我就感受出…

性能脚本设计

性能脚本设计 目标 - 性能脚本设计技巧 1. 为什么要设计性能脚本&#xff1f; 1.1 需求 100虚拟用户对(查询学院-所有)接口测试&#xff0c;以每秒启动10个用户,统计服务器平均响应时间和错误率1.2 问题 100虚拟用户请求服务器的时候&#xff0c;如何统计服务器响应时间和…

Unity_Timeline使用说明

Unity_Timeline使用说明 首先要找到工具吧&#xff1f;Unity2023.1.19f1c1打开如下&#xff1a; &#xff08;团结引擎没找见哪儿打开&#xff0c;可能是引擎问题吧&#xff1f;有知道的同学可以告诉我在哪儿打开&#xff09; Timelime使用流程&#xff1a; 打开之后会提示您…