一、Harris角点检测算法原理
1.1 数学原理
Harris角点响应函数:
R = det(M) - k*(trace(M))^2
其中:
M = ∑[Ix² IxIy]
[IxIy Iy²]
det(M) = λ1*λ2
trace(M) = λ1 + λ2
k = 0.04~0.06
1.2 算法步骤
- 计算图像梯度
- 计算自相关矩阵M
- 计算角点响应R
- 非极大值抑制
- 阈值筛选
二、VC++完整实现代码
2.1 头文件定义
cpp
// HarrisFeature.h
#pragma once
#ifndef HARRIS_FEATURE_H
#define HARRIS_FEATURE_H
#include <opencv2/core.hpp>
#include <opencv2/highgui.hpp>
#include <opencv2/imgproc.hpp>
#include <opencv2/features2d.hpp>
#include <opencv2/calib3d.hpp>
#include <vector>
#include <cmath>
#include <algorithm>
#include <iostream>
#include <fstream>
using namespace cv;
using namespace std;
// Harris角点结构
struct HarrisCorner {
Point2f pt; // 角点位置
float response; // 响应值
float orientation; // 主方向
Size scale; // 尺度
HarrisCorner() : pt(0,0), response(0), orientation(0), scale(1,1) {}
HarrisCorner(Point2f p, float r) : pt(p), response(r), orientation(0), scale(1,1) {}
bool operator<(const HarrisCorner& other) const {
return response > other.response; // 按响应值降序排列
}
};
// 匹配对结构
struct MatchPair {
Point2f pt1; // 图像1中的点
Point2f pt2; // 图像2中的点
float distance; // 描述子距离
float ratio; // 最近邻/次近邻距离比
MatchPair(Point2f p1, Point2f p2, float d, float r)
: pt1(p1), pt2(p2), distance(d), ratio(r) {}
bool operator<(const MatchPair& other) const {
return distance < other.distance; // 按距离升序排列
}
};
class HarrisFeatureDetector {
public:
HarrisFeatureDetector();
~HarrisFeatureDetector();
// Harris角点检测
void DetectHarrisCorners(const Mat& image,
vector<HarrisCorner>& corners,
float k = 0.04,
float threshold = 1e-3,
int blockSize = 3,
int apertureSize = 3);
// 计算角点方向
void ComputeCornerOrientations(const Mat& image,
vector<HarrisCorner>& corners,
int radius = 4);
// 计算局部描述子
void ComputeDescriptors(const Mat& image,
const vector<HarrisCorner>& corners,
vector<Mat>& descriptors,
int patchSize = 16,
int descriptorSize = 64);
// 特征点匹配
void MatchFeatures(const vector<Mat>& desc1,
const vector<Mat>& desc2,
vector<MatchPair>& matches,
float ratioThreshold = 0.8);
// 鲁棒匹配(RANSAC)
void RobustMatch(const vector<HarrisCorner>& corners1,
const vector<HarrisCorner>& corners2,
const vector<MatchPair>& initialMatches,
vector<MatchPair>& refinedMatches,
double confidence = 0.99,
double reprojThreshold = 3.0);
// 绘制角点
void DrawCorners(Mat& image,
const vector<HarrisCorner>& corners,
Scalar color = Scalar(0, 255, 0),
int radius = 3);
// 绘制匹配
void DrawMatches(const Mat& image1,
const Mat& image2,
const vector<HarrisCorner>& corners1,
const vector<HarrisCorner>& corners2,
const vector<MatchPair>& matches,
Mat& outImage,
int maxMatches = 50);
// 保存/加载特征点
void SaveFeatures(const string& filename,
const vector<HarrisCorner>& corners,
const vector<Mat>& descriptors);
void LoadFeatures(const string& filename,
vector<HarrisCorner>& corners,
vector<Mat>& descriptors);
private:
// 计算图像梯度
void ComputeGradients(const Mat& image, Mat& Ix, Mat& Iy);
// 高斯权重窗口
Mat CreateGaussianWindow(int size, float sigma);
// 非极大值抑制
void NonMaximumSuppression(const Mat& response,
vector<HarrisCorner>& corners,
int radius = 2);
// 计算描述子距离
float DescriptorDistance(const Mat& desc1, const Mat& desc2);
// 计算旋转不变描述子
void ComputeRotationInvariantDescriptor(const Mat& patch,
Mat& descriptor,
float orientation);
// 最近邻搜索
int FindNearestNeighbor(const Mat& descriptor,
const vector<Mat>& descriptors,
float& bestDistance,
float& secondBestDistance);
};
#endif
2.2 Harris角点检测实现
cpp
// HarrisFeature.cpp
#include "stdafx.h"
#include "HarrisFeature.h"
#include <numeric>
HarrisFeatureDetector::HarrisFeatureDetector() {
// 构造函数
}
HarrisFeatureDetector::~HarrisFeatureDetector() {
// 析构函数
}
// 计算图像梯度
void HarrisFeatureDetector::ComputeGradients(const Mat& image, Mat& Ix, Mat& Iy) {
if (image.empty()) return;
// 转换为灰度图
Mat gray;
if (image.channels() == 3) {
cvtColor(image, gray, COLOR_BGR2GRAY);
} else {
gray = image.clone();
}
// 转换为浮点型
gray.convertTo(gray, CV_32F);
// Sobel算子计算梯度
Sobel(gray, Ix, CV_32F, 1, 0, 3);
Sobel(gray, Iy, CV_32F, 0, 1, 3);
// 归一化
normalize(Ix, Ix, 0, 1, NORM_MINMAX);
normalize(Iy, Iy, 0, 1, NORM_MINMAX);
}
// 创建高斯窗口
Mat HarrisFeatureDetector::CreateGaussianWindow(int size, float sigma) {
Mat window(size, size, CV_32F);
int center = size / 2;
float sum = 0.0f;
for (int i = 0; i < size; i++) {
for (int j = 0; j < size; j++) {
float x = i - center;
float y = j - center;
float value = exp(-(x*x + y*y) / (2 * sigma * sigma));
window.at<float>(i, j) = value;
sum += value;
}
}
// 归一化
window /= sum;
return window;
}
// Harris角点检测主函数
void HarrisFeatureDetector::DetectHarrisCorners(const Mat& image,
vector<HarrisCorner>& corners,
float k,
float threshold,
int blockSize,
int apertureSize) {
corners.clear();
if (image.empty()) {
cerr << "Error: Input image is empty!" << endl;
return;
}
// 1. 计算梯度
Mat Ix, Iy;
ComputeGradients(image, Ix, Iy);
// 2. 计算自相关矩阵元素
Mat Ix2 = Ix.mul(Ix);
Mat Iy2 = Iy.mul(Iy);
Mat IxIy = Ix.mul(Iy);
// 3. 应用高斯窗口(计算局部和)
Mat A, B, C;
GaussianBlur(Ix2, A, Size(blockSize, blockSize), 1.0);
GaussianBlur(Iy2, B, Size(blockSize, blockSize), 1.0);
GaussianBlur(IxIy, C, Size(blockSize, blockSize), 1.0);
// 4. 计算Harris响应
Mat response(image.size(), CV_32F, Scalar(0));
for (int y = 0; y < image.rows; y++) {
for (int x = 0; x < image.cols; x++) {
float a = A.at<float>(y, x);
float b = B.at<float>(y, x);
float c = C.at<float>(y, x);
// 行列式和迹
float det = a * b - c * c;
float trace = a + b;
// Harris响应
float R = det - k * trace * trace;
response.at<float>(y, x) = R;
}
}
// 5. 阈值处理和非极大值抑制
NonMaximumSuppression(response, corners, 3);
// 6. 按响应值排序
sort(corners.begin(), corners.end());
// 7. 限制角点数量(可选)
int maxCorners = 1000;
if (corners.size() > maxCorners) {
corners.resize(maxCorners);
}
cout << "Detected " << corners.size() << " Harris corners." << endl;
}
// 非极大值抑制
void HarrisFeatureDetector::NonMaximumSuppression(const Mat& response,
vector<HarrisCorner>& corners,
int radius) {
corners.clear();
// 寻找局部极大值
for (int y = radius; y < response.rows - radius; y++) {
for (int x = radius; x < response.cols - radius; x++) {
float center = response.at<float>(y, x);
// 检查是否为局部极大值
bool isLocalMax = true;
for (int dy = -radius; dy <= radius && isLocalMax; dy++) {
for (int dx = -radius; dx <= radius; dx++) {
if (dx == 0 && dy == 0) continue;
float neighbor = response.at<float>(y + dy, x + dx);
if (neighbor > center) {
isLocalMax = false;
break;
}
}
}
// 检查是否超过阈值
if (isLocalMax && center > 1e-5) {
HarrisCorner corner;
corner.pt = Point2f((float)x, (float)y);
corner.response = center;
corners.push_back(corner);
}
}
}
}
// 计算角点方向
void HarrisFeatureDetector::ComputeCornerOrientations(const Mat& image,
vector<HarrisCorner>& corners,
int radius) {
if (image.empty() || corners.empty()) return;
Mat gray;
if (image.channels() == 3) {
cvtColor(image, gray, COLOR_BGR2GRAY);
} else {
gray = image.clone();
}
gray.convertTo(gray, CV_32F);
// 计算梯度
Mat Ix, Iy;
Sobel(gray, Ix, CV_32F, 1, 0, 3);
Sobel(gray, Iy, CV_32F, 0, 1, 3);
// 计算每个角点的方向
for (auto& corner : corners) {
int x = (int)corner.pt.x;
int y = (int)corner.pt.y;
// 检查边界
if (x < radius || x >= gray.cols - radius ||
y < radius || y >= gray.rows - radius) {
corner.orientation = 0;
continue;
}
// 计算局部梯度的主导方向
float sumIx = 0, sumIy = 0;
for (int dy = -radius; dy <= radius; dy++) {
for (int dx = -radius; dx <= radius; dx++) {
float gx = Ix.at<float>(y + dy, x + dx);
float gy = Iy.at<float>(y + dy, x + dx);
sumIx += gx;
sumIy += gy;
}
}
// 计算角度
corner.orientation = atan2(sumIy, sumIx);
}
}
// 计算描述子
void HarrisFeatureDetector::ComputeDescriptors(const Mat& image,
const vector<HarrisCorner>& corners,
vector<Mat>& descriptors,
int patchSize,
int descriptorSize) {
descriptors.clear();
if (image.empty() || corners.empty()) {
cerr << "Error: No image or corners to compute descriptors!" << endl;
return;
}
Mat gray;
if (image.channels() == 3) {
cvtColor(image, gray, COLOR_BGR2GRAY);
} else {
gray = image.clone();
}
gray.convertTo(gray, CV_32F);
// 归一化图像
normalize(gray, gray, 0, 1, NORM_MINMAX);
int halfSize = patchSize / 2;
for (const auto& corner : corners) {
int x = (int)corner.pt.x;
int y = (int)corner.pt.y;
// 检查边界
if (x < halfSize || x >= gray.cols - halfSize ||
y < halfSize || y >= gray.rows - halfSize) {
descriptors.push_back(Mat());
continue;
}
// 提取图像块
Rect roi(x - halfSize, y - halfSize, patchSize, patchSize);
Mat patch = gray(roi).clone();
// 计算旋转不变描述子
Mat descriptor;
ComputeRotationInvariantDescriptor(patch, descriptor, corner.orientation);
// 重采样到固定大小
if (!descriptor.empty()) {
Mat resized;
resize(descriptor, resized, Size(descriptorSize, 1));
descriptors.push_back(resized);
} else {
descriptors.push_back(Mat());
}
}
cout << "Computed " << descriptors.size() << " descriptors." << endl;
}
// 计算旋转不变描述子
void HarrisFeatureDetector::ComputeRotationInvariantDescriptor(
const Mat& patch, Mat& descriptor, float orientation) {
int patchSize = patch.rows;
int halfSize = patchSize / 2;
// 计算梯度
Mat Ix, Iy;
Sobel(patch, Ix, CV_32F, 1, 0, 3);
Sobel(patch, Iy, CV_32F, 0, 1, 3);
// 计算梯度幅值和方向
Mat magnitude, angle;
cartToPolar(Ix, Iy, magnitude, angle, true);
// 调整角度到[0, 2π)
angle = angle * CV_PI / 180.0f;
// 旋转不变:减去主方向
angle = angle - orientation;
// 将角度归一化到[0, 2π)
for (int i = 0; i < angle.rows; i++) {
for (int j = 0; j < angle.cols; j++) {
float& a = angle.at<float>(i, j);
while (a < 0) a += 2 * CV_PI;
while (a >= 2 * CV_PI) a -= 2 * CV_PI;
}
}
// 计算描述子(简单版本:统计梯度直方图)
int bins = 8;
vector<float> hist(bins, 0.0f);
for (int i = 0; i < patchSize; i++) {
for (int j = 0; j < patchSize; j++) {
float mag = magnitude.at<float>(i, j);
float ang = angle.at<float>(i, j);
// 计算所属的bin
int bin = (int)(ang * bins / (2 * CV_PI));
if (bin >= bins) bin = bins - 1;
hist[bin] += mag;
}
}
// 转换为Mat
descriptor = Mat(1, bins, CV_32F);
for (int i = 0; i < bins; i++) {
descriptor.at<float>(0, i) = hist[i];
}
// 归一化描述子
normalize(descriptor, descriptor, 1, 0, NORM_L2);
}
// 描述子距离计算
float HarrisFeatureDetector::DescriptorDistance(const Mat& desc1, const Mat& desc2) {
if (desc1.empty() || desc2.empty() ||
desc1.size() != desc2.size()) {
return FLT_MAX;
}
// 欧氏距离
float distance = 0;
for (int i = 0; i < desc1.cols; i++) {
float diff = desc1.at<float>(0, i) - desc2.at<float>(0, i);
distance += diff * diff;
}
return sqrt(distance);
}
// 最近邻搜索
int HarrisFeatureDetector::FindNearestNeighbor(const Mat& descriptor,
const vector<Mat>& descriptors,
float& bestDistance,
float& secondBestDistance) {
int bestIdx = -1;
bestDistance = FLT_MAX;
secondBestDistance = FLT_MAX;
for (size_t i = 0; i < descriptors.size(); i++) {
if (descriptors[i].empty()) continue;
float dist = DescriptorDistance(descriptor, descriptors[i]);
if (dist < bestDistance) {
secondBestDistance = bestDistance;
bestDistance = dist;
bestIdx = (int)i;
} else if (dist < secondBestDistance) {
secondBestDistance = dist;
}
}
return bestIdx;
}
// 特征点匹配
void HarrisFeatureDetector::MatchFeatures(const vector<Mat>& desc1,
const vector<Mat>& desc2,
vector<MatchPair>& matches,
float ratioThreshold) {
matches.clear();
if (desc1.empty() || desc2.empty()) {
cerr << "Error: No descriptors to match!" << endl;
return;
}
// 暴力匹配
for (size_t i = 0; i < desc1.size(); i++) {
if (desc1[i].empty()) continue;
float bestDist, secondBestDist;
int bestIdx = FindNearestNeighbor(desc1[i], desc2, bestDist, secondBestDist);
if (bestIdx >= 0 && secondBestDist > 0) {
float ratio = bestDist / secondBestDist;
// Lowe's ratio test
if (ratio < ratioThreshold) {
// 对称性检查
float revBestDist, revSecondBestDist;
int revBestIdx = FindNearestNeighbor(desc2[bestIdx], desc1,
revBestDist, revSecondBestDist);
if (revBestIdx == (int)i) {
// 对称匹配成功
matches.push_back(MatchPair(
Point2f(0, 0), // 需要外部填充实际坐标
Point2f(0, 0), // 需要外部填充实际坐标
bestDist, ratio));
}
}
}
}
// 按距离排序
sort(matches.begin(), matches.end());
cout << "Found " << matches.size() << " initial matches." << endl;
}
// 鲁棒匹配(RANSAC)
void HarrisFeatureDetector::RobustMatch(const vector<HarrisCorner>& corners1,
const vector<HarrisCorner>& corners2,
const vector<MatchPair>& initialMatches,
vector<MatchPair>& refinedMatches,
double confidence,
double reprojThreshold) {
refinedMatches.clear();
if (initialMatches.empty()) {
cerr << "Error: No initial matches for RANSAC!" << endl;
return;
}
// 准备点对
vector<Point2f> points1, points2;
vector<int> matchIndices;
for (size_t i = 0; i < initialMatches.size(); i++) {
// 这里需要从角点列表中获取实际坐标
// 假设初始匹配已经包含了正确的索引
points1.push_back(initialMatches[i].pt1);
points2.push_back(initialMatches[i].pt2);
matchIndices.push_back((int)i);
}
if (points1.size() < 4) {
cerr << "Error: Need at least 4 matches for RANSAC!" << endl;
refinedMatches = initialMatches;
return;
}
// 使用RANSAC计算单应性矩阵
Mat homography, inliers;
homography = findHomography(points1, points2, RANSAC,
reprojThreshold, inliers, 2000, confidence);
if (homography.empty()) {
cerr << "Warning: Homography estimation failed!" << endl;
refinedMatches = initialMatches;
return;
}
// 提取内点
int inlierCount = 0;
for (int i = 0; i < inliers.rows; i++) {
if (inliers.at<uchar>(i) > 0) {
refinedMatches.push_back(initialMatches[matchIndices[i]]);
inlierCount++;
}
}
cout << "RANSAC: " << inlierCount << " inliers out of "
<< initialMatches.size() << " matches." << endl;
cout << "Homography matrix:\n" << homography << endl;
}
// 绘制角点
void HarrisFeatureDetector::DrawCorners(Mat& image,
const vector<HarrisCorner>& corners,
Scalar color,
int radius) {
if (image.empty()) return;
Mat display = image.clone();
for (const auto& corner : corners) {
Point center((int)corner.pt.x, (int)corner.pt.y);
// 绘制圆
circle(display, center, radius, color, 2);
// 绘制方向
if (corner.orientation != 0) {
Point end(
center.x + (int)(radius * cos(corner.orientation)),
center.y + (int)(radius * sin(corner.orientation))
);
line(display, center, end, Scalar(0, 0, 255), 2);
}
}
image = display;
}
// 绘制匹配
void HarrisFeatureDetector::DrawMatches(const Mat& image1,
const Mat& image2,
const vector<HarrisCorner>& corners1,
const vector<HarrisCorner>& corners2,
const vector<MatchPair>& matches,
Mat& outImage,
int maxMatches) {
if (image1.empty() || image2.empty()) return;
// 创建合成图像
int rows = max(image1.rows, image2.rows);
int cols = image1.cols + image2.cols;
outImage = Mat(rows, cols, CV_8UC3, Scalar(0, 0, 0));
// 复制图像
Mat roi1 = outImage(Rect(0, 0, image1.cols, image1.rows));
image1.copyTo(roi1);
Mat roi2 = outImage(Rect(image1.cols, 0, image2.cols, image2.rows));
image2.copyTo(roi2);
// 绘制匹配线
int drawCount = min((int)matches.size(), maxMatches);
for (int i = 0; i < drawCount; i++) {
const MatchPair& match = matches[i];
Point pt1((int)match.pt1.x, (int)match.pt1.y);
Point pt2((int)(match.pt2.x + image1.cols), (int)match.pt2.y);
// 使用彩虹色表示匹配质量
float hue = 240 * (1.0f - match.ratio); // 蓝色(好) -> 红色(差)
Scalar color = HSVtoRGB(hue, 1.0, 1.0);
// 绘制线
line(outImage, pt1, pt2, color, 1);
// 绘制点
circle(outImage, pt1, 3, color, -1);
circle(outImage, pt2, 3, color, -1);
// 显示匹配编号
if (i < 20) { // 只显示前20个编号
putText(outImage, to_string(i), pt1,
FONT_HERSHEY_SIMPLEX, 0.4, Scalar(255, 255, 255), 1);
}
}
// 添加标题
putText(outImage, "Image 1", Point(10, 30),
FONT_HERSHEY_SIMPLEX, 1, Scalar(0, 255, 0), 2);
putText(outImage, "Image 2", Point(image1.cols + 10, 30),
FONT_HERSHEY_SIMPLEX, 1, Scalar(0, 255, 0), 2);
putText(outImage, "Matches: " + to_string(matches.size()),
Point(10, 60), FONT_HERSHEY_SIMPLEX, 0.7, Scalar(0, 255, 255), 2);
}
// HSV转RGB
Scalar HarrisFeatureDetector::HSVtoRGB(float h, float s, float v) {
float r, g, b;
int i = (int)(h / 60.0f) % 6;
float f = h / 60.0f - i;
float p = v * (1 - s);
float q = v * (1 - f * s);
float t = v * (1 - (1 - f) * s);
switch (i) {
case 0: r = v; g = t; b = p; break;
case 1: r = q; g = v; b = p; break;
case 2: r = p; g = v; b = t; break;
case 3: r = p; g = q; b = v; break;
case 4: r = t; g = p; b = v; break;
case 5: r = v; g = p; b = q; break;
default: r = g = b = 0; break;
}
return Scalar(b * 255, g * 255, r * 255);
}
// 保存特征点
void HarrisFeatureDetector::SaveFeatures(const string& filename,
const vector<HarrisCorner>& corners,
const vector<Mat>& descriptors) {
ofstream file(filename);
if (!file.is_open()) {
cerr << "Error: Cannot open file " << filename << " for writing!" << endl;
return;
}
file << corners.size() << endl;
for (size_t i = 0; i < corners.size(); i++) {
const HarrisCorner& corner = corners[i];
file << corner.pt.x << " " << corner.pt.y << " "
<< corner.response << " " << corner.orientation << endl;
if (i < descriptors.size() && !descriptors[i].empty()) {
const Mat& desc = descriptors[i];
file << desc.cols;
for (int j = 0; j < desc.cols; j++) {
file << " " << desc.at<float>(0, j);
}
file << endl;
} else {
file << "0" << endl;
}
}
file.close();
cout << "Saved " << corners.size() << " features to " << filename << endl;
}
// 加载特征点
void HarrisFeatureDetector::LoadFeatures(const string& filename,
vector<HarrisCorner>& corners,
vector<Mat>& descriptors) {
corners.clear();
descriptors.clear();
ifstream file(filename);
if (!file.is_open()) {
cerr << "Error: Cannot open file " << filename << " for reading!" << endl;
return;
}
int numFeatures;
file >> numFeatures;
for (int i = 0; i < numFeatures; i++) {
HarrisCorner corner;
file >> corner.pt.x >> corner.pt.y >> corner.response >> corner.orientation;
corners.push_back(corner);
int descSize;
file >> descSize;
if (descSize > 0) {
Mat desc(1, descSize, CV_32F);
for (int j = 0; j < descSize; j++) {
file >> desc.at<float>(0, j);
}
descriptors.push_back(desc);
} else {
descriptors.push_back(Mat());
}
}
file.close();
cout << "Loaded " << corners.size() << " features from " << filename << endl;
}
2.3 主程序示例
cpp
// HarrisDemo.cpp
#include "stdafx.h"
#include "HarrisFeature.h"
#include <chrono>
using namespace std;
using namespace chrono;
// 演示Harris角点检测
void DemoHarrisCornerDetection() {
cout << "=== Harris角点检测演示 ===" << endl;
// 加载图像
Mat image = imread("test_image.jpg");
if (image.empty()) {
cout << "错误: 无法加载图像!" << endl;
cout << "请确保test_image.jpg存在" << endl;
return;
}
HarrisFeatureDetector detector;
// 检测Harris角点
vector<HarrisCorner> corners;
auto start = high_resolution_clock::now();
detector.DetectHarrisCorners(image, corners, 0.04, 1e-3, 3, 3);
auto end = high_resolution_clock::now();
auto duration = duration_cast<milliseconds>(end - start);
cout << "检测到 " << corners.size() << " 个角点" << endl;
cout << "耗时: " << duration.count() << " 毫秒" << endl;
// 计算角点方向
detector.ComputeCornerOrientations(image, corners, 4);
// 绘制角点
Mat result = image.clone();
detector.DrawCorners(result, corners, Scalar(0, 255, 0), 3);
// 显示结果
namedWindow("Harris Corners", WINDOW_AUTOSIZE);
imshow("Harris Corners", result);
// 显示角点响应图
Mat gray;
cvtColor(image, gray, COLOR_BGR2GRAY);
Mat response;
cornerHarris(gray, response, 3, 3, 0.04);
normalize(response, response, 0, 255, NORM_MINMAX, CV_8U);
applyColorMap(response, response, COLORMAP_JET);
namedWindow("Harris Response", WINDOW_AUTOSIZE);
imshow("Harris Response", response);
waitKey(0);
}
// 演示特征匹配
void DemoFeatureMatching() {
cout << "\n=== Harris特征匹配演示 ===" << endl;
// 加载图像对
Mat image1 = imread("image1.jpg");
Mat image2 = imread("image2.jpg");
if (image1.empty() || image2.empty()) {
cout << "错误: 无法加载图像!" << endl;
cout << "请确保image1.jpg和image2.jpg存在" << endl;
return;
}
HarrisFeatureDetector detector;
// 检测图像1的特征
vector<HarrisCorner> corners1;
vector<Mat> descriptors1;
cout << "处理图像1..." << endl;
detector.DetectHarrisCorners(image1, corners1);
detector.ComputeCornerOrientations(image1, corners1);
detector.ComputeDescriptors(image1, corners1, descriptors1);
// 检测图像2的特征
vector<HarrisCorner> corners2;
vector<Mat> descriptors2;
cout << "处理图像2..." << endl;
detector.DetectHarrisCorners(image2, corners2);
detector.ComputeCornerOrientations(image2, corners2);
detector.ComputeDescriptors(image2, corners2, descriptors2);
// 特征匹配
vector<MatchPair> matches;
auto start = high_resolution_clock::now();
detector.MatchFeatures(descriptors1, descriptors2, matches, 0.8);
auto end = high_resolution_clock::now();
auto duration = duration_cast<milliseconds>(end - start);
cout << "找到 " << matches.size() << " 个初始匹配" << endl;
cout << "匹配耗时: " << duration.count() << " 毫秒" << endl;
// 绘制初始匹配
Mat matchImage;
detector.DrawMatches(image1, image2, corners1, corners2, matches, matchImage, 100);
namedWindow("Initial Matches", WINDOW_AUTOSIZE);
imshow("Initial Matches", matchImage);
// RANSAC提纯
cout << "\n进行RANSAC提纯..." << endl;
vector<MatchPair> refinedMatches;
start = high_resolution_clock::now();
detector.RobustMatch(corners1, corners2, matches, refinedMatches, 0.99, 3.0);
end = high_resolution_clock::now();
auto ransacDuration = duration_cast<milliseconds>(end - start);
cout << "RANSAC提纯耗时: " << ransacDuration.count() << " 毫秒" << endl;
// 绘制提纯后的匹配
Mat refinedImage;
detector.DrawMatches(image1, image2, corners1, corners2, refinedMatches, refinedImage, 100);
namedWindow("Refined Matches (RANSAC)", WINDOW_AUTOSIZE);
imshow("Refined Matches (RANSAC)", refinedImage);
// 保存特征点
detector.SaveFeatures("image1_features.txt", corners1, descriptors1);
detector.SaveFeatures("image2_features.txt", corners2, descriptors2);
waitKey(0);
}
// 与OpenCV内置Harris比较
void CompareWithOpenCVHarris() {
cout << "\n=== 与OpenCV内置Harris算法比较 ===" << endl;
Mat image = imread("test_image.jpg", IMREAD_GRAYSCALE);
if (image.empty()) {
cout << "错误: 无法加载图像!" << endl;
return;
}
// OpenCV内置Harris
Mat cvCorners, cvCornersNorm;
auto start = high_resolution_clock::now();
cornerHarris(image, cvCorners, 3, 3, 0.04);
normalize(cvCorners, cvCornersNorm, 0, 255, NORM_MINMAX, CV_8U);
auto end = high_resolution_clock::now();
auto cvDuration = duration_cast<milliseconds>(end - start);
// 自定义Harris
HarrisFeatureDetector detector;
vector<HarrisCorner> myCorners;
start = high_resolution_clock::now();
detector.DetectHarrisCorners(image, myCorners, 0.04, 1e-3, 3, 3);
end = high_resolution_clock::now();
auto myDuration = duration_cast<milliseconds>(end - start);
cout << "OpenCV Harris耗时: " << cvDuration.count() << " 毫秒" << endl;
cout << "自定义Harris耗时: " << myDuration.count() << " 毫秒" << endl;
cout << "加速比: " << (double)cvDuration.count()/myDuration.count() << " 倍" << endl;
// 可视化比较
Mat colorImage;
cvtColor(image, colorImage, COLOR_GRAY2BGR);
// 绘制OpenCV角点
Mat cvResult = colorImage.clone();
for (int y = 0; y < cvCorners.rows; y++) {
for (int x = 0; x < cvCorners.cols; x++) {
if (cvCornersNorm.at<uchar>(y, x) > 200) {
circle(cvResult, Point(x, y), 2, Scalar(0, 0, 255), -1);
}
}
}
// 绘制自定义角点
Mat myResult = colorImage.clone();
detector.DrawCorners(myResult, myCorners, Scalar(0, 255, 0), 2);
// 显示比较结果
Mat comparison;
hconcat(cvResult, myResult, comparison);
putText(comparison, "OpenCV Harris", Point(10, 30),
FONT_HERSHEY_SIMPLEX, 1, Scalar(0, 0, 255), 2);
putText(comparison, "Custom Harris", Point(cvResult.cols + 10, 30),
FONT_HERSHEY_SIMPLEX, 1, Scalar(0, 255, 0), 2);
namedWindow("Harris Comparison", WINDOW_AUTOSIZE);
imshow("Harris Comparison", comparison);
waitKey(0);
}
// 性能测试
void PerformanceTest() {
cout << "\n=== Harris算法性能测试 ===" << endl;
// 测试不同图像尺寸
vector<Size> testSizes = {
Size(320, 240),
Size(640, 480),
Size(800, 600),
Size(1024, 768),
Size(1920, 1080)
};
HarrisFeatureDetector detector;
cout << "\n图像尺寸 | 角点数 | 检测时间(ms) | 匹配时间(ms)" << endl;
cout << "---------|--------|--------------|-------------" << endl;
for (const Size& size : testSizes) {
// 生成测试图像
Mat image(size, CV_8UC3);
randu(image, Scalar(0, 0, 0), Scalar(255, 255, 255));
// 检测角点
vector<HarrisCorner> corners;
auto start = high_resolution_clock::now();
detector.DetectHarrisCorners(image, corners);
auto end = high_resolution_clock::now();
auto detectTime = duration_cast<milliseconds>(end - start).count();
// 计算描述子
vector<Mat> descriptors;
start = high_resolution_clock::now();
detector.ComputeDescriptors(image, corners, descriptors);
end = high_resolution_clock::now();
auto descTime = duration_cast<milliseconds>(end - start).count();
cout << size.width << "x" << size.height << " | "
<< corners.size() << " | "
<< detectTime << " | "
<< descTime << endl;
}
}
int _tmain(int argc, _TCHAR* argv[]) {
cout << "Harris角点检测与匹配演示程序" << endl;
cout << "============================" << endl;
int choice;
cout << "\n请选择演示模式:" << endl;
cout << "1. Harris角点检测演示" << endl;
cout << "2. 特征匹配演示" << endl;
cout << "3. 与OpenCV算法比较" << endl;
cout << "4. 性能测试" << endl;
cout << "5. 运行所有演示" << endl;
cout << "请选择 (1-5): ";
cin >> choice;
switch (choice) {
case 1:
DemoHarrisCornerDetection();
break;
case 2:
DemoFeatureMatching();
break;
case 3:
CompareWithOpenCVHarris();
break;
case 4:
PerformanceTest();
break;
case 5:
DemoHarrisCornerDetection();
DemoFeatureMatching();
CompareWithOpenCVHarris();
PerformanceTest();
break;
default:
cout << "无效选择" << endl;
}
cout << "\n演示结束,按任意键退出..." << endl;
cin.get();
cin.get();
return 0;
}
2.4 高级版本:改进的Harris算法
cpp
// ImprovedHarris.h
#pragma once
#include "HarrisFeature.h"
class ImprovedHarrisDetector : public HarrisFeatureDetector {
public:
// 自适应Harris
void AdaptiveHarris(const Mat& image,
vector<HarrisCorner>& corners,
int maxCorners = 1000,
float qualityLevel = 0.01,
int minDistance = 10);
// 多尺度Harris
void MultiScaleHarris(const Mat& image,
vector<HarrisCorner>& corners,
vector<float> scales = {1.0, 0.5, 0.25});
// 亚像素精度角点定位
void RefineCornersSubPixel(const Mat& image,
vector<HarrisCorner>& corners,
Size winSize = Size(5,5),
Size zeroZone = Size(-1,-1),
TermCriteria criteria = TermCriteria(
TermCriteria::EPS + TermCriteria::COUNT, 30, 0.01));
// 尺度不变Harris
vector<HarrisCorner> ScaleInvariantHarris(const Mat& image,
float scaleFactor = 1.2,
int numScales = 3);
// 方向估计改进
void ComputeDominantOrientation(const Mat& image,
vector<HarrisCorner>& corners,
int patchRadius = 8);
private:
// 计算自相关矩阵的特征值
void ComputeEigenvalues(const Mat& Ix, const Mat& Iy,
Mat& lambda1, Mat& lambda2);
// 计算角点度量
Mat ComputeCornerMeasure(const Mat& lambda1, const Mat& lambda2,
float k = 0.04);
// 自适应非极大值抑制
void AdaptiveNonMaximumSuppression(const Mat& response,
vector<HarrisCorner>& corners,
int numCorners = 1000);
};
cpp
// ImprovedHarris.cpp
#include "stdafx.h"
#include "ImprovedHarris.h"
// 自适应Harris
void ImprovedHarrisDetector::AdaptiveHarris(const Mat& image,
vector<HarrisCorner>& corners,
int maxCorners,
float qualityLevel,
int minDistance) {
corners.clear();
if (image.empty()) return;
Mat gray;
if (image.channels() == 3) {
cvtColor(image, gray, COLOR_BGR2GRAY);
} else {
gray = image.clone();
}
gray.convertTo(gray, CV_32F);
// 计算梯度
Mat Ix, Iy;
Sobel(gray, Ix, CV_32F, 1, 0, 3);
Sobel(gray, Iy, CV_32F, 0, 1, 3);
// 计算自相关矩阵元素
Mat Ix2 = Ix.mul(Ix);
Mat Iy2 = Iy.mul(Iy);
Mat IxIy = Ix.mul(Iy);
// 高斯平滑
GaussianBlur(Ix2, Ix2, Size(5, 5), 1.0);
GaussianBlur(Iy2, Iy2, Size(5, 5), 1.0);
GaussianBlur(IxIy, IxIy, Size(5, 5), 1.0);
// 计算特征值
Mat lambda1, lambda2;
ComputeEigenvalues(Ix2, Iy2, lambda1, lambda2);
// 计算角点响应
Mat response = ComputeCornerMeasure(lambda1, lambda2, 0.04);
// 自适应非极大值抑制
AdaptiveNonMaximumSuppression(response, corners, maxCorners);
// 按质量等级筛选
if (!corners.empty()) {
float maxResponse = corners[0].response;
float threshold = qualityLevel * maxResponse;
auto it = corners.begin();
while (it != corners.end()) {
if (it->response < threshold) {
it = corners.erase(it);
} else {
++it;
}
}
}
// 最小距离抑制
vector<bool> valid(corners.size(), true);
for (size_t i = 0; i < corners.size(); i++) {
if (!valid[i]) continue;
for (size_t j = i + 1; j < corners.size(); j++) {
if (!valid[j]) continue;
Point2f diff = corners[i].pt - corners[j].pt;
float distance = sqrt(diff.x*diff.x + diff.y*diff.y);
if (distance < minDistance) {
if (corners[i].response > corners[j].response) {
valid[j] = false;
} else {
valid[i] = false;
break;
}
}
}
}
vector<HarrisCorner> filtered;
for (size_t i = 0; i < corners.size(); i++) {
if (valid[i]) {
filtered.push_back(corners[i]);
}
}
corners = filtered;
cout << "Adaptive Harris detected " << corners.size() << " corners." << endl;
}
// 计算特征值
void ImprovedHarrisDetector::ComputeEigenvalues(const Mat& Ix2, const Mat& Iy2,
Mat& lambda1, Mat& lambda2) {
lambda1.create(Ix2.size(), CV_32F);
lambda2.create(Ix2.size(), CV_32F);
for (int y = 0; y < Ix2.rows; y++) {
for (int x = 0; x < Ix2.cols; x++) {
float a = Ix2.at<float>(y, x);
float b = Iy2.at<float>(y, x);
float c = Ix2.at<float>(y, x); // 实际上是IxIy,但这里简化为Ix2
// 计算特征值
float trace = a + b;
float det = a * b - c * c;
float sqrtTerm = sqrt(trace*trace - 4*det);
lambda1.at<float>(y, x) = 0.5f * (trace + sqrtTerm);
lambda2.at<float>(y, x) = 0.5f * (trace - sqrtTerm);
}
}
}
// 计算角点度量
Mat ImprovedHarrisDetector::ComputeCornerMeasure(const Mat& lambda1, const Mat& lambda2,
float k) {
Mat response(lambda1.size(), CV_32F);
for (int y = 0; y < lambda1.rows; y++) {
for (int x = 0; x < lambda1.cols; x++) {
float l1 = lambda1.at<float>(y, x);
float l2 = lambda2.at<float>(y, x);
// Harris响应: R = λ1*λ2 - k*(λ1+λ2)^2
float R = l1 * l2 - k * (l1 + l2) * (l1 + l2);
response.at<float>(y, x) = R;
}
}
return response;
}
// 自适应非极大值抑制
void ImprovedHarrisDetector::AdaptiveNonMaximumSuppression(const Mat& response,
vector<HarrisCorner>& corners,
int numCorners) {
corners.clear();
// 收集所有角点候选
vector<HarrisCorner> allCorners;
for (int y = 1; y < response.rows - 1; y++) {
for (int x = 1; x < response.cols - 1; x++) {
float val = response.at<float>(y, x);
// 检查是否为局部极大值
if (val > response.at<float>(y-1, x-1) &&
val > response.at<float>(y-1, x) &&
val > response.at<float>(y-1, x+1) &&
val > response.at<float>(y, x-1) &&
val > response.at<float>(y, x+1) &&
val > response.at<float>(y+1, x-1) &&
val > response.at<float>(y+1, x) &&
val > response.at<float>(y+1, x+1)) {
allCorners.push_back(HarrisCorner(Point2f((float)x, (float)y), val));
}
}
}
// 按响应值排序
sort(allCorners.begin(), allCorners.end());
// 自适应选择角点
if (allCorners.size() > numCorners) {
corners.assign(allCorners.begin(), allCorners.begin() + numCorners);
} else {
corners = allCorners;
}
}
// 亚像素精度角点定位
void ImprovedHarrisDetector::RefineCornersSubPixel(const Mat& image,
vector<HarrisCorner>& corners,
Size winSize,
Size zeroZone,
TermCriteria criteria) {
if (image.empty() || corners.empty()) return;
Mat gray;
if (image.channels() == 3) {
cvtColor(image, gray, COLOR_BGR2GRAY);
} else {
gray = image.clone();
}
gray.convertTo(gray, CV_32F);
// 准备角点坐标
vector<Point2f> corners2f;
for (const auto& corner : corners) {
corners2f.push_back(corner.pt);
}
// 亚像素精度优化
cornerSubPix(gray, corners2f, winSize, zeroZone, criteria);
// 更新角点位置
for (size_t i = 0; i < corners.size(); i++) {
corners[i].pt = corners2f[i];
}
cout << "Sub-pixel refinement completed for " << corners.size() << " corners." << endl;
}
参考代码 Harris算子提取特征点、匹配、提粗匹配程序 www.youwenfan.com/contentcsu/70332.html
三、项目配置和使用
3.1 Visual Studio 配置
包含目录:
$(OPENCV_DIR)\include
库目录:
$(OPENCV_DIR)\lib
附加依赖项:
opencv_core455.lib
opencv_highgui455.lib
opencv_imgproc455.lib
opencv_features2d455.lib
opencv_calib3d455.lib
3.2 CMakeLists.txt
cmake
cmake_minimum_required(VERSION 3.10)
project(HarrisFeature)
set(CMAKE_CXX_STANDARD 11)
# 查找OpenCV
find_package(OpenCV REQUIRED)
include_directories(${OpenCV_INCLUDE_DIRS})
# 源文件
set(SOURCE_FILES
HarrisFeature.cpp
ImprovedHarris.cpp
main.cpp
)
# 可执行文件
add_executable(HarrisFeature ${SOURCE_FILES})
# 链接库
target_link_libraries(HarrisFeature ${OpenCV_LIBS})
四、算法性能优化
4.1 GPU加速版本
cpp
// HarrisGPU.cu (CUDA版本)
#ifdef HAVE_CUDA
#include <opencv2/cudaimgproc.hpp>
#include <opencv2/cudawarping.hpp>
class HarrisGPU {
public:
void DetectCornersGPU(const cv::cuda::GpuMat& d_image,
std::vector<cv::Point2f>& corners,
float k = 0.04f) {
// GPU加速的Harris角点检测
cv::cuda::GpuMat d_gray, d_Ix, d_Iy;
cv::cuda::cvtColor(d_image, d_gray, cv::COLOR_BGR2GRAY);
// Sobel梯度
cv::Ptr<cv::cuda::Filter> sobel_x =
cv::cuda::createSobelFilter(CV_8U, CV_32F, 1, 0);
cv::Ptr<cv::cuda::Filter> sobel_y =
cv::cuda::createSobelFilter(CV_8U, CV_32F, 0, 1);
sobel_x->apply(d_gray, d_Ix);
sobel_y->apply(d_gray, d_Iy);
// 计算自相关矩阵
cv::cuda::GpuMat d_Ix2, d_Iy2, d_IxIy;
cv::cuda::multiply(d_Ix, d_Ix, d_Ix2);
cv::cuda::multiply(d_Iy, d_Iy, d_Iy2);
cv::cuda::multiply(d_Ix, d_Iy, d_IxIy);
// 高斯模糊
cv::Ptr<cv::cuda::Filter> gaussian =
cv::cuda::createGaussianFilter(CV_32F, CV_32F,
cv::Size(5,5), 1.0);
gaussian->apply(d_Ix2, d_Ix2);
gaussian->apply(d_Iy2, d_Iy2);
gaussian->apply(d_IxIy, d_IxIy);
// Harris响应
cv::cuda::GpuMat d_response(d_image.size(), CV_32F);
// 核函数计算响应
// ...
}
};
#endif
4.2 并行计算优化
cpp
// 使用OpenMP并行化
#include <omp.h>
void ParallelHarris(const Mat& image, vector<HarrisCorner>& corners) {
int rows = image.rows;
int cols = image.cols;
Mat response(rows, cols, CV_32F);
#pragma omp parallel for collapse(2)
for (int y = 0; y < rows; y++) {
for (int x = 0; x < cols; x++) {
// 并行计算Harris响应
// ...
}
}
}
五、应用场景
- 图像配准:医学图像、遥感图像对齐
- 目标跟踪:视频序列中的特征点跟踪
- 三维重建:SFM(Structure from Motion)
- 视觉SLAM:同时定位与地图构建
- 图像拼接:全景图生成
- 目标识别:基于特征的物体识别