一、概述
AGAST是一种计算二元决策树(角检测器)的技术,该技术是通用的,并且不必适应新环境。根据定义,它是完整的(没有假阳性或假阴性响应),唯一的参数是内存访问时间来加权各种像素比较。该树对于 AST 掩码中相似像素的某个概率是最优的。
通过组合两棵树,角点检测器自动适应环境,并为图像区域提供最有效的决策树,只有一个像素延迟(见下图)。因此,它产生了一个角检测器,该检测器速度更快且无需进行训练,同时保持与(完整)FAST 角检测器相同的角响应和可重复性。我们将此检测器称为 AGAST,它代表 Adaptive and Generic Accelerated Segment Test。
AGAST 所基于的 AST 是由 Edward Rosten(等人)开发的,在AGAST中,只有构建和使用 AST 的决策树的方式得到了显着改进。 AGAST 也使用与 FAST 相同的非最大抑制。
二、类参考
1、函数原型
void cv::AGAST ( InputArray image,
std::vector< KeyPoint > & keypoints,
int threshold,
bool nonmaxSuppression,
AgastFeatureDetector::DetectorType type
)
2、参数详解
image | 检测到关键点(角)的灰度图像。 |
keypoints | 在图像上检测到的关键点。 |
threshold | 中心像素的强度与围绕该像素的圆的像素之间的差异阈值。 |
nonmaxSuppression | 如果为真,则对检测到的角点(关键点)应用非最大抑制。 |
type | 论文中定义的四个邻域之一:AgastFeatureDetector::AGAST_5_8、AgastFeatureDetector::AGAST_7_12d、AgastFeatureDetector::AGAST_7_12s、AgastFeatureDetector::OAST_9_16 |
三、OpenCV源码
1、源码路径
opencv\modules\features2d\src\agast.cpp
2、源码代码
部分代码
class AgastFeatureDetector_Impl CV_FINAL : public AgastFeatureDetector
{
public:
AgastFeatureDetector_Impl( int _threshold, bool _nonmaxSuppression, DetectorType _type )
: threshold(_threshold), nonmaxSuppression(_nonmaxSuppression), type(_type)
{}
void detect( InputArray _image, std::vector<KeyPoint>& keypoints, InputArray _mask ) CV_OVERRIDE
{
CV_INSTRUMENT_REGION();
if(_image.empty())
{
keypoints.clear();
return;
}
Mat mask = _mask.getMat(), grayImage;
UMat ugrayImage;
_InputArray gray = _image;
if( _image.type() != CV_8U )
{
_OutputArray ogray = _image.isUMat() ? _OutputArray(ugrayImage) : _OutputArray(grayImage);
cvtColor( _image, ogray, COLOR_BGR2GRAY );
gray = ogray;
}
keypoints.clear();
AGAST( gray, keypoints, threshold, nonmaxSuppression, type );
KeyPointsFilter::runByPixelsMask( keypoints, mask );
}
void set(int prop, double value)
{
if(prop == THRESHOLD)
threshold = cvRound(value);
else if(prop == NONMAX_SUPPRESSION)
nonmaxSuppression = value != 0;
else
CV_Error(Error::StsBadArg, "");
}
double get(int prop) const
{
if(prop == THRESHOLD)
return threshold;
if(prop == NONMAX_SUPPRESSION)
return nonmaxSuppression;
CV_Error(Error::StsBadArg, "");
return 0;
}
void setThreshold(int threshold_) CV_OVERRIDE { threshold = threshold_; }
int getThreshold() const CV_OVERRIDE { return threshold; }
void setNonmaxSuppression(bool f) CV_OVERRIDE { nonmaxSuppression = f; }
bool getNonmaxSuppression() const CV_OVERRIDE { return nonmaxSuppression; }
void setType(DetectorType type_) CV_OVERRIDE{ type = type_; }
DetectorType getType() const CV_OVERRIDE{ return type; }
int threshold;
bool nonmaxSuppression;
DetectorType type;
};
Ptr<AgastFeatureDetector> AgastFeatureDetector::create( int threshold, bool nonmaxSuppression, AgastFeatureDetector::DetectorType type )
{
return makePtr<AgastFeatureDetector_Impl>(threshold, nonmaxSuppression, type);
}
void AGAST(InputArray _img, std::vector<KeyPoint>& keypoints, int threshold, bool nonmax_suppression, AgastFeatureDetector::DetectorType type)
{
CV_INSTRUMENT_REGION();
std::vector<KeyPoint> kpts;
// detect
switch(type) {
case AgastFeatureDetector::AGAST_5_8:
AGAST_5_8(_img, kpts, threshold);
break;
case AgastFeatureDetector::AGAST_7_12d:
AGAST_7_12d(_img, kpts, threshold);
break;
case AgastFeatureDetector::AGAST_7_12s:
AGAST_7_12s(_img, kpts, threshold);
break;
case AgastFeatureDetector::OAST_9_16:
OAST_9_16(_img, kpts, threshold);
break;
}
cv::Mat img = _img.getMat();
// score
int pixel_[16];
makeAgastOffsets(pixel_, (int)img.step, type);
std::vector<KeyPoint>::iterator kpt;
for(kpt = kpts.begin(); kpt != kpts.end(); ++kpt)
{
switch(type) {
case AgastFeatureDetector::AGAST_5_8:
kpt->response = (float)agast_cornerScore<AgastFeatureDetector::AGAST_5_8>
(&img.at<uchar>((int)kpt->pt.y, (int)kpt->pt.x), pixel_, threshold);
break;
case AgastFeatureDetector::AGAST_7_12d:
kpt->response = (float)agast_cornerScore<AgastFeatureDetector::AGAST_7_12d>
(&img.at<uchar>((int)kpt->pt.y, (int)kpt->pt.x), pixel_, threshold);
break;
case AgastFeatureDetector::AGAST_7_12s:
kpt->response = (float)agast_cornerScore<AgastFeatureDetector::AGAST_7_12s>
(&img.at<uchar>((int)kpt->pt.y, (int)kpt->pt.x), pixel_, threshold);
break;
case AgastFeatureDetector::OAST_9_16:
kpt->response = (float)agast_cornerScore<AgastFeatureDetector::OAST_9_16>
(&img.at<uchar>((int)kpt->pt.y, (int)kpt->pt.x), pixel_, threshold);
break;
}
}
// suppression
if(nonmax_suppression)
{
size_t j;
size_t curr_idx;
size_t lastRow = 0, next_lastRow = 0;
size_t num_Corners = kpts.size();
size_t lastRowCorner_ind = 0, next_lastRowCorner_ind = 0;
std::vector<int> nmsFlags;
std::vector<KeyPoint>::const_iterator currCorner;
currCorner = kpts.begin();
nmsFlags.resize((int)num_Corners);
// set all flags to MAXIMUM
for(j = 0; j < num_Corners; j++)
nmsFlags[j] = -1;
for(curr_idx = 0; curr_idx < num_Corners; curr_idx++)
{
int t;
// check above
if(lastRow + 1 < currCorner->pt.y)
{
lastRow = next_lastRow;
lastRowCorner_ind = next_lastRowCorner_ind;
}
if(next_lastRow != currCorner->pt.y)
{
next_lastRow = (size_t) currCorner->pt.y;
next_lastRowCorner_ind = curr_idx;
}
if(lastRow + 1 == currCorner->pt.y)
{
// find the corner above the current one
while( (kpts[lastRowCorner_ind].pt.x < currCorner->pt.x)
&& (kpts[lastRowCorner_ind].pt.y == lastRow) )
lastRowCorner_ind++;
if( (kpts[lastRowCorner_ind].pt.x == currCorner->pt.x)
&& (lastRowCorner_ind != curr_idx) )
{
size_t w = lastRowCorner_ind;
// find the maximum in this block
while(nmsFlags[w] != -1)
w = nmsFlags[w];
if(kpts[curr_idx].response < kpts[w].response)
nmsFlags[curr_idx] = (int)w;
else
nmsFlags[w] = (int)curr_idx;
}
}
// check left
t = (int)curr_idx - 1;
if( (curr_idx != 0) && (kpts[t].pt.y == currCorner->pt.y)
&& (kpts[t].pt.x + 1 == currCorner->pt.x) )
{
int currCornerMaxAbove_ind = nmsFlags[curr_idx];
// find the maximum in that area
while(nmsFlags[t] != -1)
t = nmsFlags[t];
// no maximum above
if(currCornerMaxAbove_ind == -1)
{
if((size_t)t != curr_idx)
{
if ( kpts[curr_idx].response < kpts[t].response )
nmsFlags[curr_idx] = t;
else
nmsFlags[t] = (int)curr_idx;
}
}
else // maximum above
{
if(t != currCornerMaxAbove_ind)
{
if(kpts[currCornerMaxAbove_ind].response < kpts[t].response)
{
nmsFlags[currCornerMaxAbove_ind] = t;
nmsFlags[curr_idx] = t;
}
else
{
nmsFlags[t] = currCornerMaxAbove_ind;
nmsFlags[curr_idx] = currCornerMaxAbove_ind;
}
}
}
}
++currCorner;
}
// collecting maximum corners
for(curr_idx = 0; curr_idx < num_Corners; curr_idx++)
{
if (nmsFlags[curr_idx] == -1)
keypoints.push_back(kpts[curr_idx]);
}
} else
{
keypoints = kpts;
}
}
String AgastFeatureDetector::getDefaultName() const
{
return(Feature2D::getDefaultName() + ".AgastFeatureDetector");
}