精华内容
下载资源
问答
  • 自由度的相关知识

    千次阅读 2017-03-30 13:29:05
    自由度   (机械系统的自由度)  编辑 根据机械原理,机构具有确定运动时所必须给定的独立运动参数的数目(亦即为了使机构的位置得以确定,必须给定的独立的广义坐标的数目),称为机构自由度(degree of ...

    自由度

     

    (机械系统的自由度)

     编辑
    根据机械原理,机构具有确定运动时所必须给定的独立运动参数的数目(亦即为了使机构的位置得以确定,必须给定的独立的广义坐标的数目),称为机构自由度(degree of freedom of mechanism),其数目常以F表示。
    中文名
    机构自由度
    外文名
    degree of freedom of mechanism

    简介

    编辑
    根据机械原理,机构具有确定运动时所必须给定的独立运动参数的数目(亦即为了使机构的位置得以确定,必须给定的独立的广义坐标的数目),称为机构自由度(degree of freedom of mechanism),其数目常以F表示。如果一个构件组合体的自由度F>0,他就可以成为一个机构,即表明各构件间可有相对运动;如果F=0,则它将是一个结构(structure),即已退化为一个构件。机构自由度又有平面机构自由度和空间机构自由度。一个原动件只能提供一个独立参数。[1] 

    分类

    编辑

    平面机构自由度

    一个杆件(刚体)在平面可以由其上任一点A的坐标x和y,以及通过A点的垂线AB与横坐标轴的夹角等3个参数来决定,因此杆件具有3个自由度。
    【计算公式】 F=3n-(2PL +Ph ) n:活动构件数,PL:低副约束数Ph:高副约束数
    计算平面机构自由度的注意事项:
    1. 复合铰链 --两个以上的构件在同一处以转动副相联。复合铰链处理方法:如有K个构件在同一处形成复合铰链,则其转动副的数目为(k-1)个。
    2. 局部自由度:构件局部运动所产生的自由度,它仅仅局限于该构件本身,而不影响其他构件的运动。局部自由度常发生在为减小高副磨损而将滑动摩擦变为滚动磨擦所增加的滚子处。处理方法:在计算自由度时,从机构自由度计算公式中将局部自由度减去。
    3. 虚约束 --对机构的运动实际不起作用的约束。计算自由度时应去掉虚约束。虚约束都是在一定的几何条件下出现的。常见有以下几种情况:
    • 两构件联接前后,联接点的轨迹重合。如:平行四边形机构,火车轮,椭圆仪。
    • 两构件构成多个移动副,且导路平行。
    • 两构件构成多个转动副,且同轴。
    • 运动时,两构件上的两点距离始终不变。
    • 对运动不起作用的对称部分。如多个行星轮
    • 两构件构成高副,两处接触,且法线重合。如等宽凸轮[1] 
      【注意】机构中出现虚约束是有条件的!虚约束一般有以下作用:改善机构受力情况;传递较大功率;
      增加机构的刚度,如轴与轴承、机床导轨;使机构运动顺利,避免运动不确定,如车轮。

    空间机构自由度

    一个杆件(刚体),在空间上完全没有约束,那么它可以在3个正交方向上平动,还可以以三个正交方向为轴进行转动,那么就有6个自由度。
    空间机构自由度的计算:
    第一种方法:
    传统方法,通过公式F=6n-
     
    也就是通过所有刚体的自由度数之和减去每一个运动副所约束的自由度数。这种方法的优点是,便于设计分析人员的分析与计算。尤其在平面机构的自由度分析上,通过计算者识别虚约束与局部自由度,几乎可以完成大部分机构的自由度计算。然而对于空间机构来说,由于虚约束与局部自由度难以识别,而且机构本身的尺寸,约束的位置不同、机构的实际运动自由度会有很大的差异。该公式已经难以胜任间机构的自由度计算任务。不过难以否认的是该公式在机械设计史上的突出贡献,很多经典的机构,机械装置都是基于该公式设计而成的。
    第二种方法
    通过构建机构的运动学分析方程并分析其秩来计算其自由度,或是拆分出机构的每一个闭链,通过虚位移矩阵法来分析机构自由度。此种方法的好处是在理论上可以完美的计算出机构的自由度,计算方法在理解上较为简单。然而该种方法虽然理解简单但计算过程本身较繁琐,而且该方法适用于对于已设计出机构的分析,利用该公式进行机构设计并不太方便。不过这种方法也较为成熟,也最好理解,很多书籍上都有介绍。
    第三种方法
    对机构的Jacobian矩阵计算其零空间,来分析机构的自由度。这种方法虽然理论上也可以解决自由度计算但是应用较为少见。其一是零空间的计算十分困难,甚至利用软件也难以解决。其二是该种方法也适用于对已有机构的分析计算,难以利用该方法实现创新。
    第四种方法
    基于群论、李代数、微分几何的知识来解决自由度计算的问题。群论、李代数、微分几何是解决复杂机构学问题的法宝。如果掌握,对于机构的设计与分析,并联机构的设计及计算,甚至机构的概念设计都有着十分积极的意义。现代的机构学与机器人学很多理论都是基于此而形成的。然而此种方法对设计人员的知识水平要求较高,对于普通的设计人员以及大学本科生来说不太实用。
    第五种方法
    基于螺旋理论的自由度计算方法。旋量也是解决机构学问题的利器。该种方法虽然并不能完美的解决所有的自由度问题。但在理解上更接近于第一种。在理解难度上大于第二种,计算难度上小于第二种。可以对于机构的概念设计有潜移默化的影响。不过对于普通的设计人员与大学本科生来说,理解还是困难的。
    总体来说,直到2015年还没有机构自由度计算的完美解决方案

    拓展

    编辑
    变自由度机构
    某些特殊的机构由于自身特殊的性质在运动过程中自由度会发生变化。

    比如变胞机构,欠驱动机构,以及一些转向机构。

    六自由度

     锁定
    物体在空间具有六个自由度,即沿x、y、z三个直角坐标轴方向的移动自由度和绕这三个坐标轴的转动自由度 。因此,要完全确定物体的位置,就必须清楚这六个自由度。[1] 
    中文名
    六自由度
    外文名
    Six Degrees of Freedom
    类    型
    坐标
    属    性
    数学
    用    途
    计算

    原理

    任何一个没有受约束的物体,在空间均具有6个独立的运动。
    以如图所示的长方形为例,它在直角坐标系oxyz中可以有3个平移运动和3个转动。3个平移运动分别是沿x,y,z轴的平移运动,3个转动分别是绕x,y,z轴的转动。习惯上把上述6个独立运动称做6个自由度。
    如果采取一定的约束措施,消除物体的6个自由度,则物体被完全定位。如图所示,采用6个按一定规则设置的支撑点,约束物体6个自由度的原理称为六点定位原理。[1] 

    应用

    六自由度机器人

    六自由度工业机器人是典型的机电一体化产品,其动作灵活性高,工作空间范围大,可以很灵活的绕过障碍物,并且结构紧凑,占地面积也比较小,关节上相对运动部件容易密封防尘,广泛应用在机床上下料、取件、弧焊、喷漆等行业,但对其实物进行研究和开发存在成本高、周期长等缺点。而针对教学和研究的需要,对六自由度工业机器人结构、运动和控制系统的认知理解和研究,要求机器人能完成相关六个自由度的运动,且要结构简单,操纵安全,成本低,一般不会造成事故。为此开发一种六自由度机器人来满足这些研究和教学的要求是很有必要的。[2] 

    六自由度运动平台

    六自由度运动平台是由六支作动筒,上、下各六只万向铰链和上、下两个平台组成,下平台固定在基础上,借助六支作动筒的伸缩运动,完成上平台在空间六个自由度(X,Y,Z,α,β,γ)的运动,从而可以模拟出各种空间运动姿态。可广泛应用到各种训练模拟器如飞行模拟器、舰艇模拟器、海军直升机起降模拟平台、坦克模拟器、汽车驾驶模拟器、火车驾驶模拟器、地震模拟器以及动感电影、娱乐设备等领域,甚至可用到空间宇宙飞船的对接,空中加油机的加油对接中。在加工业可制成六轴联动机床、灵巧机器人等。由于六自由度运动平台的研制,涉及机械、液压、电气、控制、计算机、传感器,空间运动数学模型、实时信号传输处理、图形显示、动态仿真等等一系列高科技领域,因而六自由度运动平台的研制变成了高等院校、研究院所在液压和控制领域水平的标志性象征

    如何区分运动副的低副和高副

    uxb***@163.com | 浏览 3634 次
    推荐于2016-06-21 12:10:41 最佳答案

    两构件直接接触并能产生相对运动的联接叫做运动副。高副是点或线接触,比如说两齿轮、凸轮机构;低副是面接触,如构件与滑块的接触。

    运动副按照运动副的接触形式分类:

    1. 时面和面接触的运动副在接触部分的压强较低,被称为低副,而点或线接触的运动副称为高副;

    2. 高副比低副容易磨损。低副一般有转动副,移动副,螺旋副,高副有车轮与钢轨,凸轮与从动件,齿轮传动等。

    自由度的概念

    怎么解释平面自由机构有三个自由度,而空间自由机构有六个自由度,谢谢,请详细解释一下,谢谢
    し☆ve谜儿xx | 浏览 2993 次
    推荐于2016-12-02 01:53:27
    最佳答案
    我们对空间的描述是通过三维坐标系来表征的。

    平面机构只能在平面内运动,若这个面为X-Y面,三个自由度为沿X轴的移动、沿Y轴的移动和绕Z轴的转动。

    一个物体不受约束在空间具有六个自由度,分别为沿三个坐标轴的移动和绕三个坐标轴的转动

    展开全文
  • 指南 》 进行了编写,本篇的主要内容为 “Local-Variable Syntax for Lambda Parameters ”,通过本章节的阅读,将对 JDK 中的 局部变量的类型推断、Lambda 表达式中类型推断的应用,var 关键字的正确用法,“ var ...

    前言

    在文章的前一章节中主要对 《 JShell使用教程&指南 》 进行了编写,本篇的主要内容为 “ Local-Variable Syntax for Lambda Parameters ”,通过本章节的阅读,将对 JDK 中的 局部变量的类型推断Lambda 表达式中类型推断的应用,var 关键字的正确用法,“ var ” 关键字,它不是真的关键字,博主在这儿把它定义为“ 伪关键字 ”,好吧,如果有不足的地方欢迎指正。

     

    局部变量的类型推断

    1、关于局部变量

    在之前,博主觉得还是有必要对 “ 什么是成员变量 & 局部变量? ” 进行讲解哈,因为之前博主看到网上很多有关类似的文章将 Java 和 C 中的相关概念给搞混了,而且此类的内容之多,多到惊人,至于为什么会这么多,博主也不太清楚。所以这儿再唠叨哈,可能个别人还不太清除,然后一顿操作后,说博主你这是啥,咋感觉不对喃,呃。。。好吧!

    Java中变量的分类:

     成员变量指的是类范围里定义的变量,也就是前面所说的属性;局部变量指的是一个方法内定义的变量。不管是成员变量、还是局部变量,都应该遵守相同的命名规则:从语法角度来看,只要一个合法的标识符即可,但我们应该知道,从程序可读性角度来看,应该是多个意义的单词连缀而成,其中第一个单词首字母小写,后面每个单词首字母大写。

    成员变量被分为类属性和实例属性两种,定义一个属性时不使用static修饰的就是实例属性,使用static修饰的就是类属性。其中类属性从这个类的准备阶段起开始存在,直到系统完全销毁这个类,类属性的作用域与这个类的生存范围相同;而实例属性则从这个类的实例被创建开始起存在,直到系统完全销毁这个实例,实例属性的作用域与对应实例的生存范围相同。

     

    成员变量 & 局部变量 的异同点:

    成员变量:

    在类中-在方法体外定义;存在于堆内存中;随着对象的创建而存在,随着对象的回收而消失;必须初始化值,否则在成员变量使用时,编译不通过,会报 “  ” 的错误,如下图:

     

    局部变量:

    在方法体内部定义;存在于栈中;随着方法的入栈(方法被调用时入站)而存在,随着方法的出栈(方法调用结束)而消失。

     

    *特别提示: 在Java中,一个类的生存与消亡总共分为5个阶段,从类被加载到 JVM 虚拟机中开始,直到被回收为止,整个生命周期分为:加载 -> 验证 -> 准备 -> 解析 -> 初始化 -> 使用 -> 回收

     

    推荐阅读:疯狂的Java第五版-第五章、第三节 中对 成员变量和局部变量 讲的非常详细,博主这儿只是皮毛而已。

    下载地址:《 CSDN 疯狂的Java 第5版 下载 》(或者可以在网上购买,大概65到98RMB左右)

    *注意:这儿需要注意一个问题,在 Java 中没有“ 全局变量 ”的这个概念,在 C 、C++中才有 “ 全局变量 ” 的这个概念,可惜博主在某度上随便一搜,80%以上的文章将 Java 中的“ 全局变量 ” 和 “ 成员变量 ”的概念混淆,也就是Java中的全局变量就是成员变量,而成员变量就是全局变量,这是错误的,如果您去面试时被面试官问道,Java 中的成员变量和局部变量时,你还特意的扩展的说了哈,成员变量即全局变量。。。面试官:好了,好了,您面试完了,回去等通知吧!!!

    部分截图如下:

    图一:

     

    图二:

     

    图三:

    (以上截图来至网络,只为纠正问题,如有不到之处,欢迎指出,博主会立即予以回复和处理)

     

     

    2、局部变量类型推断

    1)、局部变量类型推断的目的:

    通过减少与编写 Java 代码相关的形式来提高开发人员的体验,同时维持 Java 对静态类型安全的承诺,允许开发人员忽略局部变量类型的经常不必要的声明。 此功能将允许声明,例如:

                var list = new ArrayList<String>();  // 推断类型为: ArrayList<String>
                var stream = list.stream();          // 推断类型为: Stream<String>

     

    2)、为社么要使用局部变量类型推断:

    开发人员经常抱怨Java中所需的模板编码的程度。局部变量的清单类型声明通常被认为是不需要的,甚至是不必要的;如果给出了良好的变量命名,通常非常清楚地知道发生了什么。

    为每个变量提供一个清单类型的需要也意外地鼓励开发人员使用过于复杂的表达式; 使用较低级的声明式语法,可以减少将复杂的链式或嵌套式表达式分解为更简单的表达式的不利因素。

    几乎所有其他流行的静态类型“curly-brace”语言,无论是在JVM还是OFF,都支持某种形式的局部变量类型推断,包括:C++(auto),C#(var),Scala(var / val),Go(声明:=)。而Java几乎是唯一一种没有采用局部变量类型推断的流行的静态类型语言; 在这一点上,这应该不再是一个有争议的特性了。

    在Java SE 8中,类型推断的范围得到了显著扩展,包括对嵌套和链式泛型方法调用的扩展推断,以及对 lambda 形式的推断。这使得构建用于调用链的 API 变得容易得多,并且这样的API(例如 Streams)已经非常流行,这表明开发人员已经习惯于使用推断出的中间类型。一个简单的调用连如下:

                int maxWeight = blocks.stream()
                        .filter(b -> b.getColor() == BLUE)
                        .mapToInt(Block::getWeight)
                        .max();

    如上调用链,没有人会因为中间类型 Stream<Block> 和 IntStream,以及 lambda 表达式中 b 的类型没有显式地出现在源代码中而感到困惑(甚至根本没有人注意到)。

    局部变量类型推断允许在结构不那么紧密的 API 中产生类似的效果; 局部变量的许多用法本质上都是链,并且从推断中同样受益,例如:

                var path = Paths.get(fileName);
                var bytes = Files.readAllBytes(path);

    3)、局部变量类型推断的概述:

    对于带有初始化器的局部变量声明、增强 For 循环索引和传统 For 循环中声明的索引变量,允许用保留类型名 var 代替清单类型,示例内容如下:

                var list = new ArrayList<String>();  // 推断类型为: ArrayList<String>
                var stream = list.stream();          // 推断类型为: Stream<String>

    标识符 var 不是关键字,而是保留类型名称。 这意味着使用 var 用作变量、方法或包名称的代码不会受到影响; 使用 var 作为类或接口名称的代码则会受到影响(但这些名称在实践中很少见,因为它们违反了通常的命名约定)。

    不允许缺少初始化器、声明多个变量、具有额外数组维括号或引用正在初始化的变量的局部变量声明形式。 未经初始化就拒绝局部变量会缩小特性的范围,避免“ 远距离操作 ”推断错误,并且在典型的程序中只排除一小部分局部变量。

     

    4)、“ var ” 语法的选择

    对语法有不同的看法。这里的两个主要自由度是使用什么关键字(var、 auto 等) ,以及是否为不可变的局部变量提供一个单独的新形式(val、 let)。 考虑了以下语法的选项:

    1. var x = expr only (like C#)
    2. var,加上 val 的不可变局部变量 (like Scala, Kotlin)
    3. var, plus let for immutable locals (like Swift)
    4. auto x = expr (like C++)
    5. const x = expr (already a reserved word)
    6. final x = expr (already a reserved word)
    7. let x = expr
    8. def x = expr (like Groovy)
    9. x := expr (like Go)
       

    在收集了大量的意见后,var 显然比 Groovy、C++ 或 Go 方法更受欢迎。 对于针对不可变局部变量的第二种语法形式(val,let),存在着大量不同的意见; 这将是额外获取设计意图的额外形式的权衡。最后,选择只支持 “ var ”,到此关于 “ var ” 的局部变量类型推断便形成了。

     

    5)、“ var ” 语法局部变量类型推断小结

    一、“ var name; ” 这样的写法是错误的,因为没有推断的依据,所以无法推断,示例内容如下:

    jshell> var name;
    |  错误:
    |  无法推断本地变量 name 的类型
    |    (无法在不带初始化程序的变量上使用 'var')
    |  var name;
    |  ^-------^
    
    jshell>

    二、类的实例属性和类属性的数据类型不能使用 “ var ” 语法,示例内容如下:

    jshell> class a{
       ...> public var name;
       ...> }
    |  错误:
    |  此处不允许使用 'var'
    |  public var name;
    |         ^-^
    
    jshell> class b{
       ...> public static var name;
       ...> }
    |  错误:
    |  此处不允许使用 'var'
    |  public static var name;
    |                ^-^
    
    jshell> class c{
       ...> public String name;
       ...> public static Integer age;
       ...> }
    |  已创建 类 a
    
    jshell>

    “ var ”语法在有参数的lambda表达式使用:

    jshell> Consumer<String> consumer = (var t) -> System.out.println(t.toLowerCase()+"-world");
    consumer ==> $Lambda$15/0x00000008000b1c40@6093dd95
    |  已创建 变量 consumer : Consumer<String>
    
    jshell> consumer.accept("Hello");
    hello-world
    
    jshell> 

    关于 var 语法的更多细节,请参看:《 Local Variable Type Inference 》

     

     

     

     

     


     好了,关于 Java11新特性(四)——局部变量的类型推断 就写到这儿了,如果还有什么疑问或遇到什么问题欢迎扫码提问,也可以给我留言哦,我会一一详细的解答的。 
    歇后语:“ 共同学习,共同进步 ”,也希望大家多多关注CSND的IT社区。


    作       者: 华    仔
    联系作者: who.seek.me@java98k.vip
    来        源: CSDN (Chinese Software Developer Network)
    原        文: https://blog.csdn.net/Hello_World_QWP/article/details/88836423
    版权声明: 本文为博主原创文章,请在转载时务必注明博文出处!
    展开全文
  • 面向对象基本概念

    万次阅读 多人点赞 2019-02-06 21:56:15
    面向对象就是:把数据及对数据的操作方法放在一起,作为一个相互依存的整体...对象即为人对各种具体物体抽象后的一个概念,人们每天都要接触各种各样的对象,如手机就是一个对象。 面向对象编程(OOP:object-orie...

    面向对象就是:把数据及对数据的操作方法放在一起,作为一个相互依存的整体——对象。对同类对象抽象出其共性,形成类。类中的大多数数据,只能用本类的方法进行处理。类通过一个简单的外部接口与外界发生关系,对象与对象之间通过消息进行通信。程序流程由用户在使用中决定。对象即为人对各种具体物体抽象后的一个概念,人们每天都要接触各种各样的对象,如手机就是一个对象。

    面向对象编程(OOP: object-oriented programming)

    面向对象

     

    面向对象与面向过程的区别

    项目名称 面向对象程序设计 面向过程程序设计(也叫结构化编程)
    定义 面向对象顾名思义就是把现实中的事务都抽象成为程序设计中的“对象”,其基本思想是一切皆对象,是一种“自下而上”的设计语言,先设计组件,再完成拼装。 面向过程是“自上而下”的设计语言,先定好框架,再增砖添瓦。通俗点,就是先定好main()函数,然后再逐步实现mian()函数中所要用到的其他方法。
    特点 封装、继承、多态 算法+数据结构
    优势 适用于大型复杂系统,方便复用、 适用于简单系统,容易理解
    劣势 比较抽象、性能比面向过程低 难以应对复杂系统,难以复用,不易维护、不易扩展
    对比 易维护、易复用、易扩展,由于面向对象有封装、继承、多态性的特性,可以设计出低耦合的系统,使系统 更加灵活、更加易于维护  性能比面向对象高,因为类调用时需要实例化,开销比较大,比较消耗资源;比如单片机、嵌入式开发、 Linux/Unix等一般采用面向过程开发,性能是最重要的因素。 
    设计语言 Java、Smalltalk、EIFFEL、C++、Objective-、C#、Python等 C、Fortran
    使用场景

    30种编程语言的比较选择问题https://blog.csdn.net/ljy1988123/article/details/7782700

    类与对象的主要区别

    对象:对象是类的一个实例(对象不是找个女朋友),有状态和行为。例如,一条狗是一个对象,它的状态有:颜色、名字、品种;行为有:摇尾巴、叫、吃等。 

    :类是一个模板,它描述一类对象的行为和状态。

    1、面向对象三大主要特征(理解)

    ①封装

    两层含义:一层含义是把对象的属性和行为看成一个密不可分的整体,将这两者“封装”在一个不可分割的独立单元(即对象)中;另一层含义指“信息隐藏”,把不需要让外界知道的信息隐藏起来,有些对象的属性及行为允许外界用户知道或使用,但不允许更改,而另一些属性或行为,则不允许外界知晓,或只允许使用对象的功能,而尽可能隐藏对象的功能实现细节。

    封装的优点

    • 良好的封装能够减少耦合,符合程序设计追求“高内聚,低耦合”。

    • 类内部的结构可以自由修改。

    • 可以对成员变量进行更精确的控制。

    • 隐藏信息实现细节。

    实现Java封装的步骤

    1. 修改属性的可见性来限制对属性的访问(一般限制为private),例如:

    public class Person { 
        private String name; 
        private int age; 
    }

    这段代码中,将 name 和 age 属性设置为私有的,只能本类才能访问,其他类都访问不了,如此就对信息进行了隐藏。

    2. 对每个值属性提供对外的公共方法访问,也就是创建一对赋取值方法,用于对私有属性的访问,例如:

    public class Person{ 
        private String name;
        private int age; ​ 
        private boolean alive;
    
        public int getAge(){
            return age; 
        } 
    
        public void setAge(int age){ 
            this.age = age; 
        } ​
    
        public String getName(){ 
            return name; 
        } ​
     
        public void setName(String name){ 
            this.name = name; 
        }
    
        public boolean isAlive() {
    		return alive;
    	}
    
    	public void setAlive(boolean alive) {
    		this.alive = alive;
    	}
    
    }

    采用 this 关键字是为了解决实例变量(private String name)和局部变量(setName(String name)中的name变量)之间发生的同名的冲突。

    封装同时可以提高代码的安全性,例如普通的类属性不是private修饰就直接可以通过“对象名.属性 = xxx”对其赋值,但当我们用private修饰该属性后就不能这样对其做任意的修改了,而且我们还可以在其对外的访问方法中进行合法值校验。比如上例中的setAge()就可以更改为:

    public void setAge(int age){
        if(age > 120)
            System.out.println("Age setting error");
        else
            this.age = age;
    }

     封装的使用细节:

    1. 一般使用private访问权限
    2. 提供相应的get、set方法来访问相关属性,这些方法通常是public修饰的。以提供对属性的赋值与读取操作。(注意!boolean变量的get方法是is开头。)
    3. 一些只用于本类的辅助性方法,可以使用private修饰,希望其他类调用的方法用public修饰。

    this与super关键字:

    1、this关键字代表当前对象

    2、super可以理解为是指向自己超(父)类对象的一个指针,而这个超类指的是离自己最近的一个父类。  

     this与super对比

    • this.属性 操作当前对象的属性
    • this.方法 调用当前对象的方法 
    • 引用构造函数:调用本类中另一种形式的构造函数(应该为构造函数中的第一条语句)。
    • 普通的直接引用:与this类似,super相当于是指向当前对象的父类,这样就可以用super.xxx来引用父类的成员。
    • 子类中的成员变量或方法与父类中的成员变量或方法名同名时,表示调用父类的成员
    • 引用构造函数:调用父类中的某一个构造函数(应该为构造函数中的第一条语句)。默认在构造函数第一条语句是“super();”,无论写与否。
    • super(参数):调用基类中的某一个构造函数(应该为构造函数中的第一条语句)
    • this(参数):调用本类中另一种形成的构造函数(应该为构造函数中的第一条语句)
    • 调用super()必须写在子类构造方法的第一行,否则编译不通过。每个子类构造方法的第一条语句,都是隐含地调用 super(),如果父类没有这种形式的构造函数,那么在编译的时候就会报错。
    • super() 和 this() 类似,区别是,super() 从子类中调用父类的构造方法,this() 在同一类内调用其它方法。
    • super() 和 this() 均需放在构造方法内第一行。
    • 尽管可以用this调用一个构造器,但却不能调用两个。
    • this 和 super 不能同时出现在一个构造函数里面,因为this必然会调用其它的构造函数,其它的构造函数必然也会有 super 语句的存在,所以在同一个构造函数里面有相同的语句,就失去了语句的意义,编译器也不会通过。
    • this() 和 super() 都指的是对象,所以,均不可以在 static 环境中使用。包括:static 变量,static 方法,static 语句块。
    • 从本质上讲,this 是一个指向本对象的指针, 然而 super 是一个 Java 关键字。

    ②继承

    继承是java面向对象编程技术的一块基石,因为它允许创建分等级层次的类。

    继承就是子类继承父类的特征和行为,使得子类对象(实例)具有父类的实例域和方法,或子类从父类继承方法,使得子类具有父类相同的行为。

    类的继承格式:

    class 父类 { }

    class 子类 extends 父类 { }

    继承的类型:需要注意的是 Java 不支持多继承,但支持多重继承。

     继承的好处:

    (1)提高类代码的复用性

    (2)提高了代码的维护性

    (3)使得类和类产生了关系,是多态的前提(它也是继承的一个弊端,类的耦合性提高了)

    继承的特性

    • 子类拥有父类非 private 的属性、方法。

    • 子类可以拥有自己的属性和方法,即子类可以对父类进行扩展。

    • 子类可以用自己的方式实现父类的方法,即重写父类方法。

    • Java 的继承是单继承,但是可以多重继承,单继承就是一个子类只能继承一个父类,多重继承就是,例如 A 类继承 B 类,B 类继承 C 类,所以按照关系就是 C 类是 B 类的父类,B 类是 A 类的父类,这是 Java 继承区别于 C++ 继承的一个特性。

    • 继承可以使用 extends 和 implements 这两个关键字来实现继承,而且所有的类都是继承于 java.lang.Object,当一个类没有继承的两个关键字,则默认继承object(这个类在 java.lang 包中,所以不需要 import)祖先类。

    • 提高了类之间的耦合性(继承的缺点,耦合度高就会造成代码之间的联系越紧密,代码独立性越差)。

     final关键字:

    表示最终的意思,可以修饰类、成员变量、成员方法

    • 修饰类:类不可以被继承
    • 修饰成员变量:变量为常量,值不可以改变
    • 修饰成员方法:方法不能被重写
    • final还可以修饰局部变量:①修饰基本数据类型,值不能改变;②修饰引用数据类型,地址值不能改变

     static关键字(静态):

    static表示静态的意思,既可以修饰成员变量,又可以修饰成员方法,还有一种特殊用法修饰类

    (1)、修饰成员变量表示静态变量:static变量也称作静态变量,静态变量和非静态变量的区别是:静态变量被所有的对象所共享,在内存中只有一个副本,它当且仅当在类初次加载时会被初始化。而非静态变量是对象所拥有的,在创建对象的时候被初始化,存在多个副本,各个对象拥有的副本互不影响。static成员变量的初始化顺序按照定义的顺序进行初始化。static不能修饰局部变量。

    (2)、修饰成员方法:static方法一般称作静态方法,由于静态方法不依赖于任何对象就可以进行访问,因此对于静态方法来说,是没有this的,因为它不依附于任何对象,既然都没有对象,就谈不上this了。并且由于这个特性,在静态方法中不能访问类的非静态成员变量和非静态成员方法,因为非静态成员方法/变量都是必须依赖具体的对象才能够被调用。

    (3)static代码块:static关键字还有一个比较关键的作用就是 用来形成静态代码块以优化程序性能。static块可以置于类中的任何地方,类中可以有多个static块。在类初次被加载的时候,会按照static块的顺序来执行每个static块,并且只会执行一次。

    特点:
        1)随着类的加载而加载
        2)优先于对象存在
        3)被所有的对象所共享
            该特点是我们使用static的条件

    注意事项:
        1)在静态方法中,不能出现this/super
        2)静态方法只能访问静态成员;非静态方法既可以访问静态成员,也可以访问非静态成员
        3)工具类里面的成员一般来说是静态成员(目的:节约内存空间)

    静态变量和成员变量的区别
    1)所属不同
          静态变量属于类,也称为类变量
          成员变量属于对象,也称为实例变量
    2)内存中位置不同
          静态变量存在方法区
          成员变量存在堆中
    3)出现的时间不同
          静态变量随着类的加载而加载,随着类的消亡而消亡
          成员变量随着对象的创建而创建,随着对象的消失而消失
    4)调用方式不同
          静态变量通过类名调用,也可以通过对象名调用(不建议)
          成员变量只能通过对象名调用
    所以,成员变量可以称为对象的特有数据,静态变量称为对象的共享数据

    成员变量与局部变量的区别
    1)在类中的位置不同
           成员变量:在类中方法外面
           局部变量:在方法或者代码块中,或者方法的声明上(即在参数列表中)
    2)在内存中的位置不同,可以看看Java程序内存的简单分析
           成员变量:在堆中(方法区中的静态区)
           局部变量:在栈中
    3)生命周期不同
          成员变量:随着对象的创建而存在,随着对象的消失而消失
          局部变量:随着方法的调用或者代码块的执行而存在,随着方法的调用完毕或者代码块的执行完毕而消失
    4)初始值
          成员变量:有默认初始值
          局部变量:没有默认初始值,使用之前需要赋值,否则编译器会报错(The local variable xxx may not have been initialized)

    package se01.day02;
    
    //子父类静态代码块、构造代码块、构造方法
    class Fu{
    	String name;
    	int age;
    	
    	{
    		System.out.println("构造代码块Fu");
    	}
    	
    	static{
    		System.out.println("静态代码块Fu");
    	}
    	
    	public Fu() {
    		System.out.println("无参构造方法Fu");
    	}
    
    	public Fu(String name, int age) {
    		this.name = name;
    		this.age = age;
    		System.out.println("有参构造Fu");
    	}
    
    }
    public class Zi extends Fu{
    	int id;
    	{
    		System.out.println("构造代码块Zi");
    	}
    	static{
    		System.out.println("静态代码块Zi");
    	}
    	public Zi() {
    		System.out.println("无参构造方法Zi");
    	}
    
    	public Zi(String name, int age) {
    		this.name = name;
    		this.age = age;
    		System.out.println("有参构造Zi");
    	}
    	public static void main(String[] args) {
    		new Zi("小明",13);
    	}
    }
    
    ==========================执行结果为====================================
    静态代码块Fu
    静态代码块Zi
    构造代码块Fu
    无参构造方法Fu
    构造代码块Zi
    有参构造Zi

    static特殊用法(static修饰类):  如果一个类要被声明为static的,只有一种情况,就是静态内部类。如果在外部类声明为static,程序会编译都不会过。(在内部类中详细讲解)

    Question:在什么情况下需对属性和方法加上static关键字?

    在编写的代码中,static定义的属性出现几率不是特别高,一般只有在描述共享属性概念或者是不受实例化对象限制的属性时才会使用static定义属性,而大部分情况下依然都使用非static属性。

    产生实例化对象是因为在堆内存中可以保存属性信息,所以如果一个类中没有属性产生,就自然也没有必要去开辟堆内存保存属性内容了,所以这个时候就可以考虑类中的方法全部使用static声明。

    NOTICE:在JDK1.7之前,Java一直存在一个Bug,可以先执行静态代码块来代替主方法。按照标准来说,所有的程序应该都是从主方法开始执行,但是下例却先执行静态代码块

    	public static void main(String[] args) {
    		System.out.println("你好,世界");
    	}
    	
    	static{
    		System.out.println("Hello World");
    	}
    
    ===========输出结果为==============
    Hello World
    你好,世界

    ③多态

    多态是同一个行为具有多个不同表现形式或形态的能力。

    多态的体现

    Java语言中含有方法重载与对象多态两种形式的多态:

    方法重载:在一个类中,允许多个方法使用同一个名字,但方法的参数不同,完成的功能也不同。

    对象多态:子类对象可以与父类对象进行转换,而且根据其使用的子类不同完成的功能也不同(重写父类的方法)。

    面试题:什么是多态?实现多态的方法有哪些?

    多态是面向对象的最后一个主要特征,它本身主要分为两个方面:
           ·方法的多态性:重载与覆写
                |-重载:同一个方法名称,根据不同的参数类型及个数可以完成不同的功能。
                |-覆写:同一个方法,根据操作的子类不同,所完成的功能也不同。
           ·对象的多态:父子类对象的转换。
                |-向上转型:子类对象变为父类对象,格式:父类 父类对象 = 子类实例,自动;
                |-向下转型:父类对象变为子类对象,格式:子类 子类对象 = (子类)父类实例,强制。

    多态的优点

    • 1. 消除类型之间的耦合关系
    • 2. 可替换性
    • 3. 可扩充性
    • 4. 接口性
    • 5. 灵活性
    • 6. 简化性

    多态存在的三个必要条件

    • 继承
    • 重写
    • 父类引用指向子类对象

    比如:Parent p = new Child();

    多态的访问方式:
    (1)成员变量
        编译看左边,运行看左边
    (2)成员方法
        编译看左边,运行看右边
    (3)静态方法
        编译看左边,运行看左边

    多态的实现方式

    方式一:重载与重写:

    这个内容已经详细讲过,就不再阐述,详细请见上文。

    方式二:抽象类和抽象方法

    在Java中,一个没有方法体的方法称为抽象方法。而一个类中如果有抽象方法,那么这个类就称之为抽象类。

    格式:
        抽象类:abstract class 类名{}
        抽象方法:修饰符 abstract 返回值类型 方法名(参数列表){方法体;}

    特点:
        1)抽象类不一定有抽象方法,但是有抽象方法的类一定是抽象类
        2)抽象类不可以实例化(不能用new关键字创建抽象类实例)
        3)抽象类的子类,可以是抽象类,也可以是具体类。如果子类是具体类,需要重写抽象类里面所有抽象方法

    组成:
        1)成员变量
            可以是变量,可以是常量
        2)构造方法
            有构造方法
            抽象类不可以实例化,存在构造方法,有什么用?
            子类会调用父类的构造方法,对属性进行初始化赋值
        3)成员方法
            可以是抽象方法,也可以是具体方法

    抽象(abstract)不能与那些关键字共存?
    1).private :因为一个abstract方法需要被重写,所以不能修饰为private;
    2).final:因为一个abstract方法需要被重写。被final修饰的方法是不能被重写的,所以不能同final共存;
    3).static:因为一个abstract方法没有方法体。静态方法需要对方法体执行内容分配空间,所以不能同static共存;(abstract是没有实现  的,不能产生对象,而是static是属于类的,类本身是已经存在的对象)
    4).synchronized: 是同步的,然而同步需要具体的操作才能同步,但, abstract是只有声明没有实现的(即,使用synchronized关键字的是需要有具体的实现同步的操作的,但是使用abstract是只有声明而没有实现的,这样就产生了冲突)
    5).native:他们本身的定义就是冲突的,native声明的方法是移交本地操作系统实现的,而abstract是移交子类对象实现的,同时修饰的话,导致不知道谁实现声明的方法

    方式三:接口

    接口(英文:Interface),在JAVA编程语言中是一个抽象类型,是抽象方法的集合,接口通常以interface来声明。实际上是一个规范,它会要求你做什么,但不会要求你去怎么做。接口里面定义的是额外功能,但是不给出具体的实现。

    接口无法被实例化,但是可以被实现。一个实现接口的类,必须实现接口内所描述的所有方法,否则就必须声明为抽象类。

    接口与类相似点:

    • 一个接口可以有多个方法。
    • 接口文件保存在 .java 结尾的文件中,文件名使用接口名。
    • 接口的字节码文件保存在 .class 结尾的文件中。
    • 接口相应的字节码文件必须在与包名称相匹配的目录结构中。

    接口与类的区别:

    • 接口不能用于实例化对象。
    • 接口没有构造方法。
    • 接口中所有的方法必须是抽象方法。
    • 接口不能包含成员变量,除了 static 和 final 变量。
    • 接口不是被类继承了,而是要被类实现。
    • 接口支持多继承。

    接口特性:

    • 接口中每一个方法也是隐式抽象的,接口中的方法会被隐式的指定为 public abstract(只能是 public abstract,其他修饰符都会报错)。
    • 接口中可以含有变量,但是接口中的变量会被隐式的指定为 public static final 变量(并且只能是 public,用 private 修饰会报编译错误)。
    • 接口中的方法是不能在接口中实现的,只能由实现接口的类来实现接口中的方法。

    抽象类和接口的区别:

    • 1. 抽象类中的方法可以有方法体,就是能实现方法的具体功能,但是接口中的方法不行。
    • 2. 抽象类中的成员变量可以是各种类型的,而接口中的成员变量只能是 public static final 类型的。
    • 3. 接口中不能含有静态代码块以及静态方法(用 static 修饰的方法),而抽象类是可以有静态代码块和静态方法。
    • 4. 一个类只能继承一个抽象类,而一个类却可以实现多个接口。

    接口的多继承

    接口和接口继承关系,可以单继承,多继承,多级继承

    标记接口

    标记接口是没有任何方法和属性的接口.它仅仅表明它的类属于一个特定的类型,供其他代码来测试允许做一些事情。

    Java中的标记接口有:

    1. java.io.Serializable这个接口是用来标记类是否支持序列化的,所谓的序列化就是将对象的各种信息转换成可以存储或者传输的一种形式。如果一个类没有实现该接口,却被拿去序列化的了,那么虚拟机就会抛出不支持序列化的异常
    2. Cloneable接口在深度拷贝的时候经常被用到,在调用java.lang.Object类中clone方法的过程中,如果对象没有实现Cloneable接口,那么虚拟机就会抛出一个CloneNotSupportedException异常。
    3. java.util.RandomAccess这个接口的作用是判断集合是否能快速访问,也就是通过索引下标能否快速的移动到对应的元素上。我们在使用某个集合类中,集合中的元素可以通过索引index下标快速的访问到,那么在该类的定义处,一般会有一个RandomAccess接口的实现标签。

      比如:java.util.ArrayList<E>有这个接口,java.util.LinkedList<E>就没有

      1 public class ArrayList<E> extends AbstractList<E>
      2         implements List<E>, RandomAccess, Cloneable, java.io.Serializable
      3 {
      4 //...
      5 }
      1 public class LinkedList<E>
      2     extends AbstractSequentialList<E>
      3     implements List<E>, Deque<E>, Cloneable, java.io.Serializable
      4 {
      5     ...
      6 }

    2、数组的使用以及初始化操作

    数组是用来存储多个相同数据类型值的容器。

    数组初始化
    1)动态初始化
        只指定数组的长度,由系统分配默认值
        格式:
        数据类型[] 数组名 = new 数据类型[长度];     或者      数据类型 数组名[] = new 数据类型[长度]
        默认值:
        byte、short、int、long,默认0
        float、double,默认0.0
        boolean,默认false
        char,默认'\u0000'
        String,默认null(所有引用数据类型默认都是null)
    2)静态初始化
        指定数组里面每个元素的初始值,不指定数组的长度
        格式:
            数据类型[] 数组名 = new 数据类型[]{元素1,元素2...};
        简写:
            数据类型[] 数组名 = {元素1,元素2...};

    简单实例:

    public static void main(String[] args) {
    		  double[] myList = {1.9, 2.9, 3.4, 3.5};
    		  
    	      // 打印所有数组元素
    	      for (int i = 0; i < myList.length; i++) {
    	         System.out.print(myList[i] + " ");
    	      }
    	      System.out.println("\n=================");
    	      for (double d : myList) {
    			System.out.print(d + " ");
    		}
    	      System.out.println("\n=================");
    	      // 计算所有元素的总和
    	      double total = 0;
    	      for (int i = 0; i < myList.length; i++) {
    	         total += myList[i];
    	      }
    	      System.out.println("Total is " + total);
    	      // 查找最大元素
    	      double max = myList[0];
    	      for (int i = 1; i < myList.length; i++) {
    	         if (myList[i] > max) max = myList[i];
    	      }
    	      System.out.println("Max is " + max);
    	}
    
    
    运行结果:
    1.9 2.9 3.4 3.5 
    =================
    1.9 2.9 3.4 3.5 
    =================
    Total is 11.7
    Max is 3.5
    

     冒泡排序(相邻的元素进行比较,大值往后排):

    	public static void bubbleSort(int[] arr){
    		int temp = 0;
    		for (int i = 0; i < arr.length-1; i++) {
    			for (int j = 0; j < arr.length-1-i; j++) {
    				if(arr[j]>arr[j+1]){
    					temp = arr[j];
    					arr[j] = arr[j+1];
    					arr[j+1] = temp;
    				}
    			}
    		}
    	}

     选择排序(前面元素与后面的元素诸葛逐个进行比较,小值往前排):

    	public static void selectSort(int[] arr){
    		int temp = 0;
    		for (int i = 0; i < arr.length-1; i++) {
    			for (int j = i+1; j < arr.length; j++) {
    				if(arr[i]>arr[j]){
    					temp = arr[i];
    					arr[i] = arr[j];
    					arr[j] = temp;
    				}
    			}
    		}
    		System.out.println(Arrays.toString(arr));
    	}

     多维数组

    多维数组可以看成是数组的数组,比如二维数组就是一个特殊的一维数组,其每一个元素都是一个一维数组,例如:

    String str[][] = new String[3][4];

    多维数组初始化的方式与一维数组初始化方式类似,也分动态和静态两种方式。

    不规则数组

    “ 不规则 ” 数组 , 即数组的每一行有不同的长度。典型的案例是杨辉三角

    //不规则数组,杨辉三角
    public class LotteryArray {
    	public static void main(String[] args) {
    		final int NMAX = 10; //最大行数
    		int[][] odds = new int[NMAX][];
    		for (int n = 0; n < NMAX; n++)
    			odds[n] = new int[n + 1]; //初始化,开辟空间
    		for (int n = 0; n < odds.length; n++) {
    			for (int k = 0; k < odds[n].length; k++) {
    				/*
    				 * int lotteryOdds = 1;
    				 * for (int i = 1; i <= k; i++) 
                                     *     lotteryOdds = lotteryOdds * (n - i + 1) / i;
    				 * odds[n][k] = lotteryOdds;
    				 */
    				// 第一个和最有一个都是1,其余的都是它的上一个和它的上一个的前一个的和
    				if (k == 0 || k == odds[n].length - 1) {
    					odds[n][k] = 1;
    				} else {
    					odds[n][k] = odds[n - 1][k - 1] + odds[n - 1][k];
    				}
    
    			}
    		}
    		for (int[] row : odds) {
    			for (int odd : row)
    				System.out.printf("%4d", odd);
    			System.out.println();
    		}
    
    	}
    }
    
    
    =======================结果显示============================
       1
       1   1
       1   2   1
       1   3   3   1
       1   4   6   4   1
       1   5  10  10   5   1
       1   6  15  20  15   6   1
       1   7  21  35  35  21   7   1
       1   8  28  56  70  56  28   8   1
       1   9  36  84 126 126  84  36   9   1

    数组工具类Arrays

    修饰符和类型 方法和描述
    static <T> List<T>

    asList(T... a)

    返回由指定数组支持的固定大小的列表。

    static int

    binarySearch(byte[] a, byte key)

    使用二进制搜索算法在指定的字节数组中搜索指定的值。

    static int

    binarySearch(byte[] a, int fromIndex, int toIndex, byte key)

    使用二进制搜索算法搜索指定值的指定字节数组的范围。

    char[]、double[]、float[]、int[]、long[]、Object[]、short[]...同样适用

    static boolean[]

    copyOf(boolean[] original, int newLength)

    使用false复制指定的数组,截断或填充(如果需要),以使副本具有指定的长度。

    static byte[]

    copyOf(byte[] original, int newLength)

    使用零复制指定的数组,截断或填充(如有必要),以使副本具有指定的长度。

    char[]、double[]、float[]、int[]、long[]...同样适用

    static char[]

    copyOfRange(boolean[] original, int from, int to)

    将指定数组的指定范围复制到新数组中。

    static double[]

    copyOfRange(boolean[] original, int from, int to)

    将指定数组的指定范围复制到新数组中。

    boolean[]、char[]、float[]、int[]、long[]同样

    static boolean equals(boolean[] a, boolean[] a2)

    如果两个指定的布尔数组彼此相等,则 返回true。

    byte[]、char[]、double[]...同样适用

    static void fill(boolean[] a, boolean val)

    将指定的布尔值分配给指定的布尔数组的每个元素。

    byte[]、char[]、double[]...同样适用

    static void parallelSort(byte[] a)

    将指定的数组按升序排序。java8新特性,并行排序。

    static void sort(float[] a)

    将指定的数组按升序排序。串行排序。

    static void sort(float[] a, int fromIndex, int toIndex)

    按升序对数组的指定范围进行排序。

    int[]、short[]、long[]、double[]...同样适用

    static String toString(boolean[] a)

    返回指定数组内容的字符串表示形式。

    3、java基本数据类型和引用传递区别

    简概: ①形参为基本数据类型形参改变,实参不会发生改变
     ②形参为引用数据类型形参改变,实参也会跟着改变(有特例)

    详述:https://blog.csdn.net/sugar_no1/article/details/86506510

     

    展开全文
  • Python计算机视觉编程局部图像描述子(一)Harris 角点检测器(二)SIFT(尺度不变特征变换)2.1 兴趣点2.2 描述子2.3 检测兴趣点2.4 匹配描述子(三)匹配地理标记图像3.1 从 Panoramio 下载地理标记图像3.2 使用...

    局部图像描述子

    本节旨在寻找图像间的对应点和对应区域。介绍用于图像匹配的两种局部描述子算法。图像的局部特征是许多计算机视觉算法的基础,使用特征点来代表图像的内容包括运动目标跟踪,物体识别,图像配准,全景图像拼接,三维重建等。

    (一)Harris 角点检测器

    角点特征:

    1. 轮廓之间的交点。
    2. 对于同一场景,即使视角发生变化,通常具备稳定性质的特征。
    3. 该点附近区域的像素点无论在梯度方向上还是其梯度幅值上有着较大变化。
    4. 角点处的一阶导数最大,二阶导数为0。
    5. 角点指示了物体边缘变化不连续的方向。

    Harris 角点检测算子
    Harris 算子是一种简单的点特征提取算子,这种算子受信号处理中自相关函数的启发,给出与自相关函数相联系的矩阵M。M阵的特征值是自相关函数的一阶曲率,如果两个曲率值都高,那么就认为该点是特征点。为了消除噪声对于角点检测的影响,可以使用一个高斯滤波器来平滑图像。

    Harris 角点检测算法原理:
    利用矩形窗在图像上移动,若窗内包含有角点,则窗口向各个方向移动时,窗内的灰度值都会发生变化。从而达到检测图像角点的目的。如果像素周围显示存在多于一个方向的边,我们认为该点为兴趣点。该点就称为角点。

    数学公式:
    把图像域中点 x 上的对称半正定矩阵 MIM_I=MIM_I(x)定义为:
    在这里插入图片描述
    其中 ∆I为包含导数IxI_xIyI_y 的图像梯度。由于该定义,MIM_I 的秩为 1,特征值为 λ1=| ∆I |2 和 λ2=0。现在对于图像的每一个 像素,我们可以计算出该矩阵。

    选择权重矩阵 W(通常为高斯滤波器 Gσ),我们可以得到卷积:
    在这里插入图片描述
    该卷积的目的是得到 MIM_I 在周围像素上的局部平均。计算出的矩阵 MIM_I有称为 Harris矩阵

    方法:

    1. 计算图像I(x,y)在X和Y两个方向的梯度IxI_xIyI_y
      在这里插入图片描述             在这里插入图片描述
    2. 计算图像两个方向梯度的乘积。
      在这里插入图片描述       在这里插入图片描述       在这里插入图片描述
    3. 使用高斯函数对上述结果进行高斯加权(取σ=1),生成矩阵M的元素A、B和C。
      在这里插入图片描述
      在这里插入图片描述
      在这里插入图片描述
    4. 计算每个像素的Harris响应值R,并对小于某一阈值t的R值为0.
      在这里插入图片描述
    5. 在3x3或5x5的领域内进行非最大值抑制,局部最大值点即为图像中的角点。

    注意:
    增大α的值,将减小角点响应值R,降低角点检测的灵性,减少被检测角点的数量;减小α值,将增大角点响应值R,增加角点检测的灵敏性,增加被检测角点的数量。

    Harris角点检测算法优点

    1. 旋转不变性,椭圆转过一定角度但是其形状保持不变(特征值保持不变)。
    2. 对于图像灰度的仿射变化具有部分的不变性,由于仅仅使用了图像的一介导数,对于图像灰度平移变化不变;对于图像灰度尺度变化不变。

    Harris角点检测算法缺点

    1. 它对尺度很敏感,不具备几何尺度不变性。
    2. 提取的角点是像素级的。

    编码:

    # -*- coding: utf-8 -*-
    from pylab import *
    from PIL import Image
    from PCV.localdescriptors import harris
    
    # 读入图像
    im = array(Image.open('D:\\Python\\chapter2\\jimei2.jpg').convert('L'))
    
    # 检测harris角点
    harrisim = harris.compute_harris_response(im)
    
    # Harris响应函数
    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):
        filtered_coords = harris.get_harris_points(harrisim, 6, thres)
        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')
    
    # 原书采用的PCV中PCV harris模块
    # harris.plot_harris_points(im, filtered_coords)
    
    # plot only 200 strongest
    # harris.plot_harris_points(im, filtered_coords[:200])
    
    show()
    

    代码运行效果如下:
    在这里插入图片描述
    分析:

    增大α的值,将减小角点响应值R,降低角点检测的灵性,减少被检测角点的数量;减小α值,将增大角点响应值R,增加角点检测的灵敏性,增加被检测角点的数量。使用阈值 0.01、0.05 和 0.1 检测出的角点依次减少。

    改进:
    函数cornerHarris()识别角点

    cv2.cornerHarris(src, blockSize, ksize, k[, dst[,borderType]]) -> dst
    参数如下:
      1. src - 数据类型为 float32 的输入图像。
      2. blockSize - 角点检测中要考虑的领域大小。
      3. ksize - Sobel 求导中使用的窗口大小
      4. k - Harris 角点检测方程中的自由参数,取值参数为 [0,04,0.06].

    cornerHarris函数中最重要的参数是第三个,该参数限定了sobel算子的中孔,sobel算子通过对图像行列的变化来检测边缘,sobel算子会通过核kernel来完成检测。该参数定义了角点检测的敏感度,其取值必须介于3和31之间的奇数。

    当ksize参数设为3时:
    编写代码:

      -*- coding: utf-8 -*-
      import cv2
       import numpy as np
       filename = 'D:\\Python\\chapter2\\jimei2.png'
       img = cv2.imread(filename)
       gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
       gray = np.float32(gray)
       
       dst = cv2.cornerHarris(gray,6,3,0.04)
       img[dst>0.01*dst.max()]=[0,0,255]
    
       cv2.imshow('dst',img)
    	if cv2.waitKey(0) & 0xff == 27:
    	    cv2.destroyAllWindows()
    

    代码运行效果如下:

    当ksize参数设为23时:
    代码运行效果如下:

    可以看到,如果将参数设置为3,当检测到大楼方块的边界时,大楼中方块的所有对角线都会被认为是角点。如果参数设置为23,只有大楼方块的角点才能被检测为角点。

    调整cornerHarris的第二个参数可以改变标记角点的记号大小。
    当 blockSize参数为6时:
    编写代码:

    # -*- coding: utf-8 -*-
    import cv2
    import numpy as np
    filename = 'D:\\Python\\chapter2\\jimei2.png'
    img = cv2.imread(filename)
    gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
    gray = np.float32(gray)
    
    dst = cv2.cornerHarris(gray,6,3,0.04)
    
    # Threshold for an optimal value, it may vary depending on the image.
    img[dst>0.01*dst.max()]=[0,0,255]
    
    cv2.imshow('dst',img)
    if cv2.waitKey(0) & 0xff == 27:
        cv2.destroyAllWindows()
    

    代码运行效果如下:

    当 blockSize参数为2时:
    代码运行效果如下:

    可以看到 blockSize参数值越小,标记角点的记号越小。

    图像中寻找对应点:
    Harris 角点检测器仅仅能够检测出图像中的兴趣点,但是没有给出通过比较图像间的兴趣点来寻找匹配角点的方法。我们需要在每个点上加入描述子信息,并给出一 个比较这些描述子的方法。

    兴趣点描述子是分配给兴趣点的一个向量,描述该点附近的图像的表观信息。描述子越好,寻找到的对应点越好。我们用对应点或者点的对应来描述相同物体和场景点在不同图像上形成的像素点。

    编写代码:

     # -*- coding: utf-8 -*-
    from pylab import *
    from PIL import Image
     
    from PCV.localdescriptors import harris
    from PCV.tools.imtools import imresize
    
    im1 = array(Image.open("D:\\Python\\chapter2\\jimei2.jpg").convert("L"))
    im2 = array(Image.open("D:\\Python\\chapter2\\jimei2.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)
     
    figure()
    gray() 
    harris.plot_matches(im1, im2, filtered_coords1, filtered_coords2, matches)
    show()
    

    分析:

    该算法的结果存在一些不正确匹配。这是因为,与现代的一些方法(下面将会提到)相比,图像像素块的互相关矩阵具有较弱的描述性。实际运用中,我们通常使用更稳健的方法来处理这些对应匹配。这些描述符还有一个问题,它们不具有尺度不变性和旋转不变性,而算法中像素块的大小也会影响对应匹配的结果。

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

    前面介绍的cornerHarris函数可以很好的检测角点,这与角点本身的特性有关,这些角点在图像旋转的情况下也能被检测到。然而,如果减小(或增加)图像的大小,可能会丢失图像的某些部分,或者有可能增加角点的质量。

    这种特征损失现象需要用一种与图像比例无关的角点检测方法来解决——尺度不变特征变换(Scale-Invariant Feature Transform , SIFT),该函数会对不同的图像尺度(尺度不变特征变换)输出相同的结果。

    注意:上述概念中只是进行 “ 特征变换 ”,此意味 SIFT 会通过一个特征向量来描述关键点周围区域情况,而不检测关键点(关键点可由 Difference of Gaussians DoG检测)。

    匹配的核心问题是将同一目标在不同时间、不同分辨率、不同光照、不同方向的情况下所成的像对应起来。

    传统的匹配算法往往是直接提取角点边缘,对环境的适应能力较差,需要一种鲁棒性强,能够适应不同情况的有效的目标识别的方法。

    SIFT 特征包括兴趣点检测器和描述子。SIFT 描述子具有非常强的稳健性,这在很大程度上也是 SIFT 特征能够成功和 流行的主要原因。自从 SIFT 特征的出现,许多其他本质上使用相同描述子的方法 也相继出现。现在,SIFT 描述符经常和许多不同的兴趣点检测器相结合使用(有些情况下是区域检测器),有时甚至在整幅图像上密集地使用。SIFT 特征对于尺度、 旋转和亮度都具有不变性,因此,它可以用于三维视角和噪声的可靠匹配。

    SIFT特征检测的步骤:

    1. 尺度空间的极值检测:搜索所有尺度空间上的图像,通过高斯微分函数来识别潜在的对尺度和旋转不变的兴趣点。

    2. 特征点定位:在每个候选的位置上,通过一个拟合精细模型来确定位置尺度,关键点的选取依据他们的稳定程度。

    3. 特征方向赋值: 基于图像局部的梯度方向,分配给每个关键点位置一个或多个方向,后续的所有操作都是对于关键点的方向、尺度和位置进行变换,从而提供这些特征的不变性。

    4. 特征点描述: 在每个特征点周围的邻域内,在选定的尺度上测量图像的局部梯度,这些梯度被变换成一种表示,这种表示允许比较大的局部形状的变形和光照变换。

    SIFT算法的特点

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

    2. 独特性好,信息量丰富,适用于海量特征库进行快速、准确的匹配。

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

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

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

    可以解决的问题

    1. 目标的旋转、缩放、平移

    2. 图像的仿射,投影变换

    3. 光照影响

    4. 目标遮挡

    5. 杂物场景

    6. 噪声

    2.1 兴趣点

    SIFT 特征使用高斯差分函数来定位兴趣点:
    在这里插入图片描述
    其中,Gσ 是上一章中介绍的二维高斯核,Iσ 是使用Gσ 模糊的灰度图像,κ 是决定相差尺度的常数。兴趣点是在图像位置和尺度变化下 D(x,σ) 的最大值和最小值点。这些候选位置点通过滤波去除不稳定点。基于一些准则,比如认为低对比度和位于边上的点不是兴趣点,我们可以去除一些候选兴趣点。

    2.2 描述子

    上面讨论的兴趣点(关键点)位置描述子给出了兴趣点的位置和尺度信息。为了实现旋转不变性,基于每个点周围图像梯度的方向和大小,SIFT 描述子又引入了参考方向。SIFT 描述子使用主方向描述参考方向。主方向使用方向直方图(以大小为权重)来度量。

    2.3 检测兴趣点

    使用开源工具包 VLFeat 提供的二进制文件来计算图像的 SIFT 特征 。VLFeat 工具包可以从 http://www.vlfeat.org/ 下载,二进制文件可以在所有主要的平台上运行。

    编写代码:

    # -*- coding: utf-8 -*-
    from PIL import Image
    from pylab import *
    from PCV.localdescriptors import sift
    from PCV.localdescriptors import harris
    
    # 添加中文字体支持
    from matplotlib.font_manager import FontProperties
    
    font = FontProperties(fname=r"c:\windows\fonts\SimSun.ttc", size=14)
    
    imname = 'D:\\Python\\chapter2\\jimei2.jpg'
    im = array(Image.open(imname).convert('L'))
    sift.process_image(imname, 'empire.sift')
    l1, d1 = sift.read_features_from_file('empire.sift')
    
    figure()
    gray()
    subplot(131)
    sift.plot_features(im, l1, circle=False)
    title(u'(a)SIFT特征', fontproperties=font)
    subplot(132)
    sift.plot_features(im, l1, circle=True)
    title(u'(b)用圆圈表示SIFT特征尺度', fontproperties=font)
    
    # 检测harris角点
    harrisim = harris.compute_harris_response(im)
    
    subplot(133)
    filtered_coords = harris.get_harris_points(harrisim, 6, 0.1)
    imshow(im)
    plot([p[1] for p in filtered_coords], [p[0] for p in filtered_coords], '*')
    axis('off')
    title(u'(c)Harris角点', fontproperties=font)
    
    show()
    

    在这里插入图片描述
    分析:

    为了比较 Harris 角点和 SIFT 特征的不同,右图(图c)显示的是同一幅图像的 Harris 角点。你可以看到,两个算法所选择特征点的位置不同。

    换个方法:
    编写代码:

    # -*- coding: utf-8 -*-
    import cv2
    import sys
    import numpy as np
    
    imgpath = 'D:\\Python\\chapter2\\lovely.jpg'
    img = cv2.imread('D:\\Python\\chapter2\\lovely.jpg')
    
    gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
    
    # 创建一个sift对象
    sift = cv2.xfeatures2d.SIFT_create()
    keypoints, descriptor = sift.detectAndCompute(gray, None)
    
    img = cv2.drawKeypoints(image=img, outImage=img, keypoints=keypoints, flags=4, color=(51, 163, 236))
    
    cv2.imshow('sift_keypoints', img)
    cv2.waitKey(0)
    cv2.destroyAllWindows()
    

    代码运行效果如下:

    分析:

    1. 完成常规方法加载要处理的图像后,为了使脚本具有通用性,使用python的sys模块将图像路径通过命令行参数传递给脚本。
    2. SIFT对象会使用DoG检测关键点,并且对每个关键点周围的区域计算特征向量,由方法名称我们就可以知道,它主要包括两个操作:检测和计算,操作的返回值是关键点信息和描述符,最后在图像上绘制关键点,并用imshow函数显示这幅图像。
    3. 注意,这里将标志值4传给draeKeypoints函数,标志值4其实是下面这个cv2模块的属性值:CV2.DRAM_MATCHES_FLAGS_DRAW_RICH_KEYPOINT
      该代码对图像的每个关键点都绘制了圆圈和方向。
      pt: 图像中关键点的x,y坐标点
      size: 特征的直径
      angle: 特征的方向
      response: 关键点的强度。某些特征会通过SIFT来分类,因为它得到的特征比其他特征更好,通过查看response属性可以评估特征强度。
      octave: 特征所在金字塔的层级,算法在每次迭代时候,作为参数的图像尺寸和相邻像素都会发生变化
      kp.class_id: 对象关键点ID

    2.4 匹配描述子

    对于将一幅图像中的特征匹配到另一幅图像的特征,一种稳健的准则(同样是由 Lowe 提出的)是使用这两个特征距离和两个最匹配特征距离的比率。相比于图像中的其他特征,该准则保证能够找到足够相似的唯一特征。使用该方法可以使错误的匹配数降低。

    from PIL import Image
    from pylab import *
    import sys
    from PCV.localdescriptors import sift
    
    if len(sys.argv) >= 3:
        im1f, im2f = sys.argv[1], sys.argv[2]
    else:
        im1f = 'D:\\Python\\chapter2\\jimei2.jpg'
        im2f = 'D:\\Python\\chapter2\\jimei2.jpg'
    im1 = array(Image.open(im1f))
    im2 = array(Image.open(im2f))
    
    sift.process_image(im1f, 'out_sift_1.txt')
    l1, d1 = sift.read_features_from_file('out_sift_1.txt')
    figure()
    gray()
    subplot(121)
    sift.plot_features(im1, l1, circle=False)
    
    sift.process_image(im2f, 'out_sift_2.txt')
    l2, d2 = sift.read_features_from_file('out_sift_2.txt')
    subplot(122)
    sift.plot_features(im2, l2, circle=False)
    
    # matches = sift.match(d1, d2)
    matches = sift.match_twosided(d1, d2)
    print '{} matches'.format(len(matches.nonzero()[0]))
    
    figure()
    gray()
    sift.plot_matches(im1, im2, l1, l2, matches, show_below=True)
    show()
    

    分析:

    SIFT算法的实质是在不同的尺度空间上查找关键点(特征点),并计算出关键点的方向。SIFT所查找到的关键点是一些十分突出,不会因光照,仿射变换和噪音等因素而变化的点,如角点、边缘点、暗区的亮点及亮区的暗点等。

    下面扩展一种新算法:
    基于ORB的特征检测与特征匹配
    ORB 将基于 FAST 关键点检测的技术和基于 BRIEF 描述符的技术相结合,因此首先介绍 FAST 和 BRIEF。

    FAST:
    FAST算法会在像素周围绘制一个圆,该圆包括16个像素,然后,FAST会将每个像素与加上一个阈值的圆心像素值进行比较,如有连续,比加上一个阈值的圆心的像素值还亮或暗的像素,则可认为圆心是角点。

    BRIEF:
    BRIEF(Binary Robust Independent Elementary Features ) 是一个描述符,而不是一种算法。检测结果是一组关键点,计算结果是描述符。 关键点描述符是图像的一种表示,因此可比较两个图像的关键点描述符;并找到它们的共同之处,所以描述符可作为特征匹配的一种方法。

    在ORB的论文中,作者得到了如下结果:

    1. 向 FAST 增加一个快速、准确的方向分量(component)。
    2. 能高效计算带方向的 BRIEF 特征。
    3. 基于带方向的 BRIEF 特征的方差分析和相关性分析。
    4. 在旋转不变性条件下学习一种不相关的 BRIEF 特征,这会在最邻近的应用中得到较好的性能。

    编写代码:

    # -*- coding: utf-8 -*-
    import cv2
    from matplotlib import pyplot as plt
    
    
    # 首先加载两幅图(查询图像和训练图像)
    img1 = cv2.imread('D:\\Python\\chapter2\\dragonboat.jpg')
    img2 = cv2.imread('D:\\Python\\chapter2\\dragonboat2.jpg')
    
    # 创建ORB特征检测器和描述符
    orb = cv2.ORB_create()
    kp1, des1 = orb.detectAndCompute(img1, None)
    kp2, des2 = orb.detectAndCompute(img2, None)
    
    # 对查询图像和训练图像都要检测,然后计算关键点和描述符
    # BFMatcher 创建匹配器对象,
    bf = cv2.BFMatcher(cv2.NORM_HAMMING, crossCheck=True)
    # match 实现暴力匹配
    matches = bf.match(des1, des2)
    matches = sorted(matches, key=lambda x: x.distance)
    
    # 现已经获取所有需要的信息,将其图标来绘制这些匹配
    img3 = cv2.drawMatches(img1, kp1, img2, kp2, matches[:40], img2, flags=2)
    plt.imshow(img3)
    plt.show()
    

    分析:

    1. 匹配非常简单,遍历描述符,确定描述符是否已经匹配,然后计算匹配质量(距离)并排序,这样就可以在一定置信度下显示前 n 个匹配,以此得到那两幅图是匹配的。
    2. ORB 旨在优化和加快操作速度,同时包括非常重要的一步:以旋转感知(rotation-aware)的方式使用 BRIEF ,这样即使在训练图像和查询图像之间旋转差别很大的情况下也能够提高匹配效果。可以看到龙舟图匹配SIFT特征时匹配结果为0和1,匹配情况并不理想,而使用ORB 算法,出现了一些正确的匹配。

    (三)匹配地理标记图像

    3.1 从 Panoramio 下载地理标记图像

    根据课本要求编写的代码,发现谷歌的Panoramio已经关闭,暂时找不到类似的网站,只能先这样搁置了。

    # -*- coding: utf-8 -*-
    import json
    import os
    import urllib
    import urlparse
    from PCV.tools.imtools import get_imlist
    from pylab import *
    from PIL import Image
    
    # change the longitude and latitude here
    # here is the longitude and latitude for Oriental Pearl
    minx = '-77.037564'
    maxx = '-77.035564'
    miny = '38.896662'
    maxy = '38.898662'
    
    # number of photos
    numfrom = '0'
    numto = '20'
    url = 'http://www.panoramio.com/map/get_panoramas.php?order=popularity&set=public&from=' + numfrom + '&to=' + numto + '&minx=' + minx + '&miny=' + miny + '&maxx=' + maxx + '&maxy=' + maxy + '&size=medium'
    
    c = urllib.urlopen(url)
    
    j = json.loads(c.read())
    imurls = []
    for im in j['photos']:
        imurls.append(im['photo_file_url'])
    
    for url in imurls:
        image = urllib.URLopener()
        image.retrieve(url, os.path.basename(urlparse.urlparse(url).path))
        print 'downloading:', url
    
    # 显示下载到的20幅图像
    figure()
    gray()
    filelist = get_imlist('./')
    for i, imlist in enumerate(filelist):
        im = Image.open(imlist)
        subplot(4, 5, i + 1)
        imshow(im)
        axis('off')
    show()
    

    3.2 使用局部描述子匹配

    下面将已经下载在本地的图像,提取局部描述子。
    编写代码:

    # -*- coding: utf-8 -*-
    import json
    import os
    import urllib
    import urlparse
    from pylab import *
    from PIL import Image
    from PCV.localdescriptors import sift
    from PCV.tools import imtools
    import pydot
    
    download_path = "D:\\Python\\chapter2\\jimei"
    path = "D:\\Python\\chapter2\\jimei\\"
    
    imlist = imtools.get_imlist(download_path)
    nbr_images = len(imlist)
    
    featlist = [imname[:-3] + 'sift' for imname in imlist]
    for i, imname in enumerate(imlist):
        sift.process_image(imname, featlist[i])
    
    matchscores = zeros((nbr_images, nbr_images))
    
    for i in range(nbr_images):
        for j in range(i, nbr_images):  # only compute upper triangle
            print('comparing ', imlist[i], imlist[j])
            l1, d1 = sift.read_features_from_file(featlist[i])
            l2, d2 = sift.read_features_from_file(featlist[j])
            matches = sift.match_twosided(d1, d2)
            nbr_matches = sum(matches > 0)
            print('number of matches = ', nbr_matches)
            matchscores[i, j] = nbr_matches
    
    # copy values
    for i in range(nbr_images):
        for j in range(i + 1, nbr_images):  # no need to copy diagonal
            matchscores[j, i] = matchscores[i, j]
    
    

    代码运行效果如下:

    将每对图像间的匹配特征数保存在 matchscores 数组中。因为该“距离度量”是 对称的,所以可以不在代码的最后部分复制数值,来将 matchscores 矩阵填充完整;填充完整后的 matchscores 矩阵只是看起来更好。这些特定图像的 matchscores 矩阵里的数值如下:

    129 0 0 2 0 0 0 0 1 1 0 0 0 0 0 0 0 0 1 8 
    0 83 0 1 0 0 0 1 1 0 0 1 0 0 0 0 0 0 1 2
    0 0 41 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0
    2 1 0 51 0 0 2 2 0 0 0 2 2 0 0 0 2 3 2 0
    0 0 0 0 316 0 0 1 0 0 0 0 0 2 0 0 0 0 0 1
    0 0 0 0 0 245 0 0 1 0 0 0 0 0 0 0 0 1 1 0
    0 0 0 2 0 0 82 0 0 0 1 4 4 0 2 0 0 5 1 0
    0 1 0 2 1 0 0 249 0 0 0 1 0 0 1 0 2 0 1 1
    1 1 0 0 0 1 0 0 260 0 0 0 0 0 0 0 1 0 0 20
    0 0 0 0 0 0 0 0 0 348 0 0 1 0 0 0 0 0 0 2
    0 0 0 0 0 0 1 0 0 0 258 0 0 0 0 0 1 1 1 0
    1 1 0 2 0 0 4 1 0 0 0 332 5 2 15 0 3 6 0 0
    2 0 0 2 0 0 4 0 0 1 0 5 268 1 4 0 3 37 1 0
    0 0 1 0 2 0 0 0 0 0 0 2 1 103 1 0 0 1 0 0
    3 0 0 0 0 0 2 1 0 0 0 15 4 1 368 0 6 9 1 0
    0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 2273 0 1 0 0
    19 0 0 2 0 0 0 2 1 0 1 3 3 0 6 0 542 0 0 0
    1 0 0 3 0 1 5 0 0 0 1 6 37 1 9 1 0 527 3 0
    0 1 0 2 0 1 1 1 0 0 1 0 1 0 1 0 0 3 1139 0
    2 2 0 0 1 0 0 1 20 2 0 0 0 0 0 0 0 0 0 499
    

    使用该matchscores 矩阵作为图像间简单的距离度量方式(具有相似内容的图像间拥有更多的匹配特征数)

    3.3 可视化连接的图像

    首先通过图像间是否具有匹配的局部描述子来定义图像间的连接,然后可视化这些连接情况。为了完成可视化,我们可以在图中显示这些图像,图的边代表连接。 可以使用pydot 工具包(http://code.google.com/p/pydot/),该工具包是功能强大的GraphViz 图形库的Python 接口。

    为了创建显示可能图像组的图,如果匹配的数目高于一个阈值,我们使用边来连接相应的图像节点。为了得到图中的 图像,需要使用图像的全路径(在下面例子中,使用 path 变量表示)。为了使图像看起来漂亮,我们需要将每幅图像尺度化为缩略图形式,缩略图的最大边为 100 像素。

    编写代码:

    # -*- coding: utf-8 -*-
    import json
    import os
    import urllib
    import urlparse
    from pylab import *
    from PIL import Image
    from PCV.localdescriptors import sift
    from PCV.tools import imtools
    import pydot
    
    download_path = "D:\\Python\\chapter2\\lovely"
    path = "D:\\Python\\chapter2\\lovely\\"
    
    imlist = imtools.get_imlist(download_path)
    nbr_images = len(imlist)
    
    featlist = [imname[:-3] + 'sift' for imname in imlist]
    for i, imname in enumerate(imlist):
        sift.process_image(imname, featlist[i])
    
    matchscores = zeros((nbr_images, nbr_images))
    
    for i in range(nbr_images):
        for j in range(i, nbr_images):  # only compute upper triangle
            print('comparing ', imlist[i], imlist[j])
            l1, d1 = sift.read_features_from_file(featlist[i])
            l2, d2 = sift.read_features_from_file(featlist[j])
            matches = sift.match_twosided(d1, d2)
            nbr_matches = sum(matches > 0)
            print('number of matches = ', nbr_matches)
            matchscores[i, j] = nbr_matches
    
    # copy values
    for i in range(nbr_images):
        for j in range(i + 1, nbr_images):  # no need to copy diagonal
            matchscores[j, i] = matchscores[i, j]
    
    # 可视化
    
    threshold = 2  # min number of matches needed to create link
    
    g = pydot.Dot(graph_type='graph')  # don't want the default directed graph
    
    for i in range(nbr_images):
        for j in range(i + 1, nbr_images):
            if matchscores[i, j] > threshold:
                # first image in pair
                im = Image.open(imlist[i])
                im.thumbnail((100, 100))
                filename = path + str(i) + '.jpg'
                im.save(filename)  # need temporary files of the right size
                g.add_node(pydot.Node(str(i), fontcolor='transparent', shape='rectangle', image=filename))
    
                # second image in pair
                im = Image.open(imlist[j])
                im.thumbnail((100, 100))
                filename = path + str(j) + '.jpg'
                im.save(filename)  # need temporary files of the right size
                g.add_node(pydot.Node(str(j), fontcolor='transparent', shape='rectangle', image=filename))
    
                g.add_edge(pydot.Edge(str(i), str(j)))
    g.write_jpg('D:\\Python\\chapter2\\lovely\\lovely.jpg')
    

    代码运行效果如下:
    在这里插入图片描述

    展开全文
  • 如何理解闭包这一概念

    千次阅读 2014-04-26 09:57:26
    首先,我觉得,一个概念,如果不理解也不影响使用的话,那么,就没必要去理解它、去学习它。闭包就是这样一个概念,你不理解它也能很好的用它。俺这两年写as3程序,是天天在和它打交道,甚至有过一个function套一个...
  • C/C++面试笔试详细总结—基本概念及其它问答题

    千次阅读 多人点赞 2020-09-25 18:35:20
    干货:C/C++笔试面试详细总结—基本概念及其它问答题 前言 本文覆盖了C/C++工程师面试的95%以上的常考基础概念及技术细节问答题(不含STL和socket以及嵌入式的深入问答,仅为中等层面问答),整理总结不易,不点赞...
  • 软件体系结构基本概念汇总

    千次阅读 2015-07-28 00:36:08
    由于网状结构比较自由,松散,因此,超文本组织方法比前两种方法更易于修改构件库的结构。缺点是在某些情况下用户难以在超文本浏览过程中正确选取构件; 3、了解软件体系结构的四个发展阶段。 答: (1)“无体系...
  • 一般全局变量存放在数据区,局部变量存放在栈区, 动态变量存放在堆区,函数代码放在代码区。 --------------------------------------------------------------- 栈区是普通的栈数据结构,遵循LIFO后进先出...
  • 机器视觉的一些概念

    万次阅读 2017-05-23 14:38:14
    一些关于机器视觉的概念 2013-07-27 12:19 20722人阅读 评论(2) 收藏 举报 本文章已收录于: 目录(?)[+] 物方远心镜头及像方远心镜头介绍 四种工业相机接口技术的比较 视觉...
  • Timed-Elastic-Band局部路径规划算法

    万次阅读 多人点赞 2018-10-30 11:30:43
    早前做工程时尝试了teb局部规划算法,觉得效果非常好。由于时间关系,并未深入了解,仅依靠做对比实验调节作者给出的接口参数满足工程需求。抽空看了一下作者关于teb算法的论文,记录一些笔记。  teb局部路径规划...
  • 流形的概念与应用

    千次阅读 2018-08-29 16:59:40
    根据百度百科的解释,流形是局部具有欧几里得...周志华的西瓜书第十章里有关于流形学习的概念——流形学习(manifold learning)是一类借鉴了拓扑流形概念的降维方法。“流形”是在局部与欧式空间同胚的空间,换言之...
  • 深度学习的基本概念总结

    千次阅读 2019-08-21 11:44:02
    1. 基本概念 1.1 为什么要使用深层网络 深度神经网络的学习是特征递进的,浅层的神经元只能学习一些低层次的简单特征(如边缘、纹理),而深层神经网络可以学到更高级... 权重矩阵的退化导致模型的有效自由度...
  •  1、栈区(stack)— 由编译器自动分配释放 ,存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。  2、堆区(heap) — 一般由程序员分配释放, 若程序员不释放,程序结束时可能由os回收 。...
  • 下面有几种网上的理解,我整理一下:一: 1、栈区(stack)— 由编译器自动分配释放 ,存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。 2、堆区(heap) — 一般由程序员分配释放, 若程序员...
  • 下面按照【概念】+【原则】+【场景】+【优点】+【缺点】+【应用】分别简述一下24种设计模式:   1、抽象工厂模式(Abstract Factory) 提供一个创建一系列相关或互相依赖对象的接口,而无需指定它们具体的类。...
  • 地图开发概念

    千次阅读 2013-10-31 22:22:25
    要明白地图的数据分类,必须先理解一个概念,就是地图图层的概念: 如上图,电子地图对我们实际空间的表达,事实上是通过不同的图层去描述,然后通过图层叠加显示来进行表达的过程。 对于我们地图应用目标的不同,...
  • 结果发现是因为使用了一个局部变量导致的。 因为C语言的局部变量被编译器自动放到栈区的空间(全局变量需要手动申请并释放空间)。嵌入式系统的栈区本来就很小,而且要放进去的变量是一个结构体类型,非常庞大(大数...
  • 图像配准概念

    千次阅读 2019-05-28 21:23:34
    但由于 B 样条的计算量与控制点的个数相关,控制点越多,自由度越高, 计算量就越大因此采用多分辨率的 B 样条将肺部图像分割后再配准, 可减少对不相关数据的计算,从而降低计算量。 (3)稠密位移场:Thirion ...
  • X3D 概念

    千次阅读 2008-07-10 18:05:00
    Extensible 3D (X3D)Part 1: Architecture and base components4 Concepts概念 4.1 一般4.1.1 主题此条款描述了X3D的核心概念,包括:如何创建和回放X3D场景,X3D场景的运行时语法,组件和概貌构成的模块化结构,...
  • JAVA一些基础概念

    万次阅读 2016-08-22 21:43:25
    Java是一门面向对象编程语言,不仅吸收了C++语言的各种优点,还摒弃了C++里难以理解的多继承、指针等概念,因此Java语言具有功能强大和简单易用两个特征。Java语言作为静态面向对象编程语言的代表,极好地实现了面向...
  • 先进计算机概念

    千次阅读 2007-07-26 14:02:00
    下载第12章先进计算机概念研究计算机性能与技术是为了使计算机运行得更快,从质量上看,这意味着要减少完成指定计算或运行特定程序的时间。为了提高计算机的速度,我们必须修改已有的电路设计,甚至针对数据流通路...
  • C++堆栈概念

    千次阅读 2008-05-06 13:02:00
    堆和栈的区别一、预备知识—程序的内存分配一个由c/C++编译的程序占用的内存分为以下几个部分1、栈区(stack)— 由编译器自动分配释放 ,存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。2、...
  • 大数据之重点概念及原理

    千次阅读 多人点赞 2018-08-09 23:27:54
    (一)概念: 指的是传统数据处理应用软件不足以处理(存储和计算)它们大而复杂的数据集。 (二)数据级别: 1.MB:普通用户数据级别 2.PB:企业级数据级别 3.ZB:全球数据总量级别 (三)特点: 容量大,种类多,...
  • 图像处理总结概念

    万次阅读 多人点赞 2018-05-08 13:29:41
    这样就可以用于增强局部的对比而不影响整体的对比,直方图均衡化通过有效地扩展常用的亮度来实现这种功能。 直方图均衡化处理的 “中心思想”是把原始图像的灰度直方图从比较集中的某个灰度区间变成在全部灰度...
  • 机器学习基本概念知识汇

    千次阅读 多人点赞 2018-02-13 11:30:45
    基本概念 机器学习方法 监督学习 无监督学习 半监督式学习 强化学习 机器学习中分类与聚类的本质区别 分类 聚类 分类与聚类的比较 机器学习算法分类 回归算法 基于实例的算法 决策树学习 贝叶斯方法 基于核的...
  • 水面无人艇局部危险避障算法研究Local Risk Obstacle Avoidance Algorithm of USV博主 的硕士毕业论文第1章 绪论1.1 引言 随着世界人口的不断增长以及世界经济快速发展的需求,陆地资源被人类以更快的速度消耗着。...
  • 操作系统概念 学习笔记

    万次阅读 多人点赞 2019-03-26 23:01:14
    9.5.3 全局分配和局部分配 9.6 系统颠簸 9.6.1 系统颠簸的原因 9.7 内存映射文件 9.8 内核内存的分配 9.8.1 buddy 系统 9.8.2 slab分配 预调页 第十 文件系统接口 10.1 文件概念 ...
  • 信号处理基本概念

    千次阅读 2018-10-13 19:25:59
    惯性接收方式通过质量-弹簧单自由度振动系统接收被测振动量,工作时,其外壳固定在振动物体上,整个传感器(包括质量块在内)跟着振动物体一起振动,但其中的机电转换环节---线圈由于是用极为柔软的弹簧片固定在外壳...
  • static变量与全局、局部变量的区别

    千次阅读 2010-12-27 11:16:00
    static变量与全局、局部变量的区别全局变量(外部变量)的说明之前再冠以static 就构成了静态的全局变量。全局变量本身就是静态存储方式,静态全局变量当然也是静态存储方式。这两者在存储方式上并无不同。这两者的...
  • 局部变量,堆栈溢出。。。

    千次阅读 2012-09-26 16:20:38
    使用堆就象是自己动手做喜欢吃的菜肴,比较麻烦,但是比较符合自己的口味,而且自由度大。 堆和栈的区别主要分: 操作系统方面的堆和栈,如上面说的那些,不多说了。 还有就是数据结构方面的堆和栈,这些都...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 18,438
精华内容 7,375
关键字:

局部自由度的概念