当前位置:   article > 正文

opencv学习 特征提取

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}得到最高分辨率的拼接结果

示例代码:

  1. int level = 3;
  2. Mat smallestLevel;
  3. Mat blend(Mat &a, Mat &b, Mat &m) {
  4. int width = a.cols;
  5. int height = a.rows;
  6. Mat dst = Mat::zeros(a.size(), a.type());
  7. Vec3b rgb1;
  8. Vec3b rgb2;
  9. int r1 = 0, g1 = 0, b1 = 0;
  10. int r2 = 0, g2 = 0, b2 = 0;
  11. int red = 0, green = 0, blue = 0;
  12. int w = 0;
  13. float w1 = 0, w2 = 0;
  14. for (int row = 0; row<height; row++) {
  15. for (int col = 0; col<width; col++) {
  16. rgb1 = a.at<Vec3b>(row, col);
  17. rgb2 = b.at<Vec3b>(row, col);
  18. w = m.at<uchar>(row, col);
  19. w2 = w / 255.0f;
  20. w1 = 1.0f - w2;
  21. b1 = rgb1[0] & 0xff;
  22. g1 = rgb1[1] & 0xff;
  23. r1 = rgb1[2] & 0xff;
  24. b2 = rgb2[0] & 0xff;
  25. g2 = rgb2[1] & 0xff;
  26. r2 = rgb2[2] & 0xff;
  27. red = (int)(r1*w1 + r2*w2);
  28. green = (int)(g1*w1 + g2*w2);
  29. blue = (int)(b1*w1 + b2*w2);
  30. // output
  31. dst.at<Vec3b>(row, col)[0] = blue;
  32. dst.at<Vec3b>(row, col)[1] = green;
  33. dst.at<Vec3b>(row, col)[2] = red;
  34. }
  35. }
  36. return dst;
  37. }
  38. vector<Mat> buildGaussianPyramid(Mat &image) {
  39. vector<Mat> pyramid;
  40. Mat copy = image.clone();
  41. pyramid.push_back(image.clone());
  42. Mat dst;
  43. for (int i = 0; i<level; i++) {
  44. pyrDown(copy, dst, Size(copy.cols / 2, copy.rows / 2));
  45. dst.copyTo(copy);
  46. pyramid.push_back(dst.clone());
  47. }
  48. smallestLevel = dst;
  49. return pyramid;
  50. }
  51. vector<Mat> buildLapacianPyramid(Mat &image) {
  52. vector<Mat> lp;
  53. Mat temp;
  54. Mat copy = image.clone();
  55. Mat dst;
  56. for (int i = 0; i<level; i++) {
  57. pyrDown(copy, dst, Size(copy.cols / 2, copy.rows / 2));
  58. pyrUp(dst, temp, copy.size());
  59. Mat lapaian;
  60. subtract(copy, temp, lapaian);
  61. lp.push_back(lapaian);
  62. copy = dst.clone();
  63. }
  64. smallestLevel = dst;
  65. return lp;
  66. }
  67. void FeatureVectorOps::pyramid_blend_demo(Mat &apple, Mat &orange) {
  68. Mat mc = imread("D:/images/mask.png");
  69. if (apple.empty() || orange.empty()) {
  70. return;
  71. }
  72. imshow("苹果图像", apple);
  73. imshow("橘子图像", orange);
  74. vector<Mat> la = buildLapacianPyramid(apple);
  75. Mat leftsmallestLevel;
  76. smallestLevel.copyTo(leftsmallestLevel);
  77. vector<Mat> lb = buildLapacianPyramid(orange);
  78. Mat rightsmallestLevel;
  79. smallestLevel.copyTo(rightsmallestLevel);
  80. Mat mask;
  81. cvtColor(mc, mask, COLOR_BGR2GRAY);
  82. vector<Mat> maskPyramid = buildGaussianPyramid(mask);
  83. Mat samllmask;
  84. smallestLevel.copyTo(samllmask);
  85. Mat currentImage = blend(leftsmallestLevel, rightsmallestLevel, samllmask);
  86. imwrite("D:/samll.png", currentImage);
  87. // 重建拉普拉斯金字塔
  88. vector<Mat> ls;
  89. for (int i = 0; i<level; i++) {
  90. Mat a = la[i];
  91. Mat b = lb[i];
  92. Mat m = maskPyramid[i];
  93. ls.push_back(blend(a, b, m));
  94. }
  95. // 重建原图
  96. Mat temp;
  97. for (int i = level - 1; i >= 0; i--) {
  98. pyrUp(currentImage, temp, ls[i].size());
  99. add(temp, ls[i], currentImage);
  100. }
  101. imshow("高斯金子图像融合重建-图像", currentImage);
  102. }

Harris角点检测

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

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

