1、图像特征概述

图像特征的定义与表示

图像特征表示是该图像唯一的表述,是图像的DNA

图像特征提取概述

  • 传统图像特征提取 - 主要基于纹理、角点、颜色分布、梯度、边缘等
  • 深度卷积神经网络特征提取 - 基于监督学习、自动提取特征
  • 特征数据/特征属性
    • 尺度空间不变性
    • 像素迁移不变性
    • 光照一致性原则
    • 旋转不变性原则

图像特征应用

图像分类、对象识别、特征检测、图像对齐/匹配、对象检测、图像搜索/比对

  • 图像处理:从图像到图像
  • 特征提取:从图像到向量(数据)

2、角点检测

  • 什么是角点

  • 各个方向的梯度变化

  • Harris角点检测算法

    1. //函数说明:
    2. void cv::cornerHarris(
    3. InputArray src, //输入
    4. OutputArray dst, //输出
    5. int blockSize, //块大小
    6. int ksize, //Sobel
    7. double k, //常量系数
    8. int borderType = BORDER_DEFAULT //
    9. )
  • Shi-tomas角点检测算法

    1. //函数说明:
    2. void cv::goodFeaturesToTrack(
    3. InputArray image, //输入图像
    4. OutputArray corners, //输出的角点坐标
    5. int maxCorners, //最大角点数目
    6. double qualityLevel, //质量控制,即λ1与λ2的最小阈值
    7. double minDistance, //重叠控制,忽略多少像素值范围内重叠的角点
    8. InputArray mask = noArray(),
    9. int blockSize = 3,
    10. bool useHarrisDetector = false,
    11. double k = 0.04
    12. )
  • 代码实现

    1. #include <opencv2/opencv.hpp>
    2. #include <iostream>
    3. using namespace cv;
    4. using namespace std;
    5. int main(int argc, char** argv) {
    6. Mat src = imread("D:/images/building.png");
    7. Mat gray;
    8. cvtColor(src, gray, COLOR_BGR2GRAY);
    9. namedWindow("src", WINDOW_FREERATIO);
    10. imshow("src", src);
    11. RNG rng(12345);
    12. vector<Point> points;
    13. goodFeaturesToTrack(gray, points, 400, 0.05, 10);
    14. for (size_t t = 0; t < points.size(); t++) {
    15. circle(src, points[t], 3, Scalar(rng.uniform(0, 255), rng.uniform(0, 255), rng.uniform(0, 255)), 1.5, LINE_AA);
    16. }
    17. namedWindow("out", WINDOW_FREERATIO);
    18. imshow("out", src);
    19. waitKey(0);
    20. destroyAllWindows();
    21. return 0;
    22. }
  • 效果:

3、关键点检测

  • 图像特征点/关键点
  • 关键点检测函数
  • 代码演示

ORB关键点检测(快速)

  • ORB算法由两个部分组成:快速关键点定位+BRIEF描述子生成

  • Fast关键点检测:选择当前像素点P,阈值T,周围16个像素点,超过连续N=12个像素点大于或者小于P,Fast1:优先检测1、5、9、13,循环所有像素点

关键点检测函数

  1. //ORB对象创建
  2. Orb = cv::ORB::create(500)
  3. virtual void cv::Feature2D::detect(
  4. InputArray image, //输入图像
  5. std::vector<KeyPoint>& keypoints, //关键点
  6. InputArray mask = noArray() //支持mask
  7. )

KeyPoint数据结构-四个最重要属性:

  • pt
  • angle
  • response
  • size

代码实现:

  1. #include <opencv2/opencv.hpp>
  2. #include <iostream>
  3. using namespace cv;
  4. using namespace std;
  5. int main(int argc, char** argv) {
  6. Mat src = imread("D:/images/building.png");
  7. imshow("input", src);
  8. //Mat gray;
  9. //cvtColor(src, gray, COLOR_BGR2GRAY);
  10. auto orb = ORB::create(500);
  11. vector<KeyPoint> kypts;
  12. orb->detect(src, kypts);
  13. Mat result01, result02, result03;
  14. drawKeypoints(src, kypts, result01, Scalar::all(-1), DrawMatchesFlags::DEFAULT);
  15. drawKeypoints(src, kypts, result02, Scalar::all(-1), DrawMatchesFlags::DRAW_RICH_KEYPOINTS);
  16. imshow("ORB关键点检测default", result01);
  17. imshow("ORB关键点检测rich", result02);
  18. waitKey(0);
  19. destroyAllWindows();
  20. return 0;
  21. }

效果:

4、特征描述子

  • 基于关键点周围区域
  • 浮点数表示与二值编码
  • 描述子长度

ORB特征描述子生成步骤:

  • 提取特征关键点
  • 描述子方向指派
  • 特征描述子编码(二值编码32位)

