opencv学习 特征提取

内容来源于《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);// outputdst.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 imageInputOutputArray 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 imgstd::vector<float> & descriptorsSize 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 queryDescriptorsInputArray trainDescriptorsstd::vector<DMatch> & matchesInputArray mask=noArray
)void cv::DescriptorMatch::knnMatch(InputArray queryDescriptorsInputArray trainDescriptorsstd::vector<DMatch> & matchesint kInputArray mask=noArraybool 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 srcPointsOutputArray dstPointsint method=0double ransacReprojThreshold=3OutputArray mask=noArray()const int maxIters=2000;const double confidence=0.995
)

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

透视变换函数:

void cv::perspectiveTransform(InputArray srcOutputArray dstInputArray 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);// 单应性求Hstd::vector<Point2f> obj_pts;std::vector<Point2f> scene_pts;for (size_t i = 0; i < matches.size(); i++){//-- Get the keypoints from the good matchesobj_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 matchesnamedWindow("基于特征的对象检测", cv::WINDOW_NORMAL);imshow("基于特征的对象检测", img_bf_matches);
}

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

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

PythonWeb框架库之fastapi使用详解

概要 Python是一门广受欢迎的编程语言&#xff0c;用于构建各种类型的Web应用程序。FastAPI是一个现代、高性能的Web框架&#xff0c;它以简单的方式提供了快速构建API的能力。本文将介绍FastAPI的各种功能和用法&#xff0c;并提供丰富的示例代码&#xff0c;帮助大家开始使用…

上位机在工业控制系统中的重要作用及其与PLC的互补关系

上位机在工业控制系统中的重要作用及其与PLC的互补关系 在现代工业自动化领域&#xff0c;上位机与可编程逻辑控制器&#xff08;PLC&#xff09;都是不可或缺的重要组成部分。它们各自发挥着独特的优势&#xff0c;在实现生产过程控制、监控和数据管理中扮演了关键角色。然而&…

感谢信∣中国智能电动汽车核心零部件百强「科易动力」SRM项目上线,企企通赋能新能源企业加速重构供应链体系

近日&#xff0c;企企通收到来自苏州科易新动力科技有限公司&#xff08;以下简称“科易动力”&#xff09;的感谢信&#xff0c;对企企通团队在SRM项目实施中所付出的努力表示感谢。 在双方的共同努力下&#xff0c;科易动力采购供应链协同管理项目&#xff08;SRM&#xff09…

又涨又跌 近期现货黄金价格波动怎么看?

踏入2024年一月的下旬&#xff0c;现货黄金价格可以说没了之前火热的状态&#xff0c;盘面上是又涨又跌。面对这样的行情&#xff0c;很多投资者不知道如何看了。下面我们就来讨论一下怎么把握近期的行情。 先区分走势类型。在现货黄金市场中有两种主要的走势类型&#xff0c;一…

暴雨受邀出席太原市人工智能行业协会年度大会

2024年1月26日&#xff0c;太原市人工智能行业协会第二届二次会员大会暨2024年年会成功召开。太原市委、市工商联、市大数据应用中心、市政协经济委员会以及太原市科技局的专家领导&#xff0c;与三百多名来自各行业的人工智能企业家和协会会员一同参加了本次盛会&#xff0c;共…

《区块链简易速速上手小册》第8章:区块链的技术挑战(2024 最新版)

文章目录 8.1 可扩展性问题8.1.1 基础知识8.1.2 主要案例&#xff1a;比特币的可扩展性挑战8.1.3 拓展案例 1&#xff1a;以太坊的可扩展性改进8.1.4 拓展案例 2&#xff1a;侧链和分层解决方案 8.2 安全性与隐私8.2.1 基础知识8.2.2 主要案例&#xff1a;比特币交易的安全性8.…

uniapp底部栏设置未读红点或角标

pages.json {... // 省略"tabBar": {"color": "#333333","selectedColor": "#3296fa","backgroundColor": "#ffffff","borderStyle": "white","list": [{"pagePat…

Django模型(五)

一、数据的条件查询 参考文档:QuerySet API 参考 | Django 文档 | Django 1.1、常用检索字段 字段检索,是在字段名后加 __ 双下划线,再加关键字,类似 SQL 语句中的 where 后面的部分, 如: 字段名__关键字 exact :判断是否等于value,一般不使用,而直接使用 =contai…

32GPIO输入&按键控制LED&光敏控制蜂鸣器

目录 一.硬件 二.硬件电路 三.C语言基础 四.代码实现 1.按键控制LED (1)自己的代码逻辑 (2)视频的代码逻辑 2.光敏控制蜂鸣器 一.硬件 光线越强&#xff0c;光敏电阻的阻值越小 温度越高&#xff0c;热敏电阻的阻值就越小 红外光线越强&#xff0c;红外接收管的阻值就…

Java SE继承和组合

文章目录 1.继承1.1.继承的概念&#xff1a;1.2. 继承的语法&#xff1a;1.3.父类成员访问&#xff1a;1.3.1 子类中访问父类的成员变量&#xff1a;1.3.2 子类中访问父类的成员方法&#xff1a; 1.4.子类构造方法&#xff1a;1.5. super和this:相同点&#xff1a;不同点&#…

目标检测算法训练数据准备——Penn-Fudan数据集预处理实例说明(附代码)

目录 0. 前言 1. Penn-Fudan数据集介绍 2. Penn-Fudan数据集预处理过程 3. 结果展示 4. 完整代码 0. 前言 按照国际惯例&#xff0c;首先声明&#xff1a;本文只是我自己学习的理解&#xff0c;虽然参考了他人的宝贵见解及成果&#xff0c;但是内容可能存在不准确的地方。如…

【MBtiles数据索引和服务发布】GeoServer改造Springboot番外系列二

xyz地图服务访问示例&#xff1a;http://192.168.1.240:8081/gmserver/raster/xyz/firstWP:Imagery-raster/{z}/{x}/{y}.jpg 访问示例如下&#xff1a; mbtiles目录结构 根据z&#xff0c;x&#xff0c;y获取对应mbtiles文件路径的工具方法 说明&#xff1a;重点是使用getMb…