基于ORB算法的图像特征点提取(C语言实现)

基于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(&params);
    
    // 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;
}

 

专注于matlab/simulink,电子电路,编程