代码实现:

  1. #include <opencv2/opencv.hpp>
  2. #include <iostream>
  3. using namespace cv;
  4. using namespace std;
  5. int main(int argc, char** argv) {
  6. Mat src = imread("D:/images/building.png");
  7. imshow("input", src);
  8. //Mat gray;
  9. //cvtColor(src, gray, COLOR_BGR2GRAY);
  10. auto orb = ORB::create(500); //获取500个关键点,每个关键点计算一个orb特征描述子
  11. vector<KeyPoint> kypts;
  12. orb->detect(src, kypts);
  13. Mat result01, result02, result03;
  14. //drawKeypoints(src, kypts, result01, Scalar::all(-1), DrawMatchesFlags::DEFAULT);
  15. //不同半径代表不同层级高斯金字塔中的关键点,即图像不同尺度中的关键点
  16. drawKeypoints(src, kypts, result02, Scalar::all(-1), DrawMatchesFlags::DRAW_RICH_KEYPOINTS);
  17. Mat desc_orb;
  18. orb->compute(src, kypts, desc_orb);
  19. std::cout << desc_orb.rows << " x " << desc_orb.cols << std::endl;
  20. //imshow("ORB关键点检测default", result01);
  21. imshow("ORB关键点检测rich", result02);
  22. waitKey(0);
  23. destroyAllWindows();
  24. return 0;
  25. }

效果:

SIFT(尺度不变特征转换,Scale-invariant feature transform)特征描述子

  • 尺度空间不变性
  • 像素迁移不变性
  • 角度旋转不变性

SIFT特征提取步骤

  • 尺度空间极值检测
  • 关键点定位
  • 方向指派
  • 特征描述子

尺度空间极值检测

  • 构建尺度空间 -- 图像金字塔 + 高斯尺度空间
  • 三层空间中的极值查找

关键点定位

  • 极值点定位 - 求导拟合
  • 删除低对比度与低响应候选点

方向指派

  • 关键点方向指派
  • Scale尺度最近的图像,1.5倍大小的高斯窗口

特征描述子

  • 128维向量/特征描述子
  • 描述子编码方式

代码实现:

  1. #include <opencv2/opencv.hpp>
  2. #include <iostream>
  3. using namespace cv;
  4. using namespace std;
  5. int main(int argc, char** argv) {
  6. Mat src = imread("D:/images/building.png");
  7. imshow("input", src);
  8. //Mat gray;
  9. //cvtColor(src, gray, COLOR_BGR2GRAY);
  10. auto sift = SIFT::create(500);
  11. vector<KeyPoint> kypts;
  12. sift->detect(src, kypts);
  13. Mat result01, result02, result03;
  14. //drawKeypoints(src, kypts, result01, Scalar::all(-1), DrawMatchesFlags::DEFAULT);
  15. drawKeypoints(src, kypts, result02, Scalar::all(-1), DrawMatchesFlags::DRAW_RICH_KEYPOINTS);
  16. std::cout << kypts.size() << std::endl;
  17. for (int i = 0; i < kypts.size(); i++) {
  18. std::cout << "pt: " << kypts[i].pt << " angle: " << kypts[i].angle << " size: " << kypts[i].size << std::endl;
  19. }
  20. Mat desc_orb;
  21. sift->compute(src, kypts, desc_orb);
  22. std::cout << desc_orb.rows << " x " << desc_orb.cols << std::endl;
  23. //imshow("ORB关键点检测default", result01);
  24. imshow("ORB关键点检测rich", result02);
  25. waitKey(0);
  26. destroyAllWindows();
  27. return 0;
  28. }

效果:

5、特征匹配

  • 特征匹配算法
  • 特征匹配函数
  • 特征匹配方法对比

特征匹配算法

  • 暴力匹配,全局搜索,计算最小距离,返回相似描述子合集

  • FLANN匹配,2009年发布的开源高维数据匹配算法库,全称Fast Library for Approximate Nearest Neighbors

  • 支持KMeans、KDTree、KNN、多探针LSH等搜索与匹配算法

描述子 匹配方法
SIFT, SURF, and KAZE L1 Norm
AKAZE, ORB, and BRISK Hamming distance(二值编码)
  1. // 暴力匹配
  2. auto bfMatcher = BFMatcher::create(NORM_HAMMING, false);
  3. std::vector<DMatch> matches;
  4. bfMatcher->match(box_descriptors, scene_descriptors, matches);
  5. Mat img_orb_matches;
  6. drawMatches(box, box_kpts, box_in_scene, scene_kpts, matches, img_orb_matches);
  7. imshow("ORB暴力匹配演示", img_orb_matches);
  8. // FLANN匹配
  9. auto flannMatcher = FlannBasedMatcher(new flann::LshIndexParams(6, 12, 2));
  10. flannMatcher.match(box_descriptors, scene_descriptors, matches);
  11. Mat img_flann_matches;
  12. drawMatches(box, box_kpts, box_in_scene, scene_kpts, matches, img_flann_matches);
  13. namedWindow("FLANN匹配演示", WINDOW_FREERATIO);
  14. imshow("FLANN匹配演示", img_flann_matches);

特征匹配DMatch数据结构

DMatch数据结构:

  • queryIdx
  • trainIdx
  • distance

