计算机视觉--局部图像描述子:Harris角点检测算法、SIFT(尺度不变特征变换)

目录

一、Harris角点检测算法

1.1角点是什么

1.2角点算法的基本原理

1.3数学方法刻画角点特征

1.4角点响应函数

1.5Harris角点检测步骤

1.6角点检测实例

1.7图像中寻找对应点

二、SIFT(尺度不变特征变换)

2.1SIFT简介

2.2SIFT特点

2.3SIFT算法

2.4SIFT算法实现

一、Harris角点检测算法

1.1角点是什么

>轮廓之间的交点;

>对于同一场景,即使视角发生变化,通常具备稳定性质的特征;

>该点附近区域的像素点无论在梯度方向上还是其梯度幅值上有着较大变化;

计算机视觉--局部图像描述子:Harris角点检测算法、SIFT(尺度不变特征变换)_第1张图片

计算机视觉--局部图像描述子:Harris角点检测算法、SIFT(尺度不变特征变换)_第2张图片

1.2角点算法的基本原理 

算法基本思想是使用一个固定窗口在图像上进行任意方向上的滑动,比较滑动前与滑动后两种情况,窗口中的像素灰度变化程度,如果存在任意方向上的滑动,都有着较大灰度变化,那么我们可以认为该窗口中存在角点。

从图像局部的小窗口观察图像特征;角点定义 <-窗口向任意方向的移动都导致图像灰度的明
显变化
代码实现:
Mat RGB2GRAY(const Mat &src){
    if(!src.data || src.channels()!=3){
        exit(1);
    }
    int row = src.rows;
    int col = src.cols;
    Mat gray = Mat(row, col, CV_8UC1);
    for(int i = 0; i < row; i++){
        for(int j = 0; j < col; j++){
            gray.at(i, j) = (uchar)(0.114 * src.at(i, j)[0] + 0.587 * src.at(i, j)[1] + 0.299 * src.at(i, j)[2]);
        }
    }
    return gray;
}

void SobelGradDirction(Mat &src, Mat &sobelX, Mat &sobelY){
    int row = src.rows;
    int col = src.cols;
    sobelX = Mat::zeros(src.size(), CV_32SC1);
    sobelY = Mat::zeros(src.size(), CV_32SC1);

    for(int i = 0; i < row-1; i++){
        for(int j = 0; j < col-1; j++){
            double gradY = src.at(i+1, j-1) + src.at(i+1, j) * 2 + src.at(i+1, j+1) - src.at(i-1, j-1) - src.at(i-1, j) * 2 - src.at(i-1, j+1);
            sobelY.at(i, j) = abs(gradY);
            double gradX = src.at(i-1, j+1) + src.at(i, j+1) * 2 + src.at(i+1, j+1) - src.at(i-1, j-1) - src.at(i, j-1) * 2 - src.at(i+1, j-1);
            sobelX.at(i, j) = abs(gradX);
        }
    }
    //将梯度数组转换为8位无符号整形
    convertScaleAbs(sobelX, sobelX);
    convertScaleAbs(sobelY, sobelY);
}

Mat SobelXX(const Mat src){
    int row = src.rows;
    int col = src.cols;
    Mat_ dst(row, col, CV_32FC1);
    for(int i = 0; i < row; i++){
        for(int j = 0; j < col; j++){
            dst.at(i, j) = src.at(i, j) * src.at(i, j);
        }
    }
    return dst;
}

Mat SobelYY(const Mat src){
    int row = src.rows;
    int col = src.cols;
    Mat_ dst(row, col, CV_32FC1);
    for(int i = 0; i < row; i++){
        for(int j = 0; j < col; j++){
            dst.at(i, j) = src.at(i, j) * src.at(i, j);
        }
    }
    return dst;
}

Mat SobelXY(const Mat src1, const Mat &src2){
    int row = src1.rows;
    int col = src1.cols;
    Mat_ dst(row, col, CV_32FC1);
    for(int i = 0; i < row; i++){
        for(int j = 0; j < col; j++){
            dst.at(i, j) = src1.at(i, j) * src2.at(i, j);
        }
    }
    return dst;
}

void separateGaussianFilter(Mat_ &src, Mat_ &dst, int ksize, double sigma){
    CV_Assert(src.channels()==1 || src.channels() == 3); //只处理单通道或者三通道图像
    //生成一维的
    double *matrix = new double[ksize];
    double sum = 0;
    int origin = ksize / 2;
    for(int i = 0; i < ksize; i++){
        double g = exp(-(i-origin) * (i-origin) / (2 * sigma * sigma));
        sum += g;
        matrix[i] = g;
    }
    for(int i = 0; i < ksize; i++) matrix[i] /= sum;
    int border = ksize / 2;
    copyMakeBorder(src, dst, border, border, border, border, BORDER_CONSTANT);
    int channels = dst.channels();
    int rows = dst.rows - border;
    int cols = dst.cols - border;
    //水平方向
    for(int i = border; i < rows - border; i++){
        for(int j = border; j < cols - border; j++){
            float sum[3] = {0};
            for(int k = -border; k<=border; k++){
                if(channels == 1){
                    sum[0] += matrix[border + k] * dst.at(i, j+k);
                }else if(channels == 3){
                    Vec3f rgb = dst.at(i, j+k);
                    sum[0] += matrix[border+k] * rgb[0];
                    sum[1] += matrix[border+k] * rgb[1];
                    sum[2] += matrix[border+k] * rgb[2];
                }
            }
            for(int k = 0; k < channels; k++){
                if(sum[k] < 0) sum[k] = 0;
                else if(sum[k] > 255) sum[k] = 255;
            }
            if(channels == 1)
                dst.at(i, j) = static_cast(sum[0]);
            else if(channels == 3){
                Vec3f rgb = {static_cast(sum[0]), static_cast(sum[1]), static_cast(sum[2])};
                dst.at(i, j) = rgb;
            }
        }
    }
    //竖直方向
    for(int i = border; i < rows - border; i++){
        for(int j = border; j < cols - border; j++){
            float sum[3] = {0};
            for(int k = -border; k<=border; k++){
                if(channels == 1){
                    sum[0] += matrix[border + k] * dst.at(i+k, j);
                }else if(channels == 3){
                    Vec3f rgb = dst.at(i+k, j);
                    sum[0] += matrix[border+k] * rgb[0];
                    sum[1] += matrix[border+k] * rgb[1];
                    sum[2] += matrix[border+k] * rgb[2];
                }
            }
            for(int k = 0; k < channels; k++){
                if(sum[k] < 0) sum[k] = 0;
                else if(sum[k] > 255) sum[k] = 255;
            }
            if(channels == 1)
                dst.at(i, j) = static_cast(sum[0]);
            else if(channels == 3){
                Vec3f rgb = {static_cast(sum[0]), static_cast(sum[1]), static_cast(sum[2])};
                dst.at(i, j) = rgb;
            }
        }
    }
    delete [] matrix;
}

Mat harrisResponse(Mat_ GaussXX, Mat_ GaussYY, Mat_ GaussXY, float k){
    int row = GaussXX.rows;
    int col = GaussXX.cols;
    Mat_ dst(row, col, CV_32FC1);
    for(int i = 0; i < row; i++){
        for(int j = 0; j < col; j++){
            float a = GaussXX.at(i, j);
            float b = GaussYY.at(i, j);
            float c = GaussXY.at(i, j);
            dst.at(i, j) = a * b - c * c - k * (a + b) * (a + b);
        }
    }
    return dst;
}

int dir[8][2] = {0, 1, 0, -1, 1, 0, -1, 0, 1, 1, 1, -1, -1, 1, -1, -1};

void MaxLocalValue(Mat_&resultData, Mat srcGray, Mat &resultImage, int kSize){
    int r = kSize / 2;
    resultImage = srcGray.clone();
    int row = resultImage.rows;
    int col = resultImage.cols;
    for(int i = r; i < row - r; i++){
        for(int j = r; j < col - r; j++){
            bool flag = true;
            for(int k = 0; k < 8; k++){
                int tx = i + dir[k][0];
                int ty = j + dir[k][1];
                if(resultData.at(i, j) < resultData.at(tx, ty)){
                    flag = false;
                }
            }
            if(flag && (int)resultData.at(i, j) > 18000){
                circle(resultImage, Point(i, j), 5, Scalar(0, 0, 255), 2, 8, 0);
            }
        }
    }
}

效果图:

计算机视觉--局部图像描述子:Harris角点检测算法、SIFT(尺度不变特征变换)_第3张图片

计算机视觉--局部图像描述子:Harris角点检测算法、SIFT(尺度不变特征变换)_第4张图片

 1.3数学方法刻画角点特征

将图像窗口平移 [ u,v ] 产生灰度变化 E ( u,v )
计算机视觉--局部图像描述子:Harris角点检测算法、SIFT(尺度不变特征变换)_第5张图片

 计算机视觉--局部图像描述子:Harris角点检测算法、SIFT(尺度不变特征变换)_第6张图片

 计算机视觉--局部图像描述子:Harris角点检测算法、SIFT(尺度不变特征变换)_第7张图片

计算机视觉--局部图像描述子:Harris角点检测算法、SIFT(尺度不变特征变换)_第8张图片

计算机视觉--局部图像描述子:Harris角点检测算法、SIFT(尺度不变特征变换)_第9张图片

计算机视觉--局部图像描述子:Harris角点检测算法、SIFT(尺度不变特征变换)_第10张图片

共可分为三种情况:

   (1) 图像中的直线:一个特征值大,另一个特征值小,λ1>λ2或λ2>λ1。自相关函数值在某一方向上大,在其他方向上小。

   (2)图像中的平面:两个特征值都小,且近似相等;自相关函数数值在各个方向上都小。

   (3)图像中的角点:两个特征值都大,且近似相等,自相关函数在所有方向都增大。

 1.4角点响应函数

由于我们是通过M的两个特征值的大小对图像进行分类,所以,定义角点相应函数R: 

 

 其中k为经验常数,一般取k=0.04~0.06。增大k的值将减小响应值R,降低检测的灵敏性,减少被检测角点的数量;减小k值,将增大角点响应值R,增加被检测角点的数量。为了去除加权常数κ,我们通常使用商数detM/(traceM)2作为指示器。所以,上图可以转化为:
计算机视觉--局部图像描述子:Harris角点检测算法、SIFT(尺度不变特征变换)_第11张图片

•  R 只与M的特征值有关

               • 角点:R 为大数值正数(λ1和λ2都是很大的正数)

               • 边缘:R 为大数值负数

               • 平坦区:R 为小数值(如果λ1≈λ2≈0,该区域为空)

         在判断角点的时候,对角点响应函数R进行阈值处理:R > threshold,提取R的局部极大值。
 

1.5Harris角点检测步骤

Harris角点检测可以分为5个步骤:

(1)计算图像I(x,y)I(x,y)在xx和yy两个方向的梯度IxIx,IyIy;

(2)计算图像两个方向梯度的乘积;

(3)使用高斯函数对Ix2、Iy2、IxIy进行高斯加权(取σ=2,ksize=3),计算中心点为(x,y)(x,y)的窗口W对应的矩阵M;

(4)计算每个像素点(x,y)处的(x,y)处的Harris响应值R; 

 (5)过滤大于某一阈值t的R值;

 1.6角点检测实例

(1)代码实现:

# -*- coding: utf-8 -*-
from pylab import *
from PIL import Image
from PCV.localdescriptors import harris
from scipy.ndimage import filters

def compute_harris_response(im, sigma=3):
    # 在一幅灰度图像中,对每个像素计算Harris角点检测器响应函数
    
    # 计算导数
    k = 0.04
    imx = zeros(im.shape)
    filters.gaussian_filter(im, (sigma, sigma), (0, 1), imx)
    imy = zeros(im.shape)
    filters.gaussian_filter(im, (sigma, sigma), (1, 0), imy)
    
    # 计算harris矩阵分量
    Wxx = filters.gaussian_filter(imx * imx, sigma)
    Wxy = filters.gaussian_filter(imx * imy, sigma)
    Wyy = filters.gaussian_filter(imy * imy, sigma)
    
    # 计算矩阵的特征值和迹
    Wdet = Wxx * Wyy - Wxy ** 2
    Wtr = Wxx + Wyy
    
    # 返回像素值为 Harris 响应函数值的一幅图像
    print(Wdet-(k*Wtr)) # 输出角点响应函数R
    return Wdet / Wtr  # 此处可消除参数k的影响
    
# 读入图像(读取图像到数组中)
im = array(Image.open('p3.jpg').convert('L'))  # 括号内:读取一幅图像,并将其转换成灰度图像

# 检测harris角点
harrisim = compute_harris_response(im)
                # compute_harris_response(im, sigma):
                #     在一幅灰度图像中,对每一个像素计算Harris角点检测器响应函数
                #     im:(数组图像)  sigma:标准差默认为3
                
# Harris响应函数
# print (harrisim)
harrisim1 = 255 - harrisim
figure()
gray()

#画出Harris响应图
subplot(141)
imshow(harrisim1)
print harrisim1.shape
axis('off')
axis('equal')

threshold = [0.01, 0.05, 0.1]
for i, thres in enumerate(threshold):
    # enumerate()函数用于将一个可遍历的数据对象(如列表、元组或字符串)组合为一个索引序列,同时列出数据和数据下标,一般用在for 循环当中
        filtered_coords = harris.get_harris_points(harrisim, 6, thres)
                                # get_harris_points(harrisim, min_dist=10, threshold=0.1):
                                #       从一幅Harris 响应图像harrisim中返回角点。min_dist 为分割角点和图像边界的最少像素数目(默认为10)。
                                #       threshold 为阀值,大于阀值的harris响应函数值被认为是可能的角点,并在harrism_t矩阵中相应的位置1,其余地方置0
        subplot(1, 4, i+2)
        imshow(im)
        print im.shape
        plot([p[1] for p in filtered_coords], [p[0] for p in filtered_coords], '*') # 标出角点
        axis('off')
        
show()

上面代码中重写compute_harris_response()函数,以便将角点响应函数R输出。

截图:

计算机视觉--局部图像描述子:Harris角点检测算法、SIFT(尺度不变特征变换)_第12张图片

 (2)输入图片特征为纹理平坦丰富:

计算机视觉--局部图像描述子:Harris角点检测算法、SIFT(尺度不变特征变换)_第13张图片

计算机视觉--局部图像描述子:Harris角点检测算法、SIFT(尺度不变特征变换)_第14张图片

 1.7图像中寻找对应点

Harris 角点检测器仅仅能够检测出图像中的兴趣点,但是没有给出通过比较图像间的兴趣点来寻找匹配角点的方法。我们需要在每个点上加入描述子信息,并给出一 个比较这些描述子的方法。兴趣点描述子是分配给兴趣点的一个向量,描述该点附近的图像的表观信息。描述子越好,寻找到的对应点越好。我们用对应点或者点的对应来描述相同物体和场景点在不同图像上形成的像素点。使用归一化的互相关矩阵进行两幅图像的比较,代码如下:
 

def get_descriptors(image,filtered_coords,wid=5):
    """对于每个返回的点,返回点周围2*wid+1个像素的值(假设选取点的min_distance > wid)"""
    desc = []
    for coords in filtered_coords:
        patch = image[coords[0] - wid:coords[0]+wid+1,coords[1]-wid:coords[1]+wid+1].flatten()
        desc.append(patch)
    return desc
 
def match(desc1,desc2,threshold=0.5):
    """对于第一幅图像中的每个角点描述子,使用归一化互相关,选取它在第二幅图像中的匹配角点"""
    n = len(desc1[0])
    # 点对的距离
    d = -np.ones((len(desc1), len(desc2)))
    for i in range(len(desc1)):
        for j in range(len(desc2)):
            # mean:求平均值,std:求标准差
            d1 = (desc1[i] - np.mean(desc1[i])) / np.std(desc1[i])
            d2 = (desc2[j] - np.mean(desc2[j])) / np.std(desc2[j])
            ncc_value = sum(d1 * d2) / (n-1)
            if ncc_value > threshold:  # 数值较高的距离代表两个点
                d[i,j] = ncc_value
    # np.argsort():将数组中的元素从小到大排列,提取其对应的index(索引),然后输出
    # 数值较高的距离代表两个点能够更好的匹配,因此对距离取相反数
    ndx = np.argsort(-d)
    matchscores = ndx[:0]
    return matchscores
 
