SURF C++代码 详细阅读(二)—— 极值点检测 确定极值点精确位置

阅读(一)进行到了 2.2.2 获取特征点 buildResponseMap();

2.2.3 极值点检测
//! Find the image features and write into vector of features
void FastHessian::getIpoints()
{
    // filter index map
    static const int filter_map [OCTAVES][INTERVALS] = {{0,1,2,3}, {1,3,4,5}, {3,5,6,7}, {5,7,8,9}, {7,9,10,11}};

    // Clear the vector of exisiting ipts
    ipts.clear();

    std::clock_t start;
    start = std::clock();

    // Build the response map
    buildResponseMap();

    std::cout << "buildResponseMap took: " << (std::clock() - start) / (double)(CLOCKS_PER_SEC / 1000) << " ms" << std::endl;

    start = std::clock();

    // Get the response layers 判断是否是极值点
    ResponseLayer *b, *m, *t;

    for (int o = 0; o < octaves; ++o) 
    {
        for (int i = 0; i <= 1; ++i)
        {
            b = responseMap.at(filter_map[o][i]);
            m = responseMap.at(filter_map[o][i+1]);
            t = responseMap.at(filter_map[o][i+2]);

            for (int r = 0; r < t->height; ++r)
            {
                for (int c = 0; c < t->width; ++c)
                {
                    //图像每一点是否极值 参数:行 列 第三张图 第二张图 第一张图
                    if (isExtremum(r, c, t, m, b))
                    {
                        interpolateExtremum(r, c, t, m, b);//, &ipts_tmp[r][c], &changed[r][c]);
                    }
                }
            }
        }
    }

     std::cout << "Ipoint vector took: " << (std::clock() - start) / (double)(CLOCKS_PER_SEC / 1000) << " ms" << std::endl;
}

在这里插入图片描述
在这里插入图片描述

判断极值点,需要在每一Octave层的四个间隔层中,对于每个像素点,在相邻三个层的领域26个邻居中判断行列式近似值响应是否是极值。
代码中需要判断三项:

  • 图像滤波边缘上的点 不是极值点
  • 行列式近似值小于阈值的点不是极值点
  • 3x3x3 邻域存在更大值的点不是极值点
//! Non Maximal Suppression function
//! 图像每一点是否极值 参数:行 列 第三张图 第二张图 第一张图
int FastHessian::isExtremum(int r, int c, ResponseLayer *t, ResponseLayer *m, ResponseLayer *b)
{
    // bounds check
    //图像滤波边缘上的点 不是极值点
    int layerBorder = (t->filter + 1) / (2 * t->step);
    if (r <= layerBorder || r >= t->height - layerBorder || c <= layerBorder || c >= t->width - layerBorder)
        return 0;

    // check the candidate point in the middle layer is above thresh
    // 对于中间的图(海森矩阵响应 行列式近似值)小于阈值的点不是极值点
    float candidate = m->getResponse(r, c, t);
    if (candidate < thresh) 
        return 0; 

    int ret_val = 1;//1是极值 0不是极值
    for (int rr = -1; rr <=1; ++rr)
    {
        for (int cc = -1; cc <=1; ++cc)
        {
            // if any response in 3x3x3 is greater candidate not maximum
            if (
                t->getResponse(r+rr, c+cc) >= candidate ||
                ((rr != 0 || cc != 0) && m->getResponse(r+rr, c+cc, t) >= candidate) ||
                b->getResponse(r+rr, c+cc, t) >= candidate
                ) 
                ret_val = 0;
        }
    }

    return ret_val;
}

这里对于邻域极值判断需要还原到第三幅图(大尺度)的比例上,通过重载getResponse函数来实现。

inline float getResponse(unsigned int row, unsigned int column)
    {
        return responses[row * width + column];
    }
inline float getResponse(unsigned int row, unsigned int column, ResponseLayer *src)
    {
        int scale = this->width / src->width;
        return responses[(scale * row) * width + (scale * column)];
    }
2.2.4 确定极值点精确位置

这一步和SIFT基本一致。26个邻域的极值点检测,得到的值是“近似”的,因为最大值/最小值几乎从不精确地位于像素上。它位于像素之间。但我们无法访问像素之间的数据。因此,我们必须从数学上定位子像素位置。