distance表示距离,值越小表示匹配程度越高。

OpenCV特征匹配方法对比

代码实现:

  1. #include <opencv2/opencv.hpp>
  2. #include <iostream>
  3. using namespace cv;
  4. using namespace std;
  5. int main(int argc, char** argv) {
  6. Mat book = imread("D:/images/book.jpg");
  7. Mat book_on_desk = imread("D:/images/book_on_desk.jpg");
  8. imshow("book", book);
  9. //Mat gray;
  10. //cvtColor(src, gray, COLOR_BGR2GRAY);
  11. vector<KeyPoint> kypts_book;
  12. vector<KeyPoint> kypts_book_on_desk;
  13. Mat desc_book, desc_book_on_desk;
  14. //auto orb = ORB::create(500);
  15. //orb->detectAndCompute(book, Mat(), kypts_book, desc_book);
  16. //orb->detectAndCompute(book_on_desk, Mat(), kypts_book_on_desk, desc_book_on_desk);
  17. auto sift = SIFT::create(500);
  18. sift->detectAndCompute(book, Mat(), kypts_book, desc_book);
  19. sift->detectAndCompute(book_on_desk, Mat(), kypts_book_on_desk, desc_book_on_desk);
  20. Mat result;
  21. vector<DMatch> matches;
  22. //// 暴力匹配
  23. //auto bf_matcher = BFMatcher::create(NORM_HAMMING, false);
  24. //bf_matcher->match(desc_book, desc_book_on_desk, matches);
  25. //drawMatches(book, kypts_book, book_on_desk, kypts_book_on_desk, matches, result);
  26. // FLANN匹配
  27. //auto flannMatcher = FlannBasedMatcher(new flann::LshIndexParams(6, 12, 2));
  28. auto flannMatcher = FlannBasedMatcher();
  29. flannMatcher.match(desc_book, desc_book_on_desk, matches);
  30. Mat img_flann_matches;
  31. drawMatches(book, kypts_book, book_on_desk, kypts_book_on_desk, matches, img_flann_matches);
  32. namedWindow("SIFT-FLANN匹配演示", WINDOW_FREERATIO);
  33. imshow("SIFT-FLANN匹配演示", img_flann_matches);
  34. //namedWindow("ORB暴力匹配演示", WINDOW_FREERATIO);
  35. //imshow("ORB暴力匹配演示", result);
  36. waitKey(0);
  37. destroyAllWindows();
  38. return 0;
  39. }

效果:

1、ORB描述子匹配效果

2、SIFT描述子匹配效果

6、单应性变换/透视变换

  1. Mat cv::findHomography(
  2. InputArray srcPoints, // 输入
  3. InputArray dstPoints, // 输出
  4. int method = 0,
  5. double ransacReprojThreshold = 3,
  6. OuputArray mask = noArray(),
  7. const int maxIters = 2000,
  8. const double confidence = 0.995
  9. )

拟合方法:

  • 最小二乘法(0)
  • 随机采样一致性(RANSC)
  • 渐进采样一致性(RHO)

代码实现:

  1. #include <opencv2/opencv.hpp>
  2. #include <iostream>
  3. using namespace std;
  4. using namespace cv;
  5. int main(int argc, char** argv) {
  6. Mat input = imread("D:/images/book_on_desk.jpg");
  7. Mat gray, binary;
  8. cvtColor(input, gray, COLOR_BGR2GRAY);
  9. threshold(gray, binary, 0, 255, THRESH_BINARY | THRESH_OTSU);
  10. vector<vector<Point>> contours;
  11. vector<Vec4i> hierachy;
  12. findContours(binary, contours, hierachy, RETR_EXTERNAL, CHAIN_APPROX_SIMPLE);
  13. int index = 0;
  14. for (size_t i = 0; i < contours.size(); i++) {
  15. if (contourArea(contours[i]) > contourArea(contours[index])) {
  16. index = i;
  17. }
  18. }
  19. Mat approxCurve;
  20. approxPolyDP(contours[index], approxCurve, contours[index].size() / 10, true);
  21. //imshow("approx", approxCurve);
  22. //std::cout << contours.size() << std::endl;
  23. vector<Point2f> srcPts;
  24. vector<Point2f> dstPts;
  25. for (int i = 0; i < approxCurve.rows; i++) {
  26. Vec2i pt = approxCurve.at<Vec2i>(i, 0);
  27. srcPts.push_back(Point(pt[0], pt[1]));
  28. circle(input, Point(pt[0], pt[1]), 12, Scalar(0, 0, 255), 2, 8, 0);
  29. putText(input, std::to_string(i), Point(pt[0], pt[1]), FONT_HERSHEY_SIMPLEX, 0.5, Scalar(255, 0, 0), 1);
  30. }
  31. dstPts.push_back(Point2f(0, 0));
  32. dstPts.push_back(Point2f(0, 760));
  33. dstPts.push_back(Point2f(585, 760));
  34. dstPts.push_back(Point2f(585, 0));
  35. Mat h = findHomography(srcPts, dstPts, RANSAC); //计算单应性矩阵
  36. Mat result;
  37. warpPerspective(input, result, h, Size(585, 760)); //对原图进行透视变换获得校正后的目标区域
  38. namedWindow("result", WINDOW_FREERATIO);
  39. imshow("result", result);
  40. drawContours(input, contours, index, Scalar(0, 255, 0), 2, 8);
  41. namedWindow("轮廓", WINDOW_FREERATIO);
  42. imshow("轮廓", input);
  43. waitKey(0);
  44. return 0;
  45. }

