精华内容
下载资源
问答
  • HashMap底层实现原理

    万次阅读 2018-03-09 17:30:48
    HashMap底层实现原理 通过查看源码进行分析,即通过查看HashMap.class JDK 1.6.0_45 1、HashMap类 HashMap继承了AbstaractMap AbstractMap实现了Map接口(AbstarctMap中实现了Map中常用/常见方法...

    HashMap底层实现原理

    • 通过查看源码进行分析,即通过查看HashMap.class
    • JDK 1.6.0_45

    1、HashMap类

    • HashMap继承了AbstaractMap
    • AbstractMap实现了Map接口(AbstarctMap中实现了Map中常用/常见方法)
    • HashTable提供了Map接口所有可选的实现,并且语序key和vaule为null,HashMap基本功能和HashTable相同,都允许key和value为null,但是HashMap是非线程安全的。同时不能保证Entry的顺序
    • Hash假设能够将Entry分配到合适的bin中,put和get的时间复杂量为常量。遍历key或value和Entry的时间复杂度为HashMap的capactity + Entry的数量有关,如果对遍历Entry有一定性能的要求,那么不能将capacity设置的太高或者load factory太低
    • HashMap有两个参数初始化:capacity,load factor,可能会影响到它的性能,capacity决定HashTable的bin数量,load factor是一个衡量是否需要增加capacity的标准,当Entry的数量超过capacity 或者load factor时,则会rehashed,内部的数据结构将会重建,以保证hash table拥有2倍的buckets
    • load factor默认为0.75,它能够在时间和性能方面,提供一个折中。当空间负载越多,消耗的时间越多。在get和put的操作上,当我们设置初始化量capatity时,应该要考虑会有多少Entry,以及负载因子load factory,减少rehash的可能。如果实际的Entry容量达不到 capacity * load factor,将不会rehashed

    2、HashMap成员变量

    • DEFAULT_INITIAL_CAPACITY
    /**
    * The default initial capacity - MUST be a power of two.
    * 默认初始化容量-必须是2的幂(即:必须是2的n次方),默认是16
    */
    static final int DEFAULT_INITIAL_CAPACITY = 16;
    • MAXIMUM_CAPACITY
    /**
    * The maximum capacity, used if a higher value is implicitly specified
    * by either of the constructors with arguments.
    * MUST be a power of two <= 1<<30.
    * 任何一个构造函数隐式指定了一个具有参数的值,则使用该最大容量
    * 必须小于或者等于2的30次方(1<<30,表示 1 * 2的30次方 )
    * 即:new HashMap的时候,容量不得超过2的30次方
    */
    static final int MAXIMUM_CAPACITY = 1 << 30;
    • DEFAULT_LOAD_FACTOR
    /**
    * The load factor used when none specified in constructor.
    * 在构造函数中没有指定的负载因素的时候,使用这个成员变量(默认加载因子)
    */
    static final float DEFAULT_LOAD_FACTOR = 0.75f;
    • Entry[] table
    /**
    * The table, resized as necessary. Length MUST Always be a power of two.
    * Entry类型的数组,HashMap用这个来维护内部的数据结构,长度必须是2的n次方
    */
    transient Entry[] table;
    • int size
    /**
    * The number of key-value mappings contained in this map.
    * 在map中key-value映射数量(HashMap的大小)
    */
    transient int size;
    • threshold
    /**
    * The next size value at which to resize (capacity * load factor).
    * @serial
    * 下次扩容的临界值,大小>=threshold(容量和 * 加载因子),就会扩容
    * HashMap的极限容量
    */
    int threshold;
    • loadFactor
    /**
    * The load factor for the hash table.
    * 哈希表的加载因子
    * @serial
    */
    final float loadFactor;
    • modCount
    /**
    * The number of times this HashMap has been structurally modified
    * Structural modifications are those that change the number of mappings in
    * the HashMap or otherwise modify its internal structure (e.g.,
    * rehash).  This field is used to make iterators on Collection-views of
    * the HashMap fail-fast.  (See ConcurrentModificationException).
    *
    * HashMap结构修改的次数,结构性的修改是指,改变Entry的数量
    */
    transient volatile int modCount;

    3、HashMap构造函数

    • HashMap()
    /**
    * Constructs an empty <tt>HashMap</tt> with the default initial capacity
    * (16) and the default load factor (0.75).
    * 构造一个具有默认初始容量(16)和默认加载因子(0.75)的空HashMap
    */
    public HashMap() {
        this.loadFactor = DEFAULT_LOAD_FACTOR;
        threshold = (int)(DEFAULT_INITIAL_CAPACITY * DEFAULT_LOAD_FACTOR);
        table = new Entry[DEFAULT_INITIAL_CAPACITY];
        init();
    }
    • HashMap(int initialCapacity)
    /**
    * Constructs an empty <tt>HashMap</tt> with the specified initial
    * capacity and the default load factor (0.75).
    * 构造一个指定容量和默认加载因子(0.75)的空HashMap
    *
    * @param  initialCapacity the initial capacity.
    * @throws IllegalArgumentException if the initial capacity is negative.
    */
    public HashMap(int initialCapacity) {
        this(initialCapacity, DEFAULT_LOAD_FACTOR);
    }
    • HashMap(int initialCapacity, float loadFactor)
    /**
    * Constructs an empty <tt>HashMap</tt> with the specified initial
    * capacity and load factor.
    * 构造一个带有指定容量和指定加载因子的空HashMap
    *
    * @param  initialCapacity the initial capacity
    * @param  loadFactor      the load factor
    * @throws IllegalArgumentException if the initial capacity is negative
    *         or the load factor is nonpositive
    */
    public HashMap(int initialCapacity, float loadFactor) {
        //初始化容量<0,抛出异常
        if (initialCapacity < 0)
            throw new IllegalArgumentException("Illegal initial capacity: " +
                                               initialCapacity);
        //初始化容量>最大容量,默认使用最大容量
        if (initialCapacity > MAXIMUM_CAPACITY)
            initialCapacity = MAXIMUM_CAPACITY;
        //加载因子<=0或者为空,抛出异常
        if (loadFactor <= 0 || Float.isNaN(loadFactor))
            throw new IllegalArgumentException("Illegal load factor: " +
                                               loadFactor);
    
        // Find a power of 2 >= initialCapacity
        int capacity = 1;
        //初始化容量,做了一个移位运算,假设传入5,最终初始化容量为8
        while (capacity < initialCapacity)
            capacity <<= 1;//capacity = capacity << 1,乘以2的1次方
    
        this.loadFactor = loadFactor;
        threshold = (int)(capacity * loadFactor);
        table = new Entry[capacity];
        init();
    }
    • HashMap(Map
    /**
    * Constructs a new <tt>HashMap</tt> with the same mappings as the
    * specified <tt>Map</tt>.  The <tt>HashMap</tt> is created with
    * default load factor (0.75) and an initial capacity sufficient to
    * hold the mappings in the specified <tt>Map</tt>.
    *
    * 构建一个映射关系和指定Mpa相同的,新HashMap,默认初始化容量16,初始化加载因子0.75
    *
    * @param   m the map whose mappings are to be placed in this map
    * @throws  NullPointerException if the specified map is null
    */
    public HashMap(Map<? extends K, ? extends V> m) {
        this(Math.max((int) (m.size() / DEFAULT_LOAD_FACTOR) + 1,
        DEFAULT_INITIAL_CAPACITY), DEFAULT_LOAD_FACTOR);
        putAllForCreate(m);
    }

    4、HashMap容量/数据结构

    • 从第三小节中可以发现,所有源码最终使用的构造函数为HashMap(int initialCapacity, float loadFactor)
    • 而在HashMap(int initialCapacity, float loadFactor)构造函数中,我们来仔细看看源码
    public HashMap(int initialCapacity, float loadFactor) {
        /**
        * 这个构造函数主要做的事情:
        * 1.对传入的初始化容量、加载因子进行校验处理
        * 2.计算出大于初始化容量的最小2的N次方作为哈希表table的长度,再利用该长度创建Entry数组
        */
    
        //初始化容量<0,抛出异常
        if (initialCapacity < 0)
            throw new IllegalArgumentException("Illegal initial capacity: " +
                                               initialCapacity);
        //初始化容量>最大容量,默认使用最大容量
        if (initialCapacity > MAXIMUM_CAPACITY)
            initialCapacity = MAXIMUM_CAPACITY;
        //加载因子<=0或者为空,抛出异常
        if (loadFactor <= 0 || Float.isNaN(loadFactor))
            throw new IllegalArgumentException("Illegal load factor: " +
                                               loadFactor);
    
        // Find a power of 2 >= initialCapacity
        int capacity = 1;
        //初始化容量,做了一个移位运算,假设传入5,最终初始化容量为8
        while (capacity < initialCapacity)
            capacity <<= 1;//capacity = capacity << 1,乘以2的1次方
    
        this.loadFactor = loadFactor;
        threshold = (int)(capacity * loadFactor);
        //计算出capacity的值,创建Entry数组
        table = new Entry[capacity];
        init();
    }
    • while (capacity < initialCapacity),这句代码使用了移位运算,有效保证了HashMap的初始化容量始终为2的幂
    • 那么,为什么HashMap容量一定要为2的幂呢?

      HashMap中的数据结构是:数组 + 单列表,我们希望的是:元素存放的更加均匀,最理想的时候,Entry数组中每一个位置中只有一个元素,这样,查询的时候效率最高,不需要遍历单列表,也不需要通过equals去比较K,而且空间利用率最大,时间复杂度最低

    • 下面来看看数据结构

    • 从上图可以更容易发现,HashMap由 数组+链表 组成,每一个元素存储的是一个链表的头结点
    • 那么,元素按照什么规则存储到数组中呢?

      一般是通过 hash(key)%len获得,也就是元素的key的哈希值对数组的长度取模得到,如:12%4=0,13%4=1,17%4=1,21%4=1,所以13,17,21存储在Entry[1]中

    • 下面来看看Entry数组的结构

    static class Entry<K,V> implements Map.Entry<K,V> {
        /*
         * Entry是HashMap的内部类,它维护这一个Key-value映射关系
         * next:引用指向当前table位置的链表
         * hash值:用来确定每一个Entry链表在table中位置
         */
    
        final K key;
        V value;
        Entry<K,V> next;
        final int hash;
    
        /**
         * Creates new entry.
         */
        Entry(int h, K k, V v, Entry<K,V> n) {
            value = v;
            next = n;
            key = k;
            hash = h;
        }
    
        public final K getKey() {
            return key;
        }
    
        public final V getValue() {
            return value;
        }
    
        public final V setValue(V newValue) {
            V oldValue = value;
            value = newValue;
            return oldValue;
        }
    
        public final boolean equals(Object o) {
            if (!(o instanceof Map.Entry))
                return false;
            Map.Entry e = (Map.Entry)o;
            Object k1 = getKey();
            Object k2 = e.getKey();
            if (k1 == k2 || (k1 != null && k1.equals(k2))) {
                Object v1 = getValue();
                Object v2 = e.getValue();
                if (v1 == v2 || (v1 != null && v1.equals(v2)))
                    return true;
            }
            return false;
        }
    
        public final int hashCode() {
            return (key==null   ? 0 : key.hashCode()) ^
                (value==null ? 0 : value.hashCode());
        }
    
        public final String toString() {
            return getKey() + "=" + getValue();
        }
    
        /**
         * This method is invoked whenever the value in an entry is
         * overwritten by an invocation of put(k,v) for a key k that's already
         * in the HashMap.
         */
        void recordAccess(HashMap<K,V> m) {
        }
    
        /**
         * This method is invoked whenever the entry is
         * removed from the table.
         */
        void recordRemoval(HashMap<K,V> m) {
        }
    }

    5、HashMap实现存储

    public V put(K key, V value) {
        //如果key为空
        if (key == null)
            //如果为null,则调用putForNullKey:这就是为什么HashMap可以用null作为键的原因
            return putForNullKey(value);
        //计算key的hash值
        int hash = hash(key.hashCode());
        //计算该hash值在table中的下标
        int i = indexFor(hash, table.length);
        //对table[i]存放的链表尽心遍历
        for (Entry<K,V> e = table[i]; e != null; e = e.next) {
            Object k;
            //判断该条链上是否有hash值相同的(key相同)
            //若存在相同,则直接覆盖value,返回旧value
            //这就是为什么HashMap不能有两个相同的key的原因
            if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
                V oldValue = e.value;
                e.value = value;
                e.recordAccess(this);
                return oldValue;
            }
        }
    
        //修改次数
        modCount++;
        //把当前key,value添加到table[i]的链表中
        addEntry(hash, key, value, i);
        return null;
    }
    
    private V putForNullKey(V value) {
        //查找链表中,是否有null键
        for (Entry<K,V> e = table[0]; e != null; e = e.next) {
            if (e.key == null) {
                V oldValue = e.value;
                e.value = value;
                e.recordAccess(this);
                return oldValue;
            }
        }
        modCount++;
        //如果链中查找不到,则把该null键插入
        addEntry(0, null, value, 0);
        return null;
    }
    
    static int hash(int h) {
        //^异或(同为0,异为1) >>>转化为二进制右移位,不足补0
        h ^= (h >>> 20) ^ (h >>> 12);
        return h ^ (h >>> 7) ^ (h >>> 4);
    }
    
    static int indexFor(int h, int length) {
        //对于HashMap的table而言,数据分布需要均匀
        //怎么才能保证table元素分布均与呢?我们会想到取模,但是由于取模的消耗较大
        //而HashMap是通过&运算符(按位与操作)来实现的
        //capacity <<= 1,这样做总是能够保证HashMap的底层数组长度为2的n次方。当length为2的n次方时,h&(length - 1)就相当于对length取模
        //而且速度比直接取模快得多,这是HashMap在速度上的一个优化
        return h & (length-1);
    }
    
    void addEntry(int hash, K key, V value, int bucketIndex) {
        Entry<K,V> e = table[bucketIndex];
        table[bucketIndex] = new Entry<K,V>(hash, key, value, e);
        if (size++ >= threshold)
            resize(2 * table.length);
    }
    
    void createEntry(int hash, K key, V value, int bucketIndex) {
        Entry<K,V> e = table[bucketIndex];
        table[bucketIndex] = new Entry<K,V>(hash, key, value, e);
        size++;
    }
    • 仔细解读h & (length-1);为什么当length为2的n次方时,h&(length - 1)就相当于对length取模,而且速度更快呢?

    • 可以发现,当length=15,非2次幂的时候,存储位置放生碰撞
    • 当length=15时,即length-1=14,末尾是0,在 & 操作的时候,无论另一个是0还是1,最终结果都是0
    • 所以说,当length为2的幂时,不同hash值发生碰撞的机会比较少,这样分布的就比较均匀了,查询速度也比较快
    void createEntry(int hash, K key, V value, int bucketIndex) {
        Entry<K,V> e = table[bucketIndex];
        //首先取得bucketIndex位置的Entry头结点,并创建新节点,把该新节点插入到链表中的头部,该新节点的next指针指向原来的头结点
        table[bucketIndex] = new Entry<K,V>(hash, key, value, e);
        size++;
    }

    6、HashMap实现获取

    //读取的步骤比较简单,调用hash(key)求得key的hash值,然后调用indexFor求的hash值对应table的索引位置,然后遍历索引位置的列表,如果存在key,则返回
    public V get(Object key) {
        //如果key为null,求null键
        if (key == null)
            return getForNullKey();
        int hash = hash(key.hashCode());
        for (Entry<K,V> e = table[indexFor(hash, table.length)];
             e != null;
             e = e.next) {
            Object k;
            if (e.hash == hash && ((k = e.key) == key || key.equals(k)))
                return e.value;
        }
        return null;
    }
    展开全文
  • 简介 名称 特点 电机的分类 PMSM的机械组成 本书目的 三相PMSM的数学建模 三相PMSM的基本数学模型 PMSM转子结构分类 PMSM模型假设 ...书中坐标系MATLAB自身坐标系之间的关系 同步旋转坐标系dq下的P

    文章目录


    # 简介
    ## 名称

    • 中文:永磁同步电机

    • 英文:Permanent Magnetic Synchronous Machine

    • 缩写:PMSM

    • 永磁式:采用永磁材料作为转子来励磁建立主磁场

    • 同步: 转子旋转速度与定子绕组所产生的旋转磁场的速度是一样的

    ##特点

    • PMSM 结构简单,运行可靠,体积小,质量轻,损耗小,效率高,电机形状和尺寸可以灵活多样等
    • PMSM是一个多变量,强耦合,变参数,非线性系统
    • PMSM属于交流电机,定子绕组与异步电机相同
    • 同步电动机的电流在相位上是超前于电压的,即同步电动机是一个容性负载(如何理解???)
    • 一个典型的应用:作为牵引电机被广泛用在纯电动汽车上

    ##电机的分类
    这里写图片描述

    ##

    展开全文
  • Retinex 是两个单词合成的,它们分别是 retina (视网膜) cortex (皮层),因此 Retinex 理论很多时候也被称为是视网膜皮层理论。 最初的基于 Retinex 理论的模型提出了一个人眼视觉系统 (HVS, Human Visual ...

    1、Retinex理论

    Retinex 是两个单词合成的,它们分别是 retina (视网膜)和 cortex (皮层),因此 Retinex 理论很多时候也被称为是视网膜皮层理论。

    最初的基于 Retinex 理论的模型提出了一个人眼视觉系统 (HVS, Human Visual System) 解 释了人眼为什么对于光线波长和亮度互不对应。在这个理论中,物体能够被观察到的颜色信 息是由两个因素决定的:物体本身的反射性质和物体周围的光照强度,但根据颜色恒常性的 理论,物体有自身的固有属性,这个不会受到光照影响,一个物体对于不同光波的反射能力 才能够决定物体的颜色。Retinex 理论的基本思想就是光照强度决定了原始图像中所有像素点 的动态范围大小,而原始图像的固有属性则是由物体自身的反射系数决定,即假设反射图像 和光照图像相乘为原始图像。所以 Retinex 的思路即是去除光照的影响,保留住物体的固有属性。

    Retinex 模型示意图

    如图 所示,假设观察者处成像的图像为?(?, ?),则表达式为:

    其中,?(?, ?)表示周围光照强度信息的光照分量,?(?, ?)表示物体本身固有性质的反射分量。
    对公式 (2.14) 两边作对数处理:

    作对数处理的两大好处:首先因为人眼对亮度的感知能力不是线性的,它近似于对数曲线,如图 所示,

    人眼对亮度的感知能力

    其次是复杂的乘除在对数域中是简单的加减法,这些可以大幅度降低算法的复杂度。
    Retinex 的基本算法流程图如图 所示:

    Retinex 算法基本流程图

    2、单尺度Retinex(Single-SR)

    单尺度 Retinex 算法的处理过程非常拟合人眼的视觉成像过程,该算法的基本思路是:首 先先构建高斯环绕函数,然后利用高斯环绕函数分别对图像的三个色彩通道 (R 、 G 和 B) 进行 滤波,则滤波后的图像就是我们所估计的光照分量,接着再在对数域中对原始图像和光照分 量进行相减得到反射分量作为输出结果图像。该算法能压缩图像的动态范围、一定程度上的 保持图像的颜色和细节的增强。其具体的表达式如下:

    其中,?(?, ?)为原始图像,?(?, ?)为反射分量,?(?, ?)为光照分量,? ? 表示第 i 个色彩通道的 反射图像, * 代表卷积,?(?, ?)为高斯环绕函数,?(?, ?)的构造如下:

    其中,?被称为高斯环绕的尺度参数,它是整个算法中的唯一可调节的参数,所以它可以非常 容易影响到图像增强的最终结果。下图 是经过许多调试之后得出的最佳的尺度参数的高斯 环绕函数的模型和实验结果图。在实验过程中发现,当?比较小的时候,代表高斯模板尺度小, 此时能够较好的保持边缘的细节信息,动态范围变大,但是色彩无法保持;当?比较大的时候, 色彩恢复很好,但动态范围变小,细节保持差。

    SSR 最佳结果图和高斯环绕函数

    SSR 算法的基本流程:
    (1) 输入原始图像?(?, ?),并分离三个颜色空间分量。
    (2) 确定尺度参数?的大小,保证满足条件∬ ?(?, ?)???? = 1的 λ的值。
    (3) 根据公式 (2.17) 得到反射图像?(?, ?)。
    (4) 将?(?, ?)从对数域转换到实数域得到?(?, ?)。
    (5) 对?(?, ?)进行线性拉伸处理,得到最终结果并显示。

    3、多尺度 Retinex(MSR)

    由于 SSR 单尺度需要在颜色保真度和细节保持度上追去一个完美的平衡,而这个平衡在 应对不同图像的时候一般都有差别,所以针对这个情况, Jobson 和 Rahman 等人再次提出了 多尺度的 Retinex 算法 (MSR) ,即对一幅图像在不同的尺度上利用高斯进行滤波,然后在对 不同尺度上的滤波结果进行平均加权,获得所估计的照度图像。MSR 简单来说就是相当于做 多次的 SSR 。其公式如下:

    其中, N 是尺度参数的个数,如果 N 为 1 ,则就是前面介绍的单尺度的 Retinex 算法。通过许 多实验后发现,当 N 取 3 ,即使用三个不同尺度的高斯滤波器对原始图像进行滤波处理时, 效果最好。? ? 是第 k 个尺度在进行加权的时候的权重系数,它需要满足下面的公式:

    经过实验发现,当取平均时,能适用于大量的低照度图像,且运算简单。最后,? ? (?, ?) 是在第 k 个尺度上的高斯滤波函数,即:

    MSR 不同尺度下的单通道滤波图像及结果图

    图显示了一幅彩色图像的一个通道中在不同尺度下的滤波结果图像。

    通过实验证明了多尺度的 Retinex 在颜色保持和细节突出等方面比单尺度的 SSR 要好很 多,但是一般情况下尺度选择为 3 ,所以一次 MSR 等同于三次的 SSR ,在时间复杂度上要 超出很多。

    4、带颜色恢复的多尺度 Retinex(MSRCR)

    前面介绍了单尺度 Retinex 和多尺度 Retinex ,我们都发现它们都是直接在 RGB 颜色空 间中直接进行的像素运算,在前面的颜色空间中介绍过,当使用 RGB 颜色空间时,由于其 自身很强的耦合性,所以无法保证在经过操作后各个通道之间像素点的比值与原始图像的一 致。假设原图像的三个通道的分别为? ? (?, ?)、? ? (?, ?)和? ? (?, ?),当输入图像由 MSR 算法增 强之后三个通道变为了??? ? (?, ?)、??? ? (?, ?)和??? ? (?, ?)。这个比例关系在增强效果十分理想的情况下应该由以下的公式表示:

    然而在实际中,这种比例是很难保证的,即在增强之后,比例的数值往往都会发生改 变,着就是色彩失真的原因所在。而前面的算法都未将这点考虑进行,所以很容易就会出现 色彩失真现象。针对这个情况, Jobson 和 Rahman 等人又一次提出了新的算法,即带颜色恢 复的多尺度 Retinex(MSRCR)。该算法可以将 MSR 得到的结果按照一定的比例进行调整 以求恢复原来的比例数值,具体是通过引入了颜色恢复因子 C ,其公式如下:

    其中? ? (?, ?)是?(? ∈ {?, ?, ?})颜色通道的颜色恢复因子,?是增益常数,?是一个调节因子, 一般情况下是非线性的。为了能在正常的屏幕中显示图像,需要对对数域中的像素值进行拉 伸处理,最好结合公式 (2.19) 和 (2.23) 可以得到 MSRCR 的数学表达式:

    其中,?和?分别是增益和偏移量系数,作用是为了将最后的图像能很好的显示在屏幕中。下列表格为 MSRCR 的一些基本参数。

    下图中给出了尺度参数?分别为 30 、 80 、 200 时 MSRCR 的处理效果。

    MSRCR 算法增强效果图
    展开全文
  • SIFT算法原理

    万次阅读 多人点赞 2019-03-16 21:33:58
    1、具有较好的稳定性不变性,能够适应旋转、尺度缩放、亮度的变化,能在一定程度上不受视角变化、仿射变换、噪声的干扰。 2、区分性好,能够在海量特征数据库中进行快速准确的区分信息进行匹配 3、多量性,就算...

    SIFT算法

    SIFT即尺度不变特征变换,是用于图像处理领域的一种描述。这种描述具有尺度不变性,可在图像中检测出关键点,是一种局部特征描述子

    一、SIFT算法特点:

    1、具有较好的稳定性不变性,能够适应旋转、尺度缩放、亮度的变化,能在一定程度上不受视角变化、仿射变换、噪声的干扰。
    2、区分性好,能够在海量特征数据库中进行快速准确的区分信息进行匹配
    3、多量性,就算只有单个物体,也能产生大量特征向量
    4、高速性,能够快速的进行特征向量匹配
    5、可扩展性,能够与其它形式的特征向量进行联合

    二、SIFT算法实质

    在不同的尺度空间上查找关键点,并计算出关键点的方向。
    在这里插入图片描述

    三、SIFT算法实现特征匹配主要有以下三个流程:

    1、提取关键点:关键点是一些十分突出的不会因光照、尺度、旋转等因素而消失的点,比如角点、边缘点、暗区域的亮点以及亮区域的暗点。此步骤是搜索所有尺度空间上的图像位置。通过高斯微分函数来识别潜在的具有尺度和旋转不变的兴趣点。
    2、定位关键点并确定特征方向:在每个候选的位置上,通过一个拟合精细的模型来确定位置和尺度。关键点的选择依据于它们的稳定程度。然后基于图像局部的梯度方向,分配给每个关键点位置一个或多个方向。所有后面的对图像数据的操作都相对于关键点的方向、尺度和位置进行变换,从而提供对于这些变换的不变性。
    3. 通过各关键点的特征向量,进行两两比较找出相互匹配的若干对特征点,建立景物间的对应关系。

    四、尺度空间

    1、概念
    尺度空间即试图在图像领域中模拟人眼观察物体的概念与方法。例如:观察一颗树,关键在于我们想要观察是树叶子还是整棵树:如果是一整棵树(相当于大尺度情况下观察),那么就应该去除图像的细节部分。如果是树叶(小尺度情况下观察),那么就该观察局部细节特征。
    SIFT算法在构建尺度空间时候采取高斯核函数进行滤波,使原始图像保存最多的细节特征,经过高斯滤波后细节特征逐渐减少来模拟大尺度情况下的特征表示。
    利用高斯核函数进行滤波的主要原因有两个:
    (1)高斯核函数是唯一的尺度不变核函数。
    (2)DoG核函数可以近似为LoG函数,这样可以使特征提取更加简单。同时,David. Lowe作者在论文中提出将原始图像进行2倍上采样后滤波能够保留更多的信息便于后续特征提取与匹配。其实尺度空间图像生成就是当前图像与不同尺度核参数σ进行卷积运算后产生的图像。
    2、表示
    L(x, y, σ) ,定义为原始图像 I(x, y)与一个可变尺度的2维高斯函数G(x, y, σ) 卷积运算。
    在这里插入图片描述
    *表示卷积运算,(x,y)代表图像的像素位置。是尺度空间因子,值越小表示图像被平滑的越少,相应的尺度也就越小。大尺度对应于图像的概貌特征,小尺度对应于图像的细节特征。

    五、高斯金字塔的构建

    1、概念
    尺度空间在实现时使用高斯金字塔表示,高斯金字塔的构建分为两步:
    (1)对图像做高斯平滑;
    (2)对图像做降采样。
    在这里插入图片描述
    图像的金字塔模型是指将原始图像不断降阶采样,得到一系列大小不一的图像,由大到小,从下到上构成的塔状模型。原图像为金子塔的第一层,每次降采样所得到的新图像为金字塔的一层(每层一张图像),每个金字塔共n层。为了让尺度体现其连续性,高斯金字塔在简单降采样的基础上加上了高斯滤波。如上图所示,将图像金字塔每层的一张图像使用不同参数做高斯模糊,Octave表示一幅图像可产生的图像组数,Interval表示一组图像包括的图像层数。另外,降采样时,高斯金字塔上一组图像的初始图像(底层图像)是由前一组图像的倒数第三张图像隔点采样得到的。
    2、表示
    高斯图像金字塔共o组、s层,则有
    在这里插入图片描述
    σ:尺度空间坐标;s:sub-level层坐标;σ0:初始尺度;S:每组层数(一般为3~5)
    组内和组间尺度:
    2i-1(σ,kσ,k2σ,...,kn-1σ)   k=21/S
    i:金字塔组数;n:每一组的层数

    六、DOG空间极值检测

    1、DOG函数
    在这里插入图片描述
    2、DoG高斯差分金字塔
    (1)对应DOG算子,需构建DOG金字塔。
    可以通过高斯差分图像看出图像上的像素值变化情况。(如果没有变化,也就没有特征。特征必须是变化尽可能多的点。)DOG图像描绘的是目标的轮廓。
    在这里插入图片描述
    (2)DOG局部极值检测
    特征点是由DOG空间的局部极值点组成的。为了寻找DoG函数的极值点,每一个像素点要和它所有的相邻点比较,看其是否比它的图像域和尺度域的相邻点大或者小。特征点是由DOG空间的局部极值点组成的。为了寻找DoG函数的极值点,每一个像素点要和它所有的相邻点比较,看其是否比它的图像域和尺度域的相邻点大或者小。如下图,中间的检测点和它同尺度的8个相邻点和上下相邻尺度对应的9×2个点共26个点比较,以确保在尺度空间和二维图像空间都检测到极值点。
    在这里插入图片描述
    (2)去除边缘效应
    在边缘梯度的方向上主曲率值比较大,而沿着边缘方向则主曲率值较小。候选特征点的DoG函数D(x)的主曲率与2×2Hessian矩阵H的特征值成正比。
    在这里插入图片描述
    其中,Dxx,Dxy,Dyy是候选点邻域对应位置的差分求得的。
    H的特征值α和β代表x和y方向的梯度
    在这里插入图片描述
    表示矩阵H对角线元素之和,表示矩阵H的行列式。假设是α较大的特征值,而是β较小的特征值,令在这里插入图片描述,则
    在这里插入图片描述
    该值在两特征值相等时达最小。Lowe论文中建议阈值T为1.2,即在这里插入图片描述
    时保留关键点,反之剔除

    七、关键点方向分配

    1、通过尺度不变性求极值点,需要利用图像的局部特征为给每一个关键点分配一个基准方向,使描述子对图像旋转具有不变性。对于在DOG金字塔中检测出的关键点,采集其所在高斯金字塔图像3σ邻域窗口内像素的梯度和方向分布特征。梯度的模值和方向如下:
    在这里插入图片描述
    2、本算法采用梯度直方图统计法,统计以关键点为原点,一定区域内的图像像素点确定关键点方向。在完成关键点的梯度计算后,使用直方图统计邻域内像素的梯度和方向。梯度直方图将0~360度的方向范围分为36个柱,其中每柱10度。如下图所示,直方图的峰值方向代表了关键点的主方向,方向直方图的峰值则代表了该特征点处邻域梯度的方向,以直方图中最大值作为该关键点的主方向。为了增强匹配的鲁棒性,只保留峰值大于主方向峰值80%的方向作为该关键点的辅方向。
    在这里插入图片描述

    八、关键点描述

    对于每一个关键点,都拥有位置、尺度以及方向三个信息。为每个关键点建立一个描述符,用一组向量将这个关键点描述出来,使其不随各种变化而改变,比如光照变化、视角变化等等。这个描述子不但包括关键点,也包含关键点周围对其有贡献的像素点,并且描述符应该有较高的独特性,以便于提高特征点正确匹配的概率。
    在这里插入图片描述
    Lowe实验结果表明:描述子采用4×4×8=128维向量表征,综合效果最优(不变性与独特性)。

    九、关键点匹配

    1、分别对模板图(参考图,reference image)和实时图(观测图,
    observation image)建立关键点描述子集合。目标的识别是通过两点集内关键点描述子的比对来完成。具有128维的关键点描述子的相似性度量采用欧式距离。
    3、匹配可采取穷举法完成,但所花费的时间太多。所以一般采用kd树的数据结构来完成搜索。搜索的内容是以目标图像的关键点为基准,搜索与目标图像的特征点最邻近的原图像特征点和次邻近的原图像特征点。
    Kd树如下如所示,是个平衡二叉树
    在这里插入图片描述

    十、总结

    SIFT特征具有稳定性和不变性,在图像处理和计算机视觉领域有着很重要的作用,其本身也是非常复杂的,由于接触SIFT不是很久,对其中的相关知识了解还很不足,经多方查阅参考,写得此文,内容还不够详尽,望多多见谅。以下是SIFT算法的粗略总结。
    1、DoG尺度空间的极值检测。
    2、删除不稳定的极值点。
    3、确定特征点的主方向
    4、生成特征点的描述子进行关键点匹配。

    参考资料
    SIFT算法详解
    https://blog.csdn.net/zddblog/article/details/7521424
    SIFT算法系列之尺度空间
    https://blog.csdn.net/Small_Munich/article/details/79968229
    SIFT特征详解
    https://www.cnblogs.com/wangguchangqing/p/4853263.html

    展开全文
  • 图像分割综述

    万次阅读 多人点赞 2019-07-09 22:03:48
    其基本思想是,模拟由一些基因串控制的生物群体的进化过程,把该过程的原理应用到搜索算法中,以提高寻优的速度质量。此算法的搜索过程不直接作用在变量上,而是在参数集进行了编码的个体,这使得遗传算法可直接对...
  • 遗传算法

    万次阅读 多人点赞 2019-04-06 21:41:47
    具体做法是,将多峰函数进行两次求对数,因此,多峰函数与适应度的关系可如下表示: 用MATLAB做出适应度函数图像如下: 对比前文中的图不难看出,图像得到了有效的平缓,同时不同峰之间也保持着一定的高低之别...
  • MySQL 面试题

    万次阅读 多人点赞 2019-09-02 16:03:33
    因为 MySQL 还会有部分内容运维相关度比较高,所以本文我们分成两部分【开发】【运维】两部分。 对于【开发】部分,我们需要掌握。 对于【运维】部分,更多考验开发的知识储备情况,当然能回答出来是比较好的...
  • 格兰杰因果关系检验(原理及Python实例)

    万次阅读 热门讨论 2016-01-03 22:14:15
    格兰杰因果检验是计量经济学中一种用于推断要素间相关影响关系的重要方法,由诺贝尔经济学奖得主克莱夫·格兰杰提出。它以向量自回归(VAR,Vector Auto regression)模型为基础,结合统计推断中的F统计量,发展而来...
  • 如果页面浮动布局多,就要增加很多空div,让人感觉很不好 3,父级div定义 伪类:after 和 zoom 原理:IE8以上和非IE浏览器才支持:after,原理和方法2有点类似,zoom(IE转有属性)可解决ie6,ie7浮动问题 优点:浏览器...
  • 测试开发需要学习的知识结构

    万次阅读 多人点赞 2018-04-12 10:40:58
    利用因果图生成测试用例的基本步骤: (1) 分析软件规格说明描述中, 那些是原因(即输入条件或输入条件的等价类),那些是结果(即输出条件), 并给每个原因和结果赋予一个标识符. (2) 分析软件规格说明描述中的语义.找出...
  • 软件测试_笔记(完整版)

    万次阅读 多人点赞 2018-07-02 08:51:28
    广义的软件测试定义:人工或自动地运行或测定某系统的过程,目的在于检验它是否满足规定的需求或弄清预期结果和实际结果间的差别 为什么要做软件测试 发现软件缺陷 功能错 功能遗漏 超出需求部分(画蛇添足) ...
  • 格兰杰因果关系检验(原理及R语言应用实例)

    万次阅读 多人点赞 2010-05-14 13:00:00
    诺贝尔经济学奖获得者,计量经济学大师克莱夫·格兰杰(Clive Granger)从预测的角度给出了因果关系的一种描述性定义,这就是我们现在所熟知的Granger因果关系。格兰杰因果关系检验是计量经济方法中常用的一种检验...
  • 史上最全面Java面试汇总(面试题+答案)

    万次阅读 多人点赞 2018-07-06 14:09:25
    跨域不同 抽象类所体现的是一种继承关系,要想使得继承关系合理,父类派生类之间必须存在"is-a" 关系,即父类派生类在概念本质上应该是相同的。对于接口则不然,并不要求接口的实现者接口定义在概念本质上是...
  • 让你真正明白装饰器的工作原理和执行顺序

    万次阅读 多人点赞 2018-09-05 01:56:05
    0.什么是Python装饰器...从头往下看,让你彻底弄明白python装饰器的演变,执行顺序,多个装饰器执行顺序等工作原理。 #1.定义一个函数,在不修改函数代码的前提下,对函数的功能进行拓展。比如权限验证。 def f1(...
  • DDR3原理详解

    万次阅读 多人点赞 2018-05-04 09:42:14
    转自:http://www.360doc.com/content/14/0116/16/15528092_345730642.shtml 首先,我们先了解一下内存的大体结构工作流程,这样会比较容量理解这些参数...表格的检索原理一样,先指定一个行(Row),再指定一个...
  • GPS测量原理及应用 知识总结

    千次阅读 多人点赞 2019-12-21 14:33:16
    1. 天球坐标系地球坐标系是用来干什么的? 2. 怎么定义坐标系? 3. 坐标系统之间的转换 4. 时间系统 判断 第三章 1. 卫星的无赦运动,确定开普勒轨道方程,要知道参数 2. GPS卫星星历 第四章 1. GPS卫星的...
  • 观点来源: http://muchong.com/html/201001/1803662.html http://muchong.com/html/201009/2413619.html ... 首先,第一原理是firstprinciple,不是从头算(ab initi
  • 在图像测量过程以及机器视觉应用中,为确定空间物体表面某点的三维几何位置与其在图像中对应点之间的相互关系,必须建立相机成像的几何模型,这些几何模型参数就是相机参数。在大多数条件下这些参数必须通过实验与...
  • 深入理解Java并发之synchronized实现原理

    万次阅读 多人点赞 2017-06-04 17:44:44
    ,因为上述代码犯了严重的错误,虽然我们使用synchronized修饰了increase方法,但却new了两个不同的实例对象,这也就意味着存在着两个不同的实例对象锁,因此t1t2都会进入各自的对象锁,也就是说t1t2线程使用的...
  • MySQL的锁机制加锁原理

    万次阅读 多人点赞 2019-03-09 10:35:01
    MySQL的锁机制加锁原理 文章目录MySQL的锁机制加锁原理1.行锁2.表锁3.页锁4.乐观锁悲观锁4.1悲观锁4.2乐观锁5.MySQL/InnoDB中的行锁表锁问题5.1InnoDB锁的特性6.Record Lock、Gap Lock、Next-key Lock锁6.1...
  • 比特币原理详解

    万次阅读 多人点赞 2019-10-29 11:12:13
    一、什么是比特币 比特币是一种电子货币,是一种基于密码学的货币,在2008年11月1日由中本...其好处不多做赘述,这一层面介绍的文章很多,本文主要从更深层的技术原理角度进行介绍。 二、问题引入 假设现有4个人...
  • 通俗易懂的AI算法原理

    万次阅读 2019-06-27 08:24:55
    我想尽量用直白的语言、较少的数学知识给各位产品经理讲清楚各个算法的原理是什么。 机器学习的过程 机器学习的过程从本质上来说就是通过一堆的训练数据找到一个与理想函数(f)相接近的函数。在理想情况下,对于...
  • 镜头MTF值的基本原理和解读

    千次阅读 多人点赞 2020-07-27 06:16:39
    镜头是摄影师摄影爱好者投资最高的设备之一,也是决定拍摄质量的最重要的因素。因此,镜头的质量,历来受到极大的重视。我们当然会很关心摄影镜头的测量方法。 摄影的最终产品是照片,所以,根据拍摄照片的...
  • 说到索引,很多人都知道“索引是一个排序的列表,在这个列表中存储着索引的值包含这个值的数据所在行的物理地址,在数据十分庞大的时候,索引可以大大加快查询的速度,这是因为使用索引后可以不用扫描全表来定位某...
  • 操作系统原理

    千次阅读 多人点赞 2019-09-07 19:20:17
    目录 目录 ... 操作系统原理1 —— 概念 >> 操作系统原理2——OS结构 >> 操作系统原理3——多道程序 >> 操作系统原理4——存储管理 >> 操作系统原理5——文件管理 ...
  • 19跨考中科大计算机408经验贴

    万次阅读 多人点赞 2019-03-20 11:08:57
    中科大的复试真的噩梦,笔试四本书:离散、数据库、编译原理和系统结构,我本科学了前两本,后两本没学,编译原理真的是天书啊,太难了,我又哭唧唧(´;︵;`) 我在准备复试的时候已经做好了调剂软院的打算。过年前我...
  • Android View绘制显示原理简介

    千次阅读 多人点赞 2016-10-14 18:08:00
    现在越来越多的应用开始重视流畅度方面的测试,了解Android应用程序是如何... 首先,用一句话来概括一下Android应用程序显示的过程:Android应用程序调用SurfaceFlinger服务把经过测量、布局绘制后的Surface渲染到显
  • PCA原理分析意义(一)

    万次阅读 多人点赞 2017-03-30 09:36:56
    例如上面淘宝店铺的数据,从经验我们可以知道,“浏览量”“访客数”往往具有较强的相关关系,而“下单数”“成交数”也具有较强的相关关系。这里我们非正式的使用“相关关系”这个词,可以直观理解为“当某一天...
  • 主成分分析

    万次阅读 多人点赞 2014-03-12 10:07:24
    主成分分析 在实际问题中,我们经常会遇到研究多个变量的问题,而且在多数情况下,多个变量之间常常存在一定的相关性。由于变量个数较多再加上变量之间的相关性,势必增加了...第一节 主成分分析的原理及模型 一、
  • 关系模式设计中的问题关系数据库设计要解决的主要问题 ...产生不好模式的根本原因是数据之间存在着某些数据依赖。解决方法是通过分解关系来消除其中不合适的数据依赖。数据依赖数据依赖的定义数据依赖是数据之间的

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 241,198
精华内容 96,479
关键字:

原因和结果关系的原理