# -*- coding: utf-8 -*-
"""
Created on Sun May  5 21:36:51 2019

@author: dell
"""

#目标：
#学习SIFT算法的概念
#学习在图像中查找SIFT关键点和描述符
#
#原理：
#在前面两节的学习我们学习了一些角点检测技术，比如HARRIS等，他们具有旋转不变特性，即使图片
#发生选择，还是可以找到角点。如果对图像进行缩放呢，以下图为例，在一幅小图中使用一个小的窗口可以检测
#到一个角点，但当图像放大后，再使用同样的窗口，就检测不到角点了
#
#所以在2004年，D.Love提出了一个新的算法：尺度不变特征变换（sift），这个算法可以帮我们提取图像中
#的关键点并计算它们的描述符
#
#SIFT由四步组成：
#
#1.尺度空间的极值检测
#从上图我们可以明显看出来在不同的尺度空间，不能使用相同的窗口检测极值点。对小的角点要用小的窗口，
#对大的角点要用大的窗口，为了达到这个目的，我们要使用尺度空间滤波器，（尺度空间滤波器可以使用一些
#具有不同方差σ的高斯卷积核构成）。使用具有不同方差值σ的高斯拉普拉斯算子（LoG)对图像进行卷积，LoG由于
#具有不同的方差值σ，所以可以用来检测不同大小的斑点（当LoG的方差σ与斑点直径相等时，能够使斑点完全平滑）。
#简单来说，方差σ就是一个尺度变换因子。例如，上图使用一个小的方差σ的高斯卷积核可以很好的检测出小的角点，而
#使用大的方差σ高斯卷积核，可以很好的检测出大的角点。所以我们在尺度空间和二位平面中，检测到局部最大值，如（x,y,σ)
#,这表示在σ尺度中（x,y)可能是一个关键点。（？？？高斯方差的大小与窗口大小存在一个倍数关系：窗口大小等于六倍方差加一，
#所以方差大小也决定了窗口大小）
#但是这个LoG的计算量非常大，所以SIFT算法使用高斯差分算子（DoG）来对LoG做近似。这里需要再解释一下图像金字塔，我们可以
#通过减少采样(如只取奇数行或奇数列)来构成一组图像尺寸（1，0.5，0.25等）不同的金字塔--降采样。然后对 一“组”图像中每一“层”
#/每一张图像使用不用方差的σ构建出不同分辨率的金字塔（不同尺度空间）。DoG就是这组具有不同分辨率的图像金字塔中相邻的两层
#之间的差值。
#在DoG搞定后，就可以在不同尺度空间和2D平面中搜索局部最大值，对于图像的一个像素点而言，它需要与自己周围的8邻域，以及尺度空间中
#上下两层中相邻的18（2x9）个点相比，如果是局部最大值，它就可能是一个关键点，基本上来说，关键点是图像在相应尺度空间中的最好代表
#...........
#


import cv2
import numpy as np

img=cv2.imread("s3.jpg")
gray=cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)

sift=cv2.xfeatures2d.SIFT_create()
kp=sift.detect(gray,None)#在图像中找到关键点，如果只想在图像中的一个区域搜索的话，也可以创建一个
#掩码图像作为参数使用，返回的关键点kp是一个带有很多不同属性的特殊结构体，这些属性中包含它的坐标（x，y）
#有有意义的邻域大小，确定其方向的角度
kp,des=sift.compute(gray,kp)  #sift.jpg des是136x128 说明有136个关键点
img=cv2.drawKeypoints(img,kp,img,flags=cv2.DRAW_MATCHES_FLAGS_DRAW_RICH_KEYPOINTS)#可以在关键点部位绘制一个小圆圈，如果设置参数flags，就会绘制代表关键点的圆圈设置关键点的方向
cv2.imshow('sift',img)
#points2f = cv2.KeyPoint_convert(kp) 
#for key in points2f:
#    print(key)

#现在来计算关键点的描述符，opencv提供了两种方法
#1.由于我们已经找到了关键点，我们可以使用函数sift.compute()来计算这些关键点的描述符，例如：  kp,des=sift.compute(gray,kp)
#2.如果还没找到关键点，可以使用函数sift.detectAndCompute()一步到位直接找到关键点并计算出其描述符