效果:

7、基于匹配的对象检测

  • 基于特征的匹配与对象检测
  • ORB/AKAZE/SIFT
  • 暴力/FLANN
  • 透视变换
  • 检测框

代码实现:

  1. #include <opencv2/opencv.hpp>
  2. #include <iostream>
  3. using namespace cv;
  4. using namespace std;
  5. int main(int argc, char** argv) {
  6. // Mat image = imread("D:/images/butterfly.jpg");
  7. Mat book = imread("D:/images/book.jpg");
  8. Mat book_on_desk = imread("D:/images/book_on_desk.jpg");
  9. namedWindow("book", WINDOW_FREERATIO);
  10. imshow("book", book);
  11. auto orb = ORB::create(500);
  12. vector<KeyPoint> kypts_book;
  13. vector<KeyPoint> kypts_book_on_desk;
  14. Mat desc_book, desc_book_on_desk;
  15. orb->detectAndCompute(book, Mat(), kypts_book, desc_book);
  16. orb->detectAndCompute(book_on_desk, Mat(), kypts_book_on_desk, desc_book_on_desk);
  17. Mat result;
  18. auto bf_matcher = BFMatcher::create(NORM_HAMMING, false);
  19. vector<DMatch> matches;
  20. bf_matcher->match(desc_book, desc_book_on_desk, matches);
  21. float good_rate = 0.15f;
  22. int num_good_matches = matches.size() * good_rate;
  23. std::cout << num_good_matches << std::endl;
  24. std::sort(matches.begin(), matches.end());
  25. matches.erase(matches.begin() + num_good_matches, matches.end());
  26. drawMatches(book, kypts_book, book_on_desk, kypts_book_on_desk, matches, result);
  27. vector<Point2f> obj_pts;
  28. vector<Point2f> scene_pts;
  29. for (size_t t = 0; t < matches.size(); t++) {
  30. obj_pts.push_back(kypts_book[matches[t].queryIdx].pt);
  31. scene_pts.push_back(kypts_book_on_desk[matches[t].trainIdx].pt);
  32. }
  33. Mat h = findHomography(obj_pts, scene_pts, RANSAC); // 计算单应性矩阵h
  34. vector<Point2f> srcPts;
  35. srcPts.push_back(Point2f(0, 0));
  36. srcPts.push_back(Point2f(book.cols, 0));
  37. srcPts.push_back(Point2f(book.cols, book.rows));
  38. srcPts.push_back(Point2f(0, book.rows));
  39. std::vector<Point2f> dstPts(4);
  40. perspectiveTransform(srcPts, dstPts, h); // 计算转换后书的四个顶点
  41. for (int i = 0; i < 4; i++) {
  42. line(book_on_desk, dstPts[i], dstPts[(i + 1) % 4], Scalar(0, 0, 255), 2, 8, 0);
  43. }
  44. namedWindow("暴力匹配", WINDOW_FREERATIO);
  45. imshow("暴力匹配", result);
  46. namedWindow("对象检测", WINDOW_FREERATIO);
  47. imshow("对象检测", book_on_desk);
  48. //imwrite("D:/object_find.png", book_on_desk);
  49. waitKey(0);
  50. return 0;
  51. }

效果:

8、文档对齐

  • 模板表单/文档
  • 特征匹配与对齐

代码实现:

  1. #include <opencv2/opencv.hpp>
  2. #include <iostream>
  3. using namespace cv;
  4. using namespace std;
  5. int main(int argc, char** argv) {
  6. Mat ref_img = imread("D:/images/form.png");
  7. Mat img = imread("D:/images/form_in_doc.jpg");
  8. imshow("表单模板", ref_img);
  9. auto orb = ORB::create(500);
  10. vector<KeyPoint> kypts_ref;
  11. vector<KeyPoint> kypts_img;
  12. Mat desc_book, desc_book_on_desk;
  13. orb->detectAndCompute(ref_img, Mat(), kypts_ref, desc_book);
  14. orb->detectAndCompute(img, Mat(), kypts_img, desc_book_on_desk);
  15. Mat result;
  16. auto bf_matcher = BFMatcher::create(NORM_HAMMING, false);
  17. vector<DMatch> matches;
  18. bf_matcher->match(desc_book_on_desk, desc_book, matches);
  19. float good_rate = 0.15f;
  20. int num_good_matches = matches.size() * good_rate;
  21. std::cout << num_good_matches << std::endl;
  22. std::sort(matches.begin(), matches.end());
  23. matches.erase(matches.begin() + num_good_matches, matches.end());
  24. drawMatches(ref_img, kypts_ref, img, kypts_img, matches, result);
  25. imshow("匹配", result);
  26. imwrite("D:/images/result_doc.png", result);
  27. // Extract location of good matches
  28. std::vector<Point2f> points1, points2;
  29. for (size_t i = 0; i < matches.size(); i++)
  30. {
  31. points1.push_back(kypts_img[matches[i].queryIdx].pt);
  32. points2.push_back(kypts_ref[matches[i].trainIdx].pt);
  33. }
  34. Mat h = findHomography(points1, points2, RANSAC); // 尽量用RANSAC,比最小二乘法效果好一些
  35. Mat aligned_doc;
  36. warpPerspective(img, aligned_doc, h, ref_img.size()); // 单应性矩阵h决定了其他无效区域不会被变换,只会变换target区域
  37. imwrite("D:/images/aligned_doc.png", aligned_doc);
  38. waitKey(0);
  39. destroyAllWindows();
  40. return 0;
  41. }

