代码已经开源到github上,https://github.com/alibaba/simpleimage项目,其中的 analyze模块中。

原始图片为:

主要调用方法:

[java] view plaincopy
  1. BufferedImage img = ImageIO.read(logoFile);
  2. RenderImage ri = new RenderImage(img);
  3. SIFT sift = new SIFT();
  4. sift.detectFeatures(ri.toPixelFloatArray(null));
  5. List<KDFeaturePoint> al = sift.getGlobalKDFeaturePoints();

同样可以再读另一个张图得到另一个 List<KDFeaturePoint> al1,然后两个List进行match

[java] view plaincopy
  1. List<Match> ms = MatchKeys.findMatchesBBF(al, al1);
  2. ms = MatchKeys.filterMore(ms);

先从上面的调用入口,详细讲解图sift的特征点生,至于match,有空再说。其中最难理解的是极值点的查找,主要对这部分讲解。

一.构建尺度空间,检测极值点,获得尺度不变性

ImagePixelArray就是保存一张图片的象素点进行灰度化后的数组。在RenderImage的toPixelFloatArray方法中默认灰度处理只是简单将rgb的值换成r,g,b的平均值并做归一化处理(除以255);

从调用入口我们进入主类SIFT的用户接口detectFeatures方法,它实际调用了detectFeaturesDownscaled(ImageMap img, int bothDimHi, double startScale) ,我们看随了开始的几行init工作,最后对图片使用preprocSigma(1.5)的参数进行高斯模数预处理。

[java] view plaincopy
  1. if (preprocSigma > 0.0) {
  2. GaussianArray gaussianPre = new GaussianArray(preprocSigma);
  3. img = gaussianPre.convolve(img);
  4. }

进行高斯模糊的目的是为了使大片的灰度相近的点连成一片,而使一些比较突出的点更加区别于其它点,就象我们把一张灰度图片使用“版画”效果会把大量的连片点去掉只留下轮廓。

经过上面的预处理效果为:

现在我们还看不出强烈的效果,仅是pre的处理了一下。再接着使用Pyramid的buildOctaves方法构建8度金字塔,我们跟踪到方法内。

[java] view plaincopy
  1. public int buildOctaves(ImagePixelArray source, float scale, int levelsPerOctave, float octaveSigm, int minSize) {
  2. this.octaves = new ArrayList<OctaveSpace>();
  3. OctaveSpace downSpace = null;
  4. ImagePixelArray prev = source;
  5. while (prev != null && prev.width >= minSize && prev.height >= minSize) {
  6. OctaveSpace osp = new OctaveSpace();
  7. // Create both the gaussian filtered images and the DOG maps
  8. osp.makeGaussianImgs(prev, scale, levelsPerOctave, octaveSigm); //构建当前8度空间的高斯模糊图像
  9. osp.makeGaussianDiffImgs();
  10. octaves.add(osp);
  11. prev = osp.getLastGaussianImg().halved(); //下一个8度空间的原始图象
  12. if (downSpace != null) downSpace.up = osp;
  13. osp.down = downSpace;
  14. downSpace = osp;
  15. scale *= 2.0;
  16. }
  17. return (octaves.size());
  18. }

先不看

osp.makeGaussianImgs(prev, scale, levelsPerOctave, octaveSigm);
osp.makeGaussianDiffImgs();

整个方法就是以原图为基础不断地构造图层,这里也是高斯金字塔最微妙的地方。尺度空间概念不是简单的不同大小尺寸组成的尖塔,也不是相同大小不同模糊因子处理过的直方塔,其实说是金字塔不太准确,更象中国传统的宝塔。

它首先由原始图象根据不同的模糊因子进行模糊(说成是平滑更确切,就是把比较相近的颜色值让他们更相近以便突出反差很强的点),这是在同一尺寸上做的。这些相同尺寸不同高斯模糊因子处理过的图像集合叫一个8度空间。相当于宝塔中的一层,然后再向下采样,即以其中一幅进行1/2缩小作为原图再进行另一个8度空间的高斯模糊处理,直到图层的width或hight小于minSize(32),这样不断模糊并向下采样的构成的所有8度空间的集合才叫高斯金字塔(高斯宝塔?)。这是为了能检测到原图的某一点在不同尺度上都有稳定的特征。

然后我们回头来看osp.makeGaussianImgs方法。对1个8度空间原始图象,以不同的 sigma 参数构建多张高斯模糊图。因为最底层的原图最大,我选了塔中scale为2的那一个8度间空的smoothedImgs.