# 从第二幅图像向第一幅图像匹配,然后过滤掉在两种方法种不都是最好的匹配
def match_twosided(desc1,desc2,threshold=0.5):
    """两边对称版本的match()"""
    matches_12 = match(desc1,desc2,threshold)
    matches_21 = match(desc2,desc1,threshold)
    # np.where:找到n维数组中特定数值的索引
    ndx_12 = np.where(matches_12 >= 0)[0]
    for n in ndx_12:
        if matches_21[matches_12[n]] != n:
            matches_12[n] = -1
    return matches_12
 
# 实现匹配点的可视化
def appendimages(im1,im2):
    """返回将两幅图像并排拼接成一幅图像"""
    # 选取具有最少行数的图像,然后填充足够的空行
    rows1 = im1.shape[0]
    rows2 = im2.shape[0]
 
    if rows1 < rows2:
        im1 = np.concatenate((im1, np.zeros((rows2 - rows1, im1.shape[1]))), axis=0)
    elif rows1 > rows2:
        im2 = np.concatenate((im2, np.zeros((rows1 - rows2, im2.shape[1]))), axis=0)
    # 如果无此两种情况,则他们行数相同,无需填充
    return np.concatenate((im1,im2),axis=1)
 
def plot_matches(im1,im2,locs1,locs2,matchscores,show_below=True):
    """显示一幅带有连接匹配之间连线的图片"""
    """
    im1,im2:数组图像,locs1.locs2:特征位置,matchscores(match())的输出,
    show_below(如果图像应该显示在匹配的下方)
    """
    im3 = appendimages(im1,im2)
    if show_below:
        im3 = np.vstack((im3, im3))
    plt.imshow(im3)
    cols1 = im1.shape[1]
    for i,m in enumerate(matchscores):
        if m>0:
            plt.plot([locs1[i][1],locs2[m][1]+cols1],[locs1[i][0],locs2[m][0]],'c')
    plt.axis('off')

测试代码:

# -*- coding: utf-8 -*-
"""
@author: RRJ
@software: PyCharm
@file: harrisdemo3.py
@time: 2022/3/27 15:19
"""
import numpy as np
from PIL import Image
import matplotlib.pyplot as plt
from Localdescriptors import harris
from tools.imtools import imresize
 
# 读入图像
im1 = np.array(Image.open("../JMU_img/JMU_ZS1.jpg").convert("L"))
im2 = np.array(Image.open("../JMU_img/JMU_ZS2.jpg").convert("L"))
 