效果:

9、图像拼接

  • 特征检测与匹配
  • 图像对齐与变换
  • 图像边缘融合

代码实现:

  1. #include <opencv2/opencv.hpp>
  2. #include <iostream>
  3. #define RATIO 0.8
  4. using namespace std;
  5. using namespace cv;
  6. void linspace(Mat& image, float begin, float finish, int number, Mat &mask);
  7. void generate_mask(Mat &img, Mat &mask);
  8. int main(int argc, char** argv) {
  9. Mat left = imread("D:/images/q11.jpg");
  10. Mat right = imread("D:/images/q22.jpg");
  11. if (left.empty() || right.empty()) {
  12. printf("could not load image...\n");
  13. return -1;
  14. }
  15. // 提取特征点与描述子
  16. vector<KeyPoint> keypoints_right, keypoints_left;
  17. Mat descriptors_right, descriptors_left;
  18. auto detector = AKAZE::create();
  19. detector->detectAndCompute(left, Mat(), keypoints_left, descriptors_left);
  20. detector->detectAndCompute(right, Mat(), keypoints_right, descriptors_right);
  21. // 暴力匹配
  22. vector<DMatch> matches;
  23. auto matcher = DescriptorMatcher::create(DescriptorMatcher::BRUTEFORCE);
  24. // 发现匹配
  25. std::vector< std::vector<DMatch> > knn_matches;
  26. matcher->knnMatch(descriptors_left, descriptors_right, knn_matches, 2);
  27. const float ratio_thresh = 0.7f;
  28. std::vector<DMatch> good_matches;
  29. for (size_t i = 0; i < knn_matches.size(); i++)
  30. {
  31. if (knn_matches[i][0].distance < ratio_thresh * knn_matches[i][1].distance)
  32. {
  33. good_matches.push_back(knn_matches[i][0]);
  34. }
  35. }
  36. printf("total good match points : %d\n", good_matches.size());
  37. std::cout << std::endl;
  38. Mat dst;
  39. drawMatches(left, keypoints_left, right, keypoints_right, good_matches, dst);
  40. imshow("output", dst);
  41. imwrite("D:/images/good_matches.png", dst);
  42. //-- Localize the object
  43. std::vector<Point2f> left_pts;
  44. std::vector<Point2f> right_pts;
  45. for (size_t i = 0; i < good_matches.size(); i++)
  46. {
  47. // 收集所有好的匹配点
  48. left_pts.push_back(keypoints_left[good_matches[i].queryIdx].pt);
  49. right_pts.push_back(keypoints_right[good_matches[i].trainIdx].pt);
  50. }
  51. // 配准与对齐,对齐到第一张
  52. Mat H = findHomography(right_pts, left_pts, RANSAC);
  53. // 获取全景图大小
  54. int h = max(left.rows, right.rows);
  55. int w = left.cols + right.cols;
  56. Mat panorama_01 = Mat::zeros(Size(w, h), CV_8UC3);
  57. Rect roi;
  58. roi.x = 0;
  59. roi.y = 0;
  60. roi.width = left.cols;
  61. roi.height = left.rows;
  62. // 获取左侧与右侧对齐图像
  63. left.copyTo(panorama_01(roi));
  64. imwrite("D:/images/panorama_01.png", panorama_01);
  65. Mat panorama_02;
  66. warpPerspective(right, panorama_02, H, Size(w, h));
  67. imwrite("D:/images/panorama_02.png", panorama_02);
  68. // 计算融合重叠区域mask
  69. Mat mask = Mat::zeros(Size(w, h), CV_8UC1);
  70. generate_mask(panorama_02, mask);
  71. // 创建遮罩层并根据mask完成权重初始化
  72. Mat mask1 = Mat::ones(Size(w, h), CV_32FC1);
  73. Mat mask2 = Mat::ones(Size(w, h), CV_32FC1);
  74. // left mask
  75. linspace(mask1, 1, 0, left.cols, mask);
  76. // right mask
  77. linspace(mask2, 0, 1, left.cols, mask);
  78. namedWindow("mask1", WINDOW_FREERATIO);
  79. imshow("mask1", mask1);
  80. namedWindow("mask2", WINDOW_FREERATIO);
  81. imshow("mask2", mask2);
  82. // 左侧融合
  83. Mat m1;
  84. vector<Mat> mv;
  85. mv.push_back(mask1);
  86. mv.push_back(mask1);
  87. mv.push_back(mask1);
  88. merge(mv, m1);
  89. panorama_01.convertTo(panorama_01, CV_32F);
  90. multiply(panorama_01, m1, panorama_01);
  91. // 右侧融合
  92. mv.clear();
  93. mv.push_back(mask2);
  94. mv.push_back(mask2);
  95. mv.push_back(mask2);
  96. Mat m2;
  97. merge(mv, m2);
  98. panorama_02.convertTo(panorama_02, CV_32F);
  99. multiply(panorama_02, m2, panorama_02);
  100. // 合并全景图
  101. Mat panorama;
  102. add(panorama_01, panorama_02, panorama);
  103. panorama.convertTo(panorama, CV_8U);
  104. imwrite("D:/images/panorama.png", panorama);
  105. waitKey(0);
  106. return 0;
  107. }
  108. void generate_mask(Mat &img, Mat &mask) {
  109. int w = img.cols;
  110. int h = img.rows;
  111. for (int row = 0; row < h; row++) {
  112. for (int col = 0; col < w; col++) {
  113. Vec3b p = img.at<Vec3b>(row, col);
  114. int b = p[0];
  115. int g = p[1];
  116. int r = p[2];
  117. if (b == g && g == r && r == 0) {
  118. mask.at<uchar>(row, col) = 255;
  119. }
  120. }
  121. }
  122. imwrite("D:/images/mask.png", mask);
  123. }
  124. // 对mask中的0区域,进行逐行计算每个像素的权重值
  125. void linspace(Mat& image, float begin, float finish, int w1, Mat &mask) {
  126. int offsetx = 0;
  127. float interval = 0;
  128. float delta = 0;
  129. for (int i = 0; i < image.rows; i++) {
  130. offsetx = 0;
  131. interval = 0;
  132. delta = 0;
  133. for (int j = 0; j < image.cols; j++) {
  134. int pv = mask.at<uchar>(i, j);
  135. if (pv == 0 && offsetx == 0) {
  136. offsetx = j;
  137. delta = w1 - offsetx;
  138. interval = (finish - begin) / (delta - 1); // 计算每个像素变化的大小
  139. image.at<float>(i, j) = begin + (j - offsetx)*interval;
  140. }
  141. else if (pv == 0 && offsetx > 0 && (j - offsetx) < delta) {
  142. image.at<float>(i, j) = begin + (j - offsetx)*interval;
  143. }
  144. }
  145. }
  146. }