可以看出随着模糊因子变化模糊程度在加大。在塔中的每一个8度空间得到了一个smoothedImgs 数组。这里一共生成6幅图像,原因在方法内部有注释,我们最终要在3层的差分尺度中获取极值点,而每个尺度获取极值点都要在立体空间(这也是sift最区别于其它特征的革命性突破)上比较,即要比较它上一幅和下一幅的对应点,那么3层的尺度至少要五幅差分图像,而五幅差分图像至少要6幅高斯模糊图象才能生成。

然后对smoothedImgs中的图象通过osp.makeGaussianDiffImgs();依次求差放入diffImags数组中。

对于使用不同参数进行模糊的两张图片,象素相近的连片部分差值极小,只有边缘,转角等特征的点表现出较大的差值:

先不要在意连片的黑色,因为求差后的值很小并已经做了归一化,我把它还原到图片上时进行绝对值(可能为负)乘10然后模 255,以便清楚地显示出来。可以看出这些图片中特征强烈的点都是边缘,转角等地方。

二.特征点过滤并进行精确定位

现在回到detectFeaturesDownscaled,经过是面的处理,金字塔中每个8度空间上都有一个OctaveSpace对象保存着一个差分图象数组。下面的findPeaks其实就是从第2个图象开始到倒数第二个图象循环,当前图象上每一个点和周围的点比较,如果是最大值或是最小值就视为极值点。(这里的周围是立体的周围,不仅和当前图像上点周围的8个点比较,还要和他上一幅和下一幅对应的9个点比较)

[java] view plaincopy
  1. checkMinMax(current, c, x, y, ref, true);
  2. checkMinMax(below, c, x, y, ref, false);
  3. checkMinMax(above, c, x, y, ref, false);
  4. if (ref.isMin == false && ref.isMax == false) continue;
  5. peaks.add(new ScalePoint(x, y, curLev));

回到detectFeaturesDownscaled,下面的filterAndLocalizePeaks方法主要是根据原始论文的page12/13的方法进行过虑和精确定位。

isTooEdgelike是对太象边缘的点进行过虑,这个意思就是连续的线不是好的极值点,只有角点这样的孤立的点才是极值点,为什么“太象边缘”的连续的线不好呢?边缘点的特点是沿边缘方向的梯度很小,简单说一条线的点差别不大,而和它相切的方向梯度很大。由于梯度大它们很容易成为极值点,但因为沿线的两个点之间梯度又几乎没有区别,极值点本身是局部特征,所以边缘线上两个点对于某极值进行投射时根本无法区别是点1还是点2.所以要把这些“线性连续点去掉”。

在过虑掉“太象边缘”的点后,下面的localizeIsWeak就精确化每个尺度空间上的极值点,因为极值点是在连续的尺度空间中计算出来的非常精确化的坐标比如(0.12345678,0.23456789),而原始图象的点是以整数为坐标的,相对尺度空间的坐标而言是散列的。而极值点最终要映射到原始图像的整数坐标上,所以要有一个调整过程。

根据极值点的坐标和sigma参数,主要是三元一,二价导数和亚象素计算。这样精确匹配到原始点后会得到一个原始点的坐标,sigma参数和调整值 local.dValue,下面的过虑条件就简单了:

if (Math.abs(peak.local.scaleAdjust) > scaleAdjustThresh) continue;

if (Math.abs(peak.local.dValue) <= dValueLoThresh) continue;

简单说当匹配到原始点在某一范围之外的都过虑掉,注意原始论文上建议dValueLoThresh为0.03,这里实际是0.008。

三.为每个关键点指定方向参数

查找极值点的工作都完成了,下面就是对这些点和周转的点比较生成一些向量:

在生成关键的方向和梯度时,我们用一个pretreatMagnitudeAndDirectionImgs方法把差分图上所有点的梯度方向和梯度值先计算出来,因为特征点的方向最终是它周围的64个点的梯度方向梯值加权计算出来的,这样每一点可能被多个特征点作为“周围点”,如果当作为周围点才计算某点的梯度方向梯值加,很多点会被多次计算,这样计算的总次数会大于所有点计算一次。所以我们会把每个点先计算出来存在一个数组中。

详见 makeFeaturePoint的注释

四.生成关键点的描述子

createDescriptors中有详细注释。

from: http://blog.csdn.net/axman/article/details/9243681

