Opencv里面,特征提取可以分为两步进行:detection + computation,具体为detection后,得要一些keypoints,然后在这些keypoints上应用相应的描述符。
SIFT是一个比较常用的特征,下面就以SIFT特征为例,在Opencv进行SIFT提取(SIFT detection + SIFT description)。具体代码如下:
static inline void
unpackOctave(const KeyPoint& kpt, int& octave, int& layer, float& scale)
{
octave = kpt.octave & 255;
layer = (kpt.octave >> 8) & 255;
octave = octave < 128 ? octave : (-128 | octave);
scale = octave >= 0 ? 1.f / (1 << octave) : (float)(1 << -octave);
}
string strfixedLength(const int i, const int length)
{
ostringstream oss;
if (i < 0)
{
oss << '-1';
}
oss << setfill('0') << setw(length) << (i < 0 ? -i : i);
return oss.str();
}
int main()
{
initModule_nonfree();
int nImages = 1;
string image_path = "D:\\Datasets\\ukbench\\ukbench_test\\ukbench";
//string image_path = "D:\\Datasets\\ukbench\\ukbench_imgs\\full\\ukbench";
Mat img;
vector<KeyPoint> keypoints;
vector<KeyPoint>::iterator itr_keypoints;
Ptr<FeatureDetector> detector = FeatureDetector::create("SIFT");
Ptr<DescriptorExtractor> extractor = DescriptorExtractor::create("SIFT");
Mat sift_tmp;
ofstream img_sift_info("sift_info.txt", ofstream::out);
ofstream img_sift("sift.txt", ofstream::out);
string img_name = "ukbench";
for (int i = 0; i < nImages; ++i)
{
img_name = "ukbench";
img_name = img_name + strfixedLength(i, 5) + ".jpg";
cout << img_name << endl;
img = imread(image_path + strfixedLength(i, 5) + ".jpg", CV_LOAD_IMAGE_GRAYSCALE);
// SIFT detection
detector->detect(img, keypoints);
// SIFT description
extractor->compute(img, keypoints, sift_tmp);
if (keypoints.size() != sift_tmp.rows)
{
cout << "Error: number of keypoints should matched the number of descriptor matrix rows!" << endl;
return -1;
}
// sift file format: "x y:octave:angle:sift descriptor"
int octave;
int layer;
float scale;
for (int i = 0; i < sift_tmp.rows; ++i)
{
unpackOctave(keypoints[i], octave, layer, scale);
img_sift << keypoints[i].pt.x << " " << keypoints[i].pt.y << ":" <<
octave << ":" << keypoints[i].angle << ":";
for (int j = 0; j < sift_tmp.cols - 1; ++j)
{
img_sift << *(float *)(sift_tmp.data + sift_tmp.step[0] * i + sift_tmp.step[1] * j) << " ";
}
img_sift << *(float *)(sift_tmp.data + sift_tmp.step[0] * i + sift_tmp.step[1] * (sift_tmp.cols - 1)) << endl;
}
// sift info file format: "image_name:num. of sift"
img_sift_info << img_name << ":" << keypoints.size() << endl;
keypoints.clear();
}
img_sift.close();
img_sift_info.close();
system("pause");
return 0;
}
与SIFT特征相关的几个参数是octave,scale,angle等,其中octave要特别注意。
Opencv里面把octave,scale,layer等都编码到keypoint的octave中,具体可以看https://stackoverflow.com/questions/17015995/opencv-sift-descriptor-keypoint-radius
上面特征提取代码里面的unpackOctave(const KeyPoint& kpt, int& octave, int& layer, float& scale)函数就是用来将keypoint octave解码成对应的参数的(copy from sift.cpp)。
此外,还需注意的是,不同的detection方法(SIFT、SURF、MSER等),keypoint不同成员变量(octave、size等)的具体含义是不尽相同的,使用时需要小心。