亚像素级别的角点检测

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

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

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

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

 示例代码

  1. void FeatureVectorOps::corners_sub_pixels_demo(Mat &image) {
  2. Mat gray;
  3. cvtColor(image, gray, COLOR_BGR2GRAY);
  4. int maxCorners = 400;
  5. double qualityLevel = 0.01;
  6. std::vector<Point2f> corners;
  7. goodFeaturesToTrack(gray, corners, maxCorners, qualityLevel, 5, Mat(), 3, false, 0.04);
  8. Size winSize = Size(5, 5);
  9. Size zeroZone = Size(-1, -1);
  10. //opencv迭代终止条件类
  11. TermCriteria criteria = TermCriteria(TermCriteria::EPS + TermCriteria::COUNT, 10, 0.001);
  12. cornerSubPix(gray, corners, winSize, zeroZone, criteria);
  13. for (size_t t = 0; t < corners.size(); t++) {
  14. printf("refined Corner: %d, x:%.2f, y:%.2f\n", t, corners[t].x, corners[t].y);
  15. }
  16. }

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轴是幅值且加权)。

示例代码:

  1. virtual void cv::HOGDescriptor::compute(
  2. InputArray img
  3. std::vector<float> & descriptors
  4. Size winStride=Size()
  5. Size padding=Size()
  6. const std::vector<Point> &locations = std::vector<Point>()
  7. )
  8. void FeatureVectorOps::hog_feature_demo(Mat &image) {
  9. Mat gray;
  10. cvtColor(image, gray, COLOR_BGR2GRAY);
  11. HOGDescriptor hogDetector;
  12. std::vector<float> hog_descriptors;
  13. hogDetector.compute(gray, hog_descriptors, Size(8, 8), Size(0, 0));
  14. std::cout << hog_descriptors.size() << std::endl;
  15. for (size_t t = 0; t < hog_descriptors.size(); t++) {
  16. std::cout << hog_descriptors[t] << std::endl;
  17. }
  18. }

HOG特征行人检测

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

  1. void HOGDescriptor::detectMultiScale(
  2. InputArray img,
  3. vector<Rect>& foundLocations,
  4. double hitThreshold=0,
  5. Size winStride=Size(),
  6. Size padding=Size(),
  7. double scale=1.05,
  8. double finalThreshold=2.0,
  9. bool useMeanshiftGrouping=false
  10. )
  11. //示例代码
  12. void FeatureVectorOps::hog_detect_demo(Mat &image) {
  13. HOGDescriptor *hog = new HOGDescriptor();
  14. hog->setSVMDetector(hog->getDefaultPeopleDetector());
  15. vector<Rect> objects;
  16. hog->detectMultiScale(image, objects, 0.0, Size(4, 4), Size(8, 8), 1.25);
  17. for (int i = 0; i < objects.size(); i++) {
  18. rectangle(image, objects[i], Scalar(0, 0, 255), 2, 8, 0);
  19. }
  20. imshow("HOG行人检测", image);
  21. }

ORB特征描述子

没看懂。

描述子匹配

暴力匹配:

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

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

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

  1. void cv::DescriptorMatch::match(
  2. InputArray queryDescriptors
  3. InputArray trainDescriptors
  4. std::vector<DMatch> & matches
  5. InputArray mask=noArray
  6. )
  7. void cv::DescriptorMatch::knnMatch(
  8. InputArray queryDescriptors
  9. InputArray trainDescriptors
  10. std::vector<DMatch> & matches
  11. int k
  12. InputArray mask=noArray
  13. bool compactResult =false
  14. )
FLANN匹配:
  1. cv::FlannBasedMatcher::FlannBasedMatcher(
  2. const Ptr<flann::IndexParams> & indexParams=makePtr<flann::KDTreeIndexParams>()
  3. const Ptr<flann::SearchParams> & searchParams=makePtr<flann::SearchParams>()
  4. )

示例代码:

  1. void FeatureVectorOps::orb_match_demo(Mat &box, Mat &box_in_scene) {
  2. // ORB特征提取
  3. auto orb_detector = ORB::create();
  4. std::vector<KeyPoint> box_kpts;
  5. std::vector<KeyPoint> scene_kpts;
  6. Mat box_descriptors, scene_descriptors;
  7. orb_detector->detectAndCompute(box, Mat(), box_kpts, box_descriptors);
  8. orb_detector->detectAndCompute(box_in_scene, Mat(), scene_kpts, scene_descriptors);
  9. // 暴力匹配
  10. auto bfMatcher = BFMatcher::create(NORM_HAMMING, false);
  11. std::vector<DMatch> matches;
  12. bfMatcher->match(box_descriptors, scene_descriptors, matches);
  13. Mat img_orb_matches;
  14. drawMatches(box, box_kpts, box_in_scene, scene_kpts, matches, img_orb_matches);
  15. imshow("ORB暴力匹配演示", img_orb_matches);
  16. // FLANN匹配
  17. auto flannMatcher = FlannBasedMatcher(new flann::LshIndexParams(6, 12, 2));
  18. flannMatcher.match(box_descriptors, scene_descriptors, matches);
  19. Mat img_flann_matches;
  20. drawMatches(box, box_kpts, box_in_scene, scene_kpts, matches, img_flann_matches);
  21. namedWindow("FLANN匹配演示", WINDOW_FREERATIO);
  22. cv::namedWindow("FLANN匹配演示", cv::WINDOW_NORMAL);
  23. imshow("FLANN匹配演示", img_flann_matches);
  24. }

