基于ORB算法的图像特征点提取(C语言实现)
一、完整C语言实现方案
由于ORB算法涉及复杂的矩阵运算和图像处理,纯C实现需要大量基础库支持。以下是基于OpenCV C接口和自制简化版ORB两种实现方案。
方案一:使用OpenCV C接口(推荐)
1.1 头文件 orb_opencv.h
/**
* @file orb_opencv.h
* @brief 基于OpenCV C接口的ORB特征点提取
*/
#ifndef __ORB_OPENCV_H
#define __ORB_OPENCV_H
#include <opencv2/core/core_c.h>
#include <opencv2/imgproc/imgproc_c.h>
#include <opencv2/features2d/features2d_c.h>
/* ORB参数结构体 */
typedef struct {
int n_features; // 最大特征点数
float scale_factor; // 金字塔缩放因子
int n_levels; // 金字塔层数
int edge_threshold; // 边缘阈值
int patch_size; // 补丁大小
int fast_threshold; // FAST阈值
} ORB_Params;
/* 关键点结构体 */
typedef struct {
float x; // x坐标
float y; // y坐标
float size; // 特征点大小
float angle; // 方向角度
float response; // 响应强度
int octave; // 所在金字塔层级
} ORB_KeyPoint;
/* 描述子矩阵 */
typedef struct {
unsigned char* data; // 描述子数据
int rows; // 行数(特征点数量)
int cols; // 列数(描述子维度)
} ORB_Descriptors;
/* 函数声明 */
void ORB_Init(ORB_Params* params);
int ORB_DetectAndCompute(const char* image_path,
ORB_KeyPoint** keypoints,
ORB_Descriptors* descriptors);
void ORB_FreeKeypoints(ORB_KeyPoint* keypoints);
void ORB_FreeDescriptors(ORB_Descriptors* descriptors);
void ORB_DrawKeypoints(const char* image_path,
ORB_KeyPoint* keypoints,
int num_keypoints,
const char* output_path);
#endif /* __ORB_OPENCV_H */
1.2 源文件 orb_opencv.c
/**
* @file orb_opencv.c
* @brief ORB特征点提取实现(基于OpenCV C接口)
*/
#include "orb_opencv.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/* 全局ORB参数 */
static ORB_Params g_params = {
.n_features = 500,
.scale_factor = 1.2f,
.n_levels = 8,
.edge_threshold = 31,
.patch_size = 31,
.fast_threshold = 20
};
/* 初始化ORB参数 */
void ORB_Init(ORB_Params* params) {
if (params != NULL) {
memcpy(&g_params, params, sizeof(ORB_Params));
}
}
/* 检测并计算ORB特征 */
int ORB_DetectAndCompute(const char* image_path,
ORB_KeyPoint** keypoints,
ORB_Descriptors* descriptors) {
IplImage* image = NULL;
IplImage* gray = NULL;
CvORB* orb = NULL;
CvSeq* keypoint_seq = NULL;
CvSeq* descriptor_seq = NULL;
int num_keypoints = 0;
// 1. 读取图像
image = cvLoadImage(image_path, CV_LOAD_IMAGE_COLOR);
if (image == NULL) {
fprintf(stderr, "无法读取图像: %s\n", image_path);
return -1;
}
// 2. 转换为灰度图
gray = cvCreateImage(cvGetSize(image), IPL_DEPTH_8U, 1);
cvCvtColor(image, gray, CV_BGR2GRAY);
// 3. 创建ORB检测器
orb = cvCreateORB(g_params.n_features,
g_params.scale_factor,
g_params.n_levels,
g_params.edge_threshold,
g_params.patch_size,
g_params.fast_threshold);
if (orb == NULL) {
fprintf(stderr, "创建ORB检测器失败\n");
goto cleanup;
}
// 4. 检测关键点
keypoint_seq = cvDetectORB(orb, gray, NULL);
if (keypoint_seq == NULL) {
fprintf(stderr, "ORB关键点检测失败\n");
goto cleanup;
}
num_keypoints = keypoint_seq->total;
// 5. 计算描述子
descriptor_seq = cvComputeORB(orb, gray, keypoint_seq, NULL);
if (descriptor_seq == NULL) {
fprintf(stderr, "ORB描述子计算失败\n");
goto cleanup;
}
// 6. 分配内存并复制关键点
*keypoints = (ORB_KeyPoint*)malloc(num_keypoints * sizeof(ORB_KeyPoint));
if (*keypoints == NULL) {
fprintf(stderr, "内存分配失败\n");
goto cleanup;
}
for (int i = 0; i < num_keypoints; i++) {
CvORBKeyPoint* kp = (CvORBKeyPoint*)cvGetSeqElem(keypoint_seq, i);
(*keypoints)[i].x = kp->pt.x;
(*keypoints)[i].y = kp->pt.y;
(*keypoints)[i].size = kp->size;
(*keypoints)[i].angle = kp->angle;
(*keypoints)[i].response = kp->response;
(*keypoints)[i].octave = kp->octave;
}
// 7. 分配内存并复制描述子
descriptors->rows = num_keypoints;
descriptors->cols = 32; // ORB描述子是32字节
descriptors->data = (unsigned char*)malloc(num_keypoints * 32);
if (descriptors->data == NULL) {
fprintf(stderr, "描述子内存分配失败\n");
goto cleanup;
}
for (int i = 0; i < num_keypoints; i++) {
unsigned char* desc = (unsigned char*)cvGetSeqElem(descriptor_seq, i);
memcpy(descriptors->data + i * 32, desc, 32);
}
printf("检测到 %d 个ORB特征点\n", num_keypoints);
cleanup:
if (descriptor_seq) cvReleaseSeq(&descriptor_seq);
if (keypoint_seq) cvReleaseSeq(&keypoint_seq);
if (orb) cvReleaseORB(&orb);
if (gray) cvReleaseImage(&gray);
if (image) cvReleaseImage(&image);
return num_keypoints;
}
/* 释放关键点内存 */
void ORB_FreeKeypoints(ORB_KeyPoint* keypoints) {
if (keypoints != NULL) {
free(keypoints);
}
}
/* 释放描述子内存 */
void ORB_FreeDescriptors(ORB_Descriptors* descriptors) {
if (descriptors != NULL && descriptors->data != NULL) {
free(descriptors->data);
descriptors->data = NULL;
}
}
/* 绘制关键点 */
void ORB_DrawKeypoints(const char* image_path,
ORB_KeyPoint* keypoints,
int num_keypoints,
const char* output_path) {
IplImage* image = cvLoadImage(image_path, CV_LOAD_IMAGE_COLOR);
if (image == NULL) {
fprintf(stderr, "无法读取图像: %s\n", image_path);
return;
}
// 绘制关键点
for (int i = 0; i < num_keypoints; i++) {
CvPoint center = cvPoint((int)keypoints[i].x, (int)keypoints[i].y);
int radius = (int)(keypoints[i].size / 2);
// 绘制圆圈
cvCircle(image, center, radius, CV_RGB(0, 255, 0), 1, 8, 0);
// 绘制方向线
if (keypoints[i].angle >= 0) {
float angle_rad = keypoints[i].angle * 3.1415926f / 180.0f;
int end_x = center.x + (int)(radius * cos(angle_rad));
int end_y = center.y + (int)(radius * sin(angle_rad));
cvLine(image, center, cvPoint(end_x, end_y), CV_RGB(255, 0, 0), 1, 8, 0);
}
}
// 保存结果
cvSaveImage(output_path, image, NULL);
printf("结果已保存到: %s\n", output_path);
cvReleaseImage(&image);
}
1.3 主程序 main.c
/**
* @file main.c
* @brief ORB特征点提取测试程序
*/
#include "orb_opencv.h"
#include <stdio.h>
int main() {
ORB_KeyPoint* keypoints = NULL;
ORB_Descriptors descriptors = {NULL, 0, 0};
int num_keypoints;
// 1. 初始化ORB参数
ORB_Params params = {
.n_features = 1000,
.scale_factor = 1.2f,
.n_levels = 8,
.edge_threshold = 31,
.patch_size = 31,
.fast_threshold = 20
};
ORB_Init(¶ms);
// 2. 检测并计算特征点
printf("正在检测ORB特征点...\n");
num_keypoints = ORB_DetectAndCompute("test_image.jpg", &keypoints, &descriptors);
if (num_keypoints > 0) {
printf("成功检测到 %d 个特征点\n", num_keypoints);
printf("描述子维度: %d x %d\n", descriptors.rows, descriptors.cols);
// 3. 绘制并保存结果
ORB_DrawKeypoints("test_image.jpg", keypoints, num_keypoints, "orb_result.jpg");
// 4. 打印前5个特征点信息
printf("\n前5个特征点信息:\n");
for (int i = 0; i < (num_keypoints < 5 ? num_keypoints : 5); i++) {
printf(" 点%d: (%.1f, %.1f), 大小=%.1f, 角度=%.1f°, 响应=%.2f\n",
i+1, keypoints[i].x, keypoints[i].y,
keypoints[i].size, keypoints[i].angle, keypoints[i].response);
}
} else {
printf("未检测到特征点\n");
}
// 5. 释放内存
ORB_FreeKeypoints(keypoints);
ORB_FreeDescriptors(&descriptors);
return 0;
}
方案二:自制简化版ORB(纯C实现)
如果不想依赖OpenCV,可以实现一个简化版的ORB。以下是核心部分的简化实现:
2.1 简化版ORB头文件 simple_orb.h
/**
* @file simple_orb.h
* @brief 简化版ORB特征点提取(纯C实现)
*/
#ifndef __SIMPLE_ORB_H
#define __SIMPLE_ORB_H
#include <stdint.h>
/* 图像结构体 */
typedef struct {
uint8_t* data; // 像素数据
int width; // 图像宽度
int height; // 图像高度
int channels; // 通道数
} SimpleImage;
/* 关键点结构体 */
typedef struct {
float x, y; // 坐标
float size; // 尺度
float angle; // 方向
float response; // 响应值
} SimpleKeyPoint;
/* 简化ORB函数 */
SimpleImage* SimpleImage_Load(const char* filename);
void SimpleImage_Free(SimpleImage* img);
int SimpleORB_Detect(SimpleImage* image,
SimpleKeyPoint** keypoints,
int max_keypoints);
#endif /* __SIMPLE_ORB_H */
2.2 简化版ORB源文件 simple_orb.c
/**
* @file simple_orb.c
* @brief 简化版ORB实现(仅包含FAST检测和灰度质心方向计算)
*/
#include "simple_orb.h"
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
/* 灰度化 */
static void GrayScale(SimpleImage* src, SimpleImage* dst) {
for (int i = 0; i < src->width * src->height; i++) {
uint8_t r = src->data[i * 3];
uint8_t g = src->data[i * 3 + 1];
uint8_t b = src->data[i * 3 + 2];
dst->data[i] = (uint8_t)(0.299f * r + 0.587f * g + 0.114f * b);
}
}
/* FAST角点检测(简化版) */
static int FAST_Detect(SimpleImage* gray, int x, int y, int threshold) {
if (x < 3 || y < 3 || x >= gray->width-3 || y >= gray->height-3) return 0;
uint8_t center = gray->data[y * gray->width + x];
int count = 0;
// 检查圆周上的16个点(简化版只检查8个点)
int offsets[8][2] = {{3,0}, {3,3}, {0,3}, {-3,3}, {-3,0}, {-3,-3}, {0,-3}, {3,-3}};
for (int i = 0; i < 8; i++) {
int nx = x + offsets[i][0];
int ny = y + offsets[i][1];
uint8_t pixel = gray->data[ny * gray->width + nx];
if (abs(pixel - center) > threshold) {
count++;
}
}
return (count >= 6) ? 1 : 0; // 至少6个点满足条件
}
/* 计算灰度质心方向 */
static float ComputeOrientation(SimpleImage* gray, int cx, int cy, int radius) {
float m00 = 0, m01 = 0, m10 = 0;
for (int y = cy - radius; y <= cy + radius; y++) {
for (int x = cx - radius; x <= cx + radius; x++) {
if (x >= 0 && x < gray->width && y >= 0 && y < gray->height) {
uint8_t intensity = gray->data[y * gray->width + x];
m00 += intensity;
m10 += x * intensity;
m01 += y * intensity;
}
}
}
if (m00 == 0) return 0;
return atan2(m01 / m00 - cy, m10 / m00 - cx) * 180.0f / 3.1415926f;
}
/* 简化ORB检测 */
int SimpleORB_Detect(SimpleImage* image, SimpleKeyPoint** keypoints, int max_keypoints) {
SimpleImage* gray = (SimpleImage*)malloc(sizeof(SimpleImage));
gray->width = image->width;
gray->height = image->height;
gray->channels = 1;
gray->data = (uint8_t*)malloc(gray->width * gray->height);
// 1. 灰度化
GrayScale(image, gray);
// 2. 检测FAST角点
int capacity = 1000;
*keypoints = (SimpleKeyPoint*)malloc(capacity * sizeof(SimpleKeyPoint));
int count = 0;
for (int y = 3; y < gray->height-3; y += 4) {
for (int x = 3; x < gray->width-3; x += 4) {
if (FAST_Detect(gray, x, y, 30)) {
if (count >= capacity) {
capacity *= 2;
*keypoints = (SimpleKeyPoint*)realloc(*keypoints, capacity * sizeof(SimpleKeyPoint));
}
(*keypoints)[count].x = x;
(*keypoints)[count].y = y;
(*keypoints)[count].size = 16.0f; // 固定尺度
(*keypoints)[count].angle = ComputeOrientation(gray, x, y, 8);
(*keypoints)[count].response = 1.0f;
count++;
if (count >= max_keypoints) break;
}
}
if (count >= max_keypoints) break;
}
free(gray->data);
free(gray);
return count;
}
三、编译与运行
3.1 使用OpenCV版本的编译命令
# 使用gcc编译(需要安装OpenCV开发库)
gcc -o orb_detector main.c orb_opencv.c \
`pkg-config --cflags --libs opencv4`
# 运行程序
./orb_detector
3.2 使用简化版编译
# 编译简化版
gcc -o simple_orb main_simple.c simple_orb.c -lm
# 运行
./simple_orb
参考代码 利用Orb算法实现图像特征点的提取 www.youwenfan.com/contentcnu/56478.html
四、性能优化建议
4.1 算法优化
/* 使用SIMD指令加速 */
#ifdef __SSE2__
#include <emmintrin.h>
void FAST_Detect_SIMD(uint8_t* image, int width, int height) {
// 使用SSE2指令集加速FAST检测
}
#endif
/* 使用OpenMP并行化 */
#pragma omp parallel for
for (int y = 0; y < height; y++) {
// 并行处理每一行
}
4.2 内存优化
/* 使用内存池减少malloc/free开销 */
typedef struct {
SimpleKeyPoint* pool;
int capacity;
int used;
} KeyPointPool;
/* 使用固定大小数组避免动态分配 */
#define MAX_KEYPOINTS 1000
static SimpleKeyPoint keypoint_buffer[MAX_KEYPOINTS];
五、实际应用示例
5.1 图像拼接
/* 使用ORB特征进行图像拼接 */
int StitchImages(const char* img1, const char* img2, const char* output) {
ORB_KeyPoint* kp1, *kp2;
ORB_Descriptors desc1, desc2;
// 提取特征
ORB_DetectAndCompute(img1, &kp1, &desc1);
ORB_DetectAndCompute(img2, &kp2, &desc2);
// 匹配特征点(使用汉明距离)
// ... 匹配逻辑 ...
// 计算单应性矩阵
// ... 拼接逻辑 ...
return 0;
}
5.2 实时视频特征跟踪
/* 实时ORB特征跟踪 */
int RealTimeTracking(int camera_id) {
// 初始化摄像头
// 循环读取帧
// 提取ORB特征
// 光流跟踪
// 显示结果
return 0;
}