//! Interpolate scale-space extrema to subpixel accuracy to form an image feature.     
void FastHessian::interpolateExtremum(int r, int c, ResponseLayer *t, ResponseLayer *m, ResponseLayer *b)//, Ipoint* ipts_tmp, int* changed)
{
    // get the step distance between filters
    // check the middle filter is mid way between top and bottom
    int filterStep = (m->filter - b->filter);
    // Get the offsets to the actual location of the extremum
    double xi = 0, xr = 0, xc = 0;
    interpolateStep(r, c, t, m, b, &xi, &xr, &xc );

    // If point is sufficiently close to the actual extremum
	  //如果点距离极值位置足够近(<0.5)获取点的信息
    if( fabs( xi ) < 0.5f    &&    fabs( xr ) < 0.5f    &&    fabs( xc ) < 0.5f )
    {
        Ipoint ipt;
        ipt.x = static_cast<float>((c + xc) * t->step);
        ipt.y = static_cast<float>((r + xr) * t->step);
        ipt.scale = static_cast<float>((0.1333f) * (m->filter + xi * filterStep));
        ipt.laplacian = static_cast<int>(m->getLaplacian(r,c,t));
        ipts.push_back(ipt);
        // *ipts_tmp = ipt;
        // *changed = 1;
    }
}

通过泰勒二阶展开式,近似极值点精确位置,代码在interpolateStep
在这里插入图片描述
对x求导并让方程等于0得到x相对于插值中心偏移量
在这里插入图片描述
(其中需要用到二次型的导数,推导过程见https://blog.csdn.net/weixin_43342476/article/details/121562958):

当它在任一维度上的偏移量小于0.5时(绝对值<0.5)被认为是特征点。

2.3 新建自定义Ipoint类获得特征点

上面的过程中我们得到了特征点的位置、尺度、拉普拉斯(用来加速特征点匹配)数据,
Ipont类中定义了属性和若干方法,包括了主方向,还定义了特征点之间的欧几里得距离计算(64维描述符差的平方和)

class Ipoint {

public:

    //! Destructor
    ~Ipoint() {};

    //! Constructor
    Ipoint() : orientation(0) {};

    //! Gets the distance in descriptor space between Ipoints
	// 两个特征点之间的欧几里得距离
    float operator-(const Ipoint &rhs)
    {
        float sum=0.f;
        for(int i=0; i < 64; ++i)
            sum += (this->descriptor[i] - rhs.descriptor[i])*(this->descriptor[i] - rhs.descriptor[i]);
        return sqrt(sum);
    };

    //! Coordinates of the detected interest point
    float x, y;

    //! Detected scale
    float scale;

    //! Orientation measured anti-clockwise from +ve x-axis
    float orientation;

    //! Sign of laplacian for fast matching purposes
    int laplacian;

    //! Vector of descriptor components
    float descriptor[64];

    //! Placeholds for point motion (can be used for frame to frame motion analysis)
    float dx, dy;

    //! Used to store cluster index
    int clusterIndex;
};

如果用在图像拼接场景,还需在Ipoint类中定义特征点的匹配,扭曲变换、图像混合等函数。

void getMatches(IpVec &ipts1, IpVec &ipts2, IpPairVec &matches);
int translateCorners(IpPairVec &matches, const CvPoint src_corners[4], CvPoint dst_corners[4]);

cv::Mat getCvStitch(IplImage *src, cv::Mat warpped);
cv::Mat getWarpped(IplImage *original, cv::Mat H);
cv::Mat getCvWarpped(IpPairVec &matches, IplImage *original);
cv::Mat getWarppedReMap(IpPairVec &matches, IplImage *original);

cv::Mat findHom(IpPairVec &matches);
cv::Mat getWarppedAcc(IplImage *original, cv::Mat H);
std::vector<cv::Mat> getWarpped_blend(IplImage *original, cv::Mat H);
cv::Mat getHomography(IpPairVec &matches, IplImage *original);
cv::Mat getBlended(IplImage *img1, IplImage *img2, IpPairVec &matches, cv::Mat &warpped, cv::Mat &mask2);

接着来构造SURF描述符,未完待续。

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值