基于特征的对象检测

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

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

  1. Mat cv::findHomograph(
  2. InputArray srcPoints
  3. OutputArray dstPoints
  4. int method=0
  5. double ransacReprojThreshold=3
  6. OutputArray mask=noArray()
  7. const int maxIters=2000;
  8. const double confidence=0.995
  9. )

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

透视变换函数:

  1. void cv::perspectiveTransform(
  2. InputArray src
  3. OutputArray dst
  4. InputArray m
  5. )

示例代码:

  1. void FeatureVectorOps::find_known_object(Mat &book, Mat &book_on_desk) {
  2. // ORB特征提取
  3. auto orb_detector = ORB::create();
  4. std::vector<KeyPoint> box_kpts;
  5. std::vector<KeyPoint> scene_kpts;
  6. Mat box_descriptors, scene_descriptors;
  7. orb_detector->detectAndCompute(book, Mat(), box_kpts, box_descriptors);
  8. orb_detector->detectAndCompute(book_on_desk, Mat(), scene_kpts, scene_descriptors);
  9. // 暴力匹配
  10. auto bfMatcher = BFMatcher::create(NORM_HAMMING, false);
  11. std::vector<DMatch> matches;
  12. bfMatcher->match(box_descriptors, scene_descriptors, matches);
  13. // 好的匹配
  14. std::sort(matches.begin(), matches.end());
  15. const int numGoodMatches = matches.size() * 0.15;
  16. matches.erase(matches.begin() + numGoodMatches, matches.end());
  17. Mat img_bf_matches;
  18. drawMatches(book, box_kpts, book_on_desk, scene_kpts, matches, img_bf_matches);
  19. imshow("ORB暴力匹配演示", img_bf_matches);
  20. // 单应性求H
  21. std::vector<Point2f> obj_pts;
  22. std::vector<Point2f> scene_pts;
  23. for (size_t i = 0; i < matches.size(); i++)
  24. {
  25. //-- Get the keypoints from the good matches
  26. obj_pts.push_back(box_kpts[matches[i].queryIdx].pt);
  27. scene_pts.push_back(scene_kpts[matches[i].trainIdx].pt);
  28. }
  29. Mat H = findHomography(obj_pts, scene_pts, RANSAC);
  30. std::cout << "RANSAC estimation parameters: \n" << H << std::endl;
  31. std::cout << std::endl;
  32. H = findHomography(obj_pts, scene_pts, RHO);
  33. std::cout << "RHO estimation parameters: \n" << H << std::endl;
  34. std::cout << std::endl;
  35. H = findHomography(obj_pts, scene_pts, LMEDS);
  36. std::cout << "LMEDS estimation parameters: \n" << H << std::endl;
  37. // 变换矩阵得到目标点
  38. std::vector<Point2f> obj_corners(4);
  39. obj_corners[0] = Point(0, 0); obj_corners[1] = Point(book.cols, 0);
  40. obj_corners[2] = Point(book.cols, book.rows); obj_corners[3] = Point(0, book.rows);
  41. std::vector<Point2f> scene_corners(4);
  42. perspectiveTransform(obj_corners, scene_corners, H);
  43. // 绘制结果
  44. Mat dst;
  45. line(img_bf_matches, scene_corners[0] + Point2f(book.cols, 0), scene_corners[1] + Point2f(book.cols, 0), Scalar(0, 255, 0), 4);
  46. line(img_bf_matches, scene_corners[1] + Point2f(book.cols, 0), scene_corners[2] + Point2f(book.cols, 0), Scalar(0, 255, 0), 4);
  47. line(img_bf_matches, scene_corners[2] + Point2f(book.cols, 0), scene_corners[3] + Point2f(book.cols, 0), Scalar(0, 255, 0), 4);
  48. line(img_bf_matches, scene_corners[3] + Point2f(book.cols, 0), scene_corners[0] + Point2f(book.cols, 0), Scalar(0, 255, 0), 4);
  49. //-- Show detected matches
  50. namedWindow("基于特征的对象检测", cv::WINDOW_NORMAL);
  51. imshow("基于特征的对象检测", img_bf_matches);
  52. }

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/数据可视化灵魂/article/detail/60400?site
推荐阅读
相关标签
  

闽ICP备14008679号