ORB特徴抽出の実装手法
ORBアルゴリズムの実装には複雑な行列演算が必要なため、以下ではOpenCVを利用した方法と独自実装による簡易版の2種類のアプローチを紹介します。
OpenCVを利用した実装
ヘッダファイル定義
#ifndef ORB_FEATURE_DETECTOR_H
#define ORB_FEATURE_DETECTOR_H
#include <opencv2/core/core_c.h>
typedef struct {
int max_features;
float scale_factor;
int pyramid_levels;
int edge_margin;
int window_size;
int fast_thresh;
} ORB_Config;
typedef struct {
float pos_x;
float pos_y;
float feature_size;
float orientation;
float strength;
int pyramid_level;
} FeaturePoint;
typedef struct {
uint8_t* descriptor_data;
int point_count;
int descriptor_size;
} FeatureDescriptors;
void ORB_Configure(ORB_Config* settings);
int ORB_ExtractFeatures(const char* image_path, FeaturePoint** points, FeatureDescriptors* descriptors);
void ORB_ReleasePoints(FeaturePoint* points);
void ORB_ReleaseDescriptors(FeatureDescriptors* descriptors);
void ORB_VisualizeFeatures(const char* source_path, FeaturePoint* points, int count, const char* result_path);
#endif
特徴点抽出実装
#include "orb_feature_detector.h"
#include <opencv2/imgproc/imgproc_c.h>
static ORB_Config config = {
.max_features = 500,
.scale_factor = 1.2f,
.pyramid_levels = 8,
.edge_margin = 31,
.window_size = 31,
.fast_thresh = 20
};
void ORB_Configure(ORB_Config* settings) {
if (settings) memcpy(&config, settings, sizeof(ORB_Config));
}
int ORB_ExtractFeatures(const char* image_path, FeaturePoint** points, FeatureDescriptors* descriptors) {
IplImage* src = cvLoadImage(image_path, CV_LOAD_IMAGE_COLOR);
if (!src) return -1;
IplImage* gray = cvCreateImage(cvGetSize(src), IPL_DEPTH_8U, 1);
cvCvtColor(src, gray, CV_BGR2GRAY);
CvORB* detector = cvCreateORB(config.max_features, config.scale_factor,
config.pyramid_levels, config.edge_margin,
config.window_size, config.fast_thresh);
CvSeq* detected_points = cvDetectORB(detector, gray, NULL);
CvSeq* computed_descriptors = cvComputeORB(detector, gray, detected_points, NULL);
int count = detected_points->total;
*points = malloc(count * sizeof(FeaturePoint));
for (int i = 0; i < count; i++) {
CvORBKeyPoint* kp = cvGetSeqElem(detected_points, i);
(*points)[i] = (FeaturePoint){
.pos_x = kp->pt.x,
.pos_y = kp->pt.y,
.feature_size = kp->size,
.orientation = kp->angle,
.strength = kp->response,
.pyramid_level = kp->octave
};
}
descriptors->point_count = count;
descriptors->descriptor_size = 32;
descriptors->descriptor_data = malloc(count * 32);
for (int i = 0; i < count; i++) {
uint8_t* desc = cvGetSeqElem(computed_descriptors, i);
memcpy(descriptors->descriptor_data + i * 32, desc, 32);
}
cvReleaseORB(&detector);
cvReleaseImage(&gray);
cvReleaseImage(&src);
return count;
}
簡易版独自実装
基本構造定義
#ifndef SIMPLE_FEATURE_DETECTOR_H
#define SIMPLE_FEATURE_DETECTOR_H
typedef struct {
uint8_t* pixel_data;
int width;
int height;
int channels;
} ImageData;
typedef struct {
float x, y;
float size;
float angle;
float score;
} DetectedPoint;
ImageData* LoadImageFile(const char* filename);
void FreeImageData(ImageData* img);
int SimpleFeatureDetection(ImageData* image, DetectedPoint** points, int max_points);
#endif
特徴検出ロジック
#include "simple_feature_detector.h"
#include <math.h>
static void ConvertToGrayscale(ImageData* src, ImageData* dst) {
for (int i = 0; i < src->width * src->height; i++) {
uint8_t r = src->pixel_data[i * 3];
uint8_t g = src->pixel_data[i * 3 + 1];
uint8_t b = src->pixel_data[i * 3 + 2];
dst->pixel_data[i] = (uint8_t)(0.299 * r + 0.587 * g + 0.114 * b);
}
}
static int DetectCorner(ImageData* gray, int x, int y, int threshold) {
uint8_t center = gray->pixel_data[y * gray->width + x];
int diff_count = 0;
const int check_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 + check_offsets[i][0];
int ny = y + check_offsets[i][1];
uint8_t px = gray->pixel_data[ny * gray->width + nx];
if (abs(px - center) > threshold) diff_count++;
}
return diff_count >= 6;
}
static float CalculateOrientation(ImageData* gray, int cx, int cy, int radius) {
float total_intensity = 0, weighted_x = 0, weighted_y = 0;
for (int dy = -radius; dy <= radius; dy++) {
for (int dx = -radius; dx <= radius; dx++) {
int x = cx + dx;
int y = cy + dy;
if (x >= 0 && y >= 0 && x < gray->width && y < gray->height) {
uint8_t val = gray->pixel_data[y * gray->width + x];
total_intensity += val;
weighted_x += x * val;
weighted_y += y * val;
}
}
}
if (total_intensity == 0) return 0;
return atan2(weighted_y/total_intensity - cy, weighted_x/total_intensity - cx) * 180.0f / M_PI;
}
int SimpleFeatureDetection(ImageData* image, DetectedPoint** points, int max_points) {
ImageData* gray = malloc(sizeof(ImageData));
gray->width = image->width;
gray->height = image->height;
gray->channels = 1;
gray->pixel_data = malloc(gray->width * gray->height);
ConvertToGrayscale(image, gray);
int capacity = 1000;
*points = malloc(capacity * sizeof(DetectedPoint));
int count = 0;
for (int y = 3; y < gray->height-3; y += 4) {
for (int x = 3; x < gray->width-3; x += 4) {
if (DetectCorner(gray, x, y, 30)) {
if (count >= capacity) {
capacity *= 2;
*points = realloc(*points, capacity * sizeof(DetectedPoint));
}
(*points)[count] = (DetectedPoint){
.x = x,
.y = y,
.size = 16.0f,
.angle = CalculateOrientation(gray, x, y, 8),
.score = 1.0f
};
count++;
if (count >= max_points) break;
}
}
}
free(gray->pixel_data);
free(gray);
return count;
}
ビルド方法
# OpenCVを使用する場合
gcc -o feature_extractor main.c orb_feature_detector.c \
`pkg-config --cflags --libs opencv4`
# 簡易版の場合
gcc -o simple_detector simple_main.c simple_feature_detector.c -lm
性能最適化手法
#pragma omp parallel for
for (int y = 0; y < image_height; y++) {
// 行単位の並列処理
}
#ifdef __SSE2__
#include <emmintrin.h>
void OptimizedCornerDetection(uint8_t* pixels, int width, int height) {
// SIMD命令による高速化
}
#endif
応用例
int CombineImages(const char* image1, const char* image2, const char* output) {
FeaturePoint* points1, *points2;
FeatureDescriptors desc1, desc2;
ORB_ExtractFeatures(image1, &points1, &desc1);
ORB_ExtractFeatures(image2, &points2, &desc2);
// 特徴点マッチングと画像結合処理
return 0;
}