当前位置: 首页 > java >正文

harris角点检测

参考文献

参考论文:A Combined Corner and Edge Detector

参考博客:

参考PPT:https://github.com/CV-xueba/A01_cvclass_basic/blob/master/%E8%AE%A1%E7%AE%97%E6%9C%BA%E8%A7%86%E8%A7%89%E5%9F%BA%E7%A1%80_%E8%AF%BE%E4%BB%B6/06_corner.pdf

原理

1. 角点的定义是: 滑窗沿着任意方向移动,亮度变化都很明显.

2. 定义对应的外观变化窗口能量函数如下所示:描述的是当能量函数越大,表示检测到角点的概率就是越大的。

3. 函数的各个部分的描述如下图所示,其中窗口可以是0,1窗口,或者是高斯窗口函数。

4. 然后要知道对应的能量函数在微小变化的条件下,对应的值是如何变化的,可以对应的对上面能量函数进行二阶泰勒展开,如下所示:

5. 对应链式求导可得如下的结果:

6. 将对应的初始值0,0带入到上面的公式中可得:

7.因此对应第二次向可以简化表示为如下的形式:

8. 其中对应的M可以通过分解得到关键的对角矩阵值 lambda1 和 lambda2

9. 因此可以将对应的结果归纳如下:

10. 最后进一步总结对应的结果,通过一个阈值去判断是否是角点,公式如下所示:

代码实现

1. 要求从对应的链接下载得到 两个头文件 stb_image.h 和 stb_image_write.h 放到源代码的同一个目录下,对应的链接如下: GitHub - nothings/stb: stb single-file public domain libraries for C/C++

2. 然后直接编译即可:g++ main.cpp -o harris -std=c++11

#define STB_IMAGE_IMPLEMENTATION
#define STB_IMAGE_WRITE_IMPLEMENTATION#include "stb_image.h"
#include "stb_image_write.h"#include <iostream>
#include <vector>
#include <cmath>
#include <fstream>// Sobel 卷积核
const int SOBEL_X[3][3] = {{-1, 0, 1},{-2, 0, 2},{-1, 0, 1}
};
const int SOBEL_Y[3][3] = {{-1, -2, -1},{ 0,  0,  0},{ 1,  2,  1}
};const float k = 0.04f;
const float threshold = 1e6f;// 卷积
float convolve(const std::vector<std::vector<float>>& img, int x, int y, const int kernel[3][3]) {float sum = 0.0f;for (int dy = -1; dy <= 1; ++dy) {for (int dx = -1; dx <= 1; ++dx) {int ix = x + dx;int iy = y + dy;if (iy >= 0 && iy < img.size() && ix >= 0 && ix < img[0].size()) {sum += img[iy][ix] * kernel[dy + 1][dx + 1];}}}return sum;
}std::vector<std::pair<int, int>> harrisCornerDetect(const std::vector<std::vector<float>>& img) {int h = img.size(), w = img[0].size();std::vector<std::vector<float>> Ix(h, std::vector<float>(w));std::vector<std::vector<float>> Iy(h, std::vector<float>(w));std::vector<std::vector<float>> R(h, std::vector<float>(w));for (int y = 1; y < h - 1; ++y)for (int x = 1; x < w - 1; ++x) {Ix[y][x] = convolve(img, x, y, SOBEL_X);Iy[y][x] = convolve(img, x, y, SOBEL_Y);}for (int y = 1; y < h - 1; ++y)for (int x = 1; x < w - 1; ++x) {float sumIx2 = 0, sumIy2 = 0, sumIxy = 0;for (int dy = -1; dy <= 1; ++dy)for (int dx = -1; dx <= 1; ++dx) {float ix = Ix[y + dy][x + dx];float iy = Iy[y + dy][x + dx];sumIx2 += ix * ix;sumIy2 += iy * iy;sumIxy += ix * iy;}float det = sumIx2 * sumIy2 - sumIxy * sumIxy;float trace = sumIx2 + sumIy2;R[y][x] = det - k * trace * trace;}std::vector<std::pair<int, int>> corners;for (int y = 1; y < h - 1; ++y)for (int x = 1; x < w - 1; ++x)if (R[y][x] > threshold) {bool isMax = true;for (int dy = -1; dy <= 1; ++dy)for (int dx = -1; dx <= 1; ++dx)if (R[y + dy][x + dx] > R[y][x])isMax = false;if (isMax)corners.emplace_back(x, y);}return corners;
}void drawCorners(unsigned char* image, int width, int height, const std::vector<std::pair<int, int>>& corners) {for (const auto& [x, y] : corners) {for (int dy = -1; dy <= 1; ++dy)for (int dx = -1; dx <= 1; ++dx) {int ix = x + dx;int iy = y + dy;if (ix >= 0 && ix < width && iy >= 0 && iy < height) {int idx = (iy * width + ix) * 3;image[idx + 0] = 255; // Redimage[idx + 1] = 0;image[idx + 2] = 0;}}}
}int main() {int w, h, channels;unsigned char* data = stbi_load("119_0003.JPG", &w, &h, &channels, 1); // 灰度图if (!data) {std::cerr << "无法读取图像!" << std::endl;return 1;}std::vector<std::vector<float>> gray(h, std::vector<float>(w));for (int y = 0; y < h; ++y)for (int x = 0; x < w; ++x)gray[y][x] = static_cast<float>(data[y * w + x]);auto corners = harrisCornerDetect(gray);std::cout << "角点数量: " << corners.size() << std::endl;// 重新加载彩色图用于标记角点stbi_image_free(data);data = stbi_load("119_0003.JPG", &w, &h, &channels, 3); // 彩色图if (!data) {std::cerr << "无法读取图像(彩色)!" << std::endl;return 1;}drawCorners(data, w, h, corners);stbi_write_png("harris_result.png", w, h, 3, data, w * 3);stbi_image_free(data);std::cout << "已保存结果图像:harris_result.png" << std::endl;return 0;
}

http://www.xdnf.cn/news/7369.html

相关文章:

  • VisionPro:轴承错位标识
  • QT之绘图模块和双缓冲技术
  • MapStruct Date 转 LocalDate 偏差一天问题
  • 【C++】异常解析
  • AGI大模型(28):LangChain提示模板
  • MySQL中的Change Buffer是什么,它有什么作用?
  • 火山 RTC 引擎9 ----集成 appkey
  • 5月19日笔记
  • 4.7 时间模块
  • 网络传输(ping命令,wget命令,curl命令),端口
  • 接口排查不能靠猜:实战中如何用抓包工具精准定位问题(含 Charles 使用示例)
  • 人工智能、机器学习与深度学习:全面介绍与对比分析
  • 组态王通过开疆智能profinet转ModbusTCP网关连接西门子PLC配置案例
  • 队列和栈的区别
  • 【科研项目】大三保研人科研经历提升
  • Windows多功能工具箱软件推荐
  • gcc: attribute: packed
  • 区块链blog2_中心化与效率
  • 将 Element UI 表格拖动功能提取为公共方法
  • SpringBoot(二)--- SpringBoot基础(http协议、分层解耦)
  • HGDB中如何为表增加自增主键
  • 如何映射 MongoDB 的 _id 字段?
  • Java大厂面试实战:Spring Boot与微服务场景中的技术点解析
  • 4.6 sys模块
  • 线程池中任务队列满-如何把多余的任务存储到Redis中
  • python打卡第30天
  • 体育赛事直播App的架构设计与关键技术解析
  • JavaScript面试题之原型链详解
  • 多商户1.8.1版本前端问题优化集合指南
  • python:pymysql概念、基本操作和注入问题讲解