# resize加快匹配速度
im1 = imresize(im1, (im1.shape[1]//2, im1.shape[0]//2))
im2 = imresize(im2, (im2.shape[1]//2, im2.shape[0]//2))
 
wid = 5
harrisim = harris.compute_harris_response(im1, 5)
filtered_coords1 = harris.get_harris_points(harrisim, wid+1)
d1 = harris.get_descriptors(im1, filtered_coords1, wid)
 
harrisim = harris.compute_harris_response(im2, 5)
filtered_coords2 = harris.get_harris_points(harrisim, wid+1)
d2 = harris.get_descriptors(im2, filtered_coords2, wid)
 
print('starting matching')
matches = harris.match_twosided(d1, d2)
 
plt.figure()
plt.gray()
harris.plot_matches(im1, im2, filtered_coords1, filtered_coords2, matches)
plt.show()

运行截图:

计算机视觉--局部图像描述子:Harris角点检测算法、SIFT(尺度不变特征变换)_第15张图片

二、SIFT(尺度不变特征变换)

2.1SIFT简介

SIFT特征是基于物体上的一些局部外观的兴趣点而与影像的大小和旋转无关。对于光线、噪声、微视角改变的容忍度也相当高。基于这些特性,它们是高度显著而且相对容易撷取,在母数庞大的特征数据库中,很容易辨识物体而且鲜有误认。使用SIFT特征描述对于部分物体遮蔽的侦测率也相当高,甚至只需要3个以上的SIFT物体特征就足以计算出位置与方位。在现今的电脑硬件速度下和小型的特征数据库条件下,辨识速度可接近即时运算。SIFT特征的信息量大,适合在海量数据库中快速准确匹配。

2.2 SIFT特点

1.SIFT特征是图像的局部特征,其对旋转、尺度缩放、亮度变化保持不变性,对视角变化、仿射变换、噪声也保持一定程度的稳定性;

2. 区分性(Distinctiveness)好,信息量丰富,适用于在海量特征数据库中进行快速、准确的匹配;

3. 多量性,即使少数的几个物体也可以产生大量的SIFT特征向量;

4.高速性,经优化的SIFT匹配算法甚至可以达到实时的要求;

5.可扩展性,可以很方便的与其他形式的特征向量进行联合。

2.3SIFT算法

SIFT算法可以解决的问题

(1)目标的旋转、缩放、平移;

(2)图像仿射或投影变换;

(3)弱光照影响以及部分目标遮挡;

(4)杂物场景以及噪声点影响。

SIFT算法实现特征匹配的三个流程:

1.提取关键点;

2.对关键点附加详细的信息(局部特征),即描述符;

3.通过特征点(附带上特征向量的关键点)的两两比较找出相互匹配的若干对特征点,建立景物间的对应关系。

2.4SIFT算法实现

代码实现;

#pragma once                    //防止头文件重复包含和下面作用一样
 
#include
#include
#include
#include
 
using namespace std;
using namespace cv;
 
/*************************定义常量*****************************/
 
//高斯核大小和标准差关系,size=2*(GAUSS_KERNEL_RATIO*sigma)+1,经常设置GAUSS_KERNEL_RATIO=2-3之间
const double GAUSS_KERNEL_RATIO = 3;
 
const int MAX_OCTAVES = 8;					//金字塔最大组数
 
const float CONTR_THR = 0.04f;				//默认是的对比度阈值(D(x))
 
const float CURV_THR = 10.0f;				//关键点主曲率阈值
 
const float INIT_SIGMA = 0.5f;				//输入图像的初始尺度
 
const int IMG_BORDER = 2;					//图像边界忽略的宽度,也可以改为 1 
 
const int MAX_INTERP_STEPS = 5;				//关键点精确插值次数
 
const int ORI_HIST_BINS = 36;				//计算特征点方向直方图的BINS个数
 
const float ORI_SIG_FCTR = 1.5f;			//计算特征点主方向时候,高斯窗口的标准差因子
 
const float ORI_RADIUS = 3 * ORI_SIG_FCTR;	//计算特征点主方向时,窗口半径因子
 
const float ORI_PEAK_RATIO = 0.8f;			//计算特征点主方向时,直方图的峰值比
 
const int DESCR_WIDTH = 4;					//描述子直方图的网格大小(4x4)
 
const int DESCR_HIST_BINS = 8;				//每个网格中直方图角度方向的维度
 
const float DESCR_MAG_THR = 0.2f;			//描述子幅度阈值
 
const float DESCR_SCL_FCTR = 3.0f;			//计算描述子时,每个网格的大小因子
 
const int SAR_SIFT_GLOH_ANG_GRID = 8;		//GLOH网格沿角度方向等分区间个数
 
const int SAR_SIFT_DES_ANG_BINS = 8;		//像素梯度方向在0-360度内等分区间个数
 
const float SAR_SIFT_RADIUS_DES = 12.0f;	//描述子邻域半径		
 
const int Mmax = 8;							//像素梯度方向在0-360度内等分区间个数
 
const double T = 100.0;							//sobel算子去除冗余特征点的阈值
 
const float SAR_SIFT_GLOH_RATIO_R1_R2 = 0.73f;//GLOH网格中间圆半径和外圆半径之比
 
const float SAR_SIFT_GLOH_RATIO_R1_R3 = 0.25f;//GLOH网格最内层圆半径和外圆半径之比
 
#define Feature_Point_Minimum 1500			  //输入图像特征点最小个数
 
#define We 0.2
 
#define Wn 0.5
 
#define Row_num 3
 
#define Col_num 3
 
#define SIFT_FIXPT_SCALE 48					//不理解,后面可查原论文
 
/************************sift类*******************************/
class mySift
{
 
public:
	//默认构造函数
	mySift(int nfeatures = 0, int nOctaveLayers = 3, double contrastThreshold = 0.03,
		double edgeThreshold = 10, double sigma = 1.6, bool double_size = true) :nfeatures(nfeatures),
		nOctaveLayers(nOctaveLayers), contrastThreshold(contrastThreshold),
		edgeThreshold(edgeThreshold), sigma(sigma), double_size(double_size) {}
 
 
	//获得尺度空间每组中间层数
	int get_nOctave_layers() { return nOctaveLayers; }
 
	//获得图像尺度是否扩大一倍
	bool get_double_size() { return double_size; }
 
	//计算金字塔组数
	int num_octaves(const Mat& image);
 
	//生成高斯金字塔第一组,第一层图像
	void create_initial_image(const Mat& image, Mat& init_image);
 
	//使用 sobel 算子创建高斯金字塔第一层图像,以减少冗余特征点
	void sobel_create_initial_image(const Mat& image, Mat& init_image);
 
	//创建高斯金字塔
	void build_gaussian_pyramid(const Mat& init_image, vector>& gauss_pyramid, int nOctaves);
 
	//创建高斯差分金字塔
	void build_dog_pyramid(vector>& dog_pyramid, const vector>& gauss_pyramid);
 
	//该函数生成高斯差分金字塔
	void amplit_orient(const Mat& image, vector& amplit, vector& orient, float scale, int nums);
 
	//DOG金字塔特征点检测
	void find_scale_space_extrema(const vector>& dog_pyr, const vector>& gauss_pyr,
		vector& keypoints);
 
	//DOG金字塔特征点检测,特征点方向细化版
	void find_scale_space_extrema1(const vector>& dog_pyr, vector>& gauss_pyr,
		vector& keypoints);
 
	//计算特征点的描述子
	void calc_sift_descriptors(const vector>& gauss_pyr, vector& keypoints,
		Mat& descriptors, const vector& amplit, const vector& orient);
 
	//构建空间尺度—主要是为了获取 amplit 和 orient 在使用 GLOH 描述子的时候使用
	void build_sar_sift_space(const Mat& image, vector& sar_harris_fun, vector& amplit, vector& orient);
 
	//GLOH 计算一个特征描述子
	void calc_gloh_descriptors(const vector& amplit, const vector& orient, const vector& keys, Mat& descriptors);
 
	//特征点检测
	void detect(const Mat& image, vector>& gauss_pyr, vector>& dog_pyr, vector& keypoints,
		vector>>& all_cell_contrasts,
		vector>& average_contrast, vector>& n_cells, vector& num_cell, vector>& available_n,
		vector& available_num, vector& final_keypoints,
		vector& Final_keypoints, vector& Final_Keypoints);
 
	//特征点描述
	void comput_des(const vector>& gauss_pyr, vector& final_keypoints, const vector& amplit,
		const vector& orient, Mat& descriptors);
 
 
private:
	int nfeatures;				//设定检测的特征点的个数值,如果此值设置为0,则不影响结果
	int nOctaveLayers;			//每组金字塔中间层数
	double contrastThreshold;	//对比度阈值(D(x))
	double edgeThreshold;		//特征点边缘曲率阈值
	double sigma;				//高斯尺度空间初始层的尺度
	bool double_size;			//是否上采样原始图像
 
 
};//注意类结束的分号
#include"mySift.h"
 
#include
#include
#include			//输入输出
#include			//vector
#include
#include			//用于容器元素求和
 
#include
#include
 
#include
#include				//opencv基本数据结构
#include		//图像界面
#include		//基本图像处理函数
#include //特征提取
#include
#include
#include 
#include 
 
 
/******************根据输入图像大小计算高斯金字塔的组数****************************/
/*image表示原始输入灰度图像,inline函数必须在声明处定义
double_size_image表示是否在构建金字塔之前上采样原始图像
*/
int mySift::num_octaves(const Mat& image)
{
	int temp;
	float size_temp = (float)min(image.rows, image.cols);
	temp = cvRound(log(size_temp) / log((float)2) - 2);
 
	if (double_size)
		temp += 1;
	if (temp > MAX_OCTAVES)							//尺度空间最大组数设置为MAX_OCTAVES
		temp = MAX_OCTAVES;
 
	return temp;
}
 
 
/************************sobel滤波器计算高斯尺度空间图像梯度大小*****************************/
void sobelfilter(Mat& image, Mat& G)
{
	// image 是经过归一化后的数据 (0,1)
	int rows = image.rows;
	int cols = image.cols;
 
	float dx = 0.0, dy = 0.0;
 
	//cv::Mat Gx = cv::Mat::zeros(rows, cols, CV_32FC1);	//包含了图像像素在水平方向上的导数的近似值的图像
	//cv::Mat Gy = cv::Mat::zeros(rows, cols, CV_32FC1);	//包含了图像像素在垂直方向上的导数的近似值的图像
 
	G = Mat::zeros(rows, cols, CV_32FC1);					//在每个像素点处的灰度大小由Gx和Gy共同决定
 
	double v = 0.0, vx, vy;
 
	//利用 sobel 算子求梯度幅度图像
	for (int i = 0; i < rows; i++)
	{
		for (int j = 0; j < cols; j++)
		{
			v = 0.0;
 
			if (i == 0 || j == 0 || i == rows - 1 || j == cols - 1)
			{
				G.at(i, j) = 0.0;
			}
			else
			{
				float dx = image.at(i - 1, j + 1) - image.at(i - 1, j - 1)
					+ 2 * image.at(i, j + 1) - 2 * image.at(i, j - 1)
					+ image.at(i + 1, j + 1) - image.at(i + 1, j - 1);
 
				float dy = image.at(i + 1, j - 1) - image.at(i - 1, j - 1)
					+ 2 * image.at(i + 1, j) - 2 * image.at(i - 1, j) +
					image.at(i + 1, j + 1) - image.at(i - 1, j + 1);
 
				v = abs(dx) + abs(dy);			//简化后 G = |Gx| + |Gy|
 
				//保证像素值在有效范围内
				v = fmax(v, 0);					//返回浮点数中较大的一个
				v = fmin(v, 255);				//返回浮点数中较小的一个
 
				if (v > T)						//T为阈值等于50
					G.at(i, j) = (float)v;
				else
					G.at(i, j) = 0.0;
			}
		}
	}
 
	//水平方向上的导数的近似值的图像
	/*for (int i = 0; i < rows; i++)
	{
		for (int j = 0; j < cols; j++)
		{
			vx = 0;
			if (i == 0 || j == 0 || i == rows - 1 || j == cols - 1)
				Gx.at(i, j) = 0;
			else
			{
				dx = image.at(i - 1, j + 1) - image.at(i - 1, j - 1)
					+ 2 * image.at(i, j + 1) - 2 * image.at(i, j - 1)
					+ image.at(i + 1, j + 1) - image.at(i + 1, j - 1);
				vx = abs(dx);
				vx = fmax(vx, 0); vx = fmin(vx, 255);
				Gx.at(i, j) = (float)vx;
			}
		}
	}*/
 
	//垂直方向上的导数的近似值的图像
	/*for (int i = 0; i < rows; i++)
	{
		for (int j = 0; j < cols; j++)
		{
			vy = 0;
			if (i == 0 || j == 0 || i == rows - 1 || j == cols - 1)
				Gy.at(i, j) = 0;
			else
			{
				dy = image.at(i + 1, j - 1) - image.at(i - 1, j - 1)
					+ 2 * image.at(i + 1, j) - 2 * image.at(i - 1, j) +
					image.at(i + 1, j + 1) - image.at(i - 1, j + 1);
				vy = abs(dy);
				vy = fmax(vy, 0); vx = fmin(vy, 255);
				Gy.at(i, j) = (float)vy;
			}
		}
	}*/
 
	//cv::imshow("Gx", Gx);						// horizontal
	//cv::imshow("Gy", Gy);						// vertical
	//cv::imshow("G", G);						// gradient
}
 
 
/*********该函数根据尺度和窗口半径生成ROEWA滤波模板************/
/*size表示核半径,因此核宽度是2*size+1
 scale表示指数权重参数
 kernel表示生成的滤波核
 */
static void roewa_kernel(int size, float scale, Mat& kernel)
{
	kernel.create(2 * size + 1, 2 * size + 1, CV_32FC1);
	for (int i = -size; i <= size; ++i)
	{
		float* ptr_k = kernel.ptr(i + size);
		for (int j = -size; j <= size; ++j)
		{
			ptr_k[j + size] = exp(-1.f * (abs(i) + abs(j)) / scale);
		}
	}
}
 
 
/************************创建高斯金字塔第一组,第一层图像************************************/
/*image表示输入原始图像
 init_image表示生成的高斯尺度空间的第一层图像
 */
void mySift::create_initial_image(const Mat& image, Mat& init_image)
{
	Mat gray_image;
 
	if (image.channels() != 1)
		cvtColor(image, gray_image, CV_RGB2GRAY);		//转换为灰度图像
	else
		gray_image = image.clone();
 
	Mat floatImage;										//转换到0-1之间的浮点类型数据归一化,方便接下来的处理
 
	//float_image=(float)gray_image*(1.0/255.0)
	gray_image.convertTo(floatImage, CV_32FC1, 1.0 / 255.0, 0);
 
	double sig_diff = 0;
 
	if (double_size)
	{
		Mat temp_image;
 
		//通过插值的方法改变图像尺寸的大小
		resize(floatImage, temp_image, Size(2 * floatImage.cols, 2 * floatImage.rows), 0.0, 0.0, INTER_LINEAR);
 
		//高斯平滑的标准差,值较大时平滑效果比较明显
		sig_diff = sqrt(sigma * sigma - 4.0 * INIT_SIGMA * INIT_SIGMA);
 
		//高斯滤波窗口大小选择很重要,这里选择(4*sig_diff_1+1)-(6*sig_diff+1)之间,且四舍五入
		int kernel_width = 2 * cvRound(GAUSS_KERNEL_RATIO * sig_diff) + 1;
 
		Size kernel_size(kernel_width, kernel_width);
 
		//对图像进行平滑处理(高斯模糊),即降低图像的分辨率,高斯模糊是实现尺度变换的唯一变换核,并其实唯一的线性核
		GaussianBlur(temp_image, init_image, kernel_size, sig_diff, sig_diff);
	}
	else
	{
		sig_diff = sqrt(sigma * sigma - 1.0 * INIT_SIGMA * INIT_SIGMA);
 
		//高斯滤波窗口大小选择很重要,这里选择(4*sig_diff_1+1)-(6*sig_diff+1)之间
		int kernel_width = 2 * cvRound(GAUSS_KERNEL_RATIO * sig_diff) + 1;
 
		Size kernel_size(kernel_width, kernel_width);
 
		GaussianBlur(floatImage, init_image, kernel_size, sig_diff, sig_diff);
	}
}
 
 
/************************使用 sobel 算子创建高斯金字塔第一组,第一层图像****************************/
//目的是为了减少冗余特征点
void mySift::sobel_create_initial_image(const Mat& image, Mat& init_image)
{
	Mat gray_image, gray_images;						//gray_images用于存放经过sobel算子操作后的图像
 
	if (image.channels() != 1)
		cvtColor(image, gray_image, CV_RGB2GRAY);		//转换为灰度图像
	else
		gray_image = image.clone();
 
	sobelfilter(gray_image, gray_images);
 
	Mat floatImage;										//转换到0-1之间的浮点类型数据归一化,方便接下来的处理
 
	//float_image=(float)gray_image*(1.0/255.0)
	gray_images.convertTo(floatImage, CV_32FC1, 1.0 / 255.0, 0);
 
	double sig_diff = 0;
 
	if (double_size)
	{
		Mat temp_image;
 
		//通过插值的方法改变图像尺寸的大小
		resize(floatImage, temp_image, Size(2 * floatImage.cols, 2 * floatImage.rows), 0.0, 0.0, INTER_LINEAR);
 
		//高斯平滑的标准差,值较大时平滑效果比较明显
		sig_diff = sqrt(sigma * sigma - 4.0 * INIT_SIGMA * INIT_SIGMA);
 
		//高斯滤波窗口大小选择很重要,这里选择(4*sig_diff_1+1)-(6*sig_diff+1)之间,且四舍五入
		int kernel_width = 2 * cvRound(GAUSS_KERNEL_RATIO * sig_diff) + 1;
 
		Size kernel_size(kernel_width, kernel_width);
 
		//对图像进行平滑处理(高斯模糊),即降低图像的分辨率,高斯模糊是实现尺度变换的唯一变换核,并其实唯一的线性核
		GaussianBlur(temp_image, init_image, kernel_size, sig_diff, sig_diff);
	}
	else
	{
		sig_diff = sqrt(sigma * sigma - 1.0 * INIT_SIGMA * INIT_SIGMA);
 
		//高斯滤波窗口大小选择很重要,这里选择(4*sig_diff_1+1)-(6*sig_diff+1)之间
		int kernel_width = 2 * cvRound(GAUSS_KERNEL_RATIO * sig_diff) + 1;
 
		Size kernel_size(kernel_width, kernel_width);
 
		GaussianBlur(floatImage, init_image, kernel_size, sig_diff, sig_diff);
	}
}
 
 
 /**************************生成高斯金字塔*****************************************/
 /*init_image表示已经生成的高斯金字塔第一层图像
  gauss_pyramid表示生成的高斯金字塔
  nOctaves表示高斯金字塔的组数
 */
void mySift::build_gaussian_pyramid(const Mat& init_image, vector>& gauss_pyramid, int nOctaves)
{
	vector sig;
 
	sig.push_back(sigma);
 
	double k = pow(2.0, 1.0 / nOctaveLayers);				//高斯金字塔每一层的系数 k
 
	for (int i = 1; i < nOctaveLayers + 3; ++i)
	{
		double prev_sig = pow(k, i - 1) * sigma;				//每一个尺度层的尺度
		double curr_sig = k * prev_sig;
 
		//组内每层的尺度坐标计算公式
		sig.push_back(sqrt(curr_sig * curr_sig - prev_sig * prev_sig));
	}
 
	gauss_pyramid.resize(nOctaves);
 
	for (int i = 0; i < nOctaves; ++i)
	{
		gauss_pyramid[i].resize(nOctaveLayers + 3);
	}
 
	for (int i = 0; i < nOctaves; ++i)						//对于每一组
	{
		for (int j = 0; j < nOctaveLayers + 3; ++j)			//对于组内的每一层
		{
			if (i == 0 && j == 0)							//第一组,第一层
				gauss_pyramid[0][0] = init_image;
 
			else if (j == 0)
			{
				resize(gauss_pyramid[i - 1][3], gauss_pyramid[i][0],
					Size(gauss_pyramid[i - 1][3].cols / 2,
						gauss_pyramid[i - 1][3].rows / 2), 0, 0, INTER_LINEAR);
			}
			else
			{
				//高斯滤波窗口大小选择很重要,这里选择(4*sig_diff_1+1)-(6*sig_diff+1)之间
				int kernel_width = 2 * cvRound(GAUSS_KERNEL_RATIO * sig[j]) + 1;
				Size kernel_size(kernel_width, kernel_width);
 
				GaussianBlur(gauss_pyramid[i][j - 1], gauss_pyramid[i][j], kernel_size, sig[j], sig[j]);
			}
		}
	}
}
 
 
/*******************生成高斯差分金字塔,即LOG金字塔*************************/
/*dog_pyramid表示DOG金字塔
 gauss_pyramin表示高斯金字塔*/
void mySift::build_dog_pyramid(vector>& dog_pyramid, const vector>& gauss_pyramid)
{
	vector>::size_type nOctaves = gauss_pyramid.size();
 
	for (vector>::size_type i = 0; i < nOctaves; ++i)
	{
		//用于存放每一个梯度中的所有尺度层
		vector temp_vec;
 
		for (auto j = 0; j < nOctaveLayers + 2; ++j)
		{
			Mat temp_img = gauss_pyramid[i][j + 1] - gauss_pyramid[i][j];
 
			temp_vec.push_back(temp_img);
		}
		dog_pyramid.push_back(temp_vec);
 
		temp_vec.clear();
	}
}
 
 
/***********生成高斯差分金字塔当前层对应的梯度幅度图像和梯度方向图像***********/
/*image为高斯差分金字塔当前层图像
 *amplit为当前层梯度幅度图像
 *orient为当前层梯度方向图像
 *scale当前层尺度
 *nums为相对底层的层数
 */
void mySift::amplit_orient(const Mat& image, vector& amplit, vector& orient, float scale, int nums)
{
	//分配内存
	amplit.resize(Mmax * nOctaveLayers);
	orient.resize(Mmax * nOctaveLayers);
 
	int radius = cvRound(2 * scale);
 
	Mat kernel;												//kernel(2 * radius + 1, 2 * radius + 1, CV_32FC1);
 
	roewa_kernel(radius, scale, kernel);					//返回滤波核,也即指数部分,存放在矩阵的右下角
 
	//四个滤波模板生成
	Mat W34 = Mat::zeros(2 * radius + 1, 2 * radius + 1, CV_32FC1);		//把kernel矩阵下半部分复制到对应部分
	Mat W12 = Mat::zeros(2 * radius + 1, 2 * radius + 1, CV_32FC1);		//把kernel矩阵上半部分复制到对应部分
	Mat W14 = Mat::zeros(2 * radius + 1, 2 * radius + 1, CV_32FC1);		//把kernel矩阵右半部分复制到对应部分
	Mat W23 = Mat::zeros(2 * radius + 1, 2 * radius + 1, CV_32FC1);		//把kernel矩阵左半部分复制到对应部分
 
	kernel(Range(radius + 1, 2 * radius + 1), Range::all()).copyTo(W34(Range(radius + 1, 2 * radius + 1), Range::all()));
	kernel(Range(0, radius), Range::all()).copyTo(W12(Range(0, radius), Range::all()));
	kernel(Range::all(), Range(radius + 1, 2 * radius + 1)).copyTo(W14(Range::all(), Range(radius + 1, 2 * radius + 1)));
	kernel(Range::all(), Range(0, radius)).copyTo(W23(Range::all(), Range(0, radius)));
 
	//滤波
	Mat M34, M12, M14, M23;
	double eps = 0.00001;
 
	//float_image为图像归一化后的图像数据,做卷积运算
	filter2D(image, M34, CV_32FC1, W34, Point(-1, -1), eps);
	filter2D(image, M12, CV_32FC1, W12, Point(-1, -1), eps);
	filter2D(image, M14, CV_32FC1, W14, Point(-1, -1), eps);
	filter2D(image, M23, CV_32FC1, W23, Point(-1, -1), eps);
 
	//计算水平梯度和竖直梯度
	Mat Gx, Gy;
	log((M14) / (M23), Gx);
	log((M34) / (M12), Gy);
 
	//计算梯度幅度和梯度方向
	magnitude(Gx, Gy, amplit[nums]);					//梯度幅度图像,平方和开平方
	phase(Gx, Gy, orient[nums], true);					//梯度方向图像
 
}
 
 
/***********************该函数计算尺度空间特征点的主方向,用于后面特征点的检测***************************/
/*image表示特征点所在位置的高斯图像,后面可对着源码进行修改
 pt表示特征点的位置坐标(x,y)
 scale特征点的尺度
 n表示直方图bin个数
 hist表示计算得到的直方图
 函数返回值是直方图hist中的最大数值*/
static float clac_orientation_hist(const Mat& image, Point pt, float scale, int n, float* hist)
{
	int radius = cvRound(ORI_RADIUS * scale);			//特征点邻域半径(3*1.5*scale)
 
	int len = (2 * radius + 1) * (2 * radius + 1);		//特征点邻域像素总个数(最大值)
 
	float sigma = ORI_SIG_FCTR * scale;					//特征点邻域高斯权重标准差(1.5*scale)
 
	float exp_scale = -1.f / (2 * sigma * sigma);		//权重的指数部分
 
	//使用AutoBuffer分配一段内存,这里多出4个空间的目的是为了方便后面平滑直方图的需要
	AutoBuffer buffer((4 * len) + n + 4);
 
	//X保存水平差分,Y保存数值差分,Mag保存梯度幅度,Ori保存梯度角度,W保存高斯权重
	float* X = buffer, * Y = buffer + len, * Mag = Y, * Ori = Y + len, * W = Ori + len;
	float* temp_hist = W + len + 2;						//临时保存直方图数据
 
	for (int i = 0; i < n; ++i)
		temp_hist[i] = 0.f;								//数据清零
 
	//计算邻域像素的水平差分和竖直差分
	int k = 0;
 
	for (int i = -radius; i < radius; ++i)
	{
		int y = pt.y + i;								//邻域点的纵坐标
 
		if (y <= 0 || y >= image.rows - 1)
			continue;
 
		for (int j = -radius; j < radius; ++j)
		{
			int x = pt.x + j;
 
			if (x <= 0 || x >= image.cols - 1)
				continue;
 
			float dx = image.at(y, x + 1) - image.at(y, x - 1);	//水平差分
			float dy = image.at(y + 1, x) - image.at(y - 1, x);	//竖直差分
 
			//保存水平差分和竖直差分及其对应的权重
			X[k] = dx;
			Y[k] = dy;
			W[k] = (i * i + j * j) * exp_scale;
 
			++k;
		}
	}
 
	len = k;													//邻域内特征点的个数
 
	cv::hal::exp(W, W, len);									//计算邻域内所有像素的高斯权重
	cv::hal::fastAtan2(Y, X, Ori, len, true);					//计算邻域内所有像素的梯度方向,角度范围0-360度
	cv::hal::magnitude32f(X, Y, Mag, len);						//计算邻域内所有像素的梯度幅度,计算的是数学意义上的梯度
 
	//遍历邻域的像素
	for (int i = 0; i < len; ++i)
	{
		int bin = cvRound((n / 360.f) * Ori[i]);				//利用像素的梯度方向,约束bin的范围在[0,(n-1)]
 
		//像素点梯度方向为360度时,和0°一样
		if (bin >= n)
			bin = bin - n;
 
		if (bin < 0)
			bin = bin + n;
 
		temp_hist[bin] = temp_hist[bin] + Mag[i] * W[i];		//统计邻域内像素各个方向在梯度直方图的幅值(加权后的幅值)
	}
 
	//平滑直方图
	temp_hist[-1] = temp_hist[n - 1];
	temp_hist[-2] = temp_hist[n - 2];
	temp_hist[n] = temp_hist[0];
	temp_hist[n + 1] = temp_hist[1];
	for (int i = 0; i < n; ++i)
	{
		hist[i] = (temp_hist[i - 2] + temp_hist[i + 2]) * (1.f / 16.f) +
			(temp_hist[i - 1] + temp_hist[i + 1]) * (4.f / 16.f) +
			temp_hist[i] * (6.f / 16.f);
	}
 
	//获得直方图中最大值
	float max_value = hist[0];
	for (int i = 1; i < n; ++i)
	{
		if (hist[i] > max_value)
			max_value = hist[i];
	}
	return max_value;
}
 
 
/***********************使用 sobel 滤波器定义的新梯度计算尺度空间特征点的主方向**************************/
static float clac_orientation_hist_2(Mat& image, Point pt, float scale, int n, float* hist)
{
	Mat output_image;									//使用 sobel 滤波器计算的图像的梯度幅度图像
 
	sobelfilter(image, output_image);					//使用 sobel 滤波器求高斯差分图像的梯度幅度图像
 
	int radius = cvRound(ORI_RADIUS * scale);			//特征点邻域半径(3*1.5*scale)
 
	int len = (2 * radius + 1) * (2 * radius + 1);		//特征点邻域像素总个数(最大值)
 
	float sigma = ORI_SIG_FCTR * scale;					//特征点邻域高斯权重标准差(1.5*scale)
 
	float exp_scale = -1.f / (2 * sigma * sigma);		//权重的指数部分
 
	//使用AutoBuffer分配一段内存,这里多出4个空间的目的是为了方便后面平滑直方图的需要
	AutoBuffer buffer((4 * len) + n + 4);
 
	//X保存水平差分,Y保存数值差分,Mag保存梯度幅度,Ori保存梯度角度,W保存高斯权重
	float* X = buffer, * Y = buffer + len, * Mag = Y, * Ori = Y + len, * W = Ori + len;
	float* temp_hist = W + len + 2;						//临时保存直方图数据
 
	for (int i = 0; i < n; ++i)
		temp_hist[i] = 0.f;								//数据清零
 
	//计算邻域像素的水平差分和竖直差分
	int k = 0;
 
	for (int i = -radius; i < radius; ++i)
	{
		int y = pt.y + i;								//邻域点的纵坐标,行
 
		if (y <= 0 || y >= output_image.rows - 1)
			continue;
 
		for (int j = -radius; j < radius; ++j)
		{
			int x = pt.x + j;							//邻域点的纵坐标,列
 
			if (x <= 0 || x >= output_image.cols - 1)
				continue;
 
			//float dx = image.at(y, x + 1) - image.at(y, x - 1);	//水平差分
 
			float dx = output_image.at(y - 1, x + 1) - output_image.at(y - 1, x - 1)
				+ 2 * output_image.at(y, x + 1) - 2 * output_image.at(y, x - 1)
				+ output_image.at(y + 1, x + 1) - output_image.at(y + 1, x - 1);
 
			float dy = output_image.at(y + 1, x - 1) - output_image.at(y - 1, x - 1)
				+ 2 * output_image.at(y + 1, x) - 2 * output_image.at(y - 1, x) +
				output_image.at(y + 1, x + 1) - output_image.at(y - 1, x + 1);
 
			/*float dx = image.at(y - 1, x + 1) - image.at(y - 1, x - 1)
				+ 2 * image.at(y, x + 1) - 2 * image.at(y, x - 1)
				+ image.at(y + 1, x + 1) - image.at(y + 1, x - 1);
			float dy = image.at(y + 1, x - 1) - image.at(y - 1, x - 1)
				+ 2 * image.at(y + 1, x) - 2 * image.at(y - 1, x) +
				image.at(y + 1, x + 1) - image.at(y - 1, x + 1);*/
 
				//保存水平差分和竖直差分及其对应的权重
			X[k] = dx;
			Y[k] = dy;
			W[k] = (i * i + j * j) * exp_scale;
 
			++k;
		}
	}
 
	len = k;													//邻域内特征点的个数
 
	cv::hal::exp(W, W, len);									//计算邻域内所有像素的高斯权重
	cv::hal::fastAtan2(Y, X, Ori, len, true);					//计算邻域内所有像素的梯度方向,角度范围0-360度
	cv::hal::magnitude32f(X, Y, Mag, len);						//计算邻域内所有像素的梯度幅度,计算的是数学意义上的梯度
 
	//遍历邻域的像素
	for (int i = 0; i < len; ++i)
	{
		int bin = cvRound((n / 360.f) * Ori[i]);				//利用像素的梯度方向,约束bin的范围在[0,(n-1)]
 
		//像素点梯度方向为360度时,和0°一样
		if (bin >= n)
			bin = bin - n;
 
		if (bin < 0)
			bin = bin + n;
 
		temp_hist[bin] = temp_hist[bin] + Mag[i] * W[i];		//统计邻域内像素各个方向在梯度直方图的幅值(加权后的幅值)
	}
 
	//平滑直方图
	temp_hist[-1] = temp_hist[n - 1];
	temp_hist[-2] = temp_hist[n - 2];
	temp_hist[n] = temp_hist[0];
	temp_hist[n + 1] = temp_hist[1];
	for (int i = 0; i < n; ++i)
	{
		hist[i] = (temp_hist[i - 2] + temp_hist[i + 2]) * (1.f / 16.f) +
			(temp_hist[i - 1] + temp_hist[i + 1]) * (4.f / 16.f) +
			temp_hist[i] * (6.f / 16.f);
	}
 
	//获得直方图中最大值
	float max_value = hist[0];
	for (int i = 1; i < n; ++i)
	{
		if (hist[i] > max_value)
			max_value = hist[i];
	}
	return max_value;
}
 
 
/******************该函数计算特征点主方向,用于LOGH版本*********************/
/*amplit表示特征点所在层的梯度幅度,即输入图像对应像素点的梯度存在了对应位置
  orient表示特征点所在层的梯度方向,0-360度
  point表示特征点坐标
  scale表示特征点的所在层的尺度
  hist表示生成的直方图
  n表示主方向直方图bin个数
 */
static float calc_orient_hist(const Mat& amplit, const Mat& orient, Point pt, float scale, float* hist, int n)
{
	//暂且认为是只进行了下采样,没有进行高斯模糊
	int num_row = amplit.rows;
	int num_col = amplit.cols;
 
	Point point(cvRound(pt.x), cvRound(pt.y));
 
	//int radius = cvRound(SAR_SIFT_FACT_RADIUS_ORI * scale);
	int radius = cvRound(6 * scale);
 
	radius = min(radius, min(num_row / 2, num_col / 2));
 
	float gauss_sig = 2 * scale;							//高斯加权标准差
 
	float exp_temp = -1.f / (2 * gauss_sig * gauss_sig);	//权重指数部分
 
	//邻域区域
	int radius_x_left = point.x - radius;
	int radius_x_right = point.x + radius;
	int radius_y_up = point.y - radius;
	int radius_y_down = point.y + radius;
 
	//防止越界
	if (radius_x_left < 0)
		radius_x_left = 0;
	if (radius_x_right > num_col - 1)
		radius_x_right = num_col - 1;
	if (radius_y_up < 0)
		radius_y_up = 0;
	if (radius_y_down > num_row - 1)
		radius_y_down = num_row - 1;
 
	//此时特征点周围矩形区域相对于本矩形区域的中心
	int center_x = point.x - radius_x_left;
	int center_y = point.y - radius_y_up;
 
	//矩形区域的边界,计算高斯权值
	Range x_rng(-(point.x - radius_x_left), radius_x_right - point.x);
	Range y_rng(-(point.y - radius_y_up), radius_y_down - point.y);
 
	Mat gauss_weight;
 
	gauss_weight.create(y_rng.end - y_rng.start + 1, x_rng.end - x_rng.start + 1, CV_32FC1);
 
	//求各个像素点的高斯权重
	for (int i = y_rng.start; i <= y_rng.end; ++i)
	{
		float* ptr_gauss = gauss_weight.ptr(i - y_rng.start);
 
		for (int j = x_rng.start; j <= x_rng.end; ++j)
			ptr_gauss[j - x_rng.start] = exp((i * i + j * j) * exp_temp);
	}
 
	//索引特征点周围的像素梯度幅度,梯度方向
	Mat sub_amplit = amplit(Range(radius_y_up, radius_y_down + 1), Range(radius_x_left, radius_x_right + 1));
	Mat sub_orient = orient(Range(radius_y_up, radius_y_down + 1), Range(radius_x_left, radius_x_right + 1));
 
	//Mat W = sub_amplit.mul(gauss_weight);			//加入高斯权重,计算高斯权重时,正确匹配点对反而变少了
	Mat W = sub_amplit;								//没加高斯权重,梯度幅值
 
	//计算直方图
	AutoBuffer buffer(n + 4);
 
	float* temp_hist = buffer + 2;
 
	for (int i = 0; i < n; ++i)
		temp_hist[i] = 0.f;
 
	for (int i = 0; i < sub_orient.rows; i++)
	{
		float* ptr_1 = W.ptr(i);
		float* ptr_2 = sub_orient.ptr(i);
 
		for (int j = 0; j < sub_orient.cols; j++)
		{
			if (((i - center_y) * (i - center_y) + (j - center_x) * (j - center_x)) < radius * radius)
			{
				int bin = cvRound(ptr_2[j] * n / 360.f);
 
				if (bin > n)
					bin = bin - n;
				if (bin < 0)
					bin = bin + n;
				temp_hist[bin] += ptr_1[j];
			}
		}
	}
 
	//平滑直方图,可以防止突变
	temp_hist[-1] = temp_hist[n - 1];
	temp_hist[-2] = temp_hist[n - 2];
	temp_hist[n] = temp_hist[0];
	temp_hist[n + 1] = temp_hist[1];
 
	for (int i = 0; i < n; ++i)
	{
		hist[i] = (temp_hist[i - 2] + temp_hist[i + 2]) * (1.f / 16.f) +
			(temp_hist[i - 1] + temp_hist[i + 1]) * (4.f / 16.f) +
			temp_hist[i] * (6.f / 16.f);
	}
 
	//获得直方图中最大值
	float max_value = hist[0];
	for (int i = 1; i < n; ++i)
	{
		if (hist[i] > max_value)
			max_value = hist[i];
	}
	return max_value;
}
 
/****************************该函数精确定位特征点位置(x,y,scale),用于后面特征点的检测*************************/
/*功能:确定特征点的位置,并通过主曲率消除边缘相应点,该版本是简化版
 dog_pry表示DOG金字塔
 kpt表示精确定位后该特征点的信息
 octave表示初始特征点所在的组
 layer表示初始特征点所在的层
 row表示初始特征点在图像中的行坐标
 col表示初始特征点在图像中的列坐标
 nOctaveLayers表示DOG金字塔每组中间层数,默认是3
 contrastThreshold表示对比度阈值,默认是0.04
 edgeThreshold表示边缘阈值,默认是10
 sigma表示高斯尺度空间最底层图像尺度,默认是1.6*/
static bool adjust_local_extrema_1(const vector>& dog_pyr, KeyPoint& kpt, int octave, int& layer, int& row,
	int& col, int nOctaveLayers, float contrastThreshold, float edgeThreshold, float sigma)
{
	float xi = 0, xr = 0, xc = 0;
	int i = 0;
	for (; i < MAX_INTERP_STEPS; ++i)					//最大迭代次数
	{
		const Mat& img = dog_pyr[octave][layer];		//当前层图像索引
		const Mat& prev = dog_pyr[octave][layer - 1];	//之前层图像索引
		const Mat& next = dog_pyr[octave][layer + 1];	//下一层图像索引
 
		//特征点位置x方向,y方向,尺度方向的一阶偏导数
		float dx = (img.at(row, col + 1) - img.at(row, col - 1)) * (1.f / 2.f);
		float dy = (img.at(row + 1, col) - img.at(row - 1, col)) * (1.f / 2.f);
		float dz = (next.at(row, col) - prev.at(row, col)) * (1.f / 2.f);
 
		//计算特征点位置二阶偏导数
		float v2 = img.at(row, col);
		float dxx = img.at(row, col + 1) + img.at(row, col - 1) - 2 * v2;
		float dyy = img.at(row + 1, col) + img.at(row - 1, col) - 2 * v2;
		float dzz = prev.at(row, col) + next.at(row, col) - 2 * v2;
 
		//计算特征点周围混合二阶偏导数
		float dxy = (img.at(row + 1, col + 1) + img.at(row - 1, col - 1) -
			img.at(row + 1, col - 1) - img.at(row - 1, col + 1)) * (1.f / 4.f);
		float dxz = (next.at(row, col + 1) + prev.at(row, col - 1) -
			next.at(row, col - 1) - prev.at(row, col + 1)) * (1.f / 4.f);
		float dyz = (next.at(row + 1, col) + prev.at(row - 1, col) -
			next.at(row - 1, col) - prev.at(row + 1, col)) * (1.f / 4.f);
 
		Matx33f H(dxx, dxy, dxz, dxy, dyy, dyz, dxz, dyz, dzz);
 
		Vec3f dD(dx, dy, dz);
 
		Vec3f X = H.solve(dD, DECOMP_SVD);
 
		xc = -X[0];		//x方向偏移量
		xr = -X[1];		//y方向偏移量
		xi = -X[2];		//尺度方向偏移量
 
		//如果三个方向偏移量都小于0.5,说明已经找到特征点准确位置
		if (abs(xc) < 0.5f && abs(xr) < 0.5f && abs(xi) < 0.5f)
			break;
 
		//如果其中一个方向的偏移量过大,则删除该点
		if (abs(xc) > (float)(INT_MAX / 3) ||
			abs(xr) > (float)(INT_MAX / 3) ||
			abs(xi) > (float)(INT_MAX / 3))
			return false;
 
		col = col + cvRound(xc);
		row = row + cvRound(xr);
		layer = layer + cvRound(xi);
 
		//如果特征点定位在边界区域,同样也需要删除
		if (layer<1 || layer>nOctaveLayers ||
			colimg.cols - IMG_BORDER ||
			rowimg.rows - IMG_BORDER)
			return false;
	}
 
	//如果i=MAX_INTERP_STEPS,说明循环结束也没有满足条件,即该特征点需要被删除
	if (i >= MAX_INTERP_STEPS)
		return false;
 
	/**************************再次删除低响应点(对比度较低的点)********************************/
	//再次计算特征点位置x方向,y方向,尺度方向的一阶偏导数
	//高对比度的特征对图像的变形是稳定的
	{
		const Mat& img = dog_pyr[octave][layer];
		const Mat& prev = dog_pyr[octave][layer - 1];
		const Mat& next = dog_pyr[octave][layer + 1];
 
		float dx = (img.at(row, col + 1) - img.at(row, col - 1)) * (1.f / 2.f);
		float dy = (img.at(row + 1, col) - img.at(row - 1, col)) * (1.f / 2.f);
		float dz = (next.at(row, col) - prev.at(row, col)) * (1.f / 2.f);
		Matx31f dD(dx, dy, dz);
		float t = dD.dot(Matx31f(xc, xr, xi));
 
		float contr = img.at(row, col) + t * 0.5f;	//特征点响应 |D(x~)| 即对比度
 
		//Low建议contr阈值是0.03,但是RobHess等建议阈值为0.04/nOctaveLayers
		if (abs(contr) < contrastThreshold / nOctaveLayers)	//阈值设为0.03时特征点数量过多
			return false;
 
		/******************************删除边缘响应比较强的点************************************/
 
		//再次计算特征点位置二阶偏导数,获取特征点出的 Hessian 矩阵,主曲率通过 2X2 的 Hessian 矩阵求出
		//一个定义不好的高斯差分算子的极值在横跨边缘的地方有较大的主曲率而在垂直边缘的方向有较小的主曲率
		float v2 = img.at(row, col);
		float dxx = img.at(row, col + 1) + img.at(row, col - 1) - 2 * v2;
		float dyy = img.at(row + 1, col) + img.at(row - 1, col) - 2 * v2;
		float dxy = (img.at(row + 1, col + 1) + img.at(row - 1, col - 1) -
			img.at(row + 1, col - 1) - img.at(row - 1, col + 1)) * (1.f / 4.f);
		float det = dxx * dyy - dxy * dxy;
		float trace = dxx + dyy;
 
		//主曲率和阈值的对比判定
		if (det < 0 || (trace * trace * edgeThreshold >= det * (edgeThreshold + 1) * (edgeThreshold + 1)))
			return false;
 
		/*********到目前为止该特征的满足上面所有要求,保存该特征点信息***********/
		kpt.pt.x = ((float)col + xc) * (1 << octave);	//相对于最底层的图像的x坐标
		kpt.pt.y = ((float)row + xr) * (1 << octave);	//相对于最底层图像的y坐标
		kpt.octave = octave + (layer << 8);				//组号保存在低字节,层号保存在高字节
		//相对于最底层图像的尺度
		kpt.size = sigma * powf(2.f, (layer + xi) / nOctaveLayers) * (1 << octave);
		kpt.response = abs(contr);						//特征点响应值(对比度)
 
		return true;
	}
 
}
 
 
/****************************该函数精确定位特征点位置(x,y,scale),用于后面特征点的检测*************************/
//该版本是 SIFT 原版,检测得到的特征点数量更多
static bool adjust_local_extrema_2(const vector>& dog_pyr, KeyPoint& kpt, int octave, int& layer, int& row,
	int& col, int nOctaveLayers, float contrastThreshold, float edgeThreshold, float sigma)
{
	const float img_scale = 1.f / (255 * SIFT_FIXPT_SCALE);    //SIFT_FIXPT_SCALE=48
	const float deriv_scale = img_scale * 0.5f;
	const float second_deriv_scale = img_scale;
	const float cross_deriv_scale = img_scale * 0.25f;
 
	float xi = 0, xr = 0, xc = 0;
	int i = 0;
 
	for (; i < MAX_INTERP_STEPS; ++i)						  //最大迭代次数
	{
		const Mat& img = dog_pyr[octave][layer];			  //当前层图像索引
		const Mat& prev = dog_pyr[octave][layer - 1];		  //之前层图像索引
		const Mat& next = dog_pyr[octave][layer + 1];		  //下一层图像索引
 
		//计算一阶偏导数,通过临近点差分求得
		float dx = (img.at(row, col + 1) - img.at(row, col - 1)) * deriv_scale;
		float dy = (img.at(row + 1, col) - img.at(row - 1, col)) * deriv_scale;
		float dz = (next.at(row, col) - prev.at(row, col)) * deriv_scale;
 
		//计算特征点位置二阶偏导数
		//float v2  = img.at(row, col);
		float v2 = (float)img.at(row, col) * 2.f;
		float dxx = (img.at(row, col + 1) + img.at(row, col - 1) - v2) * second_deriv_scale;
		float dyy = (img.at(row + 1, col) + img.at(row - 1, col) - v2) * second_deriv_scale;
		float dzz = (prev.at(row, col) + next.at(row, col) - v2) * second_deriv_scale;
 
		//计算特征点周围混合二阶偏导数
		float dxy = (img.at(row + 1, col + 1) + img.at(row - 1, col - 1) -
			img.at(row + 1, col - 1) - img.at(row - 1, col + 1)) * cross_deriv_scale;
		float dxz = (next.at(row, col + 1) + prev.at(row, col - 1) -
			next.at(row, col - 1) - prev.at(row, col + 1)) * cross_deriv_scale;
		float dyz = (next.at(row + 1, col) + prev.at(row - 1, col) -
			next.at(row - 1, col) - prev.at(row + 1, col)) * cross_deriv_scale;
 
		Matx33f H(dxx, dxy, dxz, dxy, dyy, dyz, dxz, dyz, dzz);
 
		Vec3f dD(dx, dy, dz);
 
		Vec3f X = H.solve(dD, DECOMP_SVD);
 
		xc = -X[0];		//x方向偏移量
		xr = -X[1];		//y方向偏移量
		xi = -X[2];		//尺度方向偏移量
 
		//如果三个方向偏移量都小于0.5,说明已经找到特征点准确位置
		if (abs(xc) < 0.5f && abs(xr) < 0.5f && abs(xi) < 0.5f)
			break;
 
		//如果其中一个方向的偏移量过大,则删除该点
		if (abs(xc) > (float)(INT_MAX / 3) ||
			abs(xr) > (float)(INT_MAX / 3) ||
			abs(xi) > (float)(INT_MAX / 3))
			return false;
 
		col = col + cvRound(xc);
		row = row + cvRound(xr);
		layer = layer + cvRound(xi);
 
		//如果特征点定位在边界区域,同样也需要删除
		if (layer<1 || layer>nOctaveLayers ||
			col < IMG_BORDER || col >= img.cols - IMG_BORDER ||
			row < IMG_BORDER || row >= img.rows - IMG_BORDER)
			return false;
	}
	//如果i=MAX_INTERP_STEPS,说明循环结束也没有满足条件,即该特征点需要被删除
	if (i >= MAX_INTERP_STEPS)
		return false;
 
 
 
	/**************************再次删除低响应点(对比度较低的点)********************************/
	//再次计算特征点位置x方向,y方向,尺度方向的一阶偏导数
	//高对比度的特征对图像的变形是稳定的
 
	const Mat& img = dog_pyr[octave][layer];
	const Mat& prev = dog_pyr[octave][layer - 1];
	const Mat& next = dog_pyr[octave][layer + 1];
 
	float dx = (img.at(row, col + 1) - img.at(row, col - 1)) * deriv_scale;
	float dy = (img.at(row + 1, col) - img.at(row - 1, col)) * deriv_scale;
	float dz = (next.at(row, col) - prev.at(row, col)) * deriv_scale;
	Matx31f dD(dx, dy, dz);
	float t = dD.dot(Matx31f(xc, xr, xi));
 
	float contr = img.at(row, col) + t * 0.5f;	//特征点响应 |D(x~)| 即对比度
 
	//Low建议contr阈值是0.03,但是RobHess等建议阈值为0.04/nOctaveLayers
	if (abs(contr) < contrastThreshold / nOctaveLayers)	//阈值设为0.03时特征点数量过多
		return false;
 
 
	/******************************删除边缘响应比较强的点************************************/
 
	//再次计算特征点位置二阶偏导数,获取特征点出的 Hessian 矩阵,主曲率通过 2X2 的 Hessian 矩阵求出
	//一个定义不好的高斯差分算子的极值在横跨边缘的地方有较大的主曲率而在垂直边缘的方向有较小的主曲率
	float v2 = (float)img.at(row, col) * 2.f;
	float dxx = (img.at(row, col + 1) + img.at(row, col - 1) - v2) * second_deriv_scale;
	float dyy = (img.at(row + 1, col) + img.at(row - 1, col) - v2) * second_deriv_scale;
	float dxy = (img.at(row + 1, col + 1) + img.at(row - 1, col - 1) -
		img.at(row + 1, col - 1) - img.at(row - 1, col + 1)) * cross_deriv_scale;
 
	float det = dxx * dyy - dxy * dxy;
	float trace = dxx + dyy;
 
	//主曲率和阈值的对比判定
	if (det <= 0 || (trace * trace * edgeThreshold >= det * (edgeThreshold + 1) * (edgeThreshold + 1)))
		return false;
 
 
	/*********保存该特征点信息***********/
	kpt.pt.x = ((float)col + xc) * (1 << octave);		//高斯金字塔坐标根据组数扩大相应的倍数
	kpt.pt.y = ((float)row + xr) * (1 << octave);
 
	// SIFT 描述子
	kpt.octave = octave + (layer << 8) + (cvRound((xi + 0.5) * 255) << 16);				//特征点被检测出时所在的金字塔组
 
	kpt.size = sigma * powf(2.f, (layer + xi) / nOctaveLayers) * (1 << octave) * 2;		//关键点邻域直径
	kpt.response = abs(contr);							//特征点响应值(对比度)
 
	return true;
 
 
}
 
 
/************该函数在DOG金字塔上进行特征点检测,特征点精确定位,删除低对比度点,删除边缘响应较大点**********/
/*dog_pyr表示高斯金字塔			原始 SIFT 算子
 gauss_pyr表示高斯金字塔
 keypoints表示检测到的特征点*/
void mySift::find_scale_space_extrema(const vector>& dog_pyr, const vector>& gauss_pyr,
	vector& keypoints)
{
	int nOctaves = (int)dog_pyr.size();								//子八度个数
 
	//Low文章建议thresholdThreshold是0.03,Rob Hess等人使用0.04/nOctaveLayers作为阈值
	float threshold = (float)(contrastThreshold / nOctaveLayers);
	const int n = ORI_HIST_BINS;									//n=36
	float hist[n];
	KeyPoint kpt;
 
	keypoints.clear();												//先清空keypoints
 
	for (int i = 0; i < nOctaves; ++i)								//对于每一组
	{
		for (int j = 1; j <= nOctaveLayers; ++j)					//对于组内每一层
		{
			const Mat& curr_img = dog_pyr[i][j];					//当前层
			const Mat& prev_img = dog_pyr[i][j - 1];				//上一层
			const Mat& next_img = dog_pyr[i][j + 1];				//下一层
 
			int num_row = curr_img.rows;
			int num_col = curr_img.cols;							//获得当前组图像的大小
			size_t step = curr_img.step1();							//一行元素所占字节数
 
			//遍历每一个尺度层中的有效像素,像素值
			for (int r = IMG_BORDER; r < num_row - IMG_BORDER; ++r)
			{
				const float* curr_ptr = curr_img.ptr(r);		//指向的是第 r 行的起点,返回的是 float 类型的像素值
				const float* prev_ptr = prev_img.ptr(r - 1);
				const float* next_ptr = next_img.ptr(r + 1);
 
				for (int c = IMG_BORDER; c < num_col - IMG_BORDER; ++c)
				{
					float val = curr_ptr[c];						//当前中心点响应值
 
					//开始检测特征点
					if (abs(val) > threshold &&
						((val > 0 && val >= curr_ptr[c - 1] && val >= curr_ptr[c + 1] &&
							val >= curr_ptr[c - step - 1] && val >= curr_ptr[c - step] && val >= curr_ptr[c - step + 1] &&
							val >= curr_ptr[c + step - 1] && val >= curr_ptr[c + step] && val >= curr_ptr[c + step + 1] &&
							val >= prev_ptr[c] && val >= prev_ptr[c - 1] && val >= prev_ptr[c + 1] &&
							val >= prev_ptr[c - step - 1] && val >= prev_ptr[c - step] && val >= prev_ptr[c - step + 1] &&
							val >= prev_ptr[c + step - 1] && val >= prev_ptr[c + step] && val >= prev_ptr[c + step + 1] &&
							val >= next_ptr[c] && val >= next_ptr[c - 1] && val >= next_ptr[c + 1] &&
							val >= next_ptr[c - step - 1] && val >= next_ptr[c - step] && val >= next_ptr[c - step + 1] &&
							val >= next_ptr[c + step - 1] && val >= next_ptr[c + step] && val >= next_ptr[c + step + 1]) ||
							(val < 0 && val <= curr_ptr[c - 1] && val <= curr_ptr[c + 1] &&
								val <= curr_ptr[c - step - 1] && val <= curr_ptr[c - step] && val <= curr_ptr[c - step + 1] &&
								val <= curr_ptr[c + step - 1] && val <= curr_ptr[c + step] && val <= curr_ptr[c + step + 1] &&
								val <= prev_ptr[c] && val <= prev_ptr[c - 1] && val <= prev_ptr[c + 1] &&
								val <= prev_ptr[c - step - 1] && val <= prev_ptr[c - step] && val <= prev_ptr[c - step + 1] &&
								val <= prev_ptr[c + step - 1] && val <= prev_ptr[c + step] && val <= prev_ptr[c + step + 1] &&
								val <= next_ptr[c] && val <= next_ptr[c - 1] && val <= next_ptr[c + 1] &&
								val <= next_ptr[c - step - 1] && val <= next_ptr[c - step] && val <= next_ptr[c - step + 1] &&
								val <= next_ptr[c + step - 1] && val <= next_ptr[c + step] && val <= next_ptr[c + step + 1])))
					{
						//++numKeys;
						//获得特征点初始行号,列号,组号,组内层号
						int octave = i, layer = j, r1 = r, c1 = c;
 
						if (!adjust_local_extrema_1(dog_pyr, kpt, octave, layer, r1, c1,
							nOctaveLayers, (float)contrastThreshold,
							(float)edgeThreshold, (float)sigma))
						{
							continue;									//如果该初始点不满足条件,则不保存改点
						}
 
						float scale = kpt.size / float(1 << octave);	//该特征点相对于本组的尺度
 
						//max_hist值对应的方向为主方向
						float max_hist = clac_orientation_hist(gauss_pyr[octave][layer], Point(c1, r1), scale, n, hist);
 
						//大于mag_thr值对应的方向为辅助方向
						float mag_thr = max_hist * ORI_PEAK_RATIO;		//主峰值 80% 的方向作为辅助方向
 
						//遍历直方图中的 36 个bin
						for (int i = 0; i < n; ++i)
						{
							int left = i > 0 ? i - 1 : n - 1;
							int right = i < n - 1 ? i + 1 : 0;
 
							//创建新的特征点,大于主峰值 80% 的方向,赋值给该特征点,作为一个新的特征点;即有多个特征点,位置、尺度相同,方向不同
							if (hist[i] > hist[left] && hist[i] > hist[right] && hist[i] >= mag_thr)
							{
								float bin = i + 0.5f * (hist[left] - hist[right]) / (hist[left] + hist[right] - 2 * hist[i]);
								bin = bin < 0 ? n + bin : bin >= n ? bin - n : bin;
 
								kpt.angle = (360.f / n) * bin;			//原始 SIFT 算子使用的特征点的主方向0-360度
								keypoints.push_back(kpt);				//保存该特征点
 
							}
						}
					}
				}
			}
		}
	}
 
	//cout << "初始满足要求特征点个数是: " << numKeys << endl;
}
 
 
/************该函数在DOG金字塔上进行特征点检测,特征点精确定位,删除低对比度点,删除边缘响应较大点**********/
//对特征点进行方向的细化 + 增加更多的主方向版本 ——此时细化是对最后要给关键点进行赋值时的细化
//还可以考虑直接对方向直方图进行细化
void mySift::find_scale_space_extrema1(const vector>& dog_pyr, vector>& gauss_pyr,
	vector& keypoints)
{
	int nOctaves = (int)dog_pyr.size();								//子八度个数
 
	//Low文章建议thresholdThreshold是0.03,Rob Hess等人使用0.04/nOctaveLayers作为阈值
	float threshold = (float)(contrastThreshold / nOctaveLayers);
	const int n = ORI_HIST_BINS;									//n=36
	float hist[n];
	KeyPoint kpt;
 
	vector amplit;			//存放高斯差分金字塔每一层的梯度幅度图像
	vector orient;			//存放高斯差分金字塔每一层的梯度方向图像
 
	keypoints.clear();												//先清空keypoints
 
	for (int i = 0; i < nOctaves; ++i)								//对于每一组
	{
		for (int j = 1; j <= nOctaveLayers; ++j)					//对于组内每一层
		{
			const Mat& curr_img = dog_pyr[i][j];					//当前层
			const Mat& prev_img = dog_pyr[i][j - 1];				//上一层
			const Mat& next_img = dog_pyr[i][j + 1];				//下一层
 
			int num_row = curr_img.rows;
			int num_col = curr_img.cols;							//获得当前组图像的大小
			size_t step = curr_img.step1();							//一行元素所占字节数
 
			//遍历每一个尺度层中的有效像素,像素值
			for (int r = IMG_BORDER; r < num_row - IMG_BORDER; ++r)
			{
				const float* curr_ptr = curr_img.ptr(r);		//指向的是第 r 行的起点,返回的是 float 类型的像素值
				const float* prev_ptr = prev_img.ptr(r);
				const float* next_ptr = next_img.ptr(r);
 
				for (int c = IMG_BORDER; c < num_col - IMG_BORDER; ++c)
				{
					float val = curr_ptr[c];						//当前中心点响应值
 
					//开始检测特征点
					if (abs(val) > threshold &&
						((val > 0 && val >= curr_ptr[c - 1] && val >= curr_ptr[c + 1] &&
							val >= curr_ptr[c - step - 1] && val >= curr_ptr[c - step] && val >= curr_ptr[c - step + 1] &&
							val >= curr_ptr[c + step - 1] && val >= curr_ptr[c + step] && val >= curr_ptr[c + step + 1] &&
							val >= prev_ptr[c] && val >= prev_ptr[c - 1] && val >= prev_ptr[c + 1] &&
							val >= prev_ptr[c - step - 1] && val >= prev_ptr[c - step] && val >= prev_ptr[c - step + 1] &&
							val >= prev_ptr[c + step - 1] && val >= prev_ptr[c + step] && val >= prev_ptr[c + step + 1] &&
							val >= next_ptr[c] && val >= next_ptr[c - 1] && val >= next_ptr[c + 1] &&
							val >= next_ptr[c - step - 1] && val >= next_ptr[c - step] && val >= next_ptr[c - step + 1] &&
							val >= next_ptr[c + step - 1] && val >= next_ptr[c + step] && val >= next_ptr[c + step + 1]) ||
							(val < 0 && val <= curr_ptr[c - 1] && val <= curr_ptr[c + 1] &&
								val <= curr_ptr[c - step - 1] && val <= curr_ptr[c - step] && val <= curr_ptr[c - step + 1] &&
								val <= curr_ptr[c + step - 1] && val <= curr_ptr[c + step] && val <= curr_ptr[c + step + 1] &&
								val <= prev_ptr[c] && val <= prev_ptr[c - 1] && val <= prev_ptr[c + 1] &&
								val <= prev_ptr[c - step - 1] && val <= prev_ptr[c - step] && val <= prev_ptr[c - step + 1] &&
								val <= prev_ptr[c + step - 1] && val <= prev_ptr[c + step] && val <= prev_ptr[c + step + 1] &&
								val <= next_ptr[c] && val <= next_ptr[c - 1] && val <= next_ptr[c + 1] &&
								val <= next_ptr[c - step - 1] && val <= next_ptr[c - step] && val <= next_ptr[c - step + 1] &&
								val <= next_ptr[c + step - 1] && val <= next_ptr[c + step] && val <= next_ptr[c + step + 1])))
					{
						//++numKeys;
						//获得特征点初始行号,列号,组号,组内层号
						int octave = i, layer = j, r1 = r, c1 = c, nums = i * nOctaves + j;
 
						if (!adjust_local_extrema_2(dog_pyr, kpt, octave, layer, r1, c1,
							nOctaveLayers, (float)contrastThreshold,
							(float)edgeThreshold, (float)sigma))
						{
							continue;									//如果该初始点不满足条件,则不保存改点
						}
 
						float scale = kpt.size / float(1 << octave);	//该特征点相对于本组的尺度
 
						//计算梯度幅度和梯度方向
						//amplit_orient(curr_img, amplit, orient, scale, nums);
 
						//max_hist值对应的方向为主方向
						float max_hist = clac_orientation_hist(gauss_pyr[octave][layer], Point(c1, r1), scale, n, hist);
						//float max_hist = calc_orient_hist(amplit[nums], orient[nums], Point2f(c1, r1), scale, hist, n);
 
						大于mag_thr值对应的方向为辅助方向
						//float mag_thr = max_hist * ORI_PEAK_RATIO;	//主峰值 80% 的方向作为辅助方向
 
						//增加更多的主方向,以增加特征点对梯度差异的鲁棒性
						float sum = 0.0;								//直方图对应的幅值之和
						float mag_thr = 0.0;							//判断是否为主方向的阈值
 
						for (int i = 0; i < n; ++i)
						{
							sum += hist[i];
						}
						mag_thr = 0.5 * (1.0 / 36) * sum;
 
 
						//遍历直方图中的 36 个bin
						for (int i = 0; i < n; ++i)
						{
							int left = i > 0 ? i - 1 : n - 1;
							int right = i < n - 1 ? i + 1 : 0;
 
							//创建新的特征点,大于主峰值 80% 的方向,赋值给该特征点,作为一个新的特征点;即有多个特征点,位置、尺度相同,方向不同
							if (hist[i] > hist[left] && hist[i] > hist[right] && hist[i] >= mag_thr)
							{
								float bin = i + 0.5f * (hist[left] - hist[right]) / (hist[left] + hist[right] - 2 * hist[i]);
								bin = bin < 0 ? n + bin : bin >= n ? bin - n : bin;
 
 
								//修改的地方,特征点的主方向修改为了0-180度,相当于对方向做了一个细化
								float angle = (360.f / n) * bin;
								if (angle >= 1 && angle <= 180)
								{
									kpt.angle = angle;
								}
								else if (angle > 180 && angle < 360)
								{
									kpt.angle = 360 - angle;
								}
 
								//kpt.angle = (360.f / n) * bin;			//原始 SIFT 算子使用的特征点的主方向0-360度
								keypoints.push_back(kpt);					//保存该特征点
 
							}
						}
					}
				}
			}
		}
	}
 
	//cout << "初始满足要求特征点个数是: " << numKeys << endl;
}
 
 
/*该函数生成matlab中的meshgrid函数*/
/*x_range表示x方向的范围
y_range表示y方向的范围
X表示生成的沿x轴变化的网格
Y表示生成沿y轴变换的网格
*/
static void meshgrid(const Range& x_range, const Range& y_range, Mat& X, Mat& Y)
{
	int x_start = x_range.start, x_end = x_range.end;
	int y_start = y_range.start, y_end = y_range.end;
	int width = x_end - x_start + 1;
	int height = y_end - y_start + 1;
 
	X.create(height, width, CV_32FC1);
	Y.create(height, width, CV_32FC1);
 
	for (int i = y_start; i <= y_end; i++)
	{
		float* ptr_1 = X.ptr(i - y_start);
		for (int j = x_start; j <= x_end; ++j)
			ptr_1[j - x_start] = j * 1.0f;
	}
 
	for (int i = y_start; i <= y_end; i++)
	{
		float* ptr_2 = Y.ptr(i - y_start);
		for (int j = x_start; j <= x_end; ++j)
			ptr_2[j - x_start] = i * 1.0f;
	}
}
 
 
/******************************计算一个特征点的描述子***********************************/
/*gauss_image表示特征点所在的高斯层
  main_angle表示特征点的主方向,角度范围是0-360度
  pt表示特征点在高斯图像上的坐标,相对与本组,不是相对于最底层
  scale表示特征点所在层的尺度,相对于本组,不是相对于最底层
  d表示特征点邻域网格宽度
  n表示每个网格内像素梯度角度等分个数
  descriptor表示生成的特征点的描述子*/
static void calc_sift_descriptor(const Mat& gauss_image, float main_angle, Point2f pt,
	float scale, int d, int n, float* descriptor)
{
	Point ptxy(cvRound(pt.x), cvRound(pt.y));					//坐标取整
 
	float cos_t = cosf(-main_angle * (float)(CV_PI / 180));		//把角度转化为弧度,计算主方向的余弦
	float sin_t = sinf(-main_angle * (float)(CV_PI / 180));		//把角度转化为弧度,计算主方向的正弦
 
	float bins_per_rad = n / 360.f;								// n = 8 ,梯度直方图分为 8 个方向
 
	float exp_scale = -1.f / (d * d * 0.5f);					//权重指数部分
 
	float hist_width = DESCR_SCL_FCTR * scale;					//特征点邻域内子区域边长,子区域的边长
 
	int radius = cvRound(hist_width * (d + 1) * sqrt(2) * 0.5f);//特征点邻域半径(d+1)*(d+1),四舍五入
 
	int rows = gauss_image.rows, cols = gauss_image.cols;		//当前高斯层行、列信息
 
	//特征点邻域半径
	radius = min(radius, (int)sqrt((double)rows * rows + cols * cols));
 
	cos_t = cos_t / hist_width;
	sin_t = sin_t / hist_width;
 
	int len = (2 * radius + 1) * (2 * radius + 1);				//邻域内总像素数,为后面动态分配内存使用
 
	int histlen = (d + 2) * (d + 2) * (n + 2);					//值为 360 
 
	AutoBuffer buf(6 * len + histlen);
 
	//X保存水平差分,Y保存竖直差分,Mag保存梯度幅度,Angle保存特征点方向, W保存高斯权重
	float* X = buf, * Y = buf + len, * Mag = Y, * Angle = Y + len, * W = Angle + len;
	float* RBin = W + len, * CBin = RBin + len, * hist = CBin + len;
 
	//首先清空直方图数据
	for (int i = 0; i < d + 2; ++i)				// i 对应 row
	{
		for (int j = 0; j < d + 2; ++j)			// j 对应 col
		{
			for (int k = 0; k < n + 2; ++k)
 
				hist[(i * (d + 2) + j) * (n + 2) + k] = 0.f;
		}
	}
 
	//把邻域内的像素分配到相应子区域内,计算子区域内每个像素点的权重(子区域即 d*d 中每一个小方格)
	int k = 0;
 
	//实际上是在 4 x 4 的网格中找 16 个种子点,每个种子点都在子网格的正中心,
	//通过三线性插值对不同种子点间的像素点进行加权作用到不同的种子点上绘制方向直方图
 
	for (int i = -radius; i < radius; ++i)						// i 对应 y 行坐标
	{
		for (int j = -radius; j < radius; ++j)					// j 对应 x 列坐标
		{
			float c_rot = j * cos_t - i * sin_t;				//旋转后邻域内采样点的 x 坐标
			float r_rot = j * sin_t + i * cos_t;				//旋转后邻域内采样点的 y 坐标
 
			//旋转后 5 x 5 的网格中的所有像素点被分配到 4 x 4 的网格中
			float cbin = c_rot + d / 2 - 0.5f;					//旋转后的采样点落在子区域的 x 坐标
			float rbin = r_rot + d / 2 - 0.5f;					//旋转后的采样点落在子区域的 y 坐标
 
			int r = ptxy.y + i, c = ptxy.x + j;					//ptxy是高斯金字塔中的坐标
 
			//这里rbin,cbin范围是(-1,d)
			if (rbin > -1 && rbin < d && cbin>-1 && cbin < d && r>0 && r < rows - 1 && c>0 && c < cols - 1)
			{
				float dx = gauss_image.at(r, c + 1) - gauss_image.at(r, c - 1);
				float dy = gauss_image.at(r + 1, c) - gauss_image.at(r - 1, c);
 
				X[k] = dx;												//邻域内所有像素点的水平差分
				Y[k] = dy;												//邻域内所有像素点的竖直差分
 
				CBin[k] = cbin;											//邻域内所有采样点落在子区域的 x 坐标
				RBin[k] = rbin;											//邻域内所有采样点落在子区域的 y 坐标
 
				W[k] = (c_rot * c_rot + r_rot * r_rot) * exp_scale;		//高斯权值的指数部分
 
				++k;
			}
		}
	}
 
	//计算采样点落在子区域的像素梯度幅度,梯度角度,和高斯权值
	len = k;
 
	cv::hal::exp(W, W, len);						//邻域内所有采样点落在子区域的像素的高斯权值
	cv::hal::fastAtan2(Y, X, Angle, len, true);		//邻域内所有采样点落在子区域的像素的梯度方向,角度范围是0-360度
	cv::hal::magnitude(X, Y, Mag, len);				//邻域内所有采样点落在子区域的像素的梯度幅度
 
	//实际上是在 4 x 4 的网格中找 16 个种子点,每个种子点都在子网格的正中心,
	//通过三线性插值对不同种子点间的像素点进行加权作用到不同的种子点上绘制方向直方图
 
	//计算每个特征点的特征描述子
	for (k = 0; k < len; ++k)
	{
		float rbin = RBin[k], cbin = CBin[k];		//子区域内像素点坐标,rbin,cbin范围是(-1,d)
 
		改进的地方,对方向进行了一个细化,也是为了增加对梯度差异的鲁棒性
		//if (Angle[k] > 180 && Angle[k] < 360)
		//	Angle[k] = 360 - Angle[k];
 
		//子区域内像素点处理后的方向
		float temp = Angle[k] - main_angle;
 
		/*if (temp > 180 && temp < 360)
			temp = 360 - temp;*/
 
		float obin = temp * bins_per_rad;			//指定方向的数量后,邻域内像素点对应的方向
 
		float mag = Mag[k] * W[k];					//子区域内像素点乘以权值后的梯度幅值
 
		int r0 = cvFloor(rbin);						//ro取值集合是{-1,0,1,2,3},没太懂为什么?
		int c0 = cvFloor(cbin);						//c0取值集合是{-1,0,1,2,3}
		int o0 = cvFloor(obin);
 
		rbin = rbin - r0;							//子区域内像素点坐标的小数部分,用于线性插值,分配像素点的作用
		cbin = cbin - c0;
		obin = obin - o0;							//子区域方向的小数部分
 
		//限制范围为梯度直方图横坐标[0,n),8 个方向直方图
		if (o0 < 0)
			o0 = o0 + n;
		if (o0 >= n)
			o0 = o0 - n;
 
		//三线性插值用于计算落在两个子区域之间的像素对两个子区域的作用,并把其分配到对应子区域的8个方向上
		//像素对应的信息通过加权分配给其周围的种子点,并把相应方向的梯度值进行累加  
 
		float v_r1 = mag * rbin;					//第二行分配的值
		float v_r0 = mag - v_r1;					//第一行分配的值
 
 
		float v_rc11 = v_r1 * cbin;					//第二行第二列分配的值,右下角种子点
		float v_rc10 = v_r1 - v_rc11;				//第二行第一列分配的值,左下角种子点
 
		float v_rc01 = v_r0 * cbin;					//第一行第二列分配的值,右上角种子点
		float v_rc00 = v_r0 - v_rc01;				//第一行第一列分配的值,左上角种子点
 
 
		//一个像素点的方向为每个种子点的两个方向做出贡献
		float v_rco111 = v_rc11 * obin;				//右下角种子点第二个方向上分配的值
		float v_rco110 = v_rc11 - v_rco111;			//右下角种子点第一个方向上分配的值
 
		float v_rco101 = v_rc10 * obin;
		float v_rco100 = v_rc10 - v_rco101;
 
		float v_rco011 = v_rc01 * obin;
		float v_rco010 = v_rc01 - v_rco011;
 
		float v_rco001 = v_rc00 * obin;
		float v_rco000 = v_rc00 - v_rco001;
 
		//该像素所在网格的索引
		int idx = ((r0 + 1) * (d + 2) + c0 + 1) * (n + 2) + o0;
 
		hist[idx] += v_rco000;
		hist[idx + 1] += v_rco001;
		hist[idx + n + 2] += v_rco010;
		hist[idx + n + 3] += v_rco011;
		hist[idx + (d + 2) * (n + 2)] += v_rco100;
		hist[idx + (d + 2) * (n + 2) + 1] += v_rco101;
		hist[idx + (d + 3) * (n + 2)] += v_rco110;
		hist[idx + (d + 3) * (n + 2) + 1] += v_rco111;
	}
 
	//由于圆周循环的特性,对计算以后幅角小于 0 度或大于 360 度的值重新进行调整,使
	//其在 0~360 度之间
	for (int i = 0; i < d; ++i)
	{
		for (int j = 0; j < d; ++j)
		{
			//类似于 hist[0][2][3] 第 0 行,第 2 列,种子点直方图中的第 3 个 bin
			int idx = ((i + 1) * (d + 2) + (j + 1)) * (n + 2);
 
			hist[idx] += hist[idx + n];
			//hist[idx + 1] += hist[idx + n + 1];//opencv源码中这句话是多余的,hist[idx + n + 1]永远是0.0
 
			for (k = 0; k < n; ++k)
				descriptor[(i * d + j) * n + k] = hist[idx + k];
		}
	}
 
	//对描述子进行归一化
	int lenght = d * d * n;
	float norm = 0;
 
	//计算特征描述向量的模值的平方
	for (int i = 0; i < lenght; ++i)
	{
		norm = norm + descriptor[i] * descriptor[i];
	}
	norm = sqrt(norm);							//特征描述向量的模值
 
	//此次归一化能去除光照的影响
	for (int i = 0; i < lenght; ++i)
	{
		descriptor[i] = descriptor[i] / norm;
	}
 
	//阈值截断,去除特征描述向量中大于 0.2 的值,能消除非线性光照的影响(相机饱和度对某些放的梯度影响较大,对方向的影响较小)
	for (int i = 0; i < lenght; ++i)
	{
		descriptor[i] = min(descriptor[i], DESCR_MAG_THR);
	}
 
	//再次归一化,能够提高特征的独特性
	norm = 0;
	for (int i = 0; i < lenght; ++i)
	{
		norm = norm + descriptor[i] * descriptor[i];
	}
	norm = sqrt(norm);
	for (int i = 0; i < lenght; ++i)
	{
		descriptor[i] = descriptor[i] / norm;
	}
}
 
 
/*************************该函数计算每个特征点的特征描述子*****************************/
/*amplit表示特征点所在层的梯度幅度图像
  orient表示特征点所在层的梯度角度图像
  pt表示特征点的位置
  scale表示特征点所在层的尺度
  main_ori表示特征点的主方向,0-360度
  d表示GLOH角度方向区间个数,默认是8,
  n表示每个网格内角度在0-360度之间等分个数,n默认是8
 */
static void calc_gloh_descriptor(const Mat& amplit, const Mat& orient, Point2f pt, float scale, float main_ori, int d, int n, float* ptr_des)
{
	Point point(cvRound(pt.x), cvRound(pt.y));
 
	//特征点旋转方向余弦和正弦
	float cos_t = cosf(-main_ori / 180.f * (float)CV_PI);
	float sin_t = sinf(-main_ori / 180.f * (float)CV_PI);
 
	int num_rows = amplit.rows;
	int num_cols = amplit.cols;
	int radius = cvRound(SAR_SIFT_RADIUS_DES * scale);
	radius = min(radius, min(num_rows / 2, num_cols / 2));//特征点邻域半径
 
	int radius_x_left = point.x - radius;
	int radius_x_right = point.x + radius;
	int radius_y_up = point.y - radius;
	int radius_y_down = point.y + radius;
 
	//防止越界
	if (radius_x_left < 0)
		radius_x_left = 0;
	if (radius_x_right > num_cols - 1)
		radius_x_right = num_cols - 1;
	if (radius_y_up < 0)
		radius_y_up = 0;
	if (radius_y_down > num_rows - 1)
		radius_y_down = num_rows - 1;
 
	//此时特征点周围本矩形区域的中心,相对于该矩形
	int center_x = point.x - radius_x_left;
	int center_y = point.y - radius_y_up;
 
	//特征点周围区域内像素梯度幅度,梯度角度
	Mat sub_amplit = amplit(Range(radius_y_up, radius_y_down + 1), Range(radius_x_left, radius_x_right + 1));
	Mat sub_orient = orient(Range(radius_y_up, radius_y_down + 1), Range(radius_x_left, radius_x_right + 1));
 
 
	//以center_x和center_y位中心,对下面矩形区域进行旋转
	Range x_rng(-(point.x - radius_x_left), radius_x_right - point.x);
	Range y_rng(-(point.y - radius_y_up), radius_y_down - point.y);
	Mat X, Y;
	meshgrid(x_rng, y_rng, X, Y);
	Mat c_rot = X * cos_t - Y * sin_t;
	Mat r_rot = X * sin_t + Y * cos_t;
	Mat GLOH_angle, GLOH_amplit;
	phase(c_rot, r_rot, GLOH_angle, true);//角度在0-360度之间
	GLOH_amplit = c_rot.mul(c_rot) + r_rot.mul(r_rot);//为了加快速度,没有计算开方
 
	//三个圆半径平方
	float R1_pow = (float)radius * radius;//外圆半径平方
	float R2_pow = pow(radius * SAR_SIFT_GLOH_RATIO_R1_R2, 2.f);//中间圆半径平方
	float R3_pow = pow(radius * SAR_SIFT_GLOH_RATIO_R1_R3, 2.f);//内圆半径平方
 
	int sub_rows = sub_amplit.rows;
	int sub_cols = sub_amplit.cols;
 
	//开始构建描述子,在角度方向对描述子进行插值
	int len = (d * 2 + 1) * n;
	AutoBuffer hist(len);
	for (int i = 0; i < len; ++i)//清零
		hist[i] = 0;
 
	for (int i = 0; i < sub_rows; ++i)
	{
		float* ptr_amplit = sub_amplit.ptr(i);
		float* ptr_orient = sub_orient.ptr(i);
		float* ptr_GLOH_amp = GLOH_amplit.ptr(i);
		float* ptr_GLOH_ang = GLOH_angle.ptr(i);
		for (int j = 0; j < sub_cols; ++j)
		{
			if (((i - center_y) * (i - center_y) + (j - center_x) * (j - center_x)) < radius * radius)
			{
				float pix_amplit = ptr_amplit[j];//该像素的梯度幅度
				float pix_orient = ptr_orient[j];//该像素的梯度方向
				float pix_GLOH_amp = ptr_GLOH_amp[j];//该像素在GLOH网格中的半径位置
				float pix_GLOH_ang = ptr_GLOH_ang[j];//该像素在GLOH网格中的位置方向
 
				int rbin, cbin, obin;
				rbin = pix_GLOH_amp < R3_pow ? 0 : (pix_GLOH_amp > R2_pow ? 2 : 1);//rbin={0,1,2}
				cbin = cvRound(pix_GLOH_ang * d / 360.f);
				cbin = cbin > d ? cbin - d : (cbin <= 0 ? cbin + d : cbin);//cbin=[1,d]
 
				obin = cvRound(pix_orient * n / 360.f);
				obin = obin > n ? obin - n : (obin <= 0 ? obin + n : obin);//obin=[1,n]
 
				if (rbin == 0)//内圆
					hist[obin - 1] += pix_amplit;
				else
				{
					int idx = ((rbin - 1) * d + cbin - 1) * n + n + obin - 1;
					hist[idx] += pix_amplit;
				}
			}
		}
	}
 
	//对描述子进行归一化
	float norm = 0;
	for (int i = 0; i < len; ++i)
	{
		norm = norm + hist[i] * hist[i];
	}
	norm = sqrt(norm);
	for (int i = 0; i < len; ++i)
	{
		hist[i] = hist[i] / norm;
	}
 
	//阈值截断
	for (int i = 0; i < len; ++i)
	{
		hist[i] = min(hist[i], DESCR_MAG_THR);
	}
 
	//再次归一化
	norm = 0;
	for (int i = 0; i < len; ++i)
	{
		norm = norm + hist[i] * hist[i];
	}
	norm = sqrt(norm);
	for (int i = 0; i < len; ++i)
	{
		ptr_des[i] = hist[i] / norm;
	}
 
}
 
 
/******************************计算一个特征点的描述子—改进版***********************************/
static void improve_calc_sift_descriptor(const Mat& gauss_image, float main_angle, Point2f pt,
	float scale, int d, int n, float* descriptor)
{
	int n1 = 16, n2 = 6, n3 = 4;
 
	Point ptxy(cvRound(pt.x), cvRound(pt.y));					//坐标取整
 
	float cos_t = cosf(-main_angle * (float)(CV_PI / 180));		//计算主方向的余弦
	float sin_t = sinf(-main_angle * (float)(CV_PI / 180));		//计算主方向的正弦
 
	float bin_per_rad_1 = n1 / 360.f;							//n=8
	float bin_per_rad_2 = n2 / 360.f;							//原理特征点部分阈值
	float bin_per_rad_3 = n3 / 360.f;							//原理特征点部分阈值
 
	float exp_scale = -1.f / (d * d * 0.5f);					//权重指数部分
	float hist_width = DESCR_SCL_FCTR * scale;					//子区域边长,子区域的面积也即采样像素点个数
 
	int radius = cvRound(hist_width * (d + 1) * sqrt(2) * 0.5f);//特征点邻域半径(d+1)*(d+1)
 
	int rows = gauss_image.rows, cols = gauss_image.cols;
 
	//特征点邻域半径
	radius = min(radius, (int)sqrt((double)rows * rows + cols * cols));
 
	cos_t = cos_t / hist_width;
	sin_t = sin_t / hist_width;
 
	int len = (2 * radius + 1) * (2 * radius + 1);				//邻域内总像素数
	int histlen = (d + 2) * (d + 2) * (n1 + 2);
 
	AutoBuffer buf(6 * len + histlen);
 
	//X保存水平差分,Y保存竖直差分,Mag保存梯度幅度,Angle保存特征点方向, W保存高斯权重
	float* X = buf, * Y = buf + len, * Mag = Y, * Angle = Y + len, * W = Angle + len;
	float* X2 = buf, * Y2 = buf + len, * Mag2 = Y, * Angle2 = Y + len, * W2 = Angle + len;
	float* X3 = buf, * Y3 = buf + len, * Mag3 = Y, * Angle3 = Y + len, * W3 = Angle + len;
 
	float* RBin = W + len, * CBin = RBin + len, * hist = CBin + len;
	float* RBin2 = W + len, * CBin2 = RBin + len;
	float* RBin3 = W + len, * CBin3 = RBin + len;
 
	//首先清空直方图数据
	for (int i = 0; i < d + 2; ++i)
	{
		for (int j = 0; j < d + 2; ++j)
		{
			for (int k = 0; k < n + 2; ++k)
				hist[(i * (d + 2) + j) * (n + 2) + k] = 0.f;
		}
	}
 
	//把邻域内的像素分配到相应子区域内,计算子区域内每个像素点的权重(子区域即 d*d 中每一个小方格)
 
	int k1 = 0, k2 = 0, k3 = 0;
 
	vector v;									//存放外邻域像素点对应的序号
 
	for (int i = -radius; i < radius; ++i)
	{
		for (int j = -radius; j < radius; ++j)
		{
			float c_rot = j * cos_t - i * sin_t;	//旋转后邻域内采样点的 x 坐标
			float r_rot = j * sin_t + i * cos_t;	//旋转后邻域内采样点的 y 坐标
 
			float rbin = r_rot + d / 2 - 0.5f;		//旋转后的采样点落在子区域的 y 坐标
			float cbin = c_rot + d / 2 - 0.5f;		//旋转后的采样点落在子区域的 x 坐标
 
			int r = ptxy.y + i, c = ptxy.x + j;		//ptxy是高斯金字塔中的坐标
 
			//对离中心点近的部分进行操作
			if (abs(i) < (radius / 3) && abs(j) < (radius / 3))
			{
				//这里rbin,cbin范围是(-1,d)
				if (rbin > -1 && rbin < d && cbin>-1 && cbin < d &&
					r>0 && r < rows - 1 && c>0 && c < cols - 1)
				{
					float dx = gauss_image.at(r, c + 1) - gauss_image.at(r, c - 1);
					float dy = gauss_image.at(r + 1, c) - gauss_image.at(r - 1, c);
 
					X[k1] = dx;								//邻域内所有像素点的水平差分
					Y[k1] = dy;								//邻域内所有像素点的竖直差分
 
					RBin[k1] = rbin;						//邻域内所有采样点落在子区域的 y 坐标
					CBin[k1] = cbin;						//邻域内所有采样点落在子区域的 x 坐标
 
					//高斯权值的指数部分
					W[k1] = (c_rot * c_rot + r_rot * r_rot) * exp_scale;
 
					++k1;
				}
			}
			//对离中心点远的部分进行操作
			else if (abs(i) < (2 * radius / 3) && abs(i) > (radius / 3) && abs(j) < (2 * radius / 3) && abs(j) > (radius / 3))
			{
				//这里rbin,cbin范围是(-1,d)
				if (rbin > -1 && rbin < d && cbin>-1 && cbin < d &&
					r>0 && r < rows - 1 && c>0 && c < cols - 1)
				{
					float dx = gauss_image.at(r, c + 1) - gauss_image.at(r, c - 1);
					float dy = gauss_image.at(r + 1, c) - gauss_image.at(r - 1, c);
 
					X2[k2] = dx;								//邻域内所有像素点的水平差分
					Y2[k2] = dy;								//邻域内所有像素点的竖直差分
 
					RBin2[k2] = rbin;							//邻域内所有采样点落在子区域的 y 坐标
					CBin2[k2] = cbin;							//邻域内所有采样点落在子区域的 x 坐标
 
					//高斯权值的指数部分
					W2[k2] = (c_rot * c_rot + r_rot * r_rot) * exp_scale;
 
					++k2;
				}
			}
			else
			{
				//这里rbin,cbin范围是(-1,d)
				if (rbin > -1 && rbin < d && cbin>-1 && cbin < d &&
					r>0 && r < rows - 1 && c>0 && c < cols - 1)
				{
					float dx = gauss_image.at(r, c + 1) - gauss_image.at(r, c - 1);
					float dy = gauss_image.at(r + 1, c) - gauss_image.at(r - 1, c);
 
					X3[k3] = dx;								//邻域内所有像素点的水平差分
					Y3[k3] = dy;								//邻域内所有像素点的竖直差分
 
					RBin3[k3] = rbin;							//邻域内所有采样点落在子区域的 y 坐标
					CBin3[k3] = cbin;							//邻域内所有采样点落在子区域的 x 坐标
 
					//高斯权值的指数部分
					W3[k3] = (c_rot * c_rot + r_rot * r_rot) * exp_scale;
 
					++k3;
				}
			}
		}
	}
 
	//两个区域内数组的合并拼接
	for (int k = 0; k < k2; k++)
	{
		X[k1 + k] = X2[k];
		Y[k1 + k] = Y2[k];
 
		RBin[k1 + k] = RBin2[k];
		CBin[k1 + k] = CBin2[k];
 
		W[k1 + k] = W2[k];
 
	}
 
	for (int k = 0; k < k3; k++)
	{
		X[k1 + k2 + k] = X3[k];
		Y[k1 + k2 + k] = Y3[k];
 
		RBin[k1 + k2 + k] = RBin3[k];
		CBin[k1 + k2 + k] = CBin3[k];
 
		W[k1 + k2 + k] = W3[k];
 
	}
 
	//计算采样点落在子区域的像素梯度幅度,梯度角度,和高斯权值
	len = k1 + k2 + k3;
 
	cv::hal::exp(W, W, len);						//邻域内所有采样点落在子区域的像素的高斯权值
	cv::hal::fastAtan2(Y, X, Angle, len, true);		//邻域内所有采样点落在子区域的像素的梯度方向,角度范围是0-360度
	cv::hal::magnitude(X, Y, Mag, len);				//邻域内所有采样点落在子区域的像素的梯度幅度
 
	//计算每个特征点的特征描述子
	for (int k = 0; k < len; ++k)
	{
		float rbin = RBin[k], cbin = CBin[k];		//子区域内像素点坐标,rbin,cbin范围是(-1,d)
 
		//离特征点进的邻域
		if (k < k1)
		{
			//子区域内像素点处理后的方向
			float obin = (Angle[k] - main_angle) * bin_per_rad_1;
 
			float mag = Mag[k] * W[k];					//子区域内像素点乘以权值后的梯度幅值
 
			int r0 = cvFloor(rbin);						//ro取值集合是{-1,0,1,2,3},向下取整
			int c0 = cvFloor(cbin);						//c0取值集合是{-1,0,1,2,3}
			int o0 = cvFloor(obin);
 
			rbin = rbin - r0;							//子区域内像素点坐标的小数部分,用于线性插值
			cbin = cbin - c0;
			obin = obin - o0;
 
			//限制范围为梯度直方图横坐标[0,n)
			if (o0 < 0)
				o0 = o0 + n1;
			if (o0 >= n1)
				o0 = o0 - n1;
 
			//三线性插值用于计算落在两个子区域之间的像素对两个子区域的作用,并把其分配到对应子区域的8个方向上
			//使用三线性插值(即三维)方法,计算直方图
			float v_r1 = mag * rbin;					//第二行分配的值
			float v_r0 = mag - v_r1;					//第一行分配的值
 
 
			float v_rc11 = v_r1 * cbin;					//第二行第二列分配的值
			float v_rc10 = v_r1 - v_rc11;				//第二行第一列分配的值
 
			float v_rc01 = v_r0 * cbin;					//第一行第二列分配的值
			float v_rc00 = v_r0 - v_rc01;
 
 
			float v_rco111 = v_rc11 * obin;				//第二行第二列第二个方向上分配的值,每个采样点去邻近两个方向
			float v_rco110 = v_rc11 - v_rco111;			//第二行第二列第一个方向上分配的值
 
			float v_rco101 = v_rc10 * obin;
			float v_rco100 = v_rc10 - v_rco101;
 
			float v_rco011 = v_rc01 * obin;
			float v_rco010 = v_rc01 - v_rco011;
 
			float v_rco001 = v_rc00 * obin;
			float v_rco000 = v_rc00 - v_rco001;
 
			//该像素所在网格的索引
			int idx = ((r0 + 1) * (d + 2) + c0 + 1) * (n1 + 2) + o0;
 
			hist[idx] += v_rco000;
			hist[idx + 1] += v_rco001;
			hist[idx + n1 + 2] += v_rco010;
			hist[idx + n1 + 3] += v_rco011;
			hist[idx + (d + 2) * (n1 + 2)] += v_rco100;
			hist[idx + (d + 2) * (n1 + 2) + 1] += v_rco101;
			hist[idx + (d + 3) * (n1 + 2)] += v_rco110;
			hist[idx + (d + 3) * (n1 + 2) + 1] += v_rco111;
		}
 
		//离特征点远的邻域
		else if (k >= k1 && k < k2)
		{
			//子区域内像素点处理后的方向
			float obin = (Angle[k] - main_angle) * bin_per_rad_2;
 
			float mag = Mag[k] * W[k];					//子区域内像素点乘以权值后的梯度幅值
 
			int r0 = cvFloor(rbin);						//ro取值集合是{-1,0,1,2,3},向下取整
			int c0 = cvFloor(cbin);						//c0取值集合是{-1,0,1,2,3}
			int o0 = cvFloor(obin);
 
			rbin = rbin - r0;							//子区域内像素点坐标的小数部分,用于线性插值
			cbin = cbin - c0;
			obin = obin - o0;
 
			//限制范围为梯度直方图横坐标[0,n)
			if (o0 < 0)
				o0 = o0 + n2;
			if (o0 >= n1)
				o0 = o0 - n2;
 
			//三线性插值用于计算落在两个子区域之间的像素对两个子区域的作用,并把其分配到对应子区域的8个方向上
			//使用三线性插值(即三维)方法,计算直方图
			float v_r1 = mag * rbin;					//第二行分配的值
			float v_r0 = mag - v_r1;					//第一行分配的值
 
 
			float v_rc11 = v_r1 * cbin;					//第二行第二列分配的值
			float v_rc10 = v_r1 - v_rc11;				//第二行第一列分配的值
 
			float v_rc01 = v_r0 * cbin;					//第一行第二列分配的值
			float v_rc00 = v_r0 - v_rc01;
 
 
			float v_rco111 = v_rc11 * obin;				//第二行第二列第二个方向上分配的值,每个采样点去邻近两个方向
			float v_rco110 = v_rc11 - v_rco111;			//第二行第二列第一个方向上分配的值
 
			float v_rco101 = v_rc10 * obin;
			float v_rco100 = v_rc10 - v_rco101;
 
			float v_rco011 = v_rc01 * obin;
			float v_rco010 = v_rc01 - v_rco011;
 
			float v_rco001 = v_rc00 * obin;
			float v_rco000 = v_rc00 - v_rco001;
 
			//该像素所在网格的索引
			int idx = ((r0 + 1) * (d + 2) + c0 + 1) * (n2 + 2) + o0;
 
			hist[idx] += v_rco000;
			hist[idx + 1] += v_rco001;
			hist[idx + n2 + 2] += v_rco010;
			hist[idx + n2 + 3] += v_rco011;
			hist[idx + (d + 2) * (n2 + 2)] += v_rco100;
			hist[idx + (d + 2) * (n2 + 2) + 1] += v_rco101;
			hist[idx + (d + 3) * (n2 + 2)] += v_rco110;
			hist[idx + (d + 3) * (n2 + 2) + 1] += v_rco111;
		}
		else
		{
			//子区域内像素点处理后的方向
			float obin = (Angle[k] - main_angle) * bin_per_rad_3;
 
			float mag = Mag[k] * W[k];					//子区域内像素点乘以权值后的梯度幅值
 
			int r0 = cvFloor(rbin);						//ro取值集合是{-1,0,1,2,3},向下取整
			int c0 = cvFloor(cbin);						//c0取值集合是{-1,0,1,2,3}
			int o0 = cvFloor(obin);
 
			rbin = rbin - r0;							//子区域内像素点坐标的小数部分,用于线性插值
			cbin = cbin - c0;
			obin = obin - o0;
 
			//限制范围为梯度直方图横坐标[0,n)
			if (o0 < 0)
				o0 = o0 + n3;
			if (o0 >= n1)
				o0 = o0 - n3;
 
			//三线性插值用于计算落在两个子区域之间的像素对两个子区域的作用,并把其分配到对应子区域的8个方向上
			//使用三线性插值(即三维)方法,计算直方图
			float v_r1 = mag * rbin;					//第二行分配的值
			float v_r0 = mag - v_r1;					//第一行分配的值
 
 
			float v_rc11 = v_r1 * cbin;					//第二行第二列分配的值
			float v_rc10 = v_r1 - v_rc11;				//第二行第一列分配的值
 
			float v_rc01 = v_r0 * cbin;					//第一行第二列分配的值
			float v_rc00 = v_r0 - v_rc01;
 
 
			float v_rco111 = v_rc11 * obin;				//第二行第二列第二个方向上分配的值,每个采样点去邻近两个方向
			float v_rco110 = v_rc11 - v_rco111;			//第二行第二列第一个方向上分配的值
 
			float v_rco101 = v_rc10 * obin;
			float v_rco100 = v_rc10 - v_rco101;
 
			float v_rco011 = v_rc01 * obin;
			float v_rco010 = v_rc01 - v_rco011;
 
			float v_rco001 = v_rc00 * obin;
			float v_rco000 = v_rc00 - v_rco001;
 
			//该像素所在网格的索引
			int idx = ((r0 + 1) * (d + 2) + c0 + 1) * (n3 + 2) + o0;
 
			hist[idx] += v_rco000;
			hist[idx + 1] += v_rco001;
			hist[idx + n3 + 2] += v_rco010;
			hist[idx + n3 + 3] += v_rco011;
			hist[idx + (d + 2) * (n3 + 2)] += v_rco100;
			hist[idx + (d + 2) * (n3 + 2) + 1] += v_rco101;
			hist[idx + (d + 3) * (n3 + 2)] += v_rco110;
			hist[idx + (d + 3) * (n3 + 2) + 1] += v_rco111;
		}
	}
 
	//由于圆周循环的特性,对计算以后幅角小于 0 度或大于 360 度的值重新进行调整,使
	//其在 0~360 度之间
	for (int i = 0; i < d; ++i)
	{
		for (int j = 0; j < d; ++j)
		{
			int idx = ((i + 1) * (d + 2) + (j + 1)) * (n + 2);
			hist[idx] += hist[idx + n];
			//hist[idx + 1] += hist[idx + n + 1];//opencv源码中这句话是多余的,hist[idx + n + 1]永远是0.0
			for (int k = 0; k < n; ++k)
				descriptor[(i * d + j) * n + k] = hist[idx + k];
		}
	}
 
	//对描述子进行归一化
	int lenght = d * d * n;
	float norm = 0;
 
	//计算特征描述向量的模值的平方
	for (int i = 0; i < lenght; ++i)
	{
		norm = norm + descriptor[i] * descriptor[i];
	}
	norm = sqrt(norm);							//特征描述向量的模值
 
	//此次归一化能去除光照的影响
	for (int i = 0; i < lenght; ++i)
	{
		descriptor[i] = descriptor[i] / norm;
	}
 
	//阈值截断,去除特征描述向量中大于 0.2 的值,能消除非线性光照的影响(相机饱和度对某些放的梯度影响较大,对方向的影响较小)
	for (int i = 0; i < lenght; ++i)
	{
		descriptor[i] = min(descriptor[i], DESCR_MAG_THR);
	}
 
	//再次归一化,能够提高特征的独特性
	norm = 0;
 
	for (int i = 0; i < lenght; ++i)
	{
		norm = norm + descriptor[i] * descriptor[i];
	}
	norm = sqrt(norm);
	for (int i = 0; i < lenght; ++i)
	{
		descriptor[i] = descriptor[i] / norm;
	}
}
 
 
/********************************该函数计算所有特征点特征描述子***************************/
/*gauss_pyr表示高斯金字塔
  keypoints表示特征点、
  descriptors表示生成的特征点的描述子*/
void mySift::calc_sift_descriptors(const vector>& gauss_pyr, vector& keypoints,
	Mat& descriptors, const vector& amplit, const vector& orient)
{
	int d = DESCR_WIDTH;										//d=4,特征点邻域网格个数是d x d
	int n = DESCR_HIST_BINS;									//n=8,每个网格特征点梯度角度等分为8个方向
 
	descriptors.create(keypoints.size(), d * d * n, CV_32FC1);	//分配空间
 
	for (size_t i = 0; i < keypoints.size(); ++i)				//对于每一个特征点
	{
		int octaves, layer;
 
		//得到特征点所在的组号,层号
		octaves = keypoints[i].octave & 255;
		layer = (keypoints[i].octave >> 8) & 255;
 
		//得到特征点相对于本组的坐标,不是最底层
		Point2f pt(keypoints[i].pt.x / (1 << octaves), keypoints[i].pt.y / (1 << octaves));
 
		float scale = keypoints[i].size / (1 << octaves);		//得到特征点相对于本组的尺度
		float main_angle = keypoints[i].angle;					//特征点主方向
 
		//计算该点的描述子
		calc_sift_descriptor(gauss_pyr[octaves][layer], main_angle, pt, scale, d, n, descriptors.ptr((int)i));
		//improve_calc_sift_descriptor(gauss_pyr[octaves][layer], main_angle, pt, scale, d, n, descriptors.ptr((int)i));
		//calc_gloh_descriptor(amplit[octaves], orient[octaves], pt, scale, main_angle, d, n, descriptors.ptr((int)i));
 
		if (double_size)//如果图像尺寸扩大一倍
		{
			keypoints[i].pt.x = keypoints[i].pt.x / 2.f;
			keypoints[i].pt.y = keypoints[i].pt.y / 2.f;
		}
	}
 
}
 
 
/*************该函数构建SAR_SIFT尺度空间*****************/
/*image表示输入的原始图像
 sar_harris_fun表示尺度空间的Sar_harris函数
 amplit表示尺度空间像素的梯度幅度
 orient表示尺度空间像素的梯度方向
 */
void mySift::build_sar_sift_space(const Mat& image, vector& sar_harris_fun, vector& amplit, vector& orient)
{
	//转换输入图像格式
	Mat gray_image;
	if (image.channels() != 1)
		cvtColor(image, gray_image, CV_RGB2GRAY);
	else
		gray_image = image;
 
	//把图像转换为0-1之间的浮点数据
	Mat float_image;
 
	double ratio = pow(2, 1.0 / 3.0);							//相邻两层的尺度比,默认是2^(1/3)
 
	//在这里转换为0-1之间的浮点数据和转换为0-255之间的浮点数据,效果是一样的
	//gray_image.convertTo(float_image, CV_32FC1, 1.f / 255.f, 0.f);//转换为0-1之间
 
	gray_image.convertTo(float_image, CV_32FC1, 1, 0.f);		//转换为0-255之间的浮点数
 
	//分配内存
	sar_harris_fun.resize(Mmax);
	amplit.resize(Mmax);
	orient.resize(Mmax);
 
	for (int i = 0; i < Mmax; ++i)
	{
		float scale = (float)sigma * (float)pow(ratio, i);		//获得当前层的尺度
		int radius = cvRound(2 * scale);
		Mat kernel;
 
		roewa_kernel(radius, scale, kernel);
 
		//四个滤波模板生成
		Mat W34 = Mat::zeros(2 * radius + 1, 2 * radius + 1, CV_32FC1);
		Mat W12 = Mat::zeros(2 * radius + 1, 2 * radius + 1, CV_32FC1);
		Mat W14 = Mat::zeros(2 * radius + 1, 2 * radius + 1, CV_32FC1);
		Mat W23 = Mat::zeros(2 * radius + 1, 2 * radius + 1, CV_32FC1);
 
		kernel(Range(radius + 1, 2 * radius + 1), Range::all()).copyTo(W34(Range(radius + 1, 2 * radius + 1), Range::all()));
		kernel(Range(0, radius), Range::all()).copyTo(W12(Range(0, radius), Range::all()));
		kernel(Range::all(), Range(radius + 1, 2 * radius + 1)).copyTo(W14(Range::all(), Range(radius + 1, 2 * radius + 1)));
		kernel(Range::all(), Range(0, radius)).copyTo(W23(Range::all(), Range(0, radius)));
 
		//滤波
		Mat M34, M12, M14, M23;
		double eps = 0.00001;
		filter2D(float_image, M34, CV_32FC1, W34, Point(-1, -1), eps);
		filter2D(float_image, M12, CV_32FC1, W12, Point(-1, -1), eps);
		filter2D(float_image, M14, CV_32FC1, W14, Point(-1, -1), eps);
		filter2D(float_image, M23, CV_32FC1, W23, Point(-1, -1), eps);
 
		//计算水平梯度和竖直梯度
		Mat Gx, Gy;
		log((M14) / (M23), Gx);
		log((M34) / (M12), Gy);
 
		//计算梯度幅度和梯度方向
		magnitude(Gx, Gy, amplit[i]);
		phase(Gx, Gy, orient[i], true);
 
		//构建sar-Harris矩阵
		//Mat Csh_11 = log(scale)*log(scale)*Gx.mul(Gx);
		//Mat Csh_12 = log(scale)*log(scale)*Gx.mul(Gy);
		//Mat Csh_22 = log(scale)*log(scale)*Gy.mul(Gy);
 
		Mat Csh_11 = scale * scale * Gx.mul(Gx);
		Mat Csh_12 = scale * scale * Gx.mul(Gy);
		Mat Csh_22 = scale * scale * Gy.mul(Gy);//此时阈值为0.8
 
		//Mat Csh_11 = Gx.mul(Gx);
		//Mat Csh_12 = Gx.mul(Gy);
		//Mat Csh_22 = Gy.mul(Gy);//此时阈值为0.8/100
 
		//高斯权重
		float gauss_sigma = sqrt(2.f) * scale;
		int size = cvRound(3 * gauss_sigma);
 
		Size kern_size(2 * size + 1, 2 * size + 1);
		GaussianBlur(Csh_11, Csh_11, kern_size, gauss_sigma, gauss_sigma);
		GaussianBlur(Csh_12, Csh_12, kern_size, gauss_sigma, gauss_sigma);
		GaussianBlur(Csh_22, Csh_22, kern_size, gauss_sigma, gauss_sigma);
 
		/*Mat gauss_kernel;//自定义圆形高斯核
		gauss_circle(size, gauss_sigma, gauss_kernel);
		filter2D(Csh_11, Csh_11, CV_32FC1, gauss_kernel);
		filter2D(Csh_12, Csh_12, CV_32FC1, gauss_kernel);
		filter2D(Csh_22, Csh_22, CV_32FC1, gauss_kernel);*/
 
		Mat Csh_21 = Csh_12;
 
		//构建sar_harris函数
		Mat temp_add = Csh_11 + Csh_22;
 
		double d = 0.04;					//sar_haiirs函数表达式中的任意参数,默认是0.04
 
		sar_harris_fun[i] = Csh_11.mul(Csh_22) - Csh_21.mul(Csh_12) - (float)d * temp_add.mul(temp_add);
	}
}
 
 
/***************该函数计算所有特征点的特征向量*************/
/*amplit表示尺度空间像素幅度
 orient表示尺度空间像素梯度角度
 keys表示检测到的特征点
 descriptors表示特征点描述子向量,【M x N】,M表示描述子个数,N表示描述子维度
 */
void mySift::calc_gloh_descriptors(const vector& amplit, const vector& orient, const vector& keys, Mat& descriptors)
{
	int d = SAR_SIFT_GLOH_ANG_GRID;							//d=4或者d=8
	int n = SAR_SIFT_DES_ANG_BINS;							//n=8默认
 
	int num_keys = (int)keys.size();
	int grids = 2 * d + 1;
 
	//descriptors.create(num_keys, grids * n, CV_32FC1);
	descriptors.create(num_keys, grids * n, CV_32FC1);
 
	for (int i = 0; i < num_keys; ++i)
	{
		int octaves = keys[i].octave & 255;					//特征点所在层
 
		float* ptr_des = descriptors.ptr(i);
		float scale = keys[i].size / (1 << octaves);		//得到特征点相对于本组的尺度;							//特征点所在层的尺度
		float main_ori = keys[i].angle;						//特征点主方向
 
		//得到特征点相对于本组的坐标,不是最底层
		Point2f point(keys[i].pt.x / (1 << octaves), keys[i].pt.y / (1 << octaves));
 
		cout << "layer=" << octaves << endl;
		cout << "scale=" << scale << endl;
 
		//计算该特征点的特征描述子
		calc_gloh_descriptor(amplit[octaves], orient[octaves], point, scale, main_ori, d, n, ptr_des);
	}
}
 
 
//特征点检测和特征点描述把整个 SIFT 算子都涵盖在内了//
 
/******************************特征点检测*********************************/
/*image表示输入的图像
  gauss_pyr表示生成的高斯金字塔
  dog_pyr表示生成的高斯差分DOG金字塔
  keypoints表示检测到的特征点
  vector& cell_contrast 用于存放一个单元格中所有特征点的对比度
  vector& cell_contrasts用于存放一个尺度层中所有单元格中特征点的对比度
  vector>>& all_cell_contrasts用于存放所有尺度层中所有单元格的对比度
  vector>& average_contrast用于存放所有尺度层中多有单元格的平均对比度*/
void mySift::detect(const Mat& image, vector>& gauss_pyr, vector>& dog_pyr, vector& keypoints,
	vector>>& all_cell_contrasts,
	vector>& average_contrast, vector>& n_cells, vector& num_cell, vector>& available_n,
	vector& available_num, vector& final_keypoints,
	vector& Final_keypoints, vector& Final_Keypoints)
{
	if (image.empty() || image.depth() != CV_8U)
		CV_Error(CV_StsBadArg, "输入图像为空,或者图像深度不是CV_8U");
 
 
	//计算高斯金字塔组数
	int nOctaves;
	nOctaves = num_octaves(image);
 
	//生成高斯金字塔第一层图像
	Mat init_gauss;
	create_initial_image(image, init_gauss);
 
	//生成高斯尺度空间图像
	build_gaussian_pyramid(init_gauss, gauss_pyr, nOctaves);
 
	//生成高斯差分金字塔(DOG金字塔,or LOG金字塔)
	build_dog_pyramid(dog_pyr, gauss_pyr);
 
	//在DOG金字塔上检测特征点
	find_scale_space_extrema1(dog_pyr, gauss_pyr, keypoints);			//原始 SIFT 算子
																		// UR_SIFT,仅仅是检测特征点,并未对不理想的点进行筛选
	/*UR_SIFT_find_scale_space_extrema(dog_pyr, gauss_pyr, keypoints, all_cell_contrasts,
	average_contrast, n_cells, num_cell, available_n, available_num, final_keypoints, Final_keypoints, Final_Keypoints, N);*/
 
	//如果指定了特征点个数,并且设定的设置小于默认检测的特征点个数
	if (nfeatures != 0 && nfeatures < (int)keypoints.size())
	{
		//特征点响应值(即对比度,对比度越小越不稳定)从大到小排序
		sort(keypoints.begin(), keypoints.end(), [](const KeyPoint& a, const KeyPoint& b) {return a.response > b.response; });
 
		//删除点多余的特征点
		keypoints.erase(keypoints.begin() + nfeatures, keypoints.end());
	}
}
 
 
/**********************特征点描述*******************/
/*gauss_pyr表示高斯金字塔
 keypoints表示特征点集合
 descriptors表示特征点的描述子*/
void mySift::comput_des(const vector>& gauss_pyr, vector& final_keypoints, const vector& amplit,
	const vector& orient, Mat& descriptors)
{
	calc_sift_descriptors(gauss_pyr, final_keypoints, descriptors, amplit, orient);
}
 
 

实验效果图:

计算机视觉--局部图像描述子:Harris角点检测算法、SIFT(尺度不变特征变换)_第16张图片

 计算机视觉--局部图像描述子:Harris角点检测算法、SIFT(尺度不变特征变换)_第17张图片​​​​​​​

 

你可能感兴趣的:(计算机视觉)