一、函数声明
void detectAndCompute(InputArray img, InputArray mask,
std::vector<KeyPoint>& keypoints,
OutputArray descriptors,
bool useProvidedKeypoints = false) CV_OVERRIDE;
useProvidedKeypoints参数决定当前是探测关键点还是计算描述符文章来源地址https://www.toymoban.com/news/detail-604256.html
二、函数实现关键处注释
void SIFT_Impl::detectAndCompute(InputArray _image, InputArray _mask,
std::vector<KeyPoint>& keypoints,
OutputArray _descriptors,
bool useProvidedKeypoints)
{
CV_TRACE_FUNCTION();
/*
* firstOctave:当图像放大两倍时,金字塔的组号从-1开始,否则从0开始
* actualNOctaves:指图像金字塔的组数
* actualNLayers:指DoG每组内参与极值点探测的图像数,一般为3层
*/
int firstOctave = -1, actualNOctaves = 0, actualNLayers = 0;
Mat image = _image.getMat(), mask = _mask.getMat();
if( image.empty() || image.depth() != CV_8U )
CV_Error( Error::StsBadArg, "image is empty or has incorrect depth (!=CV_8U)" );
if( !mask.empty() && mask.type() != CV_8UC1 )
CV_Error( Error::StsBadArg, "mask has incorrect type (!=CV_8UC1)" );
/*
* 使用提供的关键点,用于计算描述符
*/
if( useProvidedKeypoints )
{
firstOctave = 0;
int maxOctave = INT_MIN;
/*
* 遍历所有的关键点,计算图像金字塔起始组号和总组数
*/
for( size_t i = 0; i < keypoints.size(); i++ )
{
int octave, layer;
/*
* scale指的是当前关键点所在图层的大小与原始图像大小之比;
* 比如,oct = -1时, scale = 2;
* oct = 0时, scale = 1;
*/
float scale;
/*
* OpenCV很巧妙的使用一个变量(cv::KeyPoint::octave)
* 存储了两个信息:oct和layer。具体可见本系列我的另一篇文章
*/
unpackOctave(keypoints[i], octave, layer, scale);
firstOctave = std::min(firstOctave, octave);
maxOctave = std::max(maxOctave, octave);
actualNLayers = std::max(actualNLayers, layer-2);
}
firstOctave = std::min(firstOctave, 0);
CV_Assert( firstOctave >= -1 && actualNLayers <= nOctaveLayers );
actualNOctaves = maxOctave - firstOctave + 1;
}
/*
* 图像初始化,具体可见本系列我的另一篇文章
*/
Mat base = createInitialImage(image, firstOctave < 0, (float)sigma);
std::vector<Mat> gpyr;
/*
* 只有useProvidedKeypoints为true时,actualNOctaves 才会大于 0
* 此时,使用直接使用已经计算好的actualNOctaves 即可;
* 否则,说明要进行关键点探测步骤,需要构建高斯图像金字塔,
* 使用公式 计算组数
*/
int nOctaves = actualNOctaves > 0 ? actualNOctaves : cvRound(std::log( (double)std::min( base.cols, base.rows ) ) / std::log(2.) - 2) - firstOctave;
//double t, tf = getTickFrequency();
//t = (double)getTickCount();
/*
* 建立高斯图像金字塔,具体见本系列我的另一篇文章
*/
buildGaussianPyramid(base, gpyr, nOctaves);
//t = (double)getTickCount() - t;
//printf("pyramid construction time: %g\n", t*1000./tf);
/*
* 进行关键点探测
*/
if( !useProvidedKeypoints )
{
/*
* 构建DOG,具体见本系列我的另一篇文章
*/
std::vector<Mat> dogpyr;
buildDoGPyramid(gpyr, dogpyr);
//t = (double)getTickCount();
/*
* 根据DOG探测关键点,以及极值点亚像素定位
* 具体可见本系列我的另一篇文章
*/
findScaleSpaceExtrema(gpyr, dogpyr, keypoints);
/*
* 关键点排序去重
*/
KeyPointsFilter::removeDuplicatedSorted( keypoints );
/*
* nfeatures:用户提供的最大关键点数
* 若用户提供了最大的关键点数,则根据cv::KeyPoint::response的
* 大小,去除多余关键点
*/
if( nfeatures > 0 )
KeyPointsFilter::retainBest(keypoints, nfeatures);
//t = (double)getTickCount() - t;
//printf("keypoint detection time: %g\n", t*1000./tf);
/*
* 将关键点放缩到原图尺寸下
* 经过findScaleSpaceExtrema获取的关键点坐标与高斯金字塔第一组的
* 尺寸相同,因此,当firstOctave < 0时,要将关键点乘以0.5。
* 否则,金字塔第一层与原图尺寸相同,无需处理。
*/
if( firstOctave < 0 )
for( size_t i = 0; i < keypoints.size(); i++ )
{
KeyPoint& kpt = keypoints[i];
float scale = 1.f/(float)(1 << -firstOctave);
kpt.octave = (kpt.octave & ~255) | ((kpt.octave + firstOctave) & 255);
kpt.pt *= scale;
kpt.size *= scale;
}
/*
* 如果提供了mask,过滤掉mask标记的关键点
*/
if( !mask.empty() )
KeyPointsFilter::runByPixelsMask( keypoints, mask );
}
else
{
// filter keypoints by mask
//KeyPointsFilter::runByPixelsMask( keypoints, mask );
}
/*
* 计算关键点描述符
*/
if( _descriptors.needed() )
{
//t = (double)getTickCount();
/*
* 描述符大小:128维
* 实际上 等于 SIFT_DESCR_WIDTH * SIFT_DESCR_WIDTH * SIFT_DESCR_HIST_BINS;
*/
int dsize = descriptorSize();
/*
* 构造合适大小,类型的描述符集合
*/
_descriptors.create((int)keypoints.size(), dsize, descriptor_type);
Mat descriptors = _descriptors.getMat();
/*
* 计算关键点描述符,具体可见本系列我的另一篇文章
*/
calcDescriptors(gpyr, keypoints, descriptors, nOctaveLayers, firstOctave);
//t = (double)getTickCount() - t;
//printf("descriptor extraction time: %g\n", t*1000./tf);
}
}
三、关键点总结
useProvidedKeypoints
为true时,执行compute功能,计算描述符;为false时,执行detect功能,探测关键点;- 当探测关键点时,构建的高斯金字塔首层图像会扩大两倍,
firstOctave
等于-1;而计算描述符时,firstOctave
会根据传入的关键点集合计算得到- 由
findScaleSpaceExtrema
探测到的关键点坐标,与高斯金字塔第一层的尺度相同。当firstOctave
等于-1时,坐标需要乘以0.5,缩放到原图尺寸。
文章来源:https://www.toymoban.com/news/detail-604256.html
到了这里,关于OpenCV SIFT源码详解——detectAndCompute函数详解的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!