效果:

1、图像拼接重合区域mask

2、拼接前图像

3、图像拼接效果图

10、条码标签定位与有无判定

代码实现:

ORBDetector.h

  1. #pragma once
  2. #include <opencv2/opencv.hpp>
  3. class ORBDetector {
  4. public:
  5. ORBDetector(void);
  6. ~ORBDetector(void);
  7. void initORB(cv::Mat &refImg);
  8. bool detect_and_analysis(cv::Mat &image, cv::Mat &aligned);
  9. private:
  10. cv::Ptr<cv::ORB> orb = cv::ORB::create(500);
  11. std::vector<cv::KeyPoint> tpl_kps;
  12. cv::Mat tpl_descriptors;
  13. cv::Mat tpl;
  14. };

ORBDetector.cpp

  1. #include "ORBDetector.h"
  2. ORBDetector::ORBDetector() {
  3. std::cout << "create orb detector..." << std::endl;
  4. }
  5. ORBDetector::~ORBDetector() {
  6. this->tpl_descriptors.release();
  7. this->tpl_kps.clear();
  8. this->orb.release();
  9. this->tpl.release();
  10. std::cout << "destory instance..." << std::endl;
  11. }
  12. void ORBDetector::initORB(cv::Mat &refImg) {
  13. if (!refImg.empty()) {
  14. cv::Mat tplGray;
  15. cv::cvtColor(refImg, tplGray, cv::COLOR_BGR2GRAY);
  16. orb->detectAndCompute(tplGray, cv::Mat(), this->tpl_kps, this->tpl_descriptors);
  17. tplGray.copyTo(this->tpl);
  18. }
  19. }
  20. bool ORBDetector::detect_and_analysis(cv::Mat &image, cv::Mat &aligned) {
  21. // keypoints and match threshold
  22. float GOOD_MATCH_PERCENT = 0.15f;
  23. bool found = true;
  24. // 处理数据集中每一张数据
  25. cv::Mat img2Gray;
  26. cv::cvtColor(image, img2Gray, cv::COLOR_BGR2GRAY);
  27. std::vector<cv::KeyPoint> img_kps;
  28. cv::Mat img_descriptors;
  29. orb->detectAndCompute(img2Gray, cv::Mat(), img_kps, img_descriptors);
  30. std::vector<cv::DMatch> matches;
  31. cv::Ptr<cv::DescriptorMatcher> matcher = cv::DescriptorMatcher::create("BruteForce-Hamming");
  32. // auto flann_matcher = cv::FlannBasedMatcher(new cv::flann::LshIndexParams(6, 12, 2));
  33. matcher->match(img_descriptors, this->tpl_descriptors, matches, cv::Mat());
  34. // Sort matches by score
  35. std::sort(matches.begin(), matches.end());
  36. // Remove not so good matches
  37. const int numGoodMatches = matches.size() * GOOD_MATCH_PERCENT;
  38. matches.erase(matches.begin() + numGoodMatches, matches.end());
  39. // std::cout << numGoodMatches <<"distance:"<<matches [0].distance<< std::endl;
  40. if (matches[0].distance > 30) {
  41. found = false;
  42. }
  43. // Extract location of good matches
  44. std::vector<cv::Point2f> points1, points2;
  45. for (size_t i = 0; i < matches.size(); i++)
  46. {
  47. points1.push_back(img_kps[matches[i].queryIdx].pt);
  48. points2.push_back(tpl_kps[matches[i].trainIdx].pt);
  49. }
  50. cv::Mat H = findHomography(points1, points2, cv::RANSAC);
  51. cv::Mat im2Reg;
  52. warpPerspective(image, im2Reg, H, tpl.size());
  53. // 逆时针旋转90度
  54. cv::Mat result;
  55. cv::rotate(im2Reg, result, cv::ROTATE_90_COUNTERCLOCKWISE);
  56. result.copyTo(aligned);
  57. return found;
  58. }