sift的java实现解述相关推荐

  1. java8编程开发入门 李兴华_李兴华系列--JAVA详解视频(jdk1.8)及项目实战教程

    李兴华系列--JAVA详解视频教程(jdk1.8)有源码+文档01_<Oracle从入门到精通> 02_<Java8编程入门> 03_<Java8面向对象编程> 0 ...

  2. Java详解剑指offer面试题50--第一个只出现一次的字符

    Java详解剑指offer面试题50–第一个只出现一次的字符 找出字符串中找出第一个只出现一次的字符,比如输入"abacceff",则输出'b' 要想知道某个字符是不是只出现了一次 ...

  3. java中解压tar.gz文件

    在开发中我们经常需要对gz文件进行解压缩,在java中解压gz文件还是比较繁琐的,为此写了一个工具类方便需要的时候可以直接拿过来用.代码如下: package com.eggsl.utils;impo ...

  4. java 文件解压到指定目录

    java 文件解压到指定目录是和压缩文件相反的操作,直接上测试代码: 1.工具类代码: /*** 解压文件到指定目录*/@SuppressWarnings("rawtypes")p ...

  5. java代码解压rar文件时报错,badRarArchive

    针对java代码解压rar文件时报错浅谈 1.针对所报的异常: de.innosystec.unrar.Archive setFile 警告: exception in archive constru ...

  6. JAVA2块钱瓶子_啤酒2元一瓶,4个瓶盖换一瓶,2个空瓶换一瓶,问:10元钱可以喝几瓶。用java编程解出答案,求大神们帮忙...

    点击查看啤酒2元一瓶,4个瓶盖换一瓶,2个空瓶换一瓶,问:10元钱可以喝几瓶.用java编程解出答案,求大神们帮忙具体信息 答:package test;public class Test {publ ...

  7. 数组实现环形队列——Java随解

    数组实现环形队列--Java随解 解题前言 代码语言 代码实现 运行示例 总结归纳 解题前言 环形队列是在普通队列的基础上进行修改的,目的提高队列的复用性.本次环形队列利用了数组进行模拟. 代码语言 ...

  8. OpenCV SIFT源码详解——总体概览

    OpenCV SIFT源码详解--总体概览 一.版本 二.章节系列 此系列文章源自本人硕士毕业论文,主要讲源码,对于SIFT理论知识默认大家有过了解.若文章中有不对之处还请读者指出. 一.版本 本系列 ...

  9. java程序解压/压缩.gz文件

    压缩成.gz格式 import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOu ...

最新文章

  1. 【转】CEC文件详解
  2. Zabbix 4.2 发布:支持Prometheus数据收集,可扩展性大大提升
  3. c打开指定路径文件_C++ 读取指定路径下所有的文件 (C++ get the list of files in a directory)...
  4. 《伊拉图斯死之主》:硬核游戏也有相对放松的游戏体验
  5. 2018-2019-1 20189213《Linux内核原理与分析》第四周作业
  6. java 图像膨胀_OpenCV3 Java图形图像上的膨胀(Imgproc.dilate)
  7. Linux编程(1)_关于linux基础知识
  8. 除了 996 ICU,GitHub 上还有哪些奇葩的项目?
  9. Python学习笔记(十三)文件操作函数
  10. Inception V1-V4
  11. 使用EasyRecovery轻松修复损坏的照片
  12. 最新修复版电影网站源码_2021版米酷影视v7.2.1源码 修复幻灯片 分类网址错误
  13. logistic回归详解
  14. 阿尔法策略和贝塔策略
  15. 不用安装Excel使用PSExcel自动处理Excel文件
  16. android pc模拟器哪个好用,电脑手游安卓模拟器哪个好用流畅 好用的安卓模拟器...
  17. 单片机进阶---PCB开发之照葫芦画瓢(一)
  18. 淘宝京东商品长链接缩短为腾讯,新浪短链接的接口有哪些?
  19. 使用Python调用百度地图的API在地图上添加标记
  20. dell服务器安装(dellemc服务器)

热门文章

  1. 李彦宏的文字游戏:百度10篇论文被自然语言处理顶级会议ACL 2019录用
  2. 这样用组图创作内容,能让你的文章被转发
  3. 小县城也被互联网+撞了一下腰
  4. 大数据市场规模5年将达8000亿 交易平台忙圈地
  5. Modular Arithmetic 模算术
  6. Java-Java反射
  7. python用电度数设计_在Python中动态模拟时绘制电压大小
  8. 学习笔记(十八)——MongoDB(CRUD)与Python交互
  9. java 与c 运行效率_Java语言与C语言代码运行效率的比较
  10. tomcat 显示访问的ip白名单