精华内容
参与话题
问答
  • 设计代码的经验总结

    万次阅读 2019-01-15 12:34:43
    设计模式虽然好,但也并非任何场景都适合,我觉得在设计代码时只要时刻牢记高内聚、低耦合这两个原则,并在编码中实践,写出来的代码必定不会差。 衡量代码优异的标准:拓展性,复用性,维护性;...

    设计模式虽然好,但也并非任何场景都适合,我觉得在设计代码时只要时刻牢记高内聚、低耦合这两个原则,并在编码中实践,写出来的代码必定不会差。

    衡量代码优异的标准:拓展性,复用性,维护性;23种设计模式;亦或者面向对象的封装、继承、多态在我看来无非是对高内聚低耦合原则的不同维度的阐述。

    下面分别阐述个人的一些理解。

    高内聚低耦合这两个原则都是围绕相互间的关联来定义的,内聚指的是将相互间关系密切的代码聚合到一个模块,怎么划分“关系密切”是重点,这就要强调“单一职责”了,用面向对象来解释就是定义一个类,类的属性和行为都是履行这个类的职责定义的,不属于这个类职责的属性和行为就不应该定义在这个类里,如此,类的职责就很明确了,而类里的属性和行为也都是密切相关的,也就符合所谓的高内聚。耦合从字面意思来看,耦可解释为藕断丝连,也就是关系杂乱,合可解释为串联合作,例如给耳机插入电脑合作我们就能通过耳机听音乐了,低耦合即是减少这些关系,用尽可能少的关系来完成联合工作,我觉得这个用迪米特法则来解释比较贴切,即对一个对象的了解越少越好,比如你去菜市场买猪肉,你只需要到猪肉贩那买猪肉即可,还用替猪肉贩操心你这猪肉没了帮他杀猪再进行买卖吗?将这个例子转化为程序例子即是:你得到一个猪肉贩对象,你先调用猪肉贩对象的查询猪肉量的接口,判断有没有足够你想买的猪肉量,如果没有,就调用猪肉贩对象杀头猪的接口,然后再调用买猪肉的接口,搞得人家猪肉贩跟个白痴似的。假设猪肉贩就是个白痴,没猪肉了你调用杀猪接口帮他杀猪,ok,猪肉贩以前都是在肉摊旁杀猪,所以你调用杀猪接口能帮他杀猪,但后来菜市场规定不能再菜市场内杀猪,那样太扰民了,猪肉贩找了个搭档,自己专门在肉摊上卖猪肉,搭档则负责在其他地方杀猪,要新的猪肉就扣个电话叫搭档把杀好的猪送过来,这样就相当于原本杀猪的接口没用了,如果猪肉贩还是你想象的那么白痴,那么新的接口变了的时候,你就要自己去追踪变化和修改变化,这样势必增加了编程的负担,这也是典型的一个耦合严重的例子,买个猪肉还用操那么多心。那怎么降低耦合呢?很简单,首先我们要明确职责,我只是个买主,我只想买猪肉,人家猪肉够不够卖用得着我操心吗?这个问题应该是猪肉贩的职责,杀猪也是人家猪肉贩的职责,我们只需要关心怎么买猪肉的这个接口即可。

    如果想将这个接口进一步优化,我们也可以把猪肉贩这个对象设计成超级猪肉贩,即所有的猪肉贩都继承这个超级猪肉贩,这样的话我们的设计就不是和某一猪肉贩强行捆绑在一块了,比如这个猪肉贩猪肉卖完了,市场安排了另一个猪肉贩在这个摊位卖猪肉,你不会因为之前的猪肉贩不在了就买不了猪肉了,这也就是所谓的面向对象的依赖倒置原则,即应该依赖于接口,不依赖于具体的对象。

    拓展性我的理解是对于一个模块,我可以很容易的拓展它的功能,不用担心添加新功能会给其他平级功能造成影响。我觉得编码想达到这种成效,结合设计模式的“开闭原则”即可达成,即对拓展开发,对修改关闭。

    复用性是指对于一个模块或者一个方法,我不用关心它内部的结构和运作的细节,只需要了解参数的使用即可拿来协作,而关于它的具体定义的理解,有兴趣可以看看我之前写过的这篇《代码复用性的理解》

    维护性我的理解是围绕对代码的可读和易改两个方面的阐述,如果一份代码让人看起来就特别别扭费劲,那么光理解代码就得花不少时间,不好理解的代码改起来也势必要关心备至(也就是操不少心),当然好读懂的代码也有不容易改的,比如全局变量的滥用。

    展开全文
  • Pyinstaller 打包发布经验总结

    万次阅读 多人点赞 2018-09-02 18:46:30
    使用Pyinstaller打包Python项目包含了大量的坑,这篇文章总结实践得到的Pyinstaller打包经验。本文的例子为Python3.6代码,Pyinstaller3.4,在windows下打包为64位和32位版本。 目录 Pyinstaller基本使用方法 ...

    使用Pyinstaller打包Python项目包含了大量的坑,这篇文章总结实践得到的Pyinstaller打包经验。本文的例子为Python3.6代码,Pyinstaller3.4,在windows下打包为64位和32位版本。

    目录

    Pyinstaller基本使用方法

    Python项目的打包方法

    1.spec文件生成

    2.spec文件配置

    3.使用spec执行打包命令

    Visual C++ run-time .dlls包含

    Python模块的打包问题

    冻结打包路径

    其它问题


    Pyinstaller基本使用方法

    Python项目的打包方法

    1.spec文件生成

    2.spec文件配置

    3.使用spec执行打包命令

    Python模块的打包问题

    冻结打包路径

    其它问题


    Pyinstaller基本使用方法

    Pyinstaller可以通过简单的命令进行python代码的打包工作,其基本的命令为:

    pyinstaller -option xxx.py

    options的详情可参考官方帮助文档https://pyinstaller.readthedocs.io/en/stable/usage.html

    这边只介绍用到的option:-d生成一个文件目录包含可执行文件和相关动态链接库和资源文件等;-f仅生成一个可执行文件

    -D, --onedir Create a one-folder bundle containing an executable (default)
    -F, --onefile Create a one-file bundled executable.

    对于打包结果较大的项目,选用-d生成目录相比单可执行文件的打包方式,执行速度更快,但包含更加多的文件。本文的例子选中-D方式打包。

    Python项目的打包方法

    以一个多文件和目录的Python项目为例,项目文件包含:1.Python源代码文件;2.图标资源文件;3.其它资源文件

    以图中项目为例,Python源代码文件在多个目录下:bin, lib\app, lib\models, lib\views;图标资源文件在lib\icon目录下;其它资源文件在data目录下,包括文本文件,视频文件等等。

    1.spec文件生成

    为了进行自定义配置的打包,首先需要编写打包的配置文件.spec文件。当使用pyinstaller -d xxx.py时候会生成默认的xxx.spec文件进行默认的打包配置。通过配置spec脚本,并执行pyinstaller -d xxx.spec完成自定义的打包。

    通过生成spec文件的命令,针对代码的主程序文件生成打包对应的spec文件

     pyi-makespec -w xxx.py

    打开生成的spec文件,修改其默认脚本,完成自定义打包需要的配置。spec文件是一个python脚本,其默认的结构如下例所示

    # -*- mode: python -*-
    
    block_cipher = None
    
    
    a = Analysis(['fastplot.py'],
                 pathex=['D:\\install_test\\DAGUI-0.1\\bin'],
                 binaries=[],
                 datas=[],
                 hiddenimports=[],
                 hookspath=[],
                 runtime_hooks=[],
                 excludes=[],
                 win_no_prefer_redirects=False,
                 win_private_assemblies=False,
                 cipher=block_cipher)
    pyz = PYZ(a.pure, a.zipped_data,
                 cipher=block_cipher)
    exe = EXE(pyz,
              a.scripts,
              exclude_binaries=True,
              name='fastplot',
              debug=False,
              strip=False,
              upx=True,
              console=False )
    coll = COLLECT(exe,
                   a.binaries,
                   a.zipfiles,
                   a.datas,
                   strip=False,
                   upx=True,
                   name='fastplot')

    spec文件中主要包含4个class: Analysis, PYZ, EXE和COLLECT.

    • Analysis以py文件为输入,它会分析py文件的依赖模块,并生成相应的信息

    • PYZ是一个.pyz的压缩包,包含程序运行需要的所有依赖

    • EXE根据上面两项生成

    • COLLECT生成其他部分的输出文件夹,COLLECT也可以没有

    2.spec文件配置

    首先给出举例python项目的spec文件配置

    # -*- mode: python -*-
    import sys
    import os.path as osp
    sys.setrecursionlimit(5000)
    
    block_cipher = None
    
    
    SETUP_DIR = 'D:\\install_test\\FASTPLOT\\'
    
    a = Analysis(['fastplot.py',
                  'frozen_dir.py',
                  'D:\\install_test\\FASTPLOT\\lib\\app\\start.py',
                 'D:\\install_test\\FASTPLOT\\lib\\models\\analysis_model.py',
                 'D:\\install_test\\FASTPLOT\\lib\\models\\datafile_model.py',
                 'D:\\install_test\\FASTPLOT\\lib\\models\\data_model.py',
                 'D:\\install_test\\FASTPLOT\\lib\\models\\figure_model.py',
                 'D:\\install_test\\FASTPLOT\\lib\\models\\time_model.py',
                 'D:\\install_test\\FASTPLOT\\lib\\models\\mathematics_model.py',
                 'D:\\install_test\\FASTPLOT\\lib\\views\\constant.py',
                 'D:\\install_test\\FASTPLOT\\lib\\views\\custom_dialog.py',
                 'D:\\install_test\\FASTPLOT\\lib\\views\\data_dict_window.py',
                 'D:\\install_test\\FASTPLOT\\lib\\views\\data_process_window.py',
                 'D:\\install_test\\FASTPLOT\\lib\\views\\data_sift_window.py',
                 'D:\\install_test\\FASTPLOT\\lib\\views\\mathematics_window.py',
                 'D:\\install_test\\FASTPLOT\\lib\\views\\para_temp_window.py',
                 'D:\\install_test\\FASTPLOT\\lib\\views\\mainwindow.py',
                 'D:\\install_test\\FASTPLOT\\lib\\views\\paralist_window.py',
                 'D:\\install_test\\FASTPLOT\\lib\\views\\plot_window.py'],
                 pathex=['D:\\install_test\\FASTPLOT'],
                 binaries=[],
                 datas=[(SETUP_DIR+'lib\\icon','lib\\icon'),(SETUP_DIR+'data','data')],
                 hiddenimports=['pandas','pandas._libs','pandas._libs.tslibs.np_datetime','pandas._libs.tslibs.timedeltas',
                 'pandas._libs.tslibs.nattype', 'pandas._libs.skiplist','scipy._lib','scipy._lib.messagestream'],
                 hookspath=[],
                 runtime_hooks=[],
                 excludes=[],
                 win_no_prefer_redirects=False,
                 win_private_assemblies=False,
                 cipher=block_cipher)
                                         
                
    pyz = PYZ(a.pure, a.zipped_data,
                 cipher=block_cipher)
    exe = EXE(pyz,
              a.scripts,
              exclude_binaries=True,
              name='fastplot',
              debug=False,
              strip=False,
              upx=True,
              console=True)
    coll = COLLECT(exe,
                   a.binaries,
                   a.zipfiles,
                   a.datas,
                   strip=False,
                   upx=True,
                   name='fastplot')
    

    a) py文件打包配置

    针对多目录多文件的python项目,打包时候需要将所有相关的py文件输入到Analysis类里。Analysis类中的pathex定义了打包的主目录,对于在此目录下的py文件可以只写文件名不写路径。如上的spec脚本,将所有项目中的py文件路径以列表形式写入Analysis,这里为了说明混合使用了绝对路径和相对路径。

    b) 资源文件打包配置

    资源文件包括打包的python项目使用的相关文件,如图标文件,文本文件等。对于此类资源文件的打包需要设置Analysis的datas,如例子所示datas接收元组:datas=[(SETUP_DIR+'lib\\icon','lib\\icon'),(SETUP_DIR+'data','data')]。元组的组成为(原项目中资源文件路径,打包后路径),例子中的(SETUP_DIR+'lib\\icon','lib\\icon')表示从D:\\install_test\\FASTPLOT\\lib\\icon下的图标文件打包后放入打包结果路径下的lib\\icon目录。

    c)Hidden import配置

    pyinstaller在进行打包时,会解析打包的python文件,自动寻找py源文件的依赖模块。但是pyinstaller解析模块时可能会遗漏某些模块(not visible to the analysis phase),造成打包后执行程序时出现类似No Module named xxx。这时我们就需要在Analysis下hiddenimports中加入遗漏的模块,如例子中所示。

    d)递归深度设置

    在打包导入某些模块时,常会出现"RecursionError: maximum recursion depth exceeded"的错误,这可能是打包时出现了大量的递归超出了python预设的递归深度。因此需要在spec文件上添加递归深度的设置,设置一个足够大的值来保证打包的进行,即

    import sys
    sys.setrecursionlimit(5000)
    

    e)去除不必要的模块import

    有时需要让pyinstaller不打包某些用不到的模块,可通过在excludes=[]中添加此模块实现,如

    excludes=['zmq']

    3.使用spec执行打包命令

    pyinstaller -D xxx.spec

    打包生成两个文件目录build和dist,build为临时文件目录完成打包后可以删除;dist中存放打包的结果,可执行文件和其它程序运行的关联文件都在这个目录下。

    Visual C++ run-time .dlls包含

    针对在Windows<10发布使用,且Python>=3.5的情况,Pyinstaller打包的程序可能会出现不包含Visual C++ run-time .dlls的情况,Python>=3.5需要使用Visual Studio 2015 run-time,也就是Universal CRT,这些runtime在Win10本身或Win7到Win8.1版本的更新包里,但程序打包后使用的系统里并不一定安装了。因此需要参考Universal CRT的建议,应用以下的方法解决这个问题:

    1. Build on Windows 7 which has been reported to work.

    2. Include one of the VCRedist packages (the redistributable package files) into your application’s installer. This is Microsoft’s recommended way, see “Distributing Software that uses the Universal CRT“ in the above-mentioned link, numbers 2 and 3.

    3. Install the Windows Software Development Kit (SDK) for Windows 10 and expand the .spec-file to include the required DLLs, see “Distributing Software that uses the Universal CRT“ in the above-mentioned link, number 6.

     

    Python模块的打包问题

    程序调用的很多包,在打包时候可能会出现一些问题,针对这写问题需要做一些处理才能保证打包的程序正常执行。

    1.PyQt plugins缺失

    使用PyQt编写UI交互界面的python代码在进行打包时可能会出现一些特别的问题。

    执行使用了PyQt的打包程序,常会出现这样的错误,提示缺少Qt platfrom plugin “windows”,如下图

    打包后程序运行后,使用png格式的图标可以正常显示,但使用的ico格式图标不显示(对于所有图标和关联文件都无法使用的情况涉及到路径问题,后文会另外解释)。

    这两个错误产生的问题都是因为打包时没有将PyQt相关的动态链接库目录生成到打包目录下,因此可以通过将这些需要的文件目录拷贝到打包生成目录下,解决plugin缺失问题。以使用PyQt5编写的python软件打包为例,完成打包后的结果目录下包含PyQt5文件夹,将PyQt5\Qt\plugins下的所有内容(如下图)拷贝到打包结果目录。这样就可以解决PyQt plugins缺失的问题。

    2.动态链接库缺失问题

    更一般的,打包后可能会缺失某些动态链接库,造成执行程序出错,如

    ImportError: DLL load failed: 找不到指定的模块

    在打包过程中一般会有与此相关的warning提示(lib not found)无法找到这些动态链接库。例如在32位版本的打包中,可能会出现scipy模块相关的dll文件无法找到。这时就需要在打包的spec文件中指定动态链接库路径,使其关联到打包后的路径中。

    binaries=[('C:\\Program Files\\Python36-32\\Lib\\site-packages\\scipy\\extra-dll','.')]

    Analysis下的binaries是为打包文件添加二进制文件,缺失的动态链接库可以通过这种方式自动加入到打包路径中。

    3.窗体风格变化问题

    在某些情况下,如在精简环境下的python程序打包中,执行打包后的程序会出现窗体风格变为老式的win风格,这是由于打包时候PyQt的styles动态库没有找到。因此只需要在Python 目录下找到 Lib\site-packages\PyQt5\Qt\plugins\styles,将styles整个目录复制到打包结果目录。

    4.UnicodeDecodeError

    当打包时出现类似错误时:

    UnicodeDecodeError: 'utf-8' codec can't decode byte 0xce in position 122

    可在打包的命令行中输入chcp 65001设置命令行显示utf-8字符,然后再执行打包命令。或者,修改pyinstaller包下的compat.py,根据报错对应的行将

    out = out.decode(encoding)

    改为

    out = out.decode(encoding, 'replace')

    冻结打包路径

    执行打包后的程序,经常会出现程序使用的图标无法显示,程序使用的关联文件无法关联。或者,在打包的本机上运行正常,但是将打包后的程序放到其它机器上就有问题。这些现象都很有可能是由程序使用的文件路径发生改变产生的,因此在打包时候我们需要根据执行路径进行路径“冻结”。

    1.使用绝对路径

    在python代码中使用绝对路径调用外部文件可以保证打包时候路径可追溯,因此在本机上运行打包后程序基本没问题。但是当本机上对应路径的资源文件被改变,或者将打包程序应用到别的机器,都会出现搜索不到资源文件的问题。这种方式不是合适的打包发布python软件的方式。

    2.使用冻结路径

    增加一个py文件,例如叫frozen_dir.py

    # -*- coding: utf-8 -*-
    """
    Created on Sat Aug 25 22:41:09 2018
    frozen dir
    @author: yanhua
    """
    import sys
    import os
    
    def app_path():
        """Returns the base application path."""
        if hasattr(sys, 'frozen'):
            # Handles PyInstaller
            return os.path.dirname(sys.executable)
        return os.path.dirname(__file__)

    其中的app_path()函数返回一个程序的执行路径,为了方便我们将此文件放在项目文件的根目录,通过这种方式建立了相对路径的关系。

    源代码中使用路径时,以app_path()的返回值作为基准路径,其它路径都是其相对路径。以本文中使用的python项目打包为例,如下所示

    import frozen_dir
    SETUP_DIR = frozen_dir.app_path()
    
    FONT_MSYH = matplotlib.font_manager.FontProperties(
                    fname = SETUP_DIR + r'\data\fonts\msyh.ttf',
                    size = 8)
    
    DIR_HELP_DOC = SETUP_DIR + r'\data\docs'
    DIR_HELP_VIDEO = SETUP_DIR + r'\data\videos'

    通过冻结路径,使用了基准目录下的data目录下的fonts, docs, videos。

    主程序中也做了类似的调整,改变其设置路径方法

    import frozen_dir
    
    SETUP_DIR = frozen_dir.app_path()+r'\lib'
    sys.path.append(SETUP_DIR)

    使用这样的方法进行打包,打包后的可执行程序就可以在其它机器上运行。

    其它问题

    由于操作系统和运行环境的不同,pyinstaller打包中还可能遇到很多其它问题,最后总结一些我在打包中遇到的其它坑:

    1.权限问题

    通常时在打包时出现的某些文件拒绝访问或没有权限执行某些操作等。解决这个的方法一般有这几个方面:

    a)使用管理员权限运行cmd或其它命令行窗口

    b)关闭杀毒软件

    c)使用完全权限的管理员账户

    2.中文路径

    pyinstaller打包后的路径使用中文没有问题,不过为了减少打包时候出错的可能,尽量将打包使用的资源文件和代码文件路径设置为英文。

    3.打包后文件的大小

    通常python打包为可执行文件都会得到一个较大的包,这是无法避免的,但是我们还是可以通过一些方法来尽量精简打包后的执行程序:

    a)在代码中减少不必要的import,如from xxx import *

    b)在精简的运行环境(如原生python环境)下打包,缺什么包就下什么包,避免不必要的python包被打包入程序。尤其是anaconda这样的集成环境下打包的结果会大很多。

    c)使用UPX

     

     

    展开全文
  • 为了总结自己踩过的坑,给后面要考的同学们提供一些微薄的帮助,遂作此文。 考场经验 一. 注意,考试的运行时的黑框,是不能使用Crtl+V进行粘贴的,但这不代表考试不能进行复制粘贴,PAT考试系统里的代码...

    emmmmmmm  总算是考了个满分,不用以后再交PAT考场一日游入场费了T ^ T
    第一次在去年秋天,被第一题狼人杀给干掉了〒▽〒,最后得了81分,第二次就是今年春季,侥幸满分通过了= =
    为了总结自己踩过的坑,给后面要考的同学们提供一些微薄的帮助,遂作此文。

    考场经验

    一. 

    注意,考试的运行时的黑框,是不能使用Crtl+V进行粘贴的,但这不代表考试不能进行复制粘贴,PAT考试系统里的代码需要用Ctrl+C进行复制(右键不行),黑框的粘贴功能,是在黑框上方边界右键,然后选择编辑里面的粘贴进行,具体如下图~

    在这个上面右键
    就会出现下面的情况(没法截图,只能拍照了= =)

    然后就可以愉快的粘贴啦~
    当然还有另一种操作可以一劳永逸
    依旧在菜单栏右键,单击属性

    然后把 快速编辑模式 这一项前边的勾给打上,

    然后,在黑框里面,选中一块文字右键就是复制,不选中文字右键就是粘贴,而且持续有效(即每次打开运行黑框都可以使用~)
     

    二. 

    注意编译器问题,如果有VS,尽量不要用DEV C++,这是我第一次考试时遇到的坑,就是DEV C++在考试的时候竟然不能Debug,太坑了,第二次用的VS2013就可以,至于code block,我没怎么用过,暂时不清楚= =

    三.

    如果担心环境问题,去官网上面找每个考点的联系人以及联系方式,问清楚到底提供哪些编译器,因为我今年去的考点,明明PAT上面考点信息说有VS2005和VS2008,然而去了只有VS2013,就很尴尬,不同的环境确实会出现一些以前没有过的问题,比如VS2013就不能用scanf而要用scanf_s代替而vs2008上就不会出现,所以,如果想核实环境的,最好问一下联系人,他们也会很热心解答的~

    四.

    考试需要带的东西,草稿纸会发,但是笔要自己带(不会发笔的- -),身份证,准考证,还有人,其他的就米啥要带的啦

    五.

    注意考试不要打开浏览器,如果不小心误碰,立刻关掉,如果打开浏览器超过10秒,系统会立即将你驱逐的!(也就是考试提前结束了......),然而PAT每场考试都喜欢搞一题左右的来自维基百科的链接陷阱,千万别好奇瞎点进去!

    六.

     考试的代码,是可以存储的,(一般存储在E盘,或者老师指定的代码存储盘,以防止系统崩溃无法还原),考试结束当然也可以用U盘拷走,当然你也可以不拷,因为每次考试结束,每个考生都可以在PTA系统里绑定考号,直接查看到自己提交的代码以及排名,具体操作如下:
        参加本场考试的考生,请先在“拼题A”系统注册个人账号,登录后点击用户名进入“个人中心”,将个人账号与考试账号进行绑定,方法如下:

        1. 在“绑定学号”栏目下选择“学校”为“PAT联盟”;
        2. 在姓名栏填入自己的姓名;
        3. 在学号栏填入自己的准考证号;
        4. 在验证码一栏根据自己的考试级别填入下列数字:
            顶级:xxxxxx
            甲级:xxxxxx
            乙级:xxxxxx
        5. 点击“绑定”。再进入系统应可看到本场考试的试题,并找到自己的提交代码。
        流程搬运自陈越姥姥的微博,这里的XXXXXX每场考试都不一样,注意留心姥姥的微博,每次考试结束都会发的。
        点击PTA系统左侧的题目集,在我的题集里面,就会有一个PAT 201X年X季考试 字样的一个题目集,点进去就能看到自己提交的代码啦~

    七.

    其实,AC和代码完全正确,是两个概念,如果你能确定你未通过的点的具体输入与输出,就可以进行特判,这样也能AC,只不过有点倒巧,但是,谁又说倒巧不也是一种能力呢?这只是一个例子,真正做题时还是要老老实实的做,情况紧急而且自己恰好又知道怎么倒巧的时候,再使用这种方法,技巧还有很多,比如利用while(条件)结合二分法进行测数据的等等,这个我就不多说啦,感兴趣的请自行百度~(评论区有人要这个技巧,我知道的也不多,自己整理了一下原来浙大群里的晴神讲解的机试技巧,在我的资源里,感兴趣的可以自行下载,积分是系统自己设定的我没法更改= =)

    八.

    考试其实入场之后,就可以开始了,这个不是统一时间开始的,而是看你点开始考试,然后系统进行倒计时的,倒计时结束,就不能再提交了,所以我推测,大概考试是一个时间段,大约1点到4点半,在考试时间内开始都可以,不过估计4点半应该会强制结束,除非考场有特殊情况发生,打印成绩大约是在5点之后,不出意外的话。
    每一题都提交完成后,可以选择提前结束考试,然后就可看到分数以及排名,当然,也可以选择放在那里不动,等时间结束即可,一定要确定自己每一题都提交成功了,不确定的话,就多提交几次,虽然可能会影响排名或者面试?,但是比自己做对了没有分要舒服。

    九.

    考试时的状态十分重要,我觉得其实应试都是这样,不一定能发挥自己的真实实力,但是这个机制我们无法改变,所以,只能尽可能让自己状态达到最佳,因为1:30开始,所以,尽量在之前休息一下大脑,调整到最佳的状态,才能更好的应对考试。

    十.

    做题目的方法应该是:先认真仔细的读题,理解题目每一句话的意思,将所有的关键信息转换为自己可以理解的符号记在草稿纸上(以防止遗忘导致反复读题,英文毕竟不如中文来得快),题目里的每一句话都有其存在的意义,特别要注意加黑字体,以及有的题目还会在输出样例后写一些话,必须仔细研读,然后想清楚怎么做,再开始编程,这样才能事半功倍,比想了一半就去实现最后发现思路错了要好的多。今年的题目就充分的体现了这一思想,一定要认真审题!

    十一.

    关于一些模板性的算法要不要去记忆的问题,当然是需要记忆的,毕竟考试时间有限,你还在推导基础算法结构,别人都直接AC做下一题了,万一题目再难一些,做不做得完都不知道= = ,所以,像DFS,BFS,Dijkstra算法, 树的各种遍历以及建树方法,AVL树,并查集等等等......(就不一一列举了),这些都是需要记忆的,也不用一次就记住,遇到没记清的算法,自己先想着写,能写多少写多少,实在写不出来再看书,这样加深记忆,多次重复,就记住了~

    十二.

    PAT的赛制和ACM类似,没有罚时,而且实时反馈提交代码的运行结果,考试用的OJ和PAT官网里的刷题OJ很相似,直接在官网OJ进行练习就可以了~传送门PAT官方网站(这里可以进行,考试报名,考点查询,以及真题练习)

     

    学习经验

    这里只做个人见解,请选择性接受,因为每一场考试都不一样,不具有复制性。

    一.

    尽量用C++,C就不要用了(没有STL会很痛苦),python也不要用了(超时一时爽,一直超时一直爽= =),java不清楚= =,反正C++不容易超时,而且每个考点都支持,推荐使用~

    二.

    指导书,还是推荐晴神的算法笔记呐~具体名字《算法笔记》(作者:胡凡 曾磊),讲解很详细,虽然有的题目代码有些问题会导致某个测试点无法通过(可能是因为PAT判题数据更新的问题),但是不影响学习~
    至于代码方面晴神有配套的《算法笔记 上机训练实战指南》,但是到A1107之后的就没有了,我的博客有,传送门——PAT甲级题目索引(题目+解析+AC代码)PAT乙级题目索引(题目+解析+AC代码),另外,柳神的博客也非常好,讲的十分详细,可以借鉴一下大神的解题思维~
    这里仅仅是推荐,没有任何打广告的成分,只是我自己看的是这两个而已,当然每一题在网上搜索都有一堆题解,看个人喜好啦。

    三.

    请不要刷codeup里面的题目!!!,因为里面的题目是对应章节选择的(有的很难,有的很简单,不适合训练),但是,考试用不到= =,(实在想刷,我等等贴出来我的codeup所有的代码及解析,代码及解析已经整理完成,传送门~~~codeup《算法笔记》题目索引(题目+解析+代码))对,作为一个把所有算法笔记上推荐的codeup练习题刷完的人,我很负责任的告诉你,有些题真的很坑= =、、、直接刷PAT里面的题目就行了,还有算法笔记上也不是每一章每一节都要看都要弄懂,这里给出不需要看的章节,(再次强调,请选择性接受,我只能说PAT甲乙级目前为止没有考到以后也大概率不会考到,真的考到了请不要找我0.0):
        4.7-4.7.3 随机选择算法。(后台数据不太可能随机,而且也没考到过)
        5.7 扩展欧几里得算法。(千万不要看,看完以后你会怀疑人生的,而且也不会考)
        5.8 组合数。(没考过,但是可以看一看,不难)
        9.8 哈弗曼树。(没考过)
        10.4-10.4.2~10.4.3 Bellman-Ford算法和SPFA算法以及Floyd算法(都没考过= =,但是不代表以后不会考,出现负环就会考了)
        10.5 最小生成树(没考过)
        10.6 拓扑排序(没考过,考过一题拓扑排序PAT 甲级 1146 Topological Order,但是不需要用这里面讲的复杂方法实现,而只是一个简单的模拟)
        10.7 关键路径(鄙人觉得很难考到,大概是有生之年系列了)
        11.6 DAG最长路(拓扑排序都没考,哪轮得着你??? ̄△ ̄)
        12.2 KMP算法(没考过,代码还很饶人,估计也是有生之年?)

        (关于第十一章动态规划专题,PAT前几年考过,最近几年就没有考过了,所以,如果实在不懂,不用太钻牛角尖,大概率不太会考吧0.0)
        2020.9.8更新:
        这里的不要看章节可能已经不是太准确了,看评论区也有小伙伴说今年秋季已经开始考DAG以及拓扑排序了,不好说关键路径以及 KMP会不会考,所以这个列表仅作历史记录,准备还是要更充分一些,实在没有时间再做取舍。

    四.

    刷题目尽量自己想,使劲想,自己动手实现,明知道自己写不对,也要自己写,编程最重要的就是要自己动手,刚看完题目就看答案的肯定记不住,而且自己的能力并没有得到实质性的提高,只有在自己通过多次的尝试对题目有了充分的认识之后,这时候再看指导才能有一种豁然开朗醍醐灌顶的畅快,并且记忆的也最深刻。

    五.

    做题其实有两种方式,一种是追求代码简洁方便,另一种是追求极致的运行速度,其实后一种更能学到东西,但是,前一种更适合PAT考试,如果你追求后一种,请不要忘了对前一种的训练,(比如使用读入输出优化,利用数组进行各种操作而不使用STL,这都属于追求速度,STL会慢一些,但是,它短啊!写得快啊!),毕竟,PAT考试考的是你能不能在有限的时间内AC,而不是你AC代码的运行速度有多快,不能为了追求速度而忘记了时间的消耗。可以平时追求速度,考试追求通过~~

    六.

    好像真没啥写的了,就写一些个人感悟吧, 本人二战浙大(当然很多人考PAT都是为了浙大嘛。。),但不幸与线又差了那么几分,大佬太多,自己应试能力太弱,这。。。。都是借口吧,关键还是自己不是十分努力的去学习,真的不能完全的投入兴趣,因为本身就不喜欢学习,所以,还是找准自己的定位,弄清楚自己到底真的想要什么,不要盲目跟风的报浙大,(不盲目的童鞋请无视= =,当然不报浙大也可以考PAT~)我觉得研究生不应该是为了就业(当然我们是基于计算机这个高薪行业的角度),兴趣最重要,能学到自己想学到的知识,结交与自己志趣相投的人,提升自己的能力,开阔自己的视野,为自己热爱的领域做出微薄或者巨大的贡献,我觉得这才是研究生的意义所在吧,但是,漫长的是努力,成败只在一瞬间,如今的我们,只能为了那一瞬的光影,坚持不懈的继续在黑暗中前行。共同加油!

    后记

    调剂系统开的第二天,目前已经顺利上岸上科大,选择了自己喜欢的老师以及喜欢的研究方向,虽然没能如愿浙大,但是我已经很知足了,我觉得研究生除了要有一个好的学习平台,剩下的还是要靠自己,还是再三劝告各位大佬以及萌新,选择>>>>努力,请认真评估自己的水平,理性报考。失败经验贴已写,传送门二战浙大CS失败的一些经验

    还有什么不懂的或者我没有说到的请评论告诉我,我会持续增加滴~
    觉得还不错的点个赞支持一下~~

    展开全文
  • 图像滤波总结(面试经验总结

    万次阅读 多人点赞 2018-03-10 16:19:11
    图像平滑处理,6种滤波总结的综合示例 【盒式滤波、均值滤波、高斯滤波、中值滤波、双边滤波导向滤波】 本文力求用最简洁的语言,详细的代码将此部分内容讲解清楚,但由于博主同样是刚刚接触OpenCV,或许表达上...

    图像平滑处理,6种滤波总结的综合示例

    【盒式滤波、均值滤波、高斯滤波、中值滤波、双边滤波导向滤波】

    本文力求用最简洁的语言,详细的代码将此部分内容讲解清楚,但由于博主同样是刚刚接触OpenCV,或许表达上有些瑕疵,还望读者能够指教探讨,大家共同进步。

    机器配置为:VS2017+opencv3.2.0+Win-64bit。

    若本文能给读者带来一点点启示与帮助,我就很开心了。

    ====================分割线====================

    1-图像滤波

    • 1.图像滤波,即在尽量保留图像细节特征的条件下对目标图像的噪声进行抑制,是图像预处理中不可缺少的操作,其处理效果的好坏将直接影响到后续图像处理和分析的有效性和可靠性。
    • 2.消除图像中的噪声成分叫作图像的平滑化或滤波操作。信号或图像的能量大部分集中在幅度谱的低频和中频段是很常见的,而在较高频段,感兴趣的信息经常被噪声淹没。因此一个能降低高频成分幅度的滤波器就能够减弱噪声的影响。
    • 3.平滑滤波是低频增强的空间域滤波技术。它的目的有两类:一类是模糊;另一类是消除噪音。空间域的平滑滤波一般采用简单平均法进行,就是求邻近像元点的平均亮度值。邻域的大小与平滑的效果直接相关,邻域越大平滑的效果越好,但邻域过大,平滑会使边缘信息损失的越大,从而使输出的图像变得模糊,因此需合理选择邻域的大小。
    • 4.关于滤波器,一种形象的比喻法是:我们可以把滤波器想象成一个包含加权系数的窗口,当使用这个滤波器平滑处理图像时,就把这个窗口放到图像之上,透过这个窗口来看我们得到的图像。
    • 滤波目的
    • 1、消除图像中混入的噪声。2、为图像识别抽取出图像特征。

    滤波要求

    • 1、不能损坏图像轮廓及边缘 。2、图像视觉效果应当更好。

    滤波器的种类有很多, 本文结合前几节的内容,写了个综合示例,包含6种滤波方法:

    盒式滤波、平滑处理1线性滤波之——盒式滤波(方框滤波)

    均值滤波、平滑处理2线性滤波之——均值滤波

    高斯滤波、平滑处理3线性滤波之——高斯滤波

    中值滤波、平滑处理4非线性滤波之——中值滤波

    双边滤波、平滑处理5非线性滤波之——双边滤波

    导向滤波、平滑处理6——引导滤波/导向滤波(Guided Filter)

    其中阈值量可通过滑动条来调节,下面来看看程序是如何实现的。

     

    2-代码演示

    /* 
        功能:用滚动条来控制6种滤波方式的参数值。 
              盒式滤波、均值滤波、高斯滤波、中值滤波、双边滤波、导向滤波。 
    */  
    #include <opencv2/core/core.hpp>                      
    #include <opencv2/highgui/highgui.hpp>          
    #include <opencv2/imgproc/imgproc.hpp>      
    #include <iostream>         
    using namespace std;  
    using namespace cv;  
      
    #define WINDOWNAME "【滤波处理结果窗口】"  
      
    //---------------【全局变量声明部分】-------------------------  
    Mat g_srcIamge, g_dstImage1, g_dstImage2, g_dstImage3, g_dstImage4, g_dstImage5, g_dstImage6;  
    int g_nBoxFilterValue = 1;//盒式滤波内核值  
    int g_nMeanBlurValue = 1;//均值滤波内核值  
    int g_nGaussianBlurValue = 1;//高斯滤波内核值  
    int g_nMedianBlurValue = 1;//中值滤波内核值  
    int g_nBilateralFilterValue = 1;//双边滤波内核值  
    int g_nGuidedFilterValue = 1;//导向滤波内核值  
    const int g_nMaxVal = 20; //预设滑动条最大值   
    //--------------【全局函数声明部分】-------------------------  
    //轨迹条回调函数  
    static void on_BoxFilter(int, void*);//盒式滤波器  
    static void on_MeanBlur(int, void*);//均值滤波器  
    static void on_GaussianBlur(int, void*);//高斯滤波器  
    static void on_MedianBlur(int, void*);//中值滤波器  
    static void on_BilateralFilter(int, void*);//双边滤波器  
    static void on_GuidedFilter(int, void*);//导向滤波器  
    void guidedFilter(Mat &srcMat, Mat &guidedMat, Mat &dstImage, int radius, double eps);//导向滤波器  
      
    //----------------------------【主函数】---------------------------  
    int main()  
    {  
        //------------【1】读取源图像并检查图像是否读取成功------------    
        g_srcIamge = imread("D:\\OutPutResult\\ImageTest\\boatLong.jpg");  
        if (!g_srcIamge.data)  
        {  
            cout << "读取图片错误,请重新输入正确路径!\n";  
            system("pause");  
            return -1;  
        }  
        namedWindow("【源图像】", 1);//创建窗口  
        imshow("【源图像】", g_srcIamge);//显示窗口  
        //------------【2】在WINDOWNAME窗口上分别创建滤波6个滑动条------------         
        namedWindow(WINDOWNAME);//创建窗口    
        createTrackbar("方框滤波", WINDOWNAME, &g_nBoxFilterValue, g_nMaxVal, on_BoxFilter);//创建方框滤波轨迹条  
        on_BoxFilter(g_nBoxFilterValue, 0); //轨迹条的回调函数  
        createTrackbar("均值滤波", WINDOWNAME, &g_nMeanBlurValue, g_nMaxVal, on_MeanBlur);//创建均值滤波轨迹条  
        on_MeanBlur(g_nMeanBlurValue, 0);  
        createTrackbar("高斯滤波", WINDOWNAME, &g_nGaussianBlurValue, g_nMaxVal, on_GaussianBlur);//创建高斯滤波轨迹条  
        on_GaussianBlur(g_nGaussianBlurValue, 0);  
        createTrackbar("中值滤波", WINDOWNAME, &g_nMedianBlurValue, g_nMaxVal, on_MedianBlur);//创建中值滤波轨迹条  
        on_MedianBlur(g_nMedianBlurValue, 0);  
        createTrackbar("双边滤波", WINDOWNAME, &g_nBilateralFilterValue, g_nMaxVal, on_BilateralFilter);//创建双边滤波轨迹条  
        on_BilateralFilter(g_nBilateralFilterValue, 0);  
        createTrackbar("导向滤波", WINDOWNAME, &g_nGuidedFilterValue, g_nMaxVal, on_GuidedFilter);//创建导向滤波轨迹条  
        on_GuidedFilter(g_nGuidedFilterValue, 0);  
        //------------【3】退出程序------------    
        cout << "\t按下'q'键,退出程序~!\n" << endl;  
        while (char(waitKey(1)) != 'q'){}  
        return 0;  
    }  
      
    //----------------------【on_BoxFilter()函数】------------------------  
    static void on_BoxFilter(int, void*)  
    {  
        boxFilter(g_srcIamge, g_dstImage1, -1, Size(g_nBoxFilterValue * 2 + 1, g_nBoxFilterValue * 2 + 1));  
        cout << "\n当前为【盒式滤波】处理效果,其内核大小为:" << g_nBoxFilterValue * 2 + 1 << endl;  
        imshow(WINDOWNAME, g_dstImage1);  
    }  
    //----------------------【on_MeanBlur()函数】------------------------  
    static void on_MeanBlur(int, void*)  
    {  
        blur(g_srcIamge, g_dstImage2, Size(g_nMeanBlurValue * 2 + 1, g_nMeanBlurValue * 2 + 1), Point(-1, -1));  
        cout << "\n当前为【均值滤波】处理效果,其内核大小为:" << g_nMeanBlurValue * 2 + 1 << endl;  
        imshow(WINDOWNAME, g_dstImage2);  
    }  
    //----------------------【on_GaussianBlur()函数】------------------------  
    static void on_GaussianBlur(int, void*)  
    {  
        GaussianBlur(g_srcIamge, g_dstImage3, Size(g_nGaussianBlurValue * 2 + 1, g_nGaussianBlurValue * 2 + 1), 0, 0);  
        cout << "\n当前为【高斯滤波】处理效果,其内核大小为:" << g_nGaussianBlurValue * 2 + 1 << endl;  
        imshow(WINDOWNAME, g_dstImage3);  
    }  
    //----------------------【on_MedianBlur()函数】------------------------  
    static void on_MedianBlur(int, void*)  
    {  
        medianBlur(g_srcIamge, g_dstImage4, g_nMedianBlurValue * 2 + 1);  
        cout << "\n当前为【中值滤波】处理效果,其内核大小为:" << g_nMedianBlurValue * 2 + 1 << endl;  
        imshow(WINDOWNAME, g_dstImage4);  
    }  
    //----------------------【on_BilateralFilter()函数】------------------------  
    static void on_BilateralFilter(int, void*)  
    {  
        bilateralFilter(g_srcIamge, g_dstImage5, g_nBilateralFilterValue, g_nBilateralFilterValue * 2, g_nBilateralFilterValue / 2);  
        cout << "\n当前为【双边滤波】处理效果,其内核大小为:" << g_nBilateralFilterValue << endl;  
        imshow(WINDOWNAME, g_dstImage5);  
    }  
    //----------------------【on_GuidedFilter()函数】------------------------  
    static void on_GuidedFilter(int, void*)  
    {  
        vector<Mat> vSrcImage, vResultImage;  
        //【1】对源图像进行通道分离,并对每个分通道进行导向滤波操作  
        split(g_srcIamge, vSrcImage);  
        for (int i = 0; i < 3; i++)  
        {  
            Mat tempImage;  
            vSrcImage[i].convertTo(tempImage, CV_64FC1, 1.0 / 255.0);//将分通道转换成浮点型数据  
            Mat cloneImage = tempImage.clone(); //将tempImage复制一份到cloneImage  
            Mat resultImage;  
            guidedFilter(tempImage, cloneImage, resultImage, g_nGuidedFilterValue * 2 + 1, 0.01);//对分通道分别进行导向滤波  
            vResultImage.push_back(resultImage);//将分通道导向滤波后的结果存放到vResultImage中  
        }  
        //【2】将分通道导向滤波后结果合并  
        merge(vResultImage, g_dstImage6);  
        cout << "\n当前处理为【导向滤波】,其内核大小为:" << g_nGuidedFilterValue * 2 + 1 << endl;  
        imshow(WINDOWNAME, g_dstImage6);  
    }  
      
    //-------------------【实现导向滤波器函数部分】-------------------------  
    void guidedFilter(Mat &srcMat, Mat &guidedMat, Mat &dstImage, int radius, double eps)  
    {  
        //------------【0】转换源图像信息,将输入扩展为64位浮点型,以便以后做乘法------------  
        srcMat.convertTo(srcMat, CV_64FC1);  
        guidedMat.convertTo(guidedMat, CV_64FC1);  
        //--------------【1】各种均值计算----------------------------------  
        Mat mean_p, mean_I, mean_Ip, mean_II;  
        boxFilter(srcMat, mean_p, CV_64FC1, Size(radius, radius));//生成待滤波图像均值mean_p   
        boxFilter(guidedMat, mean_I, CV_64FC1, Size(radius, radius));//生成导向图像均值mean_I     
        boxFilter(srcMat.mul(guidedMat), mean_Ip, CV_64FC1, Size(radius, radius));//生成互相关均值mean_Ip  
        boxFilter(guidedMat.mul(guidedMat), mean_II, CV_64FC1, Size(radius, radius));//生成导向图像自相关均值mean_II  
        //--------------【2】计算相关系数,计算Ip的协方差cov和I的方差var------------------  
        Mat cov_Ip = mean_Ip - mean_I.mul(mean_p);  
        Mat var_I = mean_II - mean_I.mul(mean_I);  
        //---------------【3】计算参数系数a、b-------------------  
        Mat a = cov_Ip / (var_I + eps);  
        Mat b = mean_p - a.mul(mean_I);  
        //--------------【4】计算系数a、b的均值-----------------  
        Mat mean_a, mean_b;  
        boxFilter(a, mean_a, CV_64FC1, Size(radius, radius));  
        boxFilter(b, mean_b, CV_64FC1, Size(radius, radius));  
        //---------------【5】生成输出矩阵------------------  
        dstImage = mean_a.mul(srcMat) + mean_b;  
    }  

     

    3-显示结果

    原始图像窗口,如下图:

     

    盒式滤波/方框滤波操作,如下图:

     

    均值滤波操作,如下图:

     

    高斯滤波操作,如下图:

     

    中值滤波操作,如下图:

     

    双边滤波操作,如下图:

    导向滤波操作,如下图:

     

    =====================分割线==================

    4-程序说明

    【滤波处理结果窗口】中显示的数值并非实际内核大小,真正内核大小还请看黑窗口的提示信息。

    其中前五种滤波方式:盒式滤波、均值滤波、高斯滤波、中值滤波、双边滤波,OpenCV都已封装在函数里了,我们直接调用即可,而导向滤波,是需要自己编写,然后调用即可。

     

    为了便于观察各种滤波方式的效果优缺点,我对原图进行了处理,左边的图像是添加了椒盐噪声,而右边的图像是添加了高斯噪声,同学们可以通过滑动条调节,试试看每种滤波方式对噪声的处理结果是如何情况。

    ====END===================

    角点检测(Harris,Fast,surf)

    前面分析了Sift算法的具体原理,就顺便想看看其他的角点检测算法,和对比。这些看完之后,相比sift的原理比较简单.

    Harris:

    这个算法通过两张图就看得很清楚了。将被检测点上的window移动一下,然后计算原来的window和移动之后的window对应位置像素差的平方和,如果变化不大说明这个点在物体内部,如果一个方向上变化大,另外一个方向上变化不大说明在边界上。如果任何方向上变化都很大说明这个点就是corner point。

    但是这个方法效果不是很好,主要存在两方面的问题:

    1. .0-1window对噪声比较敏感
    2. window移动的方向太少,导致该算法对边缘敏感(准确的说是和window移动方向不一致的边缘)

    harris(1988)harris(1988)年提出了改进方案。

    1. 将01window改成了Gaussian window,中心权重比较高,离中心越远权重越低。
    2. 使用泰勒展开考虑所有方向

    通过泰勒展开可以将原window和shift之后的window对应位置像素差的平方和表示为移动方向*该点Hessian矩阵*移动方向。Hessian矩阵的较大特征值和对应特征向量分别表示变化最大方向和对应变化幅度第二特征值和对应特征向量,表示和变化最大方向相垂直的方向以及该方向变化的幅度。如果最大特征值很小那么说明该点在物体内部;如果第一特征值很大,另外一个特征值接近零说明该点是一个edge point如果两个特征值都远大于零说明该点是一个corner point。
    Harris 为了简化计算提出了一个cornerness的量,越大越说明这个点越corner

    角点计算数学原理:

    基本数学公式如下:

    其中W(x, y)表示移动窗口,I(x, y)表示像素灰度值强度,范围为0~255。根据泰勒级数,计算一阶到N阶的偏导数,最终得到一个Harris矩阵公式:

    根据Harris的矩阵计算矩阵特征值,然后计算Harris角度响应值:

    其中K为系数值,通常取值范围为0.04 ~ 0.06之间。

    算法详细步骤

    1. 计算图像X方向与Y方向的一阶高斯偏导数Ix与Iy
    2. 根据第一步结果得到Ix^2 , Iy^2与Ix*Iy值
    3. 高斯模糊第二步三个值得到Sxx, Syy, Sxy
    4. 定义每个像素的Harris矩阵,计算出矩阵的两个特质值
    5. 计算出每个像素的R值
    6. 使用3X3或者5X5的窗口,实现非最大值压制
    7. 根据角度检测结果计算,最提取到的关键点以绿色标记,显示在原图上。

    Fast角点检测:

    Edward Rosten and TomDrummond 在2006年提出了一种简单快速的角点探测算法,该算法检测的角点定义为在像素点的周围邻域内有足够多的像素点与该点处于不同的区域。应用到灰度图像中,即有足够多的像素点的灰度值大于该点的灰度值或者小于该点的灰度值。

    考虑下图中p点附近半径为3的圆环上的16个点,一个思路是若其中有连续的12个点的灰度值与p点的灰度值差别超过某一阈值,则可以认为p点为角点。

    这一思路可以使用机器学习的方法进行加速。对同一类图像,例如同一场景的图像,可以在16个方向上进行训练,得到一棵决策树,从而在判定某一像素点是否为角点时,不再需要对所有方向进行检测,而只需要按照决策树指定的方向进行2-3次判定即可确定该点是否为角点。

    常见的角点检测算法比较:

      图像匹配能够应用的场合非常多,如目标跟踪,检测,识别,图像拼接等,而图像匹配最核心的技术就要属角点匹配了,所谓角点匹配是指寻找两幅图像之间的特征像素点的对应关系,从而确定两幅图像的位置关系。

      角点匹配可以分为以下四个步骤:

    1. 提取检测子:在两张待匹配的图像中寻找那些最容易识别的像素点(角点),比如纹理丰富的物体边缘点等。 
    2. 提取描述子:对于检测出的角点,用一些数学上的特征对其进行描述,如梯度直方图,局部随机二值特征等。
    3. 检测子和描述子的常用提取方法有:sift, harris, surf, fast, agast, brisk, freak, brisk,orb等。 
    4. .匹配:通过各个角点的描述子来判断它们在两张图像中的对应关系。常用方法如 flann  
    5. 去外点:去除错误匹配的外点,保留正确的内点。常用方法有Ransac, GTM。

      我对上述这些常用的检测子和描述子的性能和速度做了一个测试,以找出其中性价比最好的组合(不考虑后两步)

                            图一

     

                          图二

      上面两幅图是对上述部分检测子与描述子进行测试的结果。

      其中图一和图二的x坐标表示不同的检测子与描述子的组合,从1到30分别对应

    (surf、agast、brisk、fast、sift、orb)检测子与 (brisk、agast、surf、sift、orb)描述子的线性组合,如第四组表示surf检测子+sift描述子的组合。

      图一和图二的y坐标表示一系列测试图片组,每组由两张图组成,这些图片都是质量较低的卫星地图,约2000*2000像素,每组的两张图片是由不同卫星对地面上同一地区进行拍摄的。它们之间的关系可能包含旋转,缩放,仿射变换,亮度变化,模糊,噪音等。从1到11的测试图片组大概对应着以下变换关系:

    1. 强烈亮度变化
    2. 旋转
    3. 仿射变换+尺度变化+旋转
    4. 仿射变换+亮度变化+旋转
    5. 仿射变换+噪音
    6. 模糊+亮度变化
    7. 旋转+噪音
    8. 旋转+尺度变化
    9. 亮度变化+旋转+模糊+噪音
    10.  亮度变化+旋转+尺度变化
    11.  亮度变化+旋转+尺度变化+强烈噪音。

      图一的z坐标表示成功匹配的像素对的个数。图二的z坐标表示所需计算时间。

      从测试结果来看,orb检测子与surf描述子配合的效果是最好的,不过速度也是最慢的。古老的sift和surf依然好用,速度也还是那么不给力。在不考虑旋转和仿射变换的情况下,fast是很不错的选择,在小幅旋转(20度内)的情况下,fast也还是有一定的容错能力的。在旋转变化和尺度变化方面,各家武功相差不多,虽然在理论上sift支持旋转变化,不过测试中它并没有表现出明显的优势。在噪音方面,sift和orb明显强于其它算法;在亮度变化和仿射变换上,orb的鲁棒性是最好的;综合比较,orb的性价比在此次华山论剑中略胜一筹。

      下面两张图是上面11组图片的均值,从平均值来看,orb也是最好的

      

     

      

     

    1. :http://blog.csdn.net/ben_ben_niao/article/details/47446627
    2. :http://blog.csdn.net/sinat_36264666/article/details/77823400
    3. https://blog.csdn.net/xw20084898/article/details/21822565

     

    展开全文
  • JMeter测试WebSocket的经验总结

    万次阅读 多人点赞 2018-08-03 10:06:29
    最近有一个微信聊天系统的项目需要性能测试,既然是测试微信聊天,肯定绕不开websocket接口的测试,首选工具是Jmeter,网上能搜到现成的方法,但是网上提供的jar包往往不是最新的,既然是用最新版本的Jmeter4.0,...
  • DRP经验总结

    千次阅读 热门讨论 2013-08-06 21:49:00
    思想 指导 从开始看DRP项目到完成已经有三个月左右的时间了,这是一个足够长的视频,当看第一集的时候就再想,啥时候看完呢?其间,也断断续续,有时看的效率高有时相反,有时几天看不了几集,好在总算看完了...
  • BeautifulSoup 使用经验总结

    万次阅读 2018-09-30 17:20:29
    BeautifulSoup 使用经验总结 文章目录BeautifulSoup 使用经验总结概述安装开始使用经验总结节点对象、名称、属性节点的文本内容子节点父节点兄弟节点搜索节点使用正则表达式匹配标签名使用属性搜索使用CSS搜索使用...
  • Java面试经验总结

    千次阅读 多人点赞 2018-08-24 23:40:49
    Java面试经验总结 最近3个月一口气面了十几家公司的Java开发岗,大大小小的面试笔试加起来快20场,收获很多。本人即将毕业,在一家国企实习做java开发,感觉自己很水,刚开始面试时发现自己掌握的东西与外面企业...
  • 2019年数学建模美赛经验总结

    万次阅读 多人点赞 2019-02-10 09:48:34
    北京时间2019年1月29号上午9时,数模美赛结束。美赛结束已经10多天了,我在这次竞赛中负责建模和编程,趁现在记忆还比较清晰,写下这篇博客记录第一次参加数学建模美赛的经历。 补更:2019年4月20日公布成绩,...
  • 软件架构经验总结

    万次阅读 热门讨论 2010-11-04 22:29:00
    任何一款软件,从无到有,再到完善,一般都会经历一个漫长的过程。在这个过程当中,架构师的水平和软件体系架构本身的灵活性,就会处于一个很核心的位置。太多的软件,因为架构的问题,造成产品发布日期延迟,或者...
  • 程序设计经验总结

    千次阅读 2012-10-14 18:45:48
    程序设计经验总结  在这个行业里做了快4年了,多少总结了一些东西,成功也许很难复制,但是失败却时常被人们重复,我不敢说我做的很好,但是我希望总结出以前失败的一些教训,时不时看看,提醒自己以后再也不要犯类似的...
  • Cef功能开发经验总结

    万次阅读 热门讨论 2017-04-13 17:34:32
    转载请说明原出处,谢谢~~:http://blog.csdn.net/zhuhongshu/article/details/70159672这是我开发Cef功能时对踩过的坑,进行的总结,话说Cef坑真的不少。好在踩完后用起来还是挺爽的。最终的代码可以下载网易云信PC...
  • 程序员接私活必看经验总结以及接单平台推荐

    千次阅读 多人点赞 2019-06-16 11:15:09
    接私活三年多,起初在国外upwork做自由职业者,不过国外的客单价性价比并不高,竞争激烈,这几年国内众包外包发展起来之后,逐渐转战国内接单,总结下接私活的经验,希望对大家有帮助。 一、接私活前需知: 1、...
  • 2019中科大计算机考研初试经验总结

    万次阅读 多人点赞 2019-05-31 22:26:54
    中科大大数据学院计算机专业上岸后,总结一些快一年考研路上的经验和弯路,希望可以对学弟学妹有所帮助。 基础情况:毕业工作半年辞职考研,科班,但忘得比较多,连章节名字都忘了。 一、前期 2月开始复习 数学...
  • 运维经验总结

    千次阅读 多人点赞 2018-12-16 16:19:48
    1. 域名 从买域名开始,要买多个域名,50个甚至100个。分为主域名和推广域名(给推广链接用的)。要从godaddy上买域名,由于这里的域名稳固,不会出现被攻击等事变。同时还要买域名保护,如许互联网用户ping这个...
  • Erlang 游戏开发经验总结

    万次阅读 2016-03-20 22:43:55
    现在回头看下这个问题,总结下erlang 游戏开发经验。就当是,为我过去一段时间的erlang开发经历,画上一个小句号。在写这篇文章前,我看过孔庆泉同学写过的Erlang 性能优化总结,字里行间有一点自己的体会,使得我...
  • IT求职经验总结——面试和准备策略

    万次阅读 多人点赞 2012-11-13 11:05:47
    IT求职经验总结——面试和准备策略   风风火火的求职经历断断续续持续了大概2个月,最匆忙的时候,可以一天赶3个场子,有心灰意冷,也有身心俱疲。 幸好最后结果还算好,所以写个东西,主要还是希望师弟师妹可以...
  • 多年大数据开发经验总结

    千次阅读 热门讨论 2018-08-24 10:02:23
    多年工作经验总结 初入行,搞大数据开发。得高人指点,先学mapreduce,不足一年,tez兴,后入此道。朝夕不倦,发愤图强,才能略知一二。无奈后浪推前浪,tez被spark拍在沙滩上,遂投spark之怀。继而抖擞精神,奋袂...
  • Boost电路调试经验总结

    千次阅读 2017-08-13 10:46:36
    1、控制电路和主回路要分开供电,例如给光耦供电的12V电源和Boost的输入电压正负极都要分开接; 2、由于Boost电路的电路结构,如果一开始就加上直流电源,当MOSFET导通的瞬间由于只有电感会导致电源短路,会烧掉...
  • Web前端实践经验总结

    千次阅读 2015-01-17 20:37:20
    最近用了不少业余时间,在加强Web前端。有个很大的感触就是,web前端比较麻烦,主要是布局和样式。最主要的原因,还是之前实践得比较少,熟能生巧,不得不服啊。 自己从头开始写布局和css太费心思了,比较偏好用...
  • 慢 SQL 问题经验总结

    千次阅读 2018-08-21 08:54:07
    1、 导致慢 SQL 的原因 在遇到慢 SQL 情况时,不能简单的把原因归结为 SQL 编写问题(虽然这是最常见的因素),实际上导致慢 SQL 有很多因素,甚至包括硬件和 mysql 本身的 bug。根据出现的概率从大到小,罗列如下: ...

空空如也

1 2 3 4 5 ... 20
收藏数 71,396
精华内容 28,558
关键字:

经验总结