object_analysis.cpp

  1. #include "ORBDetector.h"
  2. #include <iostream>
  3. using namespace cv;
  4. using namespace std;
  5. int main(int argc, char** argv) {
  6. Mat refImg = imread("D:/facedb/tiaoma/tpl2.png");
  7. ORBDetector orb_detector;
  8. orb_detector.initORB(refImg);
  9. vector<std::string> files;
  10. glob("D:/facedb/orb_barcode", files);
  11. cv::Mat temp;
  12. for (auto file : files) {
  13. std::cout << file << std::endl;
  14. cv::Mat image = imread(file);
  15. int64 start = getTickCount();
  16. bool OK = orb_detector.detect_and_analysis(image, temp);
  17. double ct = (getTickCount() - start) / getTickFrequency();
  18. printf("decode time: %.5f ms\n", ct * 1000);
  19. std::cout << "标签: " << (OK == true) << std::endl;
  20. imshow("temp", temp);
  21. waitKey(0);
  22. }
  23. }

效果:

1、检测图片

2、模板图片

11、DNN概述

DNN模块介绍:

  • DNN - Deep Neutal Network
  • OpenCV3.3 开始发布
  • 支持VOC与COCO数据集的对象检测模型
  • 包括SSD/Faster-RCNN/YOLOv4等
  • 支持自定义对象检测
  • 支持人脸检测

函数知识:

  • 读取模型
  • 转换数据与设置
  • 推理输出
  1. Net net = readNetFromTensorflow(model, config); // 支持tensorflow
  2. Net net = readNetFromCaffe(config, model); // 支持caffe
  3. Net net = readNetFromONNX(onnxfile);
  4. // 读取数据
  5. Mat image = imread("D:/images/example.png");
  6. Mat blob_img = blobFromImage(image, scalefactor, size, mean, swapRB);
  7. net.setInput(blob_img);
  8. // 推理输出
  9. Mat result = net.forward();

后处理/输出解析:

  • 不同网络的输出不同
  • 如何解析要根据模型输出
  • 对象检测网络SSD/Faster-RCNN解析

SSD的输出解析:

Faster-RCNN输出解析:

YOLOv4输出解析:

