打开APP
userphoto
未登录

开通VIP,畅享免费电子书等14项超值服

开通VIP
opencv笔记
在 ”光流跟踪“ 中,使用了 Harris 角点作为 LK 光流跟踪输入点。角点定义为在两个方向上均有较大梯度变化的小区域,使用自相关函数描述。
自相关函数为为图像平移前后某一个区域的相似度度量。图像可以看作二维平面上的连续函数,使用泰勒级数可以将自相关函数转换为自相关矩阵。
通过分析自相关矩阵的特征值,可以判断在该区域内各个方向上梯度变化情况,从而决定是否为角点。
在 opencv 中,cv::goodFeatrueToTrack 函数可以提取 Harris 角点,其中参数 useHarrisDetector 决定使用 Harris 判定依据还是 Shi-Tomasi 判定依据。
使用 cv::GFTTDetector 可以实现与 cv::goodFeatrueToTrack 基本一致的功能。然而,cv::GFTTDetector 继承自 cv::Feature2D,将角点视为通用关键点,可以对角点做更多后续处理(如特征点描述)。
以下是 cv::GFTTDetector 的构造函数,其参数与 cv::goodFeatrueToTrack 基本一致,如下:
static Ptr<GFTTDetector> create( int maxCorners=1000, double qualityLevel=0.01, double minDistance=1,
int blockSize=3, bool useHarrisDetector=false, double k=0.04 );
其中,maxCorners 决定提取关键点最大个数;
qualityLevel 表示关键点强度阈值,只保留大于该阈值的关键点(阈值 = 最强关键点强度 * qualityLevel);
minDistance 决定关键点间的最小距离;
blockSize 决定自相关函数累加区域,也决定了 Sobel 梯度时窗口尺寸;
useHarrisDetector 决定使用 Harris 判定依据还是 Shi-Tomasi 判定依据;
k 仅在使用 Harris 判定依据时有效;
以下代码使用 cv::GFTTDetector 检测角点并绘制角点:
1 // 检测 keypoints 2 std::vector<cv::KeyPoint> key_points; 3 cv::Ptr<cv::GFTTDetector> gftt = cv::GFTTDetector::create(1000, .3, 5., 3, false); 4 gftt->detect(img, key_points); 5 6 // 使用 opencv 内置函数绘制 keypoints 7 cv::Mat key_points_img; 8 cv::drawKeypoints(img, key_points, key_points_img, 9 cv::Scalar(255, 0, 0), cv::DrawMatchesFlags::DEFAULT);10 11 // 读取 cv::KeyPoint 绘制 keypoints12 cv::Mat key_points_img2;13 cv::cvtColor(img, key_points_img2, CV_GRAY2BGR);14 for (int i = 0; i < key_points.size(); ++i)15 {16 cv::Point c(key_points[i].pt.x, key_points[i].pt.y);17 float r = key_points[i].size / 2.;18 int draw_r = r * 2.;19 cv::circle(key_points_img2, c, draw_r, cv::Scalar(255, 0, 0));20 }
cv::GFTTDetector 内部如何实现角点检测呢,opencv 提供了以下几个基本函数:
1)void cornerEigenValsAndVecs( InputArray src, OutputArray dst,
int blockSize, int ksize,
int borderType = BORDER_DEFAULT );
2)void cornerHarris( InputArray src, OutputArray dst, int blockSize,
int ksize, double k,
int borderType = BORDER_DEFAULT );
3) void cornerMinEigenVal( InputArray src, OutputArray dst,
int blockSize, int ksize = 3,
int borderType = BORDER_DEFAULT );
其中,函数1 仅仅级数图像上每个点上自相关函数的特征值与特征向量,函数2 在 函数1 基础上应用 Harris 判定依据检测角点,函数3 在 函数1 基础上应用 Shi-Tomasi 判定依据检测角点。
以上函数参数含义与 cv::GFTTDetector create 函数基本一致,其中 blockSize 表示自相关函数计算窗口,ksize 表示 Sobel 函数计算窗口。
通过以上函数,可以自己构造一个角点检测算法:
1 void harrisKeyPoints(cv::Mat& img, 2 std::vector<cv::Point>& key_points, std::vector<cv::Point2f>& eigen_vals) 3 { 4 cv::Mat smt_img; 5 cv::GaussianBlur(img, smt_img, cv::Size(3, 3), 0); 6 7 // 使用 cornerEigenValsAndVecs 提取特征值与特征向量 8 cv::Mat eigen_img = cv::Mat::zeros(img.size(), CV_32FC(6)); 9 cv::cornerEigenValsAndVecs(smt_img, eigen_img, 3, 3); 10 11 // 计算每个点上最小特征值 12 cv::Mat min_eigen_img = cv::Mat::zeros(eigen_img.size(), CV_32FC1); 13 cv::Mat max_eigen_img = cv::Mat::zeros(eigen_img.size(), CV_32FC1); 14 for (int y = 0; y < eigen_img.rows; ++y) 15 { 16 float *eigen_data = eigen_img.ptr<float>(y); 17 float *min_eigen_data = min_eigen_img.ptr<float>(y); 18 float *max_eigen_data = max_eigen_img.ptr<float>(y); 19 for (int x = 0; x < eigen_img.cols; ++x) 20 { 21 float eigen_val1 = eigen_data[x * 6]; 22 float eigen_val2 = eigen_data[x * 6 + 1]; 23 if (eigen_val1 > eigen_val2) 24 std::swap(eigen_val1, eigen_val2); 25 26 min_eigen_data[x] = eigen_val1; 27 max_eigen_data[x] = eigen_val2; 28 } 29 } 30 31 // 计算特征值阈值 32 double min_eigen = 0.; 33 double max_eigen = 0.; 34 minMaxIdx(min_eigen_img, &min_eigen, &max_eigen); 35 float eigen_threshold = max_eigen * .15; // 保留 15% 最大强度特征点 36 37 // 保留满足条件的特征点 38 const int min_dist_permitted = 10 * 10; // 特征点间最小距离 39 const int max_size_permitted = 50; // 特征点最大数量 40 41 key_points.clear(); 42 eigen_vals.clear(); 43 int key_points_size = 0; 44 45 float max_eigen_val = 0.; 46 float min_eigen_val = 10000.; 47 int min_eigen_val_idx = -1; 48 49 for (int y = 0; y < min_eigen_img.rows; ++y) 50 { 51 float *data = min_eigen_img.ptr<float>(y); 52 for (int x = 0; x < min_eigen_img.cols; ++x) 53 { 54 if (data[x] > eigen_threshold) 55 { 56 if (key_points_size < max_size_permitted) 57 { 58 bool already_exists = false; 59 for (int i = 0; i < key_points_size; ++i) 60 { 61 int dist = (x - key_points[i].x) * (x - key_points[i].x) + 62 (y - key_points[i].y) * (y - key_points[i].y); 63 if (dist < min_dist_permitted) 64 { 65 already_exists = true; 66 if (eigen_vals[i].x < data[x]) 67 { 68 // 列表中存在邻近点且特征值较小,使用新值替代 69 key_points[i].x = x; 70 key_points[i].y = y; 71 eigen_vals[i].x = data[x]; 72 eigen_vals[i].y = max_eigen_img.ptr<float>(y)[x]; 73 74 if (max_eigen_val < data[x]) 75 max_eigen_val = data[x]; 76 if (min_eigen_val > data[x]) 77 { 78 min_eigen_val = data[x]; 79 min_eigen_val_idx = i; 80 } 81 } 82 } 83 } 84 85 if (!already_exists) 86 { 87 // 列表中不存在邻近点,添加新值到列表中 88 key_points.push_back(cv::Point(x, y)); 89 eigen_vals.push_back(cv::Point2f(data[x], max_eigen_img.ptr<float>(y)[x])); 90 ++key_points_size; 91 92 if (max_eigen_val < data[x]) 93 max_eigen_val = data[x]; 94 if (min_eigen_val > data[x]) 95 { 96 min_eigen_val = data[x]; 97 min_eigen_val_idx = key_points_size - 1; 98 } 99 }100 }101 else102 {103 // 特征值小于列表中最小值,不更新104 if (data[x] <= min_eigen_val)105 continue;106 107 // 是否存在距离较近元素108 bool already_exists = false;109 for (int i = 0; i < key_points_size; ++i)110 {111 int dist = (x - key_points[i].x) * (x - key_points[i].x) +112 (y - key_points[i].y) * (y - key_points[i].y);113 if (dist < min_dist_permitted)114 {115 already_exists = true;116 if (eigen_vals[i].x < data[x])117 {118 key_points[i].x = x;119 key_points[i].y = y;120 eigen_vals[i].x = data[x];121 eigen_vals[i].y = max_eigen_img.ptr<float>(y)[x];122 123 if (max_eigen_val < data[x])124 max_eigen_val = data[x];125 }126 }127 }128 129 // 当不存在较近元素时替换最小元素130 if (!already_exists)131 {132 key_points[min_eigen_val_idx].x = x;133 key_points[min_eigen_val_idx].y = y;134 eigen_vals[min_eigen_val_idx].x = data[x];135 eigen_vals[min_eigen_val_idx].y = max_eigen_img.ptr<float>(y)[x];136 137 // 重新搜索最大最小元素138 max_eigen_val = 0.;139 min_eigen_val = 10000.;140 min_eigen_val_idx = -1;141 142 for (int i = 0; i < key_points_size; ++i)143 {144 float val = eigen_vals[i].x;145 if (max_eigen_val < val)146 max_eigen_val = val;147 if (min_eigen_val > val)148 {149 min_eigen_val = val;150 min_eigen_val_idx = i;151 }152 }153 }154 }155 }156 }157 }158 159 // 绘制特征点160 cv::Mat color_img;161 cv::cvtColor(img, color_img, CV_GRAY2BGR);162 for (int i = 0; i < key_points.size(); ++i)163 cv::circle(color_img, key_points[i], 5, cv::Scalar(255, 0, 0), 1, cv::LINE_AA);164 165 }
由于每一个关键点对应一对特征值,这里使用特征值作为关键点的简单描述符,尝试关键点匹配(效果较差),代码如下:
1 void matchKeypoints(std::vector<cv::Point>& prev_key_points, std::vector<cv::Point2f>& prev_eigen_vals, 2 std::vector<cv::Point>& next_key_points, std::vector<cv::Point2f>& next_eigen_vals, std::vector<cv::Point>& match) 3 { 4 5 std::vector<int> prev_used; 6 std::vector<int> next_used; 7 for (int i = 0; i < prev_eigen_vals.size(); ++i) 8 prev_used.push_back(0); 9 for (int i = 0; i < next_eigen_vals.size(); ++i)10 next_used.push_back(0);11 12 match.clear();13 for (int i = 0; i < prev_eigen_vals.size(); ++i)14 {15 if (prev_used[i] > 0)16 continue;17 18 float min_dist = .0001 * .0001;19 int min_dist_idx = -1;20 for (int j = 0; j < next_eigen_vals.size(); ++j)21 {22 if (next_used[j] > 0)23 continue;24 25 float dist = (prev_eigen_vals[i].x - next_eigen_vals[j].x) *26 (prev_eigen_vals[i].x - next_eigen_vals[j].x) +27 (prev_eigen_vals[i].y - next_eigen_vals[j].y) *28 (prev_eigen_vals[i].y - next_eigen_vals[j].y);29 if (dist < min_dist)30 {31 min_dist = dist;32 min_dist_idx = j;33 }34 }35 36 if (min_dist_idx < 0)37 continue;38 39 float min_dist2 = .0001 * .0001;40 int min_dist_idx2 = -1;41 for (int k = 0; k < prev_eigen_vals.size(); ++k)42 {43 if (prev_used[k] > 0)44 continue;45 46 float dist = (prev_eigen_vals[k].x - next_eigen_vals[min_dist_idx].x) *47 (prev_eigen_vals[k].x - next_eigen_vals[min_dist_idx].x) +48 (prev_eigen_vals[k].y - next_eigen_vals[min_dist_idx].y) *49 (prev_eigen_vals[k].y - next_eigen_vals[min_dist_idx].y);50 if (dist < min_dist2)51 {52 min_dist2 = dist;53 min_dist_idx2 = k;54 }55 }56 57 if (min_dist < min_dist2)58 {59 if (min_dist_idx >= 0)60 {61 match.push_back(cv::Point(i, min_dist_idx));62 prev_used[i] = 1;63 next_used[min_dist_idx] = 1;64 }65 }66 else67 {68 if (min_dist_idx2 >= 0)69 {70 match.push_back(cv::Point(min_dist_idx2, min_dist_idx));71 prev_used[min_dist_idx2] = 1;72 next_used[min_dist_idx] = 1;73 }74 }75 }76 }
1 // 分别求两幅图像关键点,将对应特征值向量作为关键点描述符 2 std::vector<cv::Point> key_points; 3 std::vector<cv::Point2f> eigen_vals; 4 harrisKeyPoints(img, key_points, eigen_vals, 1); 5 6 std::vector<cv::Point> key_points2; 7 std::vector<cv::Point2f> eigen_vals2; 8 harrisKeyPoints(img2, key_points2, eigen_vals2, 0); 9 10 // 简单匹配11 std::vector<cv::Point> match;12 matchKeypoints(key_points, eigen_vals, key_points2, eigen_vals2, match);13 14 // 输出匹配结果15 cv::Mat match_img = cv::Mat::zeros(img.rows, img.cols * 2 + 4, CV_8UC1);16 for (int y = 0; y < match_img.rows; ++y)17 {18 uchar *dst_data1 = match_img.ptr<uchar>(y);19 uchar *dst_data2 = dst_data1 + img.cols + 4;20 uchar *src_data1 = img.ptr<uchar>(y);21 uchar *src_data2 = img2.ptr<uchar>(y);22 23 memcpy(dst_data1, src_data1, img.cols);24 memcpy(dst_data2, src_data2, img2.cols);25 }26 27 for (int i = 0; i < match.size(); ++i)28 {29 cv::Point pt1(key_points[match[i].x]);30 cv::Point pt2(key_points2[match[i].y]);31 pt2.x += img.cols + 4;32 cv::line(match_img, pt1, pt2, cv::Scalar(255), 2);33 }34
参考资料 Learning OpenCV 3   Adrian Kaehler & Gary Bradski
语言方法
44617A0Ob
pnPYz利用抖音封面吸粉的技巧
61332009/06/15 08:15:51
本站仅提供存储服务,所有内容均由用户发布,如发现有害或侵权内容,请点击举报
打开APP,阅读全文并永久保存 查看更多类似文章
猜你喜欢
类似文章
【热】打开小程序,算一算2024你的财运
Finding distance between two curves
Armadillo,Eigen,OpenCV 矩阵操作比较(Compare Armadillo, Eigen and OpenCV)
SLAM程序阅读(第8讲 稀疏直接法)
Cv中文参考手册之二------图像轮廓处理
OpenCv常用图像和矩阵操作
详解车道线检测算法之传统图像处理
更多类似文章 >>
生活服务
热点新闻
分享 收藏 导长图 关注 下载文章
绑定账号成功
后续可登录账号畅享VIP特权!
如果VIP功能使用有故障,
可点击这里联系客服!

联系客服