OpenCV4之特征提取与对象检测的更多相关文章

  1. [OpenCV-Python] OpenCV 中计算摄影学 部分 IX 对象检测 部分 X

    部分 IX计算摄影学 OpenCV-Python 中文教程(搬运)目录 49 图像去噪目标 • 学习使用非局部平均值去噪算法去除图像中的噪音 • 学习函数 cv2.fastNlMeansDenoisi ...

  2. 浏览器。浏览器对象检测、Chrome调试工具

    chrome浏览器的flash问题: 2017-12-26 chrome浏览器的flash有无法显示无法正常运行的问题时,解决方法如下: https://qzonestyle.gtimg.cn/qzo ...

  3. 斯坦福新深度学习系统 NoScope:视频对象检测快1000倍

    以作备份,来源http://jiasuhui.com/archives/178954 本文由“新智元”(微信ID:AI_era)编译,来源:dawn.cs.stanford.edu,编译:刘小芹 斯坦 ...

  4. 计算机视觉中的对象检测,Python用几段代码就能实现

    目前计算机视觉(CV)与自然语言处理(NLP)及语音识别并列为人工智能三大热点方向,而计算机视觉中的对象检测(objectdetection)应用非常广泛,比如自动驾驶.视频监控.工业质检.医疗诊断等 ...

  5. [object_detect]使用MobileNetSSD进行对象检测

    使用MobileNetSSD进行对象检测 1.单帧图片识别 object_detection.py # 导入必要的包 import numpy as np import argparse import ...

  6. [OpenCV实战]13 OpenCV中使用Mask R-CNN进行对象检测和实例分割

    目录 1 背景介绍 1.1 什么是图像分割和实例分割 1.2 Mask-RCNN原理 2 Mask-RCNN在OpenCV中的使用 2.1 模型下载 2.2 模型初始化 2.3 模型加载 2.4 输出 ...

  7. 【YOLO】实时对象检测使用体验

    官网:https://pjreddie.com/darknet/yolo/ 以下全部在服务器上完成,服务器上是有opencv等. 1.安装Darknet git clone https://githu ...

  8. 常用 对象检测 api

    isPrototypeOf()    判断某个 proptotype 对象和某个实例之间的关系 alert(Cat.prototype.isPrototypeOf(cat1)); //true ale ...

  9. python imageai 对象检测、对象识别

    imageai库里面提供了目标识别,其实也可以说是目标检测,和现在很多的收集一样就是物体识别.他可以帮你识别出各种各样生活中遇见的事物.比如猫.狗.车.马.人.电脑.收集等等. 感觉imageai有点 ...

  10. 针对unicode对象---检测字符串是否只由数字组成

随机推荐

  1. boot-admin整合Quartz实现动态管理定时任务

    淄博烧烤爆红出了圈,当你坐在八大局的烧烤摊,面前是火炉.烤串.小饼和蘸料,音乐响起,啤酒倒满,烧烤灵魂的party即将开场的时候,你系统中的Scheduler(调试器),也自动根据设定的Trigger ...

  2. 搭建SpringBoot项目依赖和配置快速篇

    maven依赖及一些配置 这里主要是搭建项目常用到的maven依赖以及搭建项目会需要用到的一些配置文件,可能下面这些依赖还不是很全,但是应该会满足日常大部分的需求了 Spring Spring项目的依 ...

  3. 【故障补牢】贪吃的 Bing 爬虫,限量供应的应对措施

    相对于[故障公告],[故障补牢]分享的是园子在发生故障后采取的亡羊补牢措施. 在上次被微软 Bing 爬宕机后(详见 [故障公告]被放出的 Bing 爬虫,又被爬宕机的园子),我们采取了2个应对措施, ...

  4. python Unitest和pytest 介绍和安装

    前言 目前有两种纯测试的测试框架,pytest和unittest,这系列文章主要介绍pytest为主 UnitTest测试框架理论 python 自带的单元测试框架,常用在单元测试 在自动化测试中提供 ...

  5. 2023-02-24:请用go语言调用ffmpeg,解码mp4文件并保存为YUV420SP格式文件,采用YUV420P转YUV420SP的方式。

    2023-02-24:请用go语言调用ffmpeg,解码mp4文件并保存为YUV420SP格式文件,采用YUV420P转YUV420SP的方式. 答案2023-02-24: 使用 github.com ...

  6. 2022-05-01:golang里,结构体B里包含一个结构体A和一个整型成员变量。现在要给结构体A实现一个方法,让它能访问到B的整型变量,这个方法应该怎么写? 如果还有结构体C,D,E,F...都和

    2022-05-01:golang里,结构体B里包含一个结构体A和一个整型成员变量.现在要给结构体A实现一个方法,让它能访问到B的整型变量,这个方法应该怎么写? 如果还有结构体C,D,E,F-都和B一 ...

  7. 2021-10-15:单词拆分。给定一个非空字符串 s 和一个包含非空单词的列表 wordDict,判定 s 是否可以被空格拆分为一个或多个在字典中出现的单词。说明:拆分时可以重复使用字典中的单词。你

    2021-10-15:单词拆分.给定一个非空字符串 s 和一个包含非空单词的列表 wordDict,判定 s 是否可以被空格拆分为一个或多个在字典中出现的单词.说明:拆分时可以重复使用字典中的单词.你 ...

  8. PyCharm-汉化、中文语言包、英文语言包、中英文切换

    PyCharm的汉化是非常简单的,不需要繁琐的步骤,只需要到设置的插件中搜索你需要的语言包安装即可. 登录 进入项目(随便进入一个项目,新建也可以) File->settings->Plu ...

  9. 合合信息亮相CCIG2023:多位大咖共话智能文档未来,文档图像内容安全还面临哪些技术难题?

    ​ 近日,中国图象图形大会(CCIG 2023)(简称"大会")在苏州圆满落幕.本届大会以"图象图形·向未来"为主题,由中国科学技术协会指导,中国图象图形学学会 ...

  10. protoBuf 实现客户端与服务端

    转载请注明出处: 1.定义消息格式 在 src/main/proto 目录下创建 person.proto 文件,并定义消息格式,例如: syntax = "proto3"; pa ...