图像处理的有趣例子_图像识别的有趣案例 - CSDN
  • *************************************************************************************************************** 在这里,我特别声明:本文章的源作者是 杨晓冬 (个人邮箱:xdyang.ustc@...原文的链接是 ...

    ***************************************************************************************************************

             在这里,我特别声明:本文章的源作者是   杨晓冬  (个人邮箱:xdyang.ustc@gmail.com)。原文的链接是
    http://www.iask.sina.com.cn/u/2252291285/ish。版权归 杨晓冬 朋友所有。

             我非常感谢原作者辛勤地编写本文章,并愿意共享出来。我也希望转载本文的各位朋友,要注明原作者和出处,以尊重原作者!                 

    ***************************************************************************************************************

    图像处理与计算机视觉基础,经典以及最近发展

    一、 绪论

    1. 为什么要写这篇文章

           从2002年到现在,接触图像快十年了。虽然没有做出什么很出色的工作,不过在这个领域摸爬滚打了十年之后,发现自己对图像处理和计算机视觉的感情越来 越深厚。下班之后看看相关的书籍和文献是一件很惬意的事情。平常的一大业余爱好就是收集一些相关的文章,尤其是经典的文章,到现在我的电脑里面已经有了几 十G的文章。写这个文档的想法源于我前一段时间整理文献时的一个突发奇想,既然有这个多文献,何不整理出其中的经典,抓住重点来阅读,同时也可以共享给大 家。于是当时即兴写了一个《图像处理与计算机视觉中的经典论文》。现在来看,那个文档写得很一般,所共享的论文也非常之有限。就算如此,还是得到了一些网 友的夸奖,心里感激不尽。因此,一直想下定决心把这个工作给完善,力求做到尽量全面。


           本文是对现有的图像处理和计算机视觉的经典书籍(后面会有推荐)的一个补充。一般的图像处理书籍都是介绍性的介绍某个方法,在每个领域内都会引用几十上 百篇参考文献。有时候想深入研究这个领域的时候却发现文献太多,不知如何选择。但实际上在每个领域都有那么三五篇抑或更多是非读不可的经典文献。这些文献 除了提出了很经典的算法,同时他们的Introduction和Related work也是对所在的领域很好的总结。读通了这几篇文献也就等于深入了解了这个领域,比单纯的看书收获要多很多。写本文的目的就是想把自己所了解到的各个 领域的经典文章整理出来,不用迷失在参考文献的汪洋大海里。

    2. 图像处理和计算机视觉的分类

    按照当前流行的分类方法,可以分为以下三部分:
    A.图像处理:对输入的图像做某种变换,输出仍然是图像,基本不涉及或者很少涉及图像内容的分析。比较典型的有图像变换,图像增强,图像去噪,图像压      缩,图像恢复,二值图像处理等等。基于阈值的图像分割也属于图像处理的范畴。一般处理的是单幅图像。

    B.图像分析:对图像的内容进行分析,提取有意义的特征,以便于后续的处理。处理的仍然是单幅图像。


    C.计算机视觉:对图像分析得到的特征进行分析,提取场景的语义表示,让计算机具有人眼和人脑的能力。这时处理的是多幅图像或者序列图像,当然也包括部分单幅图像。


          关于图像处理,图像分析和计算机视觉的划分并没有一个很统一的标准。一般的来说,图像处理的书籍总会或多或少的介绍一些图像分析和计算机视觉的知识,比如 冈萨雷斯的数字图像处理。而计算机视觉的书籍基本上都会包括图像处理和图像分析,只是不会介绍的太详细。其实图像处理,图像分析和计算机视觉都可以纳入到 计算机视觉的范畴:图像处理->低层视觉(low level vision),图像分析->中间层视觉(middle level vision),计算机视觉->高层视觉(high level vision)。这是一般的计算机视觉或者机器视觉的划分方法。在本文中,仍然按照传统的方法把这个领域划分为图像处理,图像分析和计算机视觉。

    3. 图像处理和计算机视觉开源库以及编程语言选择

           目前在图像处理中有两种最重要的语言:c/c++和matlab。它们各有优点:c/c++比较适合大型的工程,效率较高,而且容易转成硬件语言,是工 业界的默认语言之一。而matlab实现起来比较方便,适用于算法的快速验证,而且matlab有成熟的工具箱可以使用,比如图像处理工具箱,信号处理工 具箱。它们有一个共同的特点:开源的资源非常多。在学术界matlab使用的非常多,很多作者给出的源代码都是matlab版本。最近由于OpenCV的 兴起和不断完善,c/c++在图像处理中的作用越来越大。总的来说,c/c++和matlab都必须掌握,最好是精通,当然侧重在c/c++上对找工作会 有很大帮助。
    至于开源库,个人非常推荐OpenCV,主要有以下原因:
    (1)简单易入手。OpenCV进入OpenCV2.x的时代后,使用起来越来越简单,接口越来越傻瓜化,越来越matlab化。只要会imread,imwrite,imshow和了解Mat的基本操作就可以开 始入手了。


    (2)OpenCV有一堆图像处理和计算机视觉的大牛在维护,bug在逐步减少,每个新的版本都会带来不同的惊喜。而且它已经或者逐步在移植到不懂的平台,并提供了对Python的很好的支持。


    (3)在OpenCV上可以尝试各种最新以及成熟的技术,而不需要自己从头去写,比如人脸检测(Harr,LBP),DPM(Latent SVM),高斯背景模型,特征检测,聚类,hough变换等等 。而且它还支持各种机器学习方法(SVM,NN,KNN,决策树,Boosting等),使用起来很简单。


    (4)文档内容丰富,并且给出了很多示例程序。当然也有一些地方文档描述不清楚,不过看看代码就很清楚了。


    (5)完全开源。可以从中间提取出任何需要的算法。


    (6)从学校出来后,除极少数会继续在学术圈里,大部分还是要进入工业界。现在在工 业界,c/c++仍是主流,很多公司都会优先考虑熟悉或者精通OpenCV的。事实上,在学术界,现在OpenCV也大有取代matlab之势。以前的 demo或者source code,很多作者都愿意给出matlab版本的,然后别人再呼哧呼哧改成c版本的。现在作者干脆给出c/c++版本,或者自己集成到OpenCV中去, 这样能快速提升自己的影响力。
           如果想在图像处理和计算机视觉界有比较深入的研究,并且以后打算进入这个领域工作的话,建议把OpenCV作为自己的主攻方向。如果找工作的时候敢号称自己精通OpenCV的话,肯定可以找到一份满意的工作。


    4. 本文的特点和结构,以及适合的对象

     本文面向的对象是即将进入或者刚刚进入图像处理和计算机视觉领域的童鞋,可以在阅读书籍的同时参阅这些文献,能对书中提到的算法有比较深刻的理解。由于本文涉及 到的范围比较广,如果能对计算机视觉的资深从业者也有一定的帮助,我将倍感欣慰。为了不至太误人子弟,每一篇文章都或多或少的看了一下,最不济也看了摘要 (这句话实在整理之前写的,实际上由于精力有限,好多文献都只是大概扫了一眼,然后看了看google的引用数,一般在1000以上就放上来了,把这些文 章细细品味一遍也是我近一两年之内的目标)。在成文的过程中,我本人也受益匪浅,希望能对大家也有所帮助。

    由于个人精力和视野的关系,有一些我未涉足过的领域不敢斗胆推荐,只是列出了一些引用 率比较高的文章,比如摄像机标定和立体视觉。不过将来,由于工作或者其他原因,这些领域也会接触到,我会逐步增减这些领域的文章。尽管如此,仍然会有疏 漏,忘见谅。同时文章的挑选也夹带了一些个人的喜好,比如我个人比较喜欢low level方向的,尤其是IJCV和PAMI上面的文章,因此这方面也稍微多点,希望不要引起您的反感。如果有什么意见或者建议,欢迎mail我。文章和 资源我都会在我的csdn blog和sina ishare同步更新。

    此申明:这些论文的版权归作者及其出版商所有,请勿用于商业目的。
    个人blog:       http://blog.csdn.net/dcraw
    新浪iask地址:http://iask.sina.com.cn/u/2252291285/ish?folderid=868438
          本文的安排如下。第一部分是绪论。第二部分是图像处理中所需要用到的理论基础,主要是这个领域所涉及到的一些比较好的参考书籍。第三部分是计算机视觉中所涉 及到的信号处理和模式识别文章。由于图像处理与图像分析太难区分了,第四部分集中讨论了它们。第五部分是计算机视觉部分。最后是小结。

    二、 图像处理与计算机视觉相关的书籍

    1. 数学

          我们所说的图像处理实际上就是数字图像处理,是把真实世界中的连续三维随机信号投影到传感器的二维平面上,采样并量化后得到二维矩阵。数字图像处理就是二维 矩阵的处理,而从二维图像中恢复出三维场景就是计算机视觉的主要任务之一。这里面就涉及到了图像处理所涉及到的三个重要属性:连续性,二维矩阵,随机性。 所对应的数学知识是高等数学(微积分),线性代数(矩阵论),概率论和随机过程。这三门课也是考研数学的三个组成部分,构成了图像处理和计算机视觉最基础 的数学基础。如果想要更进一步,就要到网上搜搜林达华推荐的数学书目了。

    2. 信号处理

        图像处理其实就是二维和三维信号处理,而处理的信号又有一定的随机性,因此经典信号处理和随机信号处理都是图像处理和计算机视觉中必备的理论基础。

    2.1经典信号处理

    信号与系统(第2版) Alan V.Oppenheim等著 刘树棠译

    离散时间信号处理(第2版) A.V.奥本海姆等著 刘树棠译

    数字信号处理:理论算法与实现 胡广书 (编者)

    2.2随机信号处理

    现代信号处理 张贤达著

    统计信号处理基础:估计与检测理论 Steven M.Kay等著 罗鹏飞等译

    自适应滤波器原理(第4版) Simon Haykin著 郑宝玉等译

    2.3 小波变换

    信号处理的小波导引:稀疏方法(原书第3版) tephane Malla著, 戴道清等译

    2.4 信息论

    信息论基础(原书第2版) Thomas M.Cover等著 阮吉寿等译

    3. 模式识别

    Pattern Recognition and Machine Learning Bishop, Christopher M. Springer

    模式识别(英文版)(第4版) 西奥多里德斯著

    Pattern Classification (2nd Edition) Richard O. Duda等著

    Statistical Pattern Recognition, 3rd Edition Andrew R. Webb等著

    模式识别(第3版) 张学工著

    4. 图像处理与计算机视觉的书籍推荐

    图像处理,分析与机器视觉 第三版 Sonka等著 艾海舟等译

    Image Processing, Analysis and Machine Vision

                    ( 附:这本书是图像处理与计算机视觉里面比较全的一本书了,几乎涵盖了图像视觉领域的各个方面。中文版的个人感觉也还可以,值得一看。)

    数字图像处理 第三版 冈萨雷斯等著

    Digital Image Processing

    (附:数字图像处理永远的经典,现在已经出到了第三版,相当给力。我的导师曾经说过,这本书写的很优美,对写英文论文也很有帮助,建议购买英文版的。)

    计算机视觉:理论与算法 Richard Szeliski著

    Computer Vision: Theory and Algorithm

                    (附:微软的Szeliski写的一本最新的计算机视觉著作。内容非常丰富,尤其包括了作者的研究兴趣,比如一般的书里面都没有的Image Stitching和                       Image Matting等。这也从另一个侧面说明这本书的通用性不如Sonka的那本。不过作者开放了这本书的电子版,可以有选择性的阅读。
                      http://szeliski.org/Book/
                      Multiple View Geometry in Computer Vision 第二版Harley等著
                     引用达一万多次的经典书籍了。第二版到处都有电子版的。第一版曾出过中文版的,后来绝版了。网上也可以找到中英文版的电子版。)

    计算机视觉:一种现代方法 DA Forsyth等著

    Computer Vision: A Modern Approach

    MIT的经典教材。虽然已经过去十年了,还是值得一读。期待第二版

    Machine vision: theory, algorithms, practicalities 第三版 Davies著

    (附:为数不多的英国人写的书,偏向于工业应用。)

    数字图像处理 第四版 Pratt著

    Digital Image Processing

    (附:写作风格独树一帜,也是图像处理领域很不错的一本书。网上也可以找到非常清晰的电子版。)

    5. 小结

    罗嗦了这么多,实际上就是几个建议:
    (1)基础书千万不可以扔,也不能低价处理给同学或者师弟师妹。不然到时候还得一本本从书店再买回来的。钱是一方面的问题,对着全新的书看完全没有看自己当年上过的课本有感觉。
    (2)遇到有相关的课,果断选修或者蹭之,比如随机过程,小波分析,模式识别,机器学习,数据挖掘,现代信号处理甚至泛函。多一些理论积累对将来科研和工作都有好处。
    (3)资金允许的话可以多囤一些经典的书,有的时候从牙缝里面省一点都可以买一本好书。不过千万不要像我一样只囤不看。



    三、 计算机视觉中的信号处理与模式识别

          从本章开始,进入本文的核心章节。一共分三章,分别讲述信号处理与模式识别,图像处理与分析以及计算机视觉。与其说是讲述,不如说是一些经典文章的罗列以及 自己的简单点评。与前一个版本不同的是,这次把所有的文章按类别归了类,并且增加了很多文献。分类的时候并没有按照传统的分类方法,而是划分成了一个个小 的门类,比如SIFT,Harris都作为了单独的一类,虽然它们都可以划分到特征提取里面去。这样做的目的是希望能突出这些比较实用且比较流行的方法。 为了以后维护的方便,按照字母顺序排的序。

    1. Boosting

        Boosting是最近十来年来最成功的一种模式识别方法之一,个人认为可以和SVM并称为模式识别双子星。它真正实现了“三个臭皮匠,赛过诸葛亮”。只要保证每个基本分 类器的正确率超过50%,就可以实现组合成任意精度的分类器。这样就可以使用最简单的线性分类器。Boosting在计算机视觉中的最成功的应用无疑就是 Viola-Jones提出的基于Haar特征的人脸检测方案。听起来似乎不可思议,但Haar+Adaboost确实在人脸检测上取得了巨大的成功,已 经成了工业界的事实标准,并且逐步推广到其他物体的检测。
    Rainer Lienhart在2002 ICIP发表的这篇文章是Haar+Adaboost的最好的扩展,他把原始的两个方向的Haar特征扩展到了四个方向,他本人是OpenCV积极的参与 者。现在OpenCV的库里面实现的Cascade Classification就包含了他的方法。这也说明了盛会(如ICIP,ICPR,ICASSP)也有好文章啊,只要用心去发掘。

    [1997] A Decision-Theoretic Generalization of on-Line Learning and an Application to Boosting

    [1998] Boosting the margin A new explanation for the effectiveness of voting methods

    [2002 ICIP TR] Empirical Analysis of Detection Cascades of Boosted Classifiers for Rapid Object Detection

    [2003] The Boosting Approach to Machine Learning An Overview

    [2004 IJCV] Robust Real-time Face Detection

    [1989 PAMI] Unsupervised Optimal Fuzzy Clustering

    [1991 PAMI] A validity measure for fuzzy clustering

    [1995 PAMI] On cluster validity for the fuzzy c-means model

    [1998] Some New Indexes of Cluster Validity

    [1999 ACM] Data Clustering A Review

    [1999 JIIS] On Clustering Validation Techniques

    [2001] Estimating the number of clusters in a dataset via the Gap statistic

    [2001 NIPS] On Spectral Clustering

    [2002] A stability based method for discovering structure in clustered data

    [2007] A tutorial on spectral clustering

    [2006 TIT] Compressed Sensing

    [2008 SPM] An Introduction to Compressive Sampling

    [2011 TSP] Structured Compressed Sensing From Theory to Applications

    4. Decision Trees

    对决策树感兴趣的同学这篇文章是非看不可的了。

    [1986] Introduction to Decision Trees

    5. Dynamical Programming

    动态规划也是一个比较使用的方法,这里挑选了一篇PAMI的文章以及一篇Book Chapter

    [1990 PAMI] using dynamic programming for solving variational problems in vision

    [Book Chapter] Dynamic Programming

    6. Expectation Maximization

    EM是计算机视觉中非常常见的一种方法,尤其是对参数的估计和拟合,比如高斯混合模型。EM和GMM在Bishop的PRML里单独的作为一章,讲的很不错。关于EM的tutorial,网上也可以搜到很多。

    [1977] Maximum likelihood from incomplete data via the EM algorithm

    [1996 SPM] The Expectation-Maximzation Algorithm

    7. Graphical Models

    伯克利的乔丹大师的Graphical Model,可以配合这Bishop的PRML一起看。

    [1999 ML] An Introduction to Variational Methods for Graphical Models

    8. Hidden Markov Model

    HMM在语音识别中发挥着巨大的作用。在信号处理和图像处理中也有一定的应用。最早接触它是跟小波和检索相关的,用HMM来描述小波系数之间的相互关系,并用来做检索。这里提供一篇1989年的经典综述,几篇HMM在小波,分割,检索和纹理上的应用以及一本比较早的中文电子书,现在也不知道作者是谁,在这里对作者表示感谢。

    [1989 ] A tutorial on hidden markov models and selected applications in speech recognition

    [1998 TSP] Wavelet-based statistical signal processing using hidden Markov models

    [2001 TIP] Multiscale image segmentation using wavelet-domain hidden Markov models

    [2002 TMM] Rotation invariant texture characterization and retrieval using steerable wavelet-domain hidden Markov models

    [2003 TIP] Wavelet-based texture analysis and synthesis using hidden Markov models

    Hmm Chinese book.pdf

    9. Independent Component Analysis

    同PCA一样,独立成分分析在计算机视觉中也发挥着重要的作用。这里介绍两篇综述性的文章,最后一篇是第二篇的TR版本,内容差不多,但比较清楚一些。

    [1999] Independent Component Analysis A Tutorial

    [2000 NN] Independent component analysis algorithms and applications

    [2000] Independent Component Analysis Algorithms and Applications

    10. Information Theory

    计算机视觉中的信息论。这方面有一本很不错的书Information Theory in Computer Vision and Pattern Recognition。这本书有电子版,如果需要用到的话,也可以参考这本书。

    [1995 NC] An Information-Maximization Approach to Blind Separation and Blind Deconvolution

    [2010] An information theory perspective on computational vision

    11. Kalman Filter

    这个话题在张贤达老师的现代信号处理里面讲的比较深入,还给出了一个有趣的例子。这里列出了Kalman的最早的论文以及几篇综述,还有Unscented Kalman Filter。同时也有一篇Kalman Filter在跟踪中的应用以及两本电子书。

    [1960 Kalman] A New Approach to Linear Filtering and Prediction Problems Kalman

    [1970] Least-squares estimation_from Gauss to Kalman

    [1997 SPIE] A New Extension of the Kalman Filter to Nonlinear System

    [2000] The Unscented Kalman Filter for Nonlinear Estimation

    [2001 Siggraph] An Introduction to the Kalman Filter_full

    [2003] A Study of the Kalman Filter applied to Visual Tracking

    12. Pattern Recognition and Machine Learning

    模式识别名气比较大的几篇综述

    [2000 PAMI] Statistical pattern recognition a review

    [2004 CSVT] An Introduction to Biometric Recognition

    [2010 SPM] Machine Learning in Medical Imaging

    13. Principal Component Analysis

    著名的PCA,在特征的表示和特征降维上非常有用。

    [2001 PAMI] PCA versus LDA

    [2001] Nonlinear component analysis as a kernel eigenvalue problem

    [2002] A Tutorial on Principal Component Analysis

    [2009] A Tutorial on Principal Component Analysis

    [2011] Robust Principal Component Analysis

    [Book Chapter] Singular Value Decomposition and Principal Component Analysis

    14. Random Forest

    随机森林

    [2001 ML] Random Forests

    [2009 BMVC] Performance Evaluation of RANSAC Family

    16. Singular Value Decomposition

    对于非方阵来说,就是SVD发挥作用的时刻了。一般的模式识别书都会介绍到SVD。这里列出了K-SVD以及一篇Book Chapter

    [2006 TSP] K-SVD An Algorithm for Designing Overcomplete Dictionaries for Sparse Representation

    [Book Chapter] Singular Value Decomposition and Principal Component Analysis

    17. Sparse Representation

    这里主要是Proceeding of IEEE上的几篇文章

    [2009 PAMI] Robust Face Recognition via Sparse Representation

    [2009 PIEEE] Image Decomposition and Separation Using Sparse Representations An Overview

    [2010 PIEEE] Dictionaries for Sparse Representation Modeling

    [2010 PIEEE] It's All About the Data

    [2010 PIEEE] Matrix Completion With Noise

    [2010 PIEEE] On the Role of Sparse and Redundant Representations in Image Processing

    [2010 PIEEE] Sparse Representation for Computer Vision and Pattern Recognition

    [2011 SPM] Directionary Learning

    18. Support Vector Machines

    [1998] A Tutorial on Support Vector Machines for Pattern Recognition

    [2004] LIBSVM A Library for Support Vector Machines

    19. Wavelet

    在小波变换之前,时频分析的工具只有傅立叶变换。众所周知,傅立叶变换在时域没有分辨率,不能捕捉局部频域信息。虽然短时傅立叶变换克服了这个缺点,但只能刻画恒定窗口的频率特性,并且不能很好的扩展到二维。小波变换的出现很好的解决了时频分析的问题,作为一种多分辨率分析工具,在图像处理中得到了极大的发展和应用。在小波变换的发展过程中,有几个人是不得不提的,Mallat, Daubechies,Vetteri, M.N.Do, Swelden,Donoho。Mallat和Daubechies奠定了第一代小波的框架,他们的著作更是小波变换的必读之作,相对来说,小波十讲太偏数学了,比较难懂。而Mallat的信号处理的小波导引更偏应用一点。Swelden提出了第二代小波,使小波变换能够快速方便的实现,他的功劳有点类似于FFT。而Donoho,Vetteri,Mallat及其学生们提出了Ridgelet, Curvelet, Bandelet,Contourlet等几何小波变换,让小波变换有了方向性,更便于压缩,去噪等任务。尤其要提的是M.N.Do,他是一个越南人,得过IMO的银牌,在这个领域著作颇丰。我们国家每年都有5个左右的IMO金牌,希望也有一两个进入这个领域,能够也让我等也敬仰一下。而不是一股脑的都进入金融,管理这种跟数学没有多大关系的行业,呵呵。很希望能看到中国的陶哲轩,中国的M.N.Do。
    说到小波,就不得不提JPEG2000。在JPEG2000中使用了Swelden和Daubechies提出的用提升算法实现的9/7小波和5/3小波。如果对比JPEG和JPEG2000,就会发现JPEG2000比JPEG在性能方面有太多的提升。本来我以为JPEG2000的普及只是时间的问题。但现在看来,这个想法太Naive了。现在已经过去十几年了,JPEG2000依然没有任何出头的迹象。不得不说,工业界的惯性力量太强大了。如果以前的东西没有什么硬伤的话,想改变太难了。不巧的是,JPEG2000的种种优点在最近的硬件上已经有了很大的提升。压缩率?现在动辄1T,2T的硬盘,没人太在意压缩率。渐进传输?现在的网速包括无线传输的速度已经相当快了,渐进传输也不是什么优势。感觉现在做图像压缩越来越没有前途了,从最近的会议和期刊文档也可以看出这个趋势。不管怎么说,JPEG2000的Overview还是可以看看的。

    [1989 PAMI] A theory for multiresolution signal decomposition__the wavelet representation

    [1996 PAMI] Image Representation using 2D Gabor Wavelet

    [1998 ] FACTORING WAVELET TRANSFORMS INTO LIFTING STEPS

    [1998] The Lifting Scheme_ A Construction Of Second Generation Wavelets

    [2000 TCE] The JPEG2000 still image coding system_ an overview

    [2002 TIP] The curvelet transform for image denoising

    [2003 TIP] Gray and color image contrast enhancement by the curvelet transform

    [2003 TIP] Mathematical Properties of the jpeg2000 wavelet filters

    [2003 TIP] The finite ridgelet transform for image representation

    [2005 TIP] Sparse Geometric Image Representations With Bandelets

    [2005 TIP] The Contourlet Transform_ An Efficient Directional Multiresolution Image Representation

    [2010 SPM] The Curvelet Transform



    四、 图像处理与分析

    本章主要讨论图像处理与分析。虽然后面计算机视觉部分的有些内容比如特征提取等也可以归结到图像分析中来,但鉴于它们与计算机视觉的紧密联系,以及它们的出处,没有把它们纳入到图像处理与分析中来。同样,这里面也有一些也可以划归到计算机视觉中去。这都不重要,只要知道有这么个方法,能为自己所用,或者从中得到灵感,这就够了。

    1. Bilateral Filter

    Bilateral Filter俗称双边滤波器是一种简单实用的具有保持边缘作用的平缓滤波器,由Tomasi等在1998年提出。它现在已经发挥着重大作用,尤其是在HDR领域。

    [1998 ICCV] Bilateral Filtering for Gray and Color Images

    [2008 TIP] Adaptive Bilateral Filter for Sharpness Enhancement and Noise Removal

    2. Color

    如果对颜色的形成有一定的了解,能比较深刻的理解一些算法。这方面推荐冈萨雷斯的数字图像处理中的相关章节以及Sharma在Digital Color Imaging Handbook中的第一章“Color fundamentals for digital imaging”。跟颜色相关的知识包括Gamma,颜色空间转换,颜色索引以及肤色模型等,这其中也包括著名的EMD。

    [1991 IJCV] Color Indexing

    [2000 IJCV] The Earth Mover's Distance as a Metric for Image Retrieval

    [2001 PAMI] Color invariance

    [2002 IJCV] Statistical Color Models with Application to Skin Detection

    [2003] A review of RGB color spaces

    [2007 PR]A survey of skin-color modeling and detection methods

    Gamma.pdf

    GammaFAQ.pdf

    3. Compression and Encoding

    个人以为图像压缩编码并不是当前很热的一个话题,原因前面已经提到过。这里可以看看一篇对编码方面的展望文章

    [2005 IEEE] Trends and perspectives in image and video coding

    4. Contrast Enhancement

    对比度增强一直是图像处理中的一个恒久话题,一般来说都是基于直方图的,比如直方图均衡化。冈萨雷斯的书里面对这个话题讲的比较透彻。这里推荐几篇个人认为不错的文章。

    [2002 IJCV] Vision and the Atmosphere

    [2003 TIP] Gray and color image contrast enhancement by the curvelet transform

    [2006 TIP] Gray-level grouping (GLG) an automatic method for optimized image contrast enhancement-part II

    [2006 TIP] Gray-level grouping (GLG) an automatic method for optimized image contrast Enhancement-part I

    [2007 TIP] Transform Coefficient Histogram-Based Image Enhancement Algorithms Using Contrast Entropy

    [2009 TIP] A Histogram Modification Framework and Its Application for Image Contrast Enhancement

    5. Deblur (Restoration)

    图像恢复或者图像去模糊一直是一个非常难的问题,尤其是盲图像恢复。港中文的jiaya jia老师在这方面做的不错,他在主页也给出了可执行文件。这方面的内容也建议看冈萨雷斯的书。这里列出了几篇口碑比较好的文献,包括古老的Richardson-Lucy方法,几篇盲图像恢复的综述以及最近的几篇文章,尤以Fergus和Jiaya Jia的为经典。

    [1972] Bayesian-Based Iterative Method of Image Restoration

    [1974] an iterative technique for the rectification of observed distributions

    [1990 IEEE] Iterative methods for image deblurring

    [1996 SPM] Blind Image Deconvolution

    [1997 SPM] Digital image restoration

    [2005] Digital Image Reconstruction - Deblurring and Denoising

    [2006 Siggraph] Removing Camera Shake from a Single Photograph

    [2008 Siggraph] High-quality Motion Deblurring from a Single Image

    [2011 PAMI] Richardson-Lucy Deblurring for Scenes under a Projective Motion Path

    6. Dehazing and Defog

    严格来说去雾化也算是图像对比度增强的一种。这方面最近比较好的工作就是He kaiming等提出的Dark Channel方法。这篇论文也获得了2009的CVPR 最佳论文奖。2这位003年的广东高考状元已经于2011年从港中文博士毕业加入MSRA(估计当时也就二十五六岁吧),相当了不起。

    [2008 Siggraph] Single Image Dehazing

    [2009 CVPR] Single Image Haze Removal Using Dark Channel Prior

    [2011 PAMI] Single Image Haze Removal Using Dark Channel Prior

    7. Denoising

    图像去噪也是图像处理中的一个经典问题,在数码摄影中尤其重要。主要的方法有基于小波的方法和基于偏微分方程的方法。

    [1992 SIAM] Image selective smoothing and edge detection by nonlinear diffusion. II

    [1992 SIAM] Image selective smoothing and edge detection by nonlinear diffusion

    [1992] Nonlinear total variation based noise removal algorithms

    [1994 SIAM] Signal and image restoration using shock filters and anisotropic diffusion

    [1995 TIT] De-noising by soft-thresholding

    [1998 TIP] Orientation diffusions

    [2000 TIP] Adaptive wavelet thresholding for image denoising and compression

    [2000 TIP] Fourth-order partial differential equations for noise removal

    [2001] Denoising through wavelet shrinkage

    [2002 TIP] The Curvelet Transform for Image Denoising

    [2003 TIP] Noise removal using fourth-order partial differential equation with applications to medical magnetic resonance images in space and time

    [2008 PAMI] Automatic Estimation and Removal of Noise from a Single Image

    [2009 TIP] Is Denoising Dead

    8. Edge Detection

    边缘检测也是图像处理中的一个基本任务。传统的边缘检测方法有基于梯度算子,尤其是Sobel算子,以及经典的Canny边缘检测。到现在,Canny边缘检测及其思想仍在广泛使用。关于Canny算法的具体细节可以在Sonka的书以及canny自己的论文中找到,网上也可以搜到。最快最直接的方法就是看OpenCV的源代码,非常好懂。在边缘检测方面,Berkeley的大牛J Malik和他的学生在2004年的PAMI提出的方法效果非常好,当然也比较复杂。在复杂度要求不高的情况下,还是值得一试的。MIT的Bill Freeman早期的代表作Steerable Filter在边缘检测方面效果也非常好,并且便于实现。这里给出了几篇比较好的文献,包括一篇最新的综述。边缘检测是图像处理和计算机视觉中任何方向都无法逃避的一个问题,这方面研究多深都不为过。

    [1980] theory of edge detection

    [1983 Canny Thesis] find edge

    [1986 PAMI] A Computational Approach to Edge Detection

    [1990 PAMI] Scale-space and edge detection using anisotropic diffusion

    [1991 PAMI] The design and use of steerable filters

    [1995 PR] Multiresolution edge detection techniques

    [1996 TIP] Optimal edge detection in two-dimensional images

    [1998 PAMI] Local Scale Control for Edge Detection and Blur Estimation

    [2003 PAMI] Statistical edge detection_ learning and evaluating edge cues

    [2004 IEEE] Edge Detection Revisited

    [2004 PAMI] Design of steerable filters for feature detection using canny-like criteria

    [2004 PAMI] Learning to Detect Natural Image Boundaries Using Local Brightness, Color, and Texture Cues

    [2011 IVC] Edge and line oriented contour detection State of the art

    9. Graph Cut

    基于图割的图像分割算法。在这方面没有研究,仅仅列出几篇引用比较高的文献。这里又见J Malik,当然还有华人杰出学者Jianbo Shi,他的主页非常搞笑,在醒目的位置标注Do not fly China Eastern Airlines ... 看来是被坑过,而且坑的比较厉害。这个领域,俄罗斯人比较厉害。

    [2000 PAMI] Normalized cuts and image segmentation

    [2001 PAMI] Fast approximate energy minimization via graph cuts

    [2004 PAMI] What energy functions can be minimized via graph cuts

    10. Hough Transform

    虽然霍夫变换可以扩展到广义霍夫变换,但最常用的还是检测圆和直线。这方面同样推荐看OpenCV的源代码,一目了然。Matas在2000年提出的PPHT已经集成到OpenCV中去了。

    [1986 CVGIU] A Survey of the Hough Transform

    [1989] A Comparative study of Hough transform methods for circle finding

    [1992 PAMI] Shapes recognition using the straight line Hough transform_ theory and generalization

    [1997 PR] Extraction of line features in a noisy image

    [2000 CVIU] Robust Detection of Lines Using the Progressive Probabilistic Hough Transform

    11. Image Interpolation

    图像插值,偶尔也用得上。一般来说,双三次也就够了

    [2000 TMI] Interpolation revisited

    [2008 Fnd] Image and Video Matting A Survey

    [2008 PAMI] A Closed-Form Solution to Natural Image Matting

    [2008 PAMI] Spectral Matting

    13. Image Modeling

    图像的统计模型。这方面有一本专门的著作Natural Image Statistics

    [1994] The statistics of natural images

    [2003 JMIV] On Advances in Statistical Modeling of Natural Images

    [2009 IJCV] Fields of Experts

    [2009 PAMI] Modeling multiscale subbands of photographic images with fields of Gaussian scale mixtures

    14. Image Quality Assessment

    在图像质量评价方面,Bovik是首屈一指的。这位老师也很有意思,作为编辑出版了很多书。他也是IEEE的Fellow

    [2004 TIP] Image quality assessment from error visibility to structural similarity

    [2011 TIP] blind image quality assessment From Natural Scene Statistics to Perceptual Quality

    15. Image Registration

    图像配准最早的应用在医学图像上,在图像融合之前需要对图像进行配准。在现在的计算机视觉中,配准也是一个需要理解的概念,比如跟踪,拼接等。在KLT中,也会涉及到配准。这里主要是综述文献。

    [1992 MIA] Image matching as a diffusion process

    [1992 PAMI] A Method for Registration of 3-D shapes

    [1992] a survey of image registration techniques

    [1998 MIA] A survey of medical image registration

    [2003 IVC] Image registration methods a survey

    [2003 TMI] Mutual-Information-Based Registration of Medical Survey

    [2011 TIP] Hairis registration

    16. Image Retrieval

    图像检索曾经很热,在2000年之后似乎消停了一段时间。最近各种图像的不变性特征提出来之后,再加上互联网搜索的商业需求,这个方向似乎又要火起来了,尤其是在商业界,比如淘淘搜。这仍然是一个非常值得关注的方面。而且图像检索与目标识别具有相通之处,比如特征提取和特征降维。这方面的文章值得一读。在最后给出了两篇Book chapter,其中一篇还是中文的。

    [2000 PAMI] Content-based image retrieval at the end of the early years

    [2000 TIP] PicToSeek Combining Color and Shape Invariant Features for Image Retrieval

    [2002] Content-Based Image Retrieval Systems A Survey

    [2008] Content-Based Image Retrieval-Literature Survey

    [2010] Plant Image Retrieval Using Color,Shape and Texture Features

    [2012 PAMI] A Multimedia Retrieval Framework Based on Semi-Supervised Ranking and Relevance Feedback

    CBIR Chinese

    fundament of cbir

    17. Image Segmentation

    图像分割,非常基本但又非常难的一个问题。建议看Sonka和冈萨雷斯的书。这里给出几篇比较好的文章,再次看到了J Malik。他们给出了源代码和测试集,有兴趣的话可以试试。

    [2004 IJCV] Efficient Graph-Based Image Segmentation

    [2008 CVIU] Image segmentation evaluation A survey of unsupervised methods

    [2011 PAMI] Contour Detection and Hierarchical Image Segmentation

    18. Level Set

    大名鼎鼎的水平集,解决了Snake固有的缺点。Level set的两位提出者Sethian和Osher最后反目,实在让人遗憾。个人以为,这种方法除了迭代比较费时,在真实场景中的表现让人生疑。不过,2008年ECCV上的PWP方法在结果上很吸引人。在重初始化方面,Chunming Li给出了比较好的解决方案

    [1995 PAMI] Shape modeling with front propagation_ a level set approach

    [2001 JCP] Level Set Methods_ An Overview and Some Recent Results

    [2005 CVIU] Geodesic active regions and level set methods for motion estimation and tracking

    [2007 IJCV] A Review of Statistical Approaches to Level Set Segmentation

    [2008 ECCV] Robust Real-Time Visual Tracking using Pixel-Wise Posteriors

    [2010 TIP] Distance Regularized Level Set Evolution and its Application to Image Segmentation

    19. Pyramid

    其实小波变换就是一种金字塔分解算法,而且具有无失真重构和非冗余的优点。Adelson在1983年提出的Pyramid优点是比较简单,实现起来比较方便。

    [1983] The Laplacian Pyramid as a Compact Image Code

    [1993 PAMI] Image representation via a finite Radon transform

    [1993 TIP] The fast discrete radon transform I theory

    [2007 IVC] Generalised finite radon transform for N×N images

    21. Scale Space

    尺度空间滤波在现代不变特征中是一个非常重要的概念,有人说SIFT的提出者Lowe是不变特征之父,而Linderburg是不变特征之母。虽然尺度空间滤波是Witkin最早提出的,但其理论体系的完善和应用还是Linderburg的功劳。其在1998年IJCV上的两篇文章值得一读,不管是特征提取方面还是边缘检测方面。

    [1987] Scale-space filtering

    [1990 PAMI] Scale-Space for Discrete Signals

    [1994] Scale-space theory A basic tool for analysing structures at different scales

    [1998 IJCV] Edge Detection and Ridge Detection with Automatic Scale Selection

    [1998 IJCV] Feature Detection with Automatic Scale Selection

    22. Snake

    活动轮廓模型,改变了传统的图像分割的方法,用能量收缩的方法得到一个统计意义上的能量最小(最大)的边缘。

    [1987 IJCV] Snakes Active Contour Models

    [1996 ] deformable model in medical image A Survey

    [1997 IJCV] geodesic active contour

    [1998 TIP] Snakes, shapes, and gradient vector flow

    [2000 PAMI] Geodesic active contours and level sets for the detection and tracking of moving objects

    [2001 TIP] Active contours without edges

    23. Super Resolution

    超分辨率分析。对这个方向没有研究,简单列几篇文章。其中Yang Jianchao的那篇在IEEE上的下载率一直居高不下。

    [2002] Example-Based Super-Resolution

    [2009 ICCV] Super-Resolution from a Single Image

    [2010 TIP] Image Super-Resolution Via Sparse Representation

    24. Thresholding

    阈值分割是一种简单有效的图像分割算法。这个topic在冈萨雷斯的书里面讲的比较多。这里列出OTSU的原始文章以及一篇不错的综述。

    [1979 IEEE] OTSU A threshold selection method from gray-level histograms

    [2001 JISE] A Fast Algorithm for Multilevel Thresholding

    [2004 JEI] Survey over image thresholding techniques and quantitative performance evaluation

    25. Watershed

    分水岭算法是一种非常有效的图像分割算法,它克服了传统的阈值分割方法的缺点,尤其是Marker-Controlled Watershed,值得关注。Watershed在冈萨雷斯的书里面讲的比较详细。

    [1991 PAMI] Watersheds in digital spaces an efficient algorithm based on immersion simulations

    [2001]The Watershed Transform Definitions, Algorithms and Parallelizat on Strategies


    五、 计算机视觉

    这一章是计算机视觉部分,主要侧重在底层特征提取,视频分析,跟踪,目标检测和识别方面等方面。对于自己不太熟悉的领域比如摄像机标定和立体视觉,仅仅列出上google上引用次数比较多的文献。有一些刚刚出版的文章,个人非常喜欢,也列出来了。

    1. Active Appearance Models

    活动表观模型和活动轮廓模型基本思想来源Snake,现在在人脸三维建模方面得到了很成功的应用,这里列出了三篇最早最经典的文章。对这个领域有兴趣的可以从这三篇文章开始入手。

    [1998 ECCV] Active Appearance Models

    [2001 PAMI] Active Appearance Models

    2. Active Shape Models

    [1995 CVIU]Active Shape Models-Their Training and Application

    3. Background modeling and subtraction

    背景建模一直是视频分析尤其是目标检测中的一项关键技术。虽然最近一直有一些新技术的产生,demo效果也很好,比如基于dynamical texture的方法。但最经典的还是Stauffer等在1999年和2000年提出的GMM方法,他们最大的贡献在于不用EM去做高斯拟合,而是采用了一种迭代的算法,这样就不需要保存很多帧的数据,节省了buffer。Zivkovic在2004年的ICPR和PAMI上提出了动态确定高斯数目的方法,把混合高斯模型做到了极致。这种方法效果也很好,而且易于实现。在OpenCV中有现成的函数可以调用。在背景建模大家族里,无参数方法(2000 ECCV)和Vibe方法也值得关注。

    [1997 PAMI] Pfinder Real-Time Tracking of the Human Body

    [1999 CVPR] Adaptive background mixture models for real-time tracking

    [1999 ICCV] Wallflower Principles and Practice of Background Maintenance

    [2000 ECCV] Non-parametric Model for Background Subtraction

    [2000 PAMI] Learning Patterns of Activity Using Real-Time Tracking

    [2002 PIEEE] Background and foreground modeling using nonparametric

    kernel density estimation for visual surveillance

    [2004 ICPR] Improved adaptive Gaussian mixture model for background subtraction

    [2004 PAMI] Recursive unsupervised learning of finite mixture models

    [2006 PRL] Efficient adaptive density estimation per image pixel for the task of background subtraction

    [2011 TIP] ViBe A Universal Background Subtraction Algorithm for Video Sequences

    4. Bag of Words

    词袋,在这方面暂时没有什么研究。列出三篇引用率很高的文章,以后逐步解剖之。

    [2003 ICCV] Video Google A Text Retrieval Approach to Object Matching in Videos

    [2004 ECCV] Visual Categorization with Bags of Keypoints

    [2006 CVPR] Beyond bags of features Spatial pyramid matching for recognizing natural scene categories

    5. BRIEF

    BRIEF是Binary Robust Independent Elementary Features的简称,是近年来比较受关注的特征描述的方法。ORB也是基于BRIEF的。

    [2010 ECCV] BRIEF Binary Robust Independent Elementary Features

    [2011 ICCV] ORB an efficient alternative to SIFT or SURF

    [2012 PAMI] BRIEF Computing a Local Binary Descriptor Very Fast

    6. Camera Calibration and Stereo Vision

    非常不熟悉的领域。仅仅列出了十来篇重要的文献,供以后学习。

    [1979 Marr] A Computational Theory of Human Stereo Vision

    [1985] Computational vision and regularization theory

    [1987 IEEE] A versatile camera calibration technique for

    high-accuracy 3D machine vision metrology using off-the-shelf TV cameras and lenses

    [1987] Probabilistic Solution of Ill-Posed Problems in Computational Vision

    [1988 PIEEE] Ill-Posed Problems in Early Vision

    [1989 IJCV] Kalman Filter-based Algorithms for Estimating Depth from Image Sequences

    [1990 IJCV] Relative Orientation

    [1990 IJCV] Using vanishing points for camera calibration

    [1992 ECCV] Camera self-calibration Theory and experiments

    [1992 IJCV] A theory of self-calibration of a moving camera

    [1992 PAMI] Camera calibration with distortion models and accuracy evaluation

    [1994 IJCV] The Fundamental Matrix Theory, Algorithms, and Stability Analysis

    [1994 PAMI] a stereo matching algorithm with an adaptive window theory and experiment

    [1999 ICCV] Flexible camera calibration by viewing a plane from unknown orientations

    [1999 IWAR] Marker tracking and hmd calibration for a video-based augmented reality conferencing system

    [2000 PAMI] A flexible new technique for camera calibration

    7. Color and Histogram Feature

    这里面主要来源于图像检索,早期的图像检测基本基于全局的特征,其中最显著的就是颜色特征。这一部分可以和前面的Color知识放在一起的。

    [1995 SPIE] Similarity of color images

    [1996 PR] IMAGE RETRIEVAL USING COLOR AND SHAPE

    [1996] comparing images using color coherence vectors

    [1997 ] Image Indexing Using Color Correlograms

    [2001 TIP] An Efficient Color Representation for Image Retrieval

    [2009 CVIU] Performance evaluation of local colour invariants

    8. Deformable Part Model

    大红大热的DPM,在OpenCV中有一个专门的topic讲DPM和latent svm

    [2008 CVPR] A Discriminatively Trained, Multiscale, Deformable Part Model

    [2010 CVPR] Cascade Object Detection with Deformable Part Models

    [2010 PAMI] Object Detection with Discriminatively Trained Part-Based Models

    9. Distance Transformations

    距离变换,在OpenCV中也有实现。用来在二值图像中寻找种子点非常方便。

    [1986 CVGIP] Distance Transformations in Digital Images

    [2008 ACM] 2D Euclidean Distance Transform Algorithms A Comparative Survey

    10. Face Detection

    最成熟最有名的当属Haar+Adaboost

    [1998 PAMI] Neural Network-Based Face Detection

    [2002 PAMI] Detecting faces in images a survey

    [2002 PAMI] Face Detection in Color Images

    [2004 IJCV] Robust Real-Time Face Detection

    11. Face Recognition

    不熟悉,简单罗列之。

    [1991] Face Recognition Using Eigenfaces

    [2000 PAMI] Automatic Analysis of Facial Expressions The State of the Art

    [2000] Face Recognition A Literature Survey

    [2006 PR] Face recognition from a single image per person A survey

    [2009 PAMI] Robust Face Recognition via Sparse Representation

    12. FAST

    用机器学习的方法来提取角点,号称很快很好。

    [2006 ECCV] Machine learning for high-speed corner detection

    [2010 PAMI] Faster and Better A Machine Learning Approach to Corner Detection

    13. Feature Extraction

    这里的特征主要都是各种不变性特征,SIFT,Harris,MSER等也属于这一类。把它们单独列出来是因为这些方法更流行一点。关于不变性特征,王永明与王贵锦合著的《图像局部不变性特征与描述》写的还不错。Mikolajczyk在2005年的PAMI上的文章以及2007年的综述是不错的学习材料。

    [1989 PAMI] On the detection of dominant points on digital curves

    [1997 IJCV] SUSAN—A New Approach to Low Level Image Processing

    [2004 IJCV] Matching Widely Separated Views Based on Affine Invariant Regions

    [2004 IJCV] Scale & Affine Invariant Interest Point Detectors

    [2005 PAMI] A performance evaluation of local descriptors

    [2006 IJCV] A Comparison of Affine Region Detectors

    [2007 FAT] Local Invariant Feature Detectors - A Survey

    [2011 IJCV] Evaluation of Interest Point Detectors and Feature Descriptors

    14. Feature Matching

    Fua课题组在今年PAMI上的一篇文章,感觉还不错

    [2012 PAMI] LDAHash Improved Matching with Smaller Descriptors

    15. Harris

    虽然过去了很多年,Harris角点检测仍然广泛使用,而且基于它有很多变形。如果仔细看了这种方法,从直观也可以感觉到这是一种很稳健的方法。

    [1988 Harris] A combined corner and edge detector

    16. Histograms of Oriented Gradients

    HoG方法也在OpenCV中实现了:HOGDescriptor。

    [2005 CVPR] Histograms of Oriented Gradients for Human Detection

    NavneetDalalThesis.pdf

    17. Image Distance

    [1993 PAMI] Comparing Images Using the Hausdorff Distance

    18. Image Stitching

    图像拼接,另一个相关的词是Panoramic。在Computer Vision: Algorithms and Applications一书中,有专门一章是讨论这个问题。这里的两面文章一篇是综述,一篇是这方面很经典的文章。

    [2006 Fnd] Image Alignment and Stitching A Tutorial

    [2007 IJCV] Automatic Panoramic Image Stitching using Invariant Features

    19. KLT

    KLT跟踪算法,基于Lucas-Kanade提出的配准算法。除了三篇很经典的文章,最后一篇给出了OpenCV实现KLT的细节。

    [1981] An Iterative Image Registration Technique with an Application to Stereo Vision full version

    [1994 CVPR] Good Features to Track

    [2004 IJCV] Lucas-Kanade 20 Years On A Unifying Framework

    Pyramidal Implementation of the Lucas Kanade Feature Tracker OpenCV

    20. Local Binary Pattern

    LBP。OpenCV的Cascade分类器也支持LBP,用来取代Haar特征。

    [2002 PAMI] Multiresolution gray-scale and rotation Invariant Texture Classification with Local Binary Patterns

    [2004 ECCV] Face Recognition with Local Binary Patterns

    [2006 PAMI] Face Description with Local Binary Patterns

    [2011 TIP] Rotation-Invariant Image and Video Description With Local Binary Pattern Features

    21. Low-Level Vision

    关于Low level vision的两篇很不错的文章

    [1998 TIP] A general framework for low level vision

    [2000 IJCV] Learning Low-Level Vision

    22. Mean Shift

    均值漂移算法,在跟踪中非常流行的方法。Comaniciu在这个方面做出了重要的贡献。最后三篇,一篇是CVIU上的top download文章,一篇是最新的PAMI上关于Mean Shift的文章,一篇是OpenCV实现的文章。

    [1995 PAMI] Mean shift, mode seeking, and clustering

    [2002 PAMI] Mean shift a robust approach toward feature space analysis

    [2003 CVPR] Mean-shift blob tracking through scale space

    [2009 CVIU] Object tracking using SIFT features and mean shift

    [2012 PAMI] Mean Shift Trackers with Cross-Bin Metrics

    OpenCV Computer Vision Face Tracking For Use in a Perceptual User Interface

    23. MSER

    这篇文章发表在2002年的BMVC上,后来直接录用到2004年的IVC上,内容差不多。MSER在Sonka的书里面也有提到。

    [2002 BMVC] Robust Wide Baseline Stereo from Maximally Stable Extremal Regions

    [2003] MSER Author Presentation

    [2004 IVC] Robust wide-baseline stereo from maximally stable extremal regions

    [2011 PAMI] Are MSER Features Really Interesting

    24. Object Detection

    首先要说的是第一篇文章的作者,Kah-Kay Sung。他是MIT的博士,后来到新加坡国立任教,极具潜力的一个老师。不幸的是,他和他的妻子都在2000年的新加坡空难中遇难,让人唏嘘不已。

    http://en.wikipedia.org/wiki/Singapore_Airlines_Flight_006

    最后一篇文章也是Fua课题组的,作者给出的demo效果相当好。

    [1998 PAMI] Example-based learning for view-based human face detection

    [2003 IJCV] Learning the Statistics of People in Images and Video

    [2011 PAMI] Learning to Detect a Salient Object

    [2012 PAMI] A Real-Time Deformable Detector

    25. Object Tracking

    跟踪也是计算机视觉中的经典问题。粒子滤波,卡尔曼滤波,KLT,mean shift,光流都跟它有关系。这里列出的是传统意义上的跟踪,尤其值得一看的是2008的Survey和2003年的Kernel based tracking。

    [2003 PAMI] Kernel-based object tracking

    [2007 PAMI] Tracking People by Learning Their Appearance

    [2008 ACM] Object Tracking A Survey

    [2008 PAMI] Segmentation and Tracking of Multiple Humans in Crowded Environments

    [2011 PAMI] Hough Forests for Object Detection, Tracking, and Action Recognition

    [2011 PAMI] Robust Object Tracking with Online Multiple Instance Learning

    [2012 IJCV] PWP3D Real-Time Segmentation and Tracking of 3D Objects

    26. OCR

    一个非常成熟的领域,已经很好的商业化了。

    [1992 IEEE] Historical review of OCR research and development

    Video OCR A Survey and Practitioner's Guide

    27. Optical Flow

    光流法,视频分析所必需掌握的一种算法。

    [1981 AI] Determine Optical Flow

    [1994 IJCV] Performance of optical flow techniques

    [1995 ACM] The Computation of Optical Flow

    [2004 TR] Tutorial Computing 2D and 3D Optical Flow

    [2005 BOOK] Optical Flow Estimation

    [2008 ECCV] Learning Optical Flow

    [2011 IJCV] A Database and Evaluation Methodology for Optical Flow

    28. Particle Filter

    粒子滤波,主要给出的是综述以及1998 IJCV上的关于粒子滤波发展早期的经典文章。

    [1998 IJCV] CONDENSATION—Conditional Density Propagation for Visual Tracking

    [2002 TSP] A tutorial on particle filters for online nonlinear non-Gaussian Bayesian tracking

    [2002 TSP] Particle filters for positioning, navigation, and tracking

    [2003 SPM] particle filter

    29. Pedestrian and Human detection

    仍然是综述类,关于行人和人体的运动检测和动作识别。

    [1999 CVIU] Visual analysis of human movement_ A survey

    [2001 CVIU] A Survey of Computer Vision-Based Human Motion Capture

    [2005 TIP] Image change detection algorithms a systematic survey

    [2006 CVIU] a survey of avdances in vision based human motion capture

    [2007 CVIU] Vision-based human motion analysis An overview

    [2007 IJCV] Pedestrian Detection via Periodic Motion Analysis

    [2007 PR] A survey of skin-color modeling and detection methods

    [2010 IVC] A survey on vision-based human action recognition

    [2012 PAMI] Pedestrian Detection An Evaluation of the State of the Art

    30. Scene Classification

    当相机越来越傻瓜化的时候,自动场景识别就非常重要。这是比拼谁家的Auto功能做的比较好的时候了。

    [2001 IJCV] Modeling the Shape of the Scene A Holistic Representation of the Spatial Envelope

    [2001 PAMI] Visual Word Ambiguity

    [2007 PAMI] A Thousand Words in a Scene

    [2010 PAMI] Evaluating Color Descriptors for Object and Scene Recognition

    [2011 PAMI] CENTRIST A Visual Descriptor for Scene Categorization

    31. Shadow Detection

    [2003 PAMI] Detecting moving shadows-- algorithms and evaluation

    32. Shape

    关于形状,主要是两个方面:形状的表示和形状的识别。形状的表示主要是从边缘或者区域当中提取不变性特征,用来做检索或者识别。这方面Sonka的书讲的比较系统。2008年的那篇综述在这方面也讲的不错。至于形状识别,最牛的当属J Malik等提出的Shape Context。

    [1993 PR] IMPROVED MOMENT INVARIANTS FOR SHAPE DISCRIMINATION

    [1993 PR] Pattern Recognition by Affine Moment Invariants

    [1996 PR] IMAGE RETRIEVAL USING COLOR AND SHAPE

    [2001 SMI] Shape matching similarity measures and algorithms

    [2002 PAMI] Shape matching and object recognition using shape contexts

    [2004 PR] Review of shape representation and description techniques

    [2006 PAMI] Integral Invariants for Shape Matching

    [2008] A Survey of Shape Feature Extraction Techniques

    33. SIFT

    关于SIFT,实在不需要介绍太多,一万多次的引用已经说明问题了。SURF和PCA-SIFT也是属于这个系列。后面列出了几篇跟SIFT有关的问题。

    [1999 ICCV] Object recognition from local scale-invariant features

    [2000 IJCV] Evaluation of Interest Point Detectors

    [2003 CVIU] Speeded-Up Robust Features (SURF)

    [2004 CVPR] PCA-SIFT A More Distinctive Representation for Local Image Descriptors

    [2004 IJCV] Distinctive Image Features from Scale-Invariant Keypoints

    [2010 IJCV] Improving Bag-of-Features for Large Scale Image Search

    [2011 PAMI] SIFTflow Dense Correspondence across Scenes and its Applications

    34. SLAM

    Simultaneous Localization and Mapping, 同步定位与建图。
    SLAM问题可以描述为: 机器人在未知环境中从一个未知位置开始移动,在移动过程中根据位置估计和地图进行自身定位,同时在自身定位的基础上建造增量式地图,实现机器人的自主定位和导航。

    [2002 PAMI] Simultaneous Localization and Map-Building Using Active Vision

    [2007 PAMI] MonoSLAM Real-Time Single Camera SLAM

    35. Texture Feature

    纹理特征也是物体识别和检索的一个重要特征集。

    [1973] Textural features for image classification

    [1979 ] Statistical and structural approaches to texture

    [1996 PAMI] Texture features for browsing and retrieval of image data

    [2002 PR] Brief review of invariant texture analysis methods

    [2012 TIP] Color Local Texture Features for Color Face Recognition

    36. TLD

    Kadal创立了TLD,跟踪学习检测同步进行,达到稳健跟踪的目的。他的两个导师也是大名鼎鼎,一个是发明MSER的Matas,一个是Mikolajczyk。他还创立了一个公司TLD Vision s.r.o. 这里给出了他的系列文章,最后一篇是刚出来的PAMI。

    [2009] Online learning of robust object detectors during unstable tracking

    [2010 CVPR] P-N Learning Bootstrapping Binary Classifiers by Structural Constraints

    [2010 ICIP] FACE-TLD TRACKING-LEARNING-DETECTION APPLIED TO FACES

    [2012 PAMI] Tracking-Learning-Detection

    37. Video Surveillance

    前两篇是两个很有名的视频监控系统,里面包含了很丰富的信息量,比如CMU的那个系统里面的背景建模算法也是相当简单有效的。最后一篇是比较近的综述。

    [2000 CMU TR] A System for Video Surveillance and Monitoring

    [2000 PAMI] W4-- real-time surveillance of people and their activities

    [2008 MVA] The evolution of video surveillance an overview

    38. Viola-Jones

      Haar+Adaboost的弱弱联手,组成了最强大的利器。在OpenCV里面有它的实现,也可以选择用LBP来代替Haar特征。

    [2001 CVPR] Rapid object detection using a boosted cascade of simple features

    [2004 IJCV] Robust Real-time Face Detection

    六、 结束语


    历时一个多月,终于用业余时间把这些资料整理出来了,总算了却了一块心病,也不至于再看着一堆资料发愁了。以后可能会有些小修小补,但不会有太大的变化了。万里长征走完了第一步,剩下的就是理解和消化了。借新浪ishare共享出来,希望能够对你的科研也有一定的帮助。最后简单统计一下各个年份出现的频率。

    文章总数:372

    2012年: 10

    2011年: 20

    2010年: 20

    2009年: 14

    2008年: 18

    2007年: 13

    2006年: 14

    2005年: 9

    2004年: 24

    2003年: 22

    2002年: 21

    2001年: 21

    2000年: 23

    1999年: 10

    1998年: 22

    1997年: 8

    1996年: 9

    1995年: 9

    1994年: 7

    1993年: 5

    1992年: 11

    1991年: 5

    1990年: 6

    1980-1989: 22

    1960-1979: 9

    转载于:https://www.cnblogs.com/zhiyinglky/p/4850058.html

    展开全文
  • 深度有趣 | 25 图像标题生成

    千次阅读 2018-09-21 20:05:22
    介绍基于注意力机制的图像标题生成模型的原理和实现 原理 输入是一张图片,输出是一句对图片进行描述的文本,这就是图像标题生成 基本思路是先通过预训练的图像分类模型,从某一个卷积层得到原始图片的表示,或者...

    简介

    介绍基于注意力机制的图像标题生成模型的原理和实现

    原理

    输入是一张图片,输出是一句对图片进行描述的文本,这就是图像标题生成

    基本思路是先通过预训练的图像分类模型,从某一个卷积层得到原始图片的表示,或者称为上下文contexts

    例如从VGG19的conv5_3拿到原始图片的表示,shape为14*14*512,即512张14*14的小图

    这样一来,可以理解为将原始图片分成14*14共196个小块,每个小块对应一个512维的特征

    图像标题生成模型结构图

    根据contexts使用LSTM逐步生成单词,即可产生原始图片对应的描述文本

    在生成每一个单词时,应该对196个块有不同的偏重,即所谓的注意力机制

    就像我们人一样,考虑下一个词时,对图片的不同区域会有不同的关注度,相关性更强的区域会获得更多的注意力即更高的权重

    根据注意力权重对196个512维的特征进行加权求和,即可得到基于注意力机制的上下文context

    和之前介绍过的Seq2Seq Learning联系起来,图像标题生成便属于one to many这种情况

    数据

    使用COCO2014数据,http://cocodataset.org/#download,训练集包括8W多张图片,验证集包括4W多张图片,并且提供了每张图片对应的标题

    每张图片的标题不止一个,因此训练集一共411593个标题,而验证集一共201489个标题,平均一张图片五个标题

    实现

    基于以下项目实现,https://github.com/yunjey/show-attend-and-tell

    训练

    首先是训练部分代码

    加载库

    # -*- coding: utf-8 -*-
    
    import tensorflow as tf
    import numpy as np
    import matplotlib.pyplot as plt
    %matplotlib inline
    from sklearn.utils import shuffle
    from imageio import imread
    import scipy.io
    import cv2
    import os
    import json
    from tqdm import tqdm
    import pickle
    

    加载数据,因为一张图片可能对应多个标题,因此以一个图片id和一个标题为一条数据。对于图片内容,保留中心正方形区域并缩放;对于标题,长度超过20个词则去除

    batch_size = 128
    maxlen = 20
    image_size = 224
    
    MEAN_VALUES = np.array([123.68, 116.779, 103.939]).reshape((1, 1, 3))
    
    def load_data(image_dir, annotation_path):
        with open(annotation_path, 'r') as fr:
            annotation = json.load(fr)
            
        ids = []
        captions = []
        image_dict = {}
        for i in tqdm(range(len(annotation['annotations']))):
            item = annotation['annotations'][i]
            caption = item['caption'].strip().lower()
            caption = caption.replace('.', '').replace(',', '').replace("'", '').replace('"', '')
            caption = caption.replace('&', 'and').replace('(', '').replace(')', '').replace('-', ' ').split()
            caption = [w for w in caption if len(w) > 0]
            
            if len(caption) <= maxlen:
                if not item['image_id'] in image_dict:
                    img = imread(image_dir + '%012d.jpg' % item['image_id'])
                    h = img.shape[0]
                    w = img.shape[1]
                    if h > w:
                        img = img[h // 2 - w // 2: h // 2 + w // 2, :]
                    else:
                        img = img[:, w // 2 - h // 2: w // 2 + h // 2]   
                    img = cv2.resize(img, (image_size, image_size))
                    
                    if len(img.shape) < 3:
                        img = np.expand_dims(img, -1)
                        img = np.concatenate([img, img, img], axis=-1)
                    
                    image_dict[item['image_id']] = img
                
                ids.append(item['image_id'])
                captions.append(caption)
        
        return ids, captions, image_dict
    
    train_json = 'data/train/captions_train2014.json'
    train_ids, train_captions, train_dict = load_data('data/train/images/COCO_train2014_', train_json)
    print(len(train_ids))
    

    查看一下标题标注

    data_index = np.arange(len(train_ids))
    np.random.shuffle(data_index)
    N = 4
    data_index = data_index[:N]
    plt.figure(figsize=(12, 20))
    for i in range(N):
        caption = train_captions[data_index[i]]
        img = train_dict[train_ids[data_index[i]]]
        plt.subplot(4, 1, i + 1)
        plt.imshow(img)
        plt.title(' '.join(caption))
        plt.axis('off')
    

    整理词典,一共23728个词,建立词和id之间的映射,并使用到三个特殊词

    vocabulary = {}
    for caption in train_captions:
        for word in caption:
            vocabulary[word] = vocabulary.get(word, 0) + 1
    
    vocabulary = sorted(vocabulary.items(), key=lambda x:-x[1])
    vocabulary = [w[0] for w in vocabulary]
    
    word2id = {'<pad>': 0, '<start>': 1, '<end>': 2}
    for i, w in enumerate(vocabulary):
        word2id[w] = i + 3
    id2word = {i: w for w, i in word2id.items()}
    
    print(len(vocabulary), vocabulary[:20])
    
    with open('dictionary.pkl', 'wb') as fw:
        pickle.dump([vocabulary, word2id, id2word], fw)
    
    def translate(ids):
        words = [id2word[i] for i in ids if i >= 3]
        return ' '.join(words) + '.'
    

    将标题转换为id序列

    def convert_captions(data):
        result = []
        for caption in data:
            vector = [word2id['<start>']]
            for word in caption:
                if word in word2id:
                    vector.append(word2id[word])
            vector.append(word2id['<end>'])
            result.append(vector)
            
        array = np.zeros((len(data), maxlen + 2), np.int32)
        for i in tqdm(range(len(result))):
            array[i, :len(result[i])] = result[i]
        return array
    
    train_captions = convert_captions(train_captions)
    print(train_captions.shape)
    print(train_captions[0])
    print(translate(train_captions[0]))
    

    使用图像风格迁移中用过的imagenet-vgg-verydeep-19.mat来提取图像特征,加载vgg19模型并定义一个函数,对于给定的输入,返回vgg19各个层的输出值,通过variable_scope实现网络的重用,将conv5_3的输出作为原始图片的表示

    vgg = scipy.io.loadmat('imagenet-vgg-verydeep-19.mat')
    vgg_layers = vgg['layers']
    
    def vgg_endpoints(inputs, reuse=None):
        with tf.variable_scope('endpoints', reuse=reuse):
            def _weights(layer, expected_layer_name):
                W = vgg_layers[0][layer][0][0][2][0][0]
                b = vgg_layers[0][layer][0][0][2][0][1]
                layer_name = vgg_layers[0][layer][0][0][0][0]
                assert layer_name == expected_layer_name
                return W, b
    
            def _conv2d_relu(prev_layer, layer, layer_name):
                W, b = _weights(layer, layer_name)
                W = tf.constant(W)
                b = tf.constant(np.reshape(b, (b.size)))
                return tf.nn.relu(tf.nn.conv2d(prev_layer, filter=W, strides=[1, 1, 1, 1], padding='SAME') + b)
    
            def _avgpool(prev_layer):
                return tf.nn.avg_pool(prev_layer, ksize=[1, 2, 2, 1], strides=[1, 2, 2, 1], padding='SAME')
    
            graph = {}
            graph['conv1_1']  = _conv2d_relu(inputs, 0, 'conv1_1')
            graph['conv1_2']  = _conv2d_relu(graph['conv1_1'], 2, 'conv1_2')
            graph['avgpool1'] = _avgpool(graph['conv1_2'])
            graph['conv2_1']  = _conv2d_relu(graph['avgpool1'], 5, 'conv2_1')
            graph['conv2_2']  = _conv2d_relu(graph['conv2_1'], 7, 'conv2_2')
            graph['avgpool2'] = _avgpool(graph['conv2_2'])
            graph['conv3_1']  = _conv2d_relu(graph['avgpool2'], 10, 'conv3_1')
            graph['conv3_2']  = _conv2d_relu(graph['conv3_1'], 12, 'conv3_2')
            graph['conv3_3']  = _conv2d_relu(graph['conv3_2'], 14, 'conv3_3')
            graph['conv3_4']  = _conv2d_relu(graph['conv3_3'], 16, 'conv3_4')
            graph['avgpool3'] = _avgpool(graph['conv3_4'])
            graph['conv4_1']  = _conv2d_relu(graph['avgpool3'], 19, 'conv4_1')
            graph['conv4_2']  = _conv2d_relu(graph['conv4_1'], 21, 'conv4_2')
            graph['conv4_3']  = _conv2d_relu(graph['conv4_2'], 23, 'conv4_3')
            graph['conv4_4']  = _conv2d_relu(graph['conv4_3'], 25, 'conv4_4')
            graph['avgpool4'] = _avgpool(graph['conv4_4'])
            graph['conv5_1']  = _conv2d_relu(graph['avgpool4'], 28, 'conv5_1')
            graph['conv5_2']  = _conv2d_relu(graph['conv5_1'], 30, 'conv5_2')
            graph['conv5_3']  = _conv2d_relu(graph['conv5_2'], 32, 'conv5_3')
            graph['conv5_4']  = _conv2d_relu(graph['conv5_3'], 34, 'conv5_4')
            graph['avgpool5'] = _avgpool(graph['conv5_4'])
    
            return graph
    
    X = tf.placeholder(tf.float32, [None, image_size, image_size, 3])
    encoded = vgg_endpoints(X - MEAN_VALUES)['conv5_3']
    print(encoded)
    

    基于以上contexts,实现初始化、词嵌入、特征映射等部分

    k_initializer = tf.contrib.layers.xavier_initializer()
    b_initializer = tf.constant_initializer(0.0)
    e_initializer = tf.random_uniform_initializer(-1.0, 1.0)
    
    def dense(inputs, units, activation=tf.nn.tanh, use_bias=True, name=None):
        return tf.layers.dense(inputs, units, activation, use_bias,
                               kernel_initializer=k_initializer, bias_initializer=b_initializer, name=name)
    
    def batch_norm(inputs, name):
        return tf.contrib.layers.batch_norm(inputs, decay=0.95, center=True, scale=True, is_training=True, 
                                            updates_collections=None, scope=name)
    
    def dropout(inputs):
        return tf.layers.dropout(inputs, rate=0.5, training=True)
    
    num_block = 14 * 14
    num_filter = 512
    hidden_size = 1024
    embedding_size = 512
    
    encoded = tf.reshape(encoded, [-1, num_block, num_filter]) # batch_size, num_block, num_filter
    contexts = batch_norm(encoded, 'contexts')
    
    Y = tf.placeholder(tf.int32, [None, maxlen + 2])
    Y_in = Y[:, :-1]
    Y_out = Y[:, 1:]
    mask = tf.to_float(tf.not_equal(Y_out, word2id['<pad>']))
    
    with tf.variable_scope('initialize'):
        context_mean = tf.reduce_mean(contexts, 1)
        state = dense(context_mean, hidden_size, name='initial_state')
        memory = dense(context_mean, hidden_size, name='initial_memory')
        
    with tf.variable_scope('embedding'):
        embeddings = tf.get_variable('weights', [len(word2id), embedding_size], initializer=e_initializer)
        embedded = tf.nn.embedding_lookup(embeddings, Y_in)
        
    with tf.variable_scope('projected'):
        projected_contexts = tf.reshape(contexts, [-1, num_filter]) # batch_size * num_block, num_filter
        projected_contexts = dense(projected_contexts, num_filter, activation=None, use_bias=False, name='projected_contexts')
        projected_contexts = tf.reshape(projected_contexts, [-1, num_block, num_filter]) # batch_size, num_block, num_filter
    
    lstm = tf.nn.rnn_cell.BasicLSTMCell(hidden_size)
    loss = 0
    alphas = []
    

    依次生成标题中的每个词,包括计算注意力和context、计算选择器、lstm处理、计算输出、计算损失函数几个部分

    for t in range(maxlen + 1):
        with tf.variable_scope('attend'):
            h0 = dense(state, num_filter, activation=None, name='fc_state') # batch_size, num_filter
            h0 = tf.nn.relu(projected_contexts + tf.expand_dims(h0, 1)) # batch_size, num_block, num_filter
            h0 = tf.reshape(h0, [-1, num_filter]) # batch_size * num_block, num_filter
            h0 = dense(h0, 1, activation=None, use_bias=False, name='fc_attention') # batch_size * num_block, 1
            h0 = tf.reshape(h0, [-1, num_block]) # batch_size, num_block
            
            alpha = tf.nn.softmax(h0) # batch_size, num_block
            # contexts:                 batch_size, num_block, num_filter
            # tf.expand_dims(alpha, 2): batch_size, num_block, 1
            context = tf.reduce_sum(contexts * tf.expand_dims(alpha, 2), 1, name='context') # batch_size, num_filter
            alphas.append(alpha)
            
        with tf.variable_scope('selector'):
            beta = dense(state, 1, activation=tf.nn.sigmoid, name='fc_beta') # batch_size, 1
            context = tf.multiply(beta, context, name='selected_context')  # batch_size, num_filter
            
        with tf.variable_scope('lstm'):
            h0 = tf.concat([embedded[:, t, :], context], 1) # batch_size, embedding_size + num_filter
            _, (memory, state) = lstm(inputs=h0, state=[memory, state])
        
        with tf.variable_scope('decode'):
            h0 = dropout(state)
            h0 = dense(h0, embedding_size, activation=None, name='fc_logits_state')
            h0 += dense(context, embedding_size, activation=None, use_bias=False, name='fc_logits_context')
            h0 += embedded[:, t, :]
            h0 = tf.nn.tanh(h0)
            
            h0 = dropout(h0)
            logits = dense(h0, len(word2id), activation=None, name='fc_logits')
        
        loss += tf.reduce_sum(tf.nn.sparse_softmax_cross_entropy_with_logits(labels=Y_out[:, t], logits=logits) * mask[:, t])
        tf.get_variable_scope().reuse_variables()
    

    在损失函数中加入注意力正则项,定义优化器

    alphas = tf.transpose(tf.stack(alphas), (1, 0, 2)) # batch_size, maxlen + 1, num_block
    alphas = tf.reduce_sum(alphas, 1) # batch_size, num_block
    attention_loss = tf.reduce_sum(((maxlen + 1) / num_block - alphas) ** 2)
    total_loss = (loss + attention_loss) / batch_size
    
    with tf.variable_scope('optimizer', reuse=tf.AUTO_REUSE):
        global_step = tf.Variable(0, trainable=False)
        vars_t = [var for var in tf.trainable_variables() if not var.name.startswith('endpoints')]
        train = tf.contrib.layers.optimize_loss(total_loss, global_step, 0.001, 'Adam', clip_gradients=5.0, variables=vars_t)
    

    训练模型,将一些tensor的值写入events文件,便于后续使用tensorboard查看

    sess = tf.Session()
    sess.run(tf.global_variables_initializer())
    
    saver = tf.train.Saver()
    OUTPUT_DIR = 'model'
    if not os.path.exists(OUTPUT_DIR):
        os.mkdir(OUTPUT_DIR)
    
    tf.summary.scalar('losses/loss', loss)
    tf.summary.scalar('losses/attention_loss', attention_loss)
    tf.summary.scalar('losses/total_loss', total_loss)
    summary = tf.summary.merge_all()
    writer = tf.summary.FileWriter(OUTPUT_DIR)
    
    epochs = 20
    for e in range(epochs):
        train_ids, train_captions = shuffle(train_ids, train_captions)
        for i in tqdm(range(len(train_ids) // batch_size)):
            X_batch = np.array([train_dict[x] for x in train_ids[i * batch_size: i * batch_size + batch_size]])
            Y_batch = train_captions[i * batch_size: i * batch_size + batch_size]
    
            _ = sess.run(train, feed_dict={X: X_batch, Y: Y_batch})
    
            if i > 0 and i % 100 == 0:
                writer.add_summary(sess.run(summary, 
                                            feed_dict={X: X_batch, Y: Y_batch}), 
                                            e * len(train_ids) // batch_size + i)
                writer.flush()
                
        saver.save(sess, os.path.join(OUTPUT_DIR, 'image_caption'))
    

    使用以下命令可以在tensorboard中查看历史训练数据

    tensorboard --logdir=model
    

    经过20轮训练后,损失函数曲线如下

    图像标题生成模型训练损失函数

    验证

    接下来是验证部分代码,即在验证集上生成每张图片的标题,然后和标注进行对比和评估

    在生成每一个词的时候,可以选择概率最大的词,即贪婪的做法,但不一定最优,因为当前概率最大的词并不能保证之后产生的序列整体概率最大

    也不能像中文分词中那样使用viterbi算法,因为viterbi算法要求整个序列的概率分布已知,才能使用动态规划找到最大概率路径

    但生成标题的时候,是一个词一个词地生成,而且选择的类别等于词典的大小,远远超出中文分词序列标注中的四分类,因此不可能穷尽所有可能的序列

    一种折中的做法是使用beam search,涉及一个参数beam size,举个例子,当beam size等于3时

    • 生成第一个词时,保留概率最大的三个词
    • 生成第二个词时,在以上三个词的基础上,进一步生成九个词,保留九个序列中概率最大的三个
    • 生成第n个词时,基于上一步保留下来的三个序列,进一步生成九个词,保留新的九个序列中概率最大的三个
    • 就好比一棵树,每一次所有的树枝都会进一步长出三个子树枝,然后对于所有树枝,保留最好的三个,其他全部砍掉
    • 重复以上过程,直到生成了结束词,或者生成的序列达到了最大长度

    验证部分的大多数代码和训练部分相同

    加载库

    # -*- coding: utf-8 -*-
    
    import tensorflow as tf
    import numpy as np
    import matplotlib.pyplot as plt
    %matplotlib inline
    from sklearn.utils import shuffle
    from imageio import imread
    import scipy.io
    import cv2
    import os
    import json
    from tqdm import tqdm
    import pickle
    

    加载数据

    batch_size = 128
    maxlen = 20
    image_size = 224
    
    MEAN_VALUES = np.array([123.68, 116.779, 103.939]).reshape((1, 1, 3))
    
    def load_data(image_dir, annotation_path):
        with open(annotation_path, 'r') as fr:
            annotation = json.load(fr)
            
        ids = []
        captions = []
        image_dict = {}
        for i in tqdm(range(len(annotation['annotations']))):
            item = annotation['annotations'][i]
            caption = item['caption'].strip().lower()
            caption = caption.replace('.', '').replace(',', '').replace("'", '').replace('"', '')
            caption = caption.replace('&', 'and').replace('(', '').replace(')', '').replace('-', ' ').split()
            caption = [w for w in caption if len(w) > 0]
            
            if len(caption) <= maxlen:
                if not item['image_id'] in image_dict:
                    img = imread(image_dir + '%012d.jpg' % item['image_id'])
                    h = img.shape[0]
                    w = img.shape[1]
                    if h > w:
                        img = img[h // 2 - w // 2: h // 2 + w // 2, :]
                    else:
                        img = img[:, w // 2 - h // 2: w // 2 + h // 2]   
                    img = cv2.resize(img, (image_size, image_size))
                    
                    if len(img.shape) < 3:
                        img = np.expand_dims(img, -1)
                        img = np.concatenate([img, img, img], axis=-1)
                    
                    image_dict[item['image_id']] = img
                
                ids.append(item['image_id'])
                captions.append(caption)
        
        return ids, captions, image_dict
    
    val_json = 'data/val/captions_val2014.json'
    val_ids, val_captions, val_dict = load_data('data/val/images/COCO_val2014_', val_json)
    print(len(val_ids))
    

    整理正确答案

    gt = {}
    for i in tqdm(range(len(val_ids))):
        val_id = val_ids[i]
        if not val_id in gt:
            gt[val_id] = []
        gt[val_id].append(' '.join(val_captions[i]) + ' .')
    print(len(gt))
    

    加载训练部分整理好的词典

    with open('dictionary.pkl', 'rb') as fr:
        [vocabulary, word2id, id2word] = pickle.load(fr)
    
    def translate(ids):
        words = [id2word[i] for i in ids if i >= 3]
        return ' '.join(words) + ' .'
    

    加载vgg19模型

    vgg = scipy.io.loadmat('imagenet-vgg-verydeep-19.mat')
    vgg_layers = vgg['layers']
    
    def vgg_endpoints(inputs, reuse=None):
        with tf.variable_scope('endpoints', reuse=reuse):
            def _weights(layer, expected_layer_name):
                W = vgg_layers[0][layer][0][0][2][0][0]
                b = vgg_layers[0][layer][0][0][2][0][1]
                layer_name = vgg_layers[0][layer][0][0][0][0]
                assert layer_name == expected_layer_name
                return W, b
    
            def _conv2d_relu(prev_layer, layer, layer_name):
                W, b = _weights(layer, layer_name)
                W = tf.constant(W)
                b = tf.constant(np.reshape(b, (b.size)))
                return tf.nn.relu(tf.nn.conv2d(prev_layer, filter=W, strides=[1, 1, 1, 1], padding='SAME') + b)
    
            def _avgpool(prev_layer):
                return tf.nn.avg_pool(prev_layer, ksize=[1, 2, 2, 1], strides=[1, 2, 2, 1], padding='SAME')
    
            graph = {}
            graph['conv1_1']  = _conv2d_relu(inputs, 0, 'conv1_1')
            graph['conv1_2']  = _conv2d_relu(graph['conv1_1'], 2, 'conv1_2')
            graph['avgpool1'] = _avgpool(graph['conv1_2'])
            graph['conv2_1']  = _conv2d_relu(graph['avgpool1'], 5, 'conv2_1')
            graph['conv2_2']  = _conv2d_relu(graph['conv2_1'], 7, 'conv2_2')
            graph['avgpool2'] = _avgpool(graph['conv2_2'])
            graph['conv3_1']  = _conv2d_relu(graph['avgpool2'], 10, 'conv3_1')
            graph['conv3_2']  = _conv2d_relu(graph['conv3_1'], 12, 'conv3_2')
            graph['conv3_3']  = _conv2d_relu(graph['conv3_2'], 14, 'conv3_3')
            graph['conv3_4']  = _conv2d_relu(graph['conv3_3'], 16, 'conv3_4')
            graph['avgpool3'] = _avgpool(graph['conv3_4'])
            graph['conv4_1']  = _conv2d_relu(graph['avgpool3'], 19, 'conv4_1')
            graph['conv4_2']  = _conv2d_relu(graph['conv4_1'], 21, 'conv4_2')
            graph['conv4_3']  = _conv2d_relu(graph['conv4_2'], 23, 'conv4_3')
            graph['conv4_4']  = _conv2d_relu(graph['conv4_3'], 25, 'conv4_4')
            graph['avgpool4'] = _avgpool(graph['conv4_4'])
            graph['conv5_1']  = _conv2d_relu(graph['avgpool4'], 28, 'conv5_1')
            graph['conv5_2']  = _conv2d_relu(graph['conv5_1'], 30, 'conv5_2')
            graph['conv5_3']  = _conv2d_relu(graph['conv5_2'], 32, 'conv5_3')
            graph['conv5_4']  = _conv2d_relu(graph['conv5_3'], 34, 'conv5_4')
            graph['avgpool5'] = _avgpool(graph['conv5_4'])
    
            return graph
    
    X = tf.placeholder(tf.float32, [None, image_size, image_size, 3])
    encoded = vgg_endpoints(X - MEAN_VALUES)['conv5_3']
    print(encoded)
    

    验证部分需要定义几个placeholder,因为要使用到beam search,所以每生成一个词就需要输入一些相关的值

    k_initializer = tf.contrib.layers.xavier_initializer()
    b_initializer = tf.constant_initializer(0.0)
    e_initializer = tf.random_uniform_initializer(-1.0, 1.0)
    
    def dense(inputs, units, activation=tf.nn.tanh, use_bias=True, name=None):
        return tf.layers.dense(inputs, units, activation, use_bias,
                               kernel_initializer=k_initializer, bias_initializer=b_initializer, name=name)
    
    def batch_norm(inputs, name):
        return tf.contrib.layers.batch_norm(inputs, decay=0.95, center=True, scale=True, is_training=False, 
                                            updates_collections=None, scope=name)
    
    def dropout(inputs):
        return tf.layers.dropout(inputs, rate=0.5, training=False)
    
    num_block = 14 * 14
    num_filter = 512
    hidden_size = 1024
    embedding_size = 512
    
    encoded = tf.reshape(encoded, [-1, num_block, num_filter]) # batch_size, num_block, num_filter
    contexts = batch_norm(encoded, 'contexts')
    
    with tf.variable_scope('initialize'):
        context_mean = tf.reduce_mean(contexts, 1)
        initial_state = dense(context_mean, hidden_size, name='initial_state')
        initial_memory = dense(context_mean, hidden_size, name='initial_memory')
        
    contexts_phr = tf.placeholder(tf.float32, [None, num_block, num_filter])
    last_memory = tf.placeholder(tf.float32, [None, hidden_size])
    last_state = tf.placeholder(tf.float32, [None, hidden_size])
    last_word = tf.placeholder(tf.int32, [None])
    
    with tf.variable_scope('embedding'):
        embeddings = tf.get_variable('weights', [len(word2id), embedding_size], initializer=e_initializer)
        embedded = tf.nn.embedding_lookup(embeddings, last_word)
        
    with tf.variable_scope('projected'):
        projected_contexts = tf.reshape(contexts_phr, [-1, num_filter]) # batch_size * num_block, num_filter
        projected_contexts = dense(projected_contexts, num_filter, activation=None, use_bias=False, name='projected_contexts')
        projected_contexts = tf.reshape(projected_contexts, [-1, num_block, num_filter]) # batch_size, num_block, num_filter
    
    lstm = tf.nn.rnn_cell.BasicLSTMCell(hidden_size)
    

    生成部分,不需要循环,重复一次即可,后面进行beam search时再进行多次输入数据并得到输出

    with tf.variable_scope('attend'):
        h0 = dense(last_state, num_filter, activation=None, name='fc_state') # batch_size, num_filter
        h0 = tf.nn.relu(projected_contexts + tf.expand_dims(h0, 1)) # batch_size, num_block, num_filter
        h0 = tf.reshape(h0, [-1, num_filter]) # batch_size * num_block, num_filter
        h0 = dense(h0, 1, activation=None, use_bias=False, name='fc_attention') # batch_size * num_block, 1
        h0 = tf.reshape(h0, [-1, num_block]) # batch_size, num_block
    
        alpha = tf.nn.softmax(h0) # batch_size, num_block
        # contexts:                 batch_size, num_block, num_filter
        # tf.expand_dims(alpha, 2): batch_size, num_block, 1
        context = tf.reduce_sum(contexts_phr * tf.expand_dims(alpha, 2), 1, name='context') # batch_size, num_filter
    
    with tf.variable_scope('selector'):
        beta = dense(last_state, 1, activation=tf.nn.sigmoid, name='fc_beta') # batch_size, 1
        context = tf.multiply(beta, context, name='selected_context')  # batch_size, num_filter
    
    with tf.variable_scope('lstm'):
        h0 = tf.concat([embedded, context], 1) # batch_size, embedding_size + num_filter
        _, (current_memory, current_state) = lstm(inputs=h0, state=[last_memory, last_state])
    
    with tf.variable_scope('decode'):
        h0 = dropout(current_state)
        h0 = dense(h0, embedding_size, activation=None, name='fc_logits_state')
        h0 += dense(context, embedding_size, activation=None, use_bias=False, name='fc_logits_context')
        h0 += embedded
        h0 = tf.nn.tanh(h0)
    
        h0 = dropout(h0)
        logits = dense(h0, len(word2id), activation=None, name='fc_logits')
        probs = tf.nn.softmax(logits)
    

    加载训练好的模型,对每个batch的数据进行beam search,依次生成每一个词

    这里beam size设为1主要是为了节省时间,验证共花了10个小时,具体应用时可以适当加大beam size

    MODEL_DIR = 'model'
    sess = tf.Session()
    sess.run(tf.global_variables_initializer())
    saver = tf.train.Saver()
    saver.restore(sess, tf.train.latest_checkpoint(MODEL_DIR))
    
    beam_size = 1
    id2sentence = {}
    
    val_ids = list(set(val_ids))
    if len(val_ids) % batch_size != 0:
        for i in range(batch_size - len(val_ids) % batch_size):
            val_ids.append(val_ids[0])
    print(len(val_ids))
    
    for i in tqdm(range(len(val_ids) // batch_size)):
        X_batch = np.array([val_dict[x] for x in val_ids[i * batch_size: i * batch_size + batch_size]])
        contexts_, initial_memory_, initial_state_ = sess.run([contexts, initial_memory, initial_state], feed_dict={X: X_batch})
    
        result = []
        complete = []
        for b in range(batch_size):
            result.append([{
                'sentence': [], 
                'memory': initial_memory_[b], 
                'state': initial_state_[b],
                'score': 1.0,
                'alphas': []
            }])
            complete.append([])
    
        for t in range(maxlen + 1):
            cache = [[] for b in range(batch_size)]
            step = 1 if t == 0 else beam_size
            for s in range(step):
                if t == 0:
                    last_word_ = np.ones([batch_size], np.int32) * word2id['<start>']
                else:
                    last_word_ = np.array([result[b][s]['sentence'][-1] for b in range(batch_size)], np.int32)
    
                last_memory_ = np.array([result[b][s]['memory'] for b in range(batch_size)], np.float32)
                last_state_ = np.array([result[b][s]['state'] for b in range(batch_size)], np.float32)
    
                current_memory_, current_state_, probs_, alpha_ = sess.run(
                    [current_memory, current_state, probs, alpha], feed_dict={
                        contexts_phr: contexts_, 
                        last_memory: last_memory_,
                        last_state: last_state_,
                        last_word: last_word_
                        })
                
                for b in range(batch_size):
                    word_and_probs = [[w, p] for w, p in enumerate(probs_[b])]
                    word_and_probs.sort(key=lambda x:-x[1])
                    word_and_probs = word_and_probs[:beam_size + 1]
    
                    for w, p in word_and_probs:
                        item = {
                            'sentence': result[b][s]['sentence'] + [w], 
                            'memory': current_memory_[b], 
                            'state': current_state_[b],
                            'score': result[b][s]['score'] * p,
                            'alphas': result[b][s]['alphas'] + [alpha_[b]]
                        }
    
                        if id2word[w] == '<end>':
                            complete[b].append(item)
                        else:
                            cache[b].append(item)
            
            for b in range(batch_size):
                cache[b].sort(key=lambda x:-x['score'])
                cache[b] = cache[b][:beam_size]
            result = cache.copy()
        
        for b in range(batch_size):
            if len(complete[b]) == 0:
                final_sentence = result[b][0]['sentence']
            else:
                final_sentence = complete[b][0]['sentence']
            
            val_id = val_ids[i * batch_size + b] 
            if not val_id in id2sentence:
                id2sentence[val_id] = [translate(final_sentence)]
    
    print(len(id2sentence))
    

    将标题生成结果写入文件,便于后续评估

    with open('generated.txt', 'w') as fw:
        for i in id2sentence.keys():
            fw.write(str(i) + '^' + id2sentence[i][0] + '^' + '_'.join(gt[i]) + '\n')
    

    调用以下项目进行评估,https://github.com/tylin/coco-caption,评估指标包括BLEU、Rouge、Cider三个

    其中BLEU在图像标题生成、机器翻译等任务中用得比较多,可以简单理解为1-gram、2-gram、3-gram、4-gram的命中率

    吴恩达深度学习微专业课中关于BLEU的介绍,http://mooc.study.163.com/course/2001280005?tid=2001391038#/info

    from pycocoevalcap.bleu.bleu import Bleu
    from pycocoevalcap.rouge.rouge import Rouge
    from pycocoevalcap.cider.cider import Cider
    
    id2sentence = {}
    gt = {}
    with open('generated.txt', 'r') as fr:
        lines = fr.readlines()
        for line in lines:
            line = line.strip('\n').split('^')
            i = line[0]
            id2sentence[i] = [line[1]]
            gt[i] = line[2].split('_')
    
    scorers = [
        (Bleu(4), ['Bleu_1', 'Bleu_2', 'Bleu_3', 'Bleu_4']),
        (Rouge(), 'ROUGE_L'),
        (Cider(), 'CIDEr')
    ]
    
    for scorer, name in scorers:
        score, _ = scorer.compute_score(gt, id2sentence)
        if type(score) == list:
            for n, s in zip(name, score):
                print(n, s)
        else:
            print(name, score)
    

    评估结果如下,适当加大beam size可以进一步提高各项指标

    • Bleu_1:0.6878
    • Bleu_2:0.4799
    • Bleu_3:0.3347
    • Bleu_4:0.2355
    • ROUGE: 0.5304
    • CIDEr: 0.7293

    使用

    最后,通过以下代码在本机上使用训练好的模型,为任意图片生成标题

    整体代码结构和验证部分比较类似,但是由于只需要对一张图片生成标题,所以beam search部分的代码简化很多

    # -*- coding: utf-8 -*-
    
    import tensorflow as tf
    import numpy as np
    import matplotlib.pyplot as plt
    from imageio import imread
    import scipy.io
    import cv2
    import os
    import pickle
    
    batch_size = 1
    maxlen = 20
    image_size = 224
    
    MEAN_VALUES = np.array([123.68, 116.779, 103.939]).reshape((1, 1, 3))
    
    with open('dictionary.pkl', 'rb') as fr:
        [vocabulary, word2id, id2word] = pickle.load(fr)
    
    def translate(ids):
        words = [id2word[i] for i in ids if i >= 3]
        return ' '.join(words) + ' .'
    
    vgg = scipy.io.loadmat('imagenet-vgg-verydeep-19.mat')
    vgg_layers = vgg['layers']
    
    def vgg_endpoints(inputs, reuse=None):
        with tf.variable_scope('endpoints', reuse=reuse):
            def _weights(layer, expected_layer_name):
                W = vgg_layers[0][layer][0][0][2][0][0]
                b = vgg_layers[0][layer][0][0][2][0][1]
                layer_name = vgg_layers[0][layer][0][0][0][0]
                assert layer_name == expected_layer_name
                return W, b
    
            def _conv2d_relu(prev_layer, layer, layer_name):
                W, b = _weights(layer, layer_name)
                W = tf.constant(W)
                b = tf.constant(np.reshape(b, (b.size)))
                return tf.nn.relu(tf.nn.conv2d(prev_layer, filter=W, strides=[1, 1, 1, 1], padding='SAME') + b)
    
            def _avgpool(prev_layer):
                return tf.nn.avg_pool(prev_layer, ksize=[1, 2, 2, 1], strides=[1, 2, 2, 1], padding='SAME')
    
            graph = {}
            graph['conv1_1']  = _conv2d_relu(inputs, 0, 'conv1_1')
            graph['conv1_2']  = _conv2d_relu(graph['conv1_1'], 2, 'conv1_2')
            graph['avgpool1'] = _avgpool(graph['conv1_2'])
            graph['conv2_1']  = _conv2d_relu(graph['avgpool1'], 5, 'conv2_1')
            graph['conv2_2']  = _conv2d_relu(graph['conv2_1'], 7, 'conv2_2')
            graph['avgpool2'] = _avgpool(graph['conv2_2'])
            graph['conv3_1']  = _conv2d_relu(graph['avgpool2'], 10, 'conv3_1')
            graph['conv3_2']  = _conv2d_relu(graph['conv3_1'], 12, 'conv3_2')
            graph['conv3_3']  = _conv2d_relu(graph['conv3_2'], 14, 'conv3_3')
            graph['conv3_4']  = _conv2d_relu(graph['conv3_3'], 16, 'conv3_4')
            graph['avgpool3'] = _avgpool(graph['conv3_4'])
            graph['conv4_1']  = _conv2d_relu(graph['avgpool3'], 19, 'conv4_1')
            graph['conv4_2']  = _conv2d_relu(graph['conv4_1'], 21, 'conv4_2')
            graph['conv4_3']  = _conv2d_relu(graph['conv4_2'], 23, 'conv4_3')
            graph['conv4_4']  = _conv2d_relu(graph['conv4_3'], 25, 'conv4_4')
            graph['avgpool4'] = _avgpool(graph['conv4_4'])
            graph['conv5_1']  = _conv2d_relu(graph['avgpool4'], 28, 'conv5_1')
            graph['conv5_2']  = _conv2d_relu(graph['conv5_1'], 30, 'conv5_2')
            graph['conv5_3']  = _conv2d_relu(graph['conv5_2'], 32, 'conv5_3')
            graph['conv5_4']  = _conv2d_relu(graph['conv5_3'], 34, 'conv5_4')
            graph['avgpool5'] = _avgpool(graph['conv5_4'])
    
            return graph
    
    X = tf.placeholder(tf.float32, [None, image_size, image_size, 3])
    encoded = vgg_endpoints(X - MEAN_VALUES)['conv5_3']
    
    k_initializer = tf.contrib.layers.xavier_initializer()
    b_initializer = tf.constant_initializer(0.0)
    e_initializer = tf.random_uniform_initializer(-1.0, 1.0)
    
    def dense(inputs, units, activation=tf.nn.tanh, use_bias=True, name=None):
        return tf.layers.dense(inputs, units, activation, use_bias,
                               kernel_initializer=k_initializer, bias_initializer=b_initializer, name=name)
    
    def batch_norm(inputs, name):
        return tf.contrib.layers.batch_norm(inputs, decay=0.95, center=True, scale=True, is_training=False, 
                                            updates_collections=None, scope=name)
    
    def dropout(inputs):
        return tf.layers.dropout(inputs, rate=0.5, training=False)
    
    num_block = 14 * 14
    num_filter = 512
    hidden_size = 1024
    embedding_size = 512
    
    encoded = tf.reshape(encoded, [-1, num_block, num_filter]) # batch_size, num_block, num_filter
    contexts = batch_norm(encoded, 'contexts')
    
    with tf.variable_scope('initialize'):
        context_mean = tf.reduce_mean(contexts, 1)
        initial_state = dense(context_mean, hidden_size, name='initial_state')
        initial_memory = dense(context_mean, hidden_size, name='initial_memory')
        
    contexts_phr = tf.placeholder(tf.float32, [None, num_block, num_filter])
    last_memory = tf.placeholder(tf.float32, [None, hidden_size])
    last_state = tf.placeholder(tf.float32, [None, hidden_size])
    last_word = tf.placeholder(tf.int32, [None])
    
    with tf.variable_scope('embedding'):
        embeddings = tf.get_variable('weights', [len(word2id), embedding_size], initializer=e_initializer)
        embedded = tf.nn.embedding_lookup(embeddings, last_word)
        
    with tf.variable_scope('projected'):
        projected_contexts = tf.reshape(contexts_phr, [-1, num_filter]) # batch_size * num_block, num_filter
        projected_contexts = dense(projected_contexts, num_filter, activation=None, use_bias=False, name='projected_contexts')
        projected_contexts = tf.reshape(projected_contexts, [-1, num_block, num_filter]) # batch_size, num_block, num_filter
    
    lstm = tf.nn.rnn_cell.BasicLSTMCell(hidden_size)
    
    with tf.variable_scope('attend'):
        h0 = dense(last_state, num_filter, activation=None, name='fc_state') # batch_size, num_filter
        h0 = tf.nn.relu(projected_contexts + tf.expand_dims(h0, 1)) # batch_size, num_block, num_filter
        h0 = tf.reshape(h0, [-1, num_filter]) # batch_size * num_block, num_filter
        h0 = dense(h0, 1, activation=None, use_bias=False, name='fc_attention') # batch_size * num_block, 1
        h0 = tf.reshape(h0, [-1, num_block]) # batch_size, num_block
    
        alpha = tf.nn.softmax(h0) # batch_size, num_block
        # contexts:                 batch_size, num_block, num_filter
        # tf.expand_dims(alpha, 2): batch_size, num_block, 1
        context = tf.reduce_sum(contexts_phr * tf.expand_dims(alpha, 2), 1, name='context') # batch_size, num_filter
    
    with tf.variable_scope('selector'):
        beta = dense(last_state, 1, activation=tf.nn.sigmoid, name='fc_beta') # batch_size, 1
        context = tf.multiply(beta, context, name='selected_context')  # batch_size, num_filter
    
    with tf.variable_scope('lstm'):
        h0 = tf.concat([embedded, context], 1) # batch_size, embedding_size + num_filter
        _, (current_memory, current_state) = lstm(inputs=h0, state=[last_memory, last_state])
    
    with tf.variable_scope('decode'):
        h0 = dropout(current_state)
        h0 = dense(h0, embedding_size, activation=None, name='fc_logits_state')
        h0 += dense(context, embedding_size, activation=None, use_bias=False, name='fc_logits_context')
        h0 += embedded
        h0 = tf.nn.tanh(h0)
    
        h0 = dropout(h0)
        logits = dense(h0, len(word2id), activation=None, name='fc_logits')
        probs = tf.nn.softmax(logits)
    
    MODEL_DIR = 'model'
    sess = tf.Session()
    sess.run(tf.global_variables_initializer())
    saver = tf.train.Saver()
    saver.restore(sess, tf.train.latest_checkpoint(MODEL_DIR))
    
    beam_size = 3
    img = imread('test.png')
    if img.shape[-1] == 4:
        img = img[:, :, :-1]
    h = img.shape[0]
    w = img.shape[1]
    if h > w:
        img = img[h // 2 - w // 2: h // 2 + w // 2, :]
    else:
        img = img[:, w // 2 - h // 2: w // 2 + h // 2]
    img = cv2.resize(img, (image_size, image_size))
    X_data = np.expand_dims(img, 0)
    
    contexts_, initial_memory_, initial_state_ = sess.run([contexts, initial_memory, initial_state], feed_dict={X: X_data})
    
    result = [{
        'sentence': [], 
        'memory': initial_memory_[0], 
        'state': initial_state_[0],
        'score': 1.0,
        'alphas': []
    }]
    complete = []
    for t in range(maxlen + 1):
        cache = []
        step = 1 if t == 0 else beam_size
        for s in range(step):
            if t == 0:
                last_word_ = np.ones([batch_size], np.int32) * word2id['<start>']
            else:
                last_word_ = np.array([result[s]['sentence'][-1]], np.int32)
    
            last_memory_ = np.array([result[s]['memory']], np.float32)
            last_state_ = np.array([result[s]['state']], np.float32)
    
            current_memory_, current_state_, probs_, alpha_ = sess.run(
                [current_memory, current_state, probs, alpha], feed_dict={
                    contexts_phr: contexts_, 
                    last_memory: last_memory_,
                    last_state: last_state_,
                    last_word: last_word_
                    })
            
            word_and_probs = [[w, p] for w, p in enumerate(probs_[0])]
            word_and_probs.sort(key=lambda x:-x[1])
            word_and_probs = word_and_probs[:beam_size + 1]
    
            for w, p in word_and_probs:
                item = {
                    'sentence': result[s]['sentence'] + [w], 
                    'memory': current_memory_[0], 
                    'state': current_state_[0],
                    'score': result[s]['score'] * p,
                    'alphas': result[s]['alphas'] + [alpha_[0]]
                }
                if id2word[w] == '<end>':
                    complete.append(item)
                else:
                    cache.append(item)
    
        cache.sort(key=lambda x:-x['score'])
        cache = cache[:beam_size]
        result = cache.copy()
    
    if len(complete) == 0:
        final_sentence = result[0]['sentence']
        alphas = result[0]['alphas']
    else:
        final_sentence = complete[0]['sentence']
        alphas = complete[0]['alphas']
    
    sentence = translate(final_sentence)
    print(sentence)
    sentence = sentence.split(' ')
    
    img = (img - img.min()) / (img.max() - img.min())
    n = int(np.ceil(np.sqrt(len(sentence))))
    plt.figure(figsize=(10, 8))
    for i in range(len(sentence)):
        word = sentence[i]
        a = np.reshape(alphas[i], (14, 14))
        a = cv2.resize(a, (image_size, image_size))
        a = np.expand_dims(a, -1)
        a = (a - a.min()) / (a.max() - a.min())
        combine = 0.5 * img +  0.5 * a
        plt.subplot(n, n, i + 1)
        plt.text(0, 1, word, color='black', backgroundcolor='white', fontsize=12)
        plt.imshow(combine)
        plt.axis('off')
    plt.show()
    

    标题生成结果如下,非常准确地涵盖了新娘、新郎、摆pose、拍照等关键词,并且注意力可视化也很好地反映了生成每个词时模型对图片不同区域的关注度

    图像标题生成结果

    参考

    视频讲解课程

    深度有趣(一)

    展开全文
  • 点击上方“AI算法与图像处理”,选择加"星标"或“置顶”重磅干货,第一时间送达前言小伙伴们,大家好!好久没有给大家分享图像处理相关的实战内容了,图像处理给我的感觉是真的体...

    点击上方AI算法与图像处理”,选择加"星标"或“置顶”

    重磅干货,第一时间送达

    前言

    小伙伴们,大家好!好久没有给大家分享图像处理相关的实战内容了,图像处理给我的感觉是真的体现数学之美,例如我之前分享的关于极坐标转换的全景图的变换,虽然搞了很久才想明白,不过确实挺有意思的。

    希望今天的文章能让大家有所收获,有用可以帮我转发一下。

    个人觉得虽然这个项目不一定你会用到,但是对于创作者,遇到一个问题如何去分解问题,解决问题的思路和方法或许才是真正对我们个人成长有帮助的点。

    文章链接:酷炫骚操作,票圈装13神技,极坐标全景图

    今天要分享的内容是关于页面扭曲矫正的内容,为了让大家有一个相关的概念,下面先预览一下效果图


    页面扭曲矫正的原理

    作者写这篇文章的目的也很有意思,因为 作者的妻子是一个老师,她的学生经常要发一些图片的作业给她,但是难免由于拍照技术和条件等各种原因,导致批改作业的难度提升,因此作者为了他的老婆,做出来了这一套方案。(爱的魔力)

    对于一些经典的文本矫正的思路(例如Leptonica dewarping method 和 the CTM method)将问题分解为:

    1. 将文本分成几行线

    2.  找到使得线平行和水平的映射或坐标变换

    本文作者同样采用了类似的思路,构建了一个参数模型用于页面(文本)的矫正,模型主要考虑的参数:

    • 页面在三维空间中的旋转向量  r和平移向量 t

    • 指定页面表面的两个曲率  α and β

    • 页面上n个水平跨度的垂直偏移

    • 对于每个跨度,水平跨度中 m个点的水平偏移量

    对于上面的一些参数,本人的理解是页面扭曲存在这旋转和偏移的变换,因为把文本内容分成几行,因此将每一行文本看成是一条曲线,这里假设扭曲后变成三次样条曲线,由两个系数 a 和 b 控制。垂直偏移是指分割后的每行之间的距离,水平偏移是指对每条线上设置点,用于后续的矫正变换处理。

    (个人理解,可能与原文存在一点的偏差)


    接下来的操作就非常牛了,作者通过对页面进行模拟,设置不同参数用于观察其中的规律

    最后作者发现当设置的参数固定后,页面上的每个关键点都能在图片的平面内找到确定的对应点。

    红色的是文本上检测到的关键点,蓝色的是通过模型的重投影的点。左图的蓝色点都是共线的(假设一开始文本是直线的),而右图是将确定页面的姿态/形状,使得蓝色点几乎上红色点上。


    实现的细节

    上面是大致的实现原理,接下来是作者实现功能的主要步骤:

    1、获取页面的边界。这里并不是采用整幅图去处理,而是非常的巧妙采用内部文本内容以及文本与边界大致距离去确定页面的边界。

    2、检测文本的轮廓。通过自适应阈值——> 膨胀+腐蚀——> 连通区域分析+PCA来近似文本

    3、将文本组成跨度

    4、样本跨度。在每个跨度上生成代表性点。

    5、创建初始参数估计。实现投影到图片平面内。

    6、优化!使重投影的误差变小

    7、重新映射图片和阈值


    总结

    该项目是之前用到的,今天特意分享给大家,百度搜索出来的相关内容很少,因为个人觉得分享出来还是蛮有意义的,对于内容的理解由于本人水平有限,可能会存在一定的偏差,但是真的是花了挺大功夫来解读的。

    使用起来并不难,但是由于作者的假设,对于并非这情况的文本,以及变形过大的矫正效果并不是非常理想。创作不易,希望能点个在看,转发一手,由于缺少留言功能,因此,只能通过后台的数据反馈才知道自己分享的内容是否真的有帮助到大家,大家的每个阅读在看和转发,我都非常的感谢。

    代码和原文链接可以在后台回复”page_dewarp“,获取,建议复制,避免错误。

    秘密基地

    我偷偷弄了一个公众号,专门用于分享各种实用的软件,工具,网站和app。期待你的关注。

    兄弟们,开车啦

    展开全文
  • python图像处理较为全面

    万次阅读 2018-06-28 19:08:01
    第 1 章 基本的图像操作和处理本章讲解操作和处理图像的基础知识,将通过大量...1.1 PIL:Python图像处理类库PIL(Python Imaging Library Python,图像处理类库)提供了通用的图像处理功能,以及大量有用的基本图...

    第 1 章 基本的图像操作和处理

    本章讲解操作和处理图像的基础知识,将通过大量示例介绍处理图像所需的 Python 工具包,并介绍用于读取图像、图像转换和缩放、计算导数、画图和保存结果等的基本工具。这些工具的使用将贯穿本书的剩余章节。

    1.1 PIL:Python图像处理类库

    PIL(Python Imaging Library Python,图像处理类库)提供了通用的图像处理功能,以及大量有用的基本图像操作,比如图像缩放、裁剪、旋转、颜色转换等。PIL 是免费的,可以从 http://www.pythonware.com/products/pil/ 下载。

    利用 PIL 中的函数,我们可以从大多数图像格式的文件中读取数据,然后写入最常见的图像格式文件中。PIL 中最重要的模块为 Image。要读取一幅图像,可以使用:

    from PIL import Image
    
    pil_im = Image.open('empire.jpg')

    上述代码的返回值 pil_im 是一个 PIL 图像对象。

    图像的颜色转换可以使用 convert() 方法来实现。要读取一幅图像,并将其转换成灰度图像,只需要加上 convert('L'),如下所示:

    pil_im = Image.open('empire.jpg').convert('L')

    在 PIL 文档中有一些例子,参见 http://www.pythonware.com/library/pil/handbook/index.htm。这些例子的输出结果如图 1-1 所示。

    图 1-1:用 PIL 处理图像的例子

    1.1.1 转换图像格式

    通过 save() 方法,PIL 可以将图像保存成多种格式的文件。下面的例子从文件名列表(filelist)中读取所有的图像文件,并转换成 JPEG 格式:

    from PIL import Image
    import os
    
    for infile in filelist:
      outfile = os.path.splitext(infile)[0] + ".jpg"
      if infile != outfile:
        try:
          Image.open(infile).save(outfile)
        except IOError:
          print "cannot convert", infile
    

    PIL 的 open() 函数用于创建 PIL 图像对象,save() 方法用于保存图像到具有指定文件名的文件。除了后缀变为“.jpg”,上述代码的新文件名和原文件名相同。PIL 是个足够智能的类库,可以根据文件扩展名来判定图像的格式。PIL 函数会进行简单的检查,如果文件不是 JPEG 格式,会自动将其转换成 JPEG 格式;如果转换失败,它会在控制台输出一条报告失败的消息。

    本书会处理大量图像列表。下面将创建一个包含文件夹中所有图像文件的文件名列表。首先新建一个文件,命名为 imtools.py,来存储一些经常使用的图像操作,然后将下面的函数添加进去:

    import os
    def get_imlist(path):
    
    """ 返回目录中所有JPG 图像的文件名列表"""
    
    return [os.path.join(path,f) for f in os.listdir(path) if f.endswith('.jpg')]

    现在,回到 PIL。

    1.1.2 创建缩略图

    使用 PIL 可以很方便地创建图像的缩略图。thumbnail() 方法接受一个元组参数(该参数指定生成缩略图的大小),然后将图像转换成符合元组参数指定大小的缩略图。例如,创建最长边为 128 像素的缩略图,可以使用下列命令:

    pil_im.thumbnail((128,128))

    1.1.3 复制和粘贴图像区域

    使用 crop() 方法可以从一幅图像中裁剪指定区域:

    box = (100,100,400,400)
    region = pil_im.crop(box)

    该区域使用四元组来指定。四元组的坐标依次是(左,上,右,下)。PIL 中指定坐标系的左上角坐标为(0,0)。我们可以旋转上面代码中获取的区域,然后使用 paste() 方法将该区域放回去,具体实现如下:

    region = region.transpose(Image.ROTATE_180)
    pil_im.paste(region,box)

    1.1.4 调整尺寸和旋转

    要调整一幅图像的尺寸,我们可以调用 resize() 方法。该方法的参数是一个元组,用来指定新图像的大小:

    out = pil_im.resize((128,128))

    要旋转一幅图像,可以使用逆时针方式表示旋转角度,然后调用 rotate() 方法:

    out = pil_im.rotate(45)

    上述例子的输出结果如图 1-1 所示。最左端是原始图像,然后是灰度图像、粘贴有旋转后裁剪图像的原始图像,最后是缩略图。

    1.2 Matplotlib

    我们处理数学运算、绘制图表,或者在图像上绘制点、直线和曲线时,Matplotlib 是个很好的类库,具有比 PIL 更强大的绘图功能。Matplotlib 可以绘制出高质量的图表,就像本书中的许多插图一样。Matplotlib 中的 PyLab 接口包含很多方便用户创建图像的函数。Matplotlib 是开源工具,可以从 http://matplotlib.sourceforge.net/ 免费下载。该链接中包含非常详尽的使用说明和教程。下面的例子展示了本书中需要使用的大部分函数。

    1.2.1 绘制图像、点和线

    尽管 Matplotlib 可以绘制出较好的条形图、饼状图、散点图等,但是对于大多数计算机视觉应用来说,仅仅需要用到几个绘图命令。最重要的是,我们想用点和线来表示一些事物,比如兴趣点、对应点以及检测出的物体。下面是用几个点和一条线绘制图像的例子:

    from PIL import Image
    from pylab import *
    
    # 读取图像到数组中
    im = array(Image.open('empire.jpg'))
    
    # 绘制图像
    imshow(im)
    
    # 一些点
    x = [100,100,400,400]
    y = [200,500,200,500]
    
    # 使用红色星状标记绘制点
    plot(x,y,'r*')
    
    # 绘制连接前两个点的线
    plot(x[:2],y[:2])
    
    # 添加标题,显示绘制的图像
    title('Plotting: "empire.jpg"')
    show()

    上面的代码首先绘制出原始图像,然后在 x 和 y 列表中给定点的 x 坐标和 y 坐标上绘制出红色星状标记点,最后在两个列表表示的前两个点之间绘制一条线段(默认为蓝色)。该例子的绘制结果如图 1-2 所示。show() 命令首先打开图形用户界面(GUI),然后新建一个图像窗口。该图形用户界面会循环阻断脚本,然后暂停,直到最后一个图像窗口关闭。在每个脚本里,你只能调用一次 show() 命令,而且通常是在脚本的结尾调用。注意,在 PyLab 库中,我们约定图像的左上角为坐标原点。

    图像的坐标轴是一个很有用的调试工具;但是,如果你想绘制出较美观的图像,加上下列命令可以使坐标轴不显示:

    axis('off')

    上面的命令将绘制出如图 1-2 右边所示的图像。

    图 1-2:Matplotlib 绘图示例。带有坐标轴和不带坐标轴的包含点和一条线段的图像

    在绘图时,有很多选项可以控制图像的颜色和样式。最有用的一些短命令如表 1-1、表 1-2 和表 1-3 所示。使用方法见下面的例子:

    plot(x,y)         # 默认为蓝色实线
    plot(x,y,'r*')    # 红色星状标记
    plot(x,y,'go-')   # 带有圆圈标记的绿线
    plot(x,y,'ks:')   # 带有正方形标记的黑色虚线

    表1-1:用PyLab库绘图的基本颜色格式命令

    颜色

     

    'b'

    蓝色

    'g'

    绿色

    'r'

    红色

    'c'

    青色

    'm'

    品红

    'y'

    黄色

    'k'

    黑色

    'w'

    白色

    表1-2:用PyLab库绘图的基本线型格式命令

    线型

     

    '-'

    实线

    '--'

    虚线

    ':'

    点线

    表1-3:用PyLab库绘图的基本绘制标记格式命令

    标记

     

    '.'

    'o'

    圆圈

    's'

    正方形

    '*'

    星形

    '+'

    加号

    'x'

    叉号

    1.2.2 图像轮廓和直方图

    下面来看两个特别的绘图示例:图像的轮廓和直方图。绘制图像的轮廓(或者其他二维函数的等轮廓线)在工作中非常有用。因为绘制轮廓需要对每个坐标 [x, y] 的像素值施加同一个阈值,所以首先需要将图像灰度化:

    from PIL import Image
    from pylab import *
    
    # 读取图像到数组中
    im = array(Image.open('empire.jpg').convert('L'))
    
    # 新建一个图像
    figure()
    # 不使用颜色信息
    gray()
    # 在原点的左上角显示轮廓图像
    contour(im, origin='image')
    axis('equal')
    axis('off')

    像之前的例子一样,这里用 PIL 的 convert() 方法将图像转换成灰度图像。

    图像的直方图用来表征该图像像素值的分布情况。用一定数目的小区间(bin)来指定表征像素值的范围,每个小区间会得到落入该小区间表示范围的像素数目。该(灰度)图像的直方图可以使用 hist() 函数绘制:

    figure()
    hist(im.flatten(),128)
    show()

    hist() 函数的第二个参数指定小区间的数目。需要注意的是,因为 hist() 只接受一维数组作为输入,所以我们在绘制图像直方图之前,必须先对图像进行压平处理。flatten() 方法将任意数组按照行优先准则转换成一维数组。图 1-3 为等轮廓线和直方图图像。

    图 1-3:用 Matplotlib 绘制图像等轮廓线和直方图

    1.2.3 交互式标注

    有时用户需要和某些应用交互,例如在一幅图像中标记一些点,或者标注一些训练数据。PyLab 库中的 ginput() 函数就可以实现交互式标注。下面是一个简短的例子:

    from PIL import Image
    from pylab import *
    
    im = array(Image.open('empire.jpg'))
    imshow(im)
    print 'Please click 3 points'
    x = ginput(3)
    print 'you clicked:',x
    show()

    上面的脚本首先绘制一幅图像,然后等待用户在绘图窗口的图像区域点击三次。程序将这些点击的坐标 [x, y] 自动保存在 x 列表里。

    1.3 NumPy

    NumPyhttp://www.scipy.org/NumPy/)是非常有名的 Python 科学计算工具包,其中包含了大量有用的思想,比如数组对象(用来表示向量、矩阵、图像等)以及线性代数函数。NumPy 中的数组对象几乎贯穿用于本书的所有例子中 1 数组对象可以帮助你实现数组中重要的操作,比如矩阵乘积、转置、解方程系统、向量乘积和归一化,这为图像变形、对变化进行建模、图像分类、图像聚类等提供了基础。

    1PyLab 实际上包含 NumPy 的一些内容,如数组类型。这也是我们能够在 1.2 节使用数组类型的原因。

    NumPy 可以从 http://www.scipy.org/Download 免费下载,在线说明文档(http://docs.scipy.org/doc/numpy/)包含了你可能遇到的大多数问题的答案。关于 NumPy 的更多内容,请参考开源书籍 [24]。

    1.3.1 图像数组表示

    在先前的例子中,当载入图像时,我们通过调用 array() 方法将图像转换成 NumPy 的数组对象,但当时并没有进行详细介绍。NumPy 中的数组对象是多维的,可以用来表示向量、矩阵和图像。一个数组对象很像一个列表(或者是列表的列表),但是数组中所有的元素必须具有相同的数据类型。除非创建数组对象时指定数据类型,否则数据类型会按照数据的类型自动确定。

    对于图像数据,下面的例子阐述了这一点:

    im = array(Image.open('empire.jpg'))
    print im.shape, im.dtype
    
    im = array(Image.open('empire.jpg').convert('L'),'f')
    print im.shape, im.dtype
    

    控制台输出结果如下所示:

    (800, 569, 3) uint8
    (800, 569) float32
    

    每行的第一个元组表示图像数组的大小(行、列、颜色通道),紧接着的字符串表示数组元素的数据类型。因为图像通常被编码成无符号八位整数(uint8),所以在第一种情况下,载入图像并将其转换到数组中,数组的数据类型为“uint8”。在第二种情况下,对图像进行灰度化处理,并且在创建数组时使用额外的参数“f”;该参数将数据类型转换为浮点型。关于更多数据类型选项,可以参考图书 [24]。注意,由于灰度图像没有颜色信息,所以在形状元组中,它只有两个数值。

    数组中的元素可以使用下标访问。位于坐标 ij,以及颜色通道 k 的像素值可以像下面这样访问:

    value = im[i,j,k]

    多个数组元素可以使用数组切片方式访问。切片方式返回的是以指定间隔下标访问该数组的元素值。下面是有关灰度图像的一些例子:

    im[i,:] = im[j,:]      # 将第 j 行的数值赋值给第 i 行
    im[:,i] = 100          # 将第 i 列的所有数值设为100
    im[:100,:50].sum()     # 计算前100 行、前 50 列所有数值的和
    im[50:100,50:100]      # 50~100 行,50~100 列(不包括第 100 行和第 100 列)
    im[i].mean()           # 第 i 行所有数值的平均值
    im[:,-1]               # 最后一列
    im[-2,:] (or im[-2])   # 倒数第二行

    注意,示例仅仅使用一个下标访问数组。如果仅使用一个下标,则该下标为行下标。注意,在最后几个例子中,负数切片表示从最后一个元素逆向计数。我们将会频繁地使用切片技术访问像素值,这也是一个很重要的思想。

    我们有很多操作和方法来处理数组对象。本书将在使用到的地方逐一介绍。你可以查阅在线文档或者开源图书 [24] 获取更多信息。

    1.3.2 灰度变换

    将图像读入 NumPy 数组对象后,我们可以对它们执行任意数学操作。一个简单的例子就是图像的灰度变换。考虑任意函数 f,它将 0...255 区间(或者 0...1 区间)映射到自身(意思是说,输出区间的范围和输入区间的范围相同)。下面是关于灰度变换的一些例子:

    from PIL import Image
    from numpy import *
    
    im = array(Image.open('empire.jpg').convert('L'))
    
    im2 = 255 - im # 对图像进行反相处理
    
    im3 = (100.0/255) * im + 100 # 将图像像素值变换到100...200 区间
    
    im4 = 255.0 * (im/255.0)**2 # 对图像像素值求平方后得到的图像

    第一个例子将灰度图像进行反相处理;第二个例子将图像的像素值变换到 100...200 区间;第三个例子对图像使用二次函数变换,使较暗的像素值变得更小。图 1-4 为所使用的变换函数图像。图 1-5 是输出的图像结果。你可以使用下面的命令查看图像中的最小和最大像素值:

    print int(im.min()), int(im.max())

    图 1-4:灰度变换示例。三个例子中所使用函数的图像,其中虚线表示恒等变换

    图 1-5:灰度变换。对图像应用图 1-4 中的函数:f(x)=255-x 对图像进行反相处理(左);f(x)=(100/255)x+100 对图像进行变换(中);f(x)=255(x/255)2 对图像做二次变换(右)

    如果试着对上面例子查看最小值和最大值,可以得到下面的输出结果:

    2 255
    0 253
    100 200
    0 255

    array() 变换的相反操作可以使用 PIL 的 fromarray() 函数完成:

    pil_im = Image.fromarray(im)

    如果你通过一些操作将“uint8”数据类型转换为其他数据类型,比如之前例子中的 im3 或者 im4,那么在创建 PIL 图像之前,需要将数据类型转换回来:

    pil_im = Image.fromarray(uint8(im))

    如果你并不十分确定输入数据的类型,安全起见,应该先转换回来。注意,NumPy 总是将数组数据类型转换成能够表示数据的“最低”数据类型。对浮点数做乘积或除法操作会使整数类型的数组变成浮点类型。

    1.3.3 图像缩放

    NumPy 的数组对象是我们处理图像和数据的主要工具。想要对图像进行缩放处理没有现成简单的方法。我们可以使用之前 PIL 对图像对象转换的操作,写一个简单的用于图像缩放的函数。把下面的函数添加到 imtool.py 文件里:

    def imresize(im,sz):
      """ 使用PIL 对象重新定义图像数组的大小"""
      pil_im = Image.fromarray(uint8(im))
    
      return array(pil_im.resize(sz))

    我们将会在接下来的内容中使用这个函数。

    1.3.4 直方图均衡化

    图像灰度变换中一个非常有用的例子就是直方图均衡化。直方图均衡化是指将一幅图像的灰度直方图变平,使变换后的图像中每个灰度值的分布概率都相同。在对图像做进一步处理之前,直方图均衡化通常是对图像灰度值进行归一化的一个非常好的方法,并且可以增强图像的对比度。

    在这种情况下,直方图均衡化的变换函数是图像中像素值的累积分布函数(cumulative distribution function,简写为 cdf,将像素值的范围映射到目标范围的归一化操作)。

    下面的函数是直方图均衡化的具体实现。将这个函数添加到 imtool.py 里:

    def histeq(im,nbr_bins=256):
      """ 对一幅灰度图像进行直方图均衡化"""
    
      # 计算图像的直方图
      imhist,bins = histogram(im.flatten(),nbr_bins,normed=True)
      cdf = imhist.cumsum() # cumulative distribution function
      cdf = 255 * cdf / cdf[-1] # 归一化
    
      # 使用累积分布函数的线性插值,计算新的像素值
      im2 = interp(im.flatten(),bins[:-1],cdf)
    
      return im2.reshape(im.shape), cdf
    

    该函数有两个输入参数,一个是灰度图像,一个是直方图中使用小区间的数目。函数返回直方图均衡化后的图像,以及用来做像素值映射的累积分布函数。注意,函数中使用到累积分布函数的最后一个元素(下标为 -1),目的是将其归一化到 0...1 范围。你可以像下面这样使用该函数:

    from PIL import Image
    from numpy import *
    
    im = array(Image.open('AquaTermi_lowcontrast.jpg').convert('L'))
    im2,cdf = imtools.histeq(im)

    图 1-6 和图 1-7 为上面直方图均衡化例子的结果。上面一行显示的分别是直方图均衡化之前和之后的灰度直方图,以及累积概率分布函数映射图像。可以看到,直方图均衡化后图像的对比度增强了,原先图像灰色区域的细节变得清晰。

    图 1-6:直方图均衡化示例。左侧为原始图像和直方图,中间图为灰度变换函数,右侧为直方图均衡化后的图像和相应直方图

    图 1-7:直方图均衡化示例。左侧为原始图像和直方图,中间图为灰度变换函数,右侧为直方图均衡化后的图像和相应直方图

    1.3.5 图像平均

    图像平均操作是减少图像噪声的一种简单方式,通常用于艺术特效。我们可以简单地从图像列表中计算出一幅平均图像。假设所有的图像具有相同的大小,我们可以将这些图像简单地相加,然后除以图像的数目,来计算平均图像。下面的函数可以用于计算平均图像,将其添加到 imtool.py 文件里:

    def compute_average(imlist):
      """ 计算图像列表的平均图像"""
    
      # 打开第一幅图像,将其存储在浮点型数组中
      averageim = array(Image.open(imlist[0]), 'f')
    
      for imname in imlist[1:]:
        try:
          averageim += array(Image.open(imname))
        except:
          print imname + '...skipped'
      averageim /= len(imlist)
    
      # 返回uint8 类型的平均图像
      return array(averageim, 'uint8')

    该函数包括一些基本的异常处理技巧,可以自动跳过不能打开的图像。我们还可以使用 mean() 函数计算平均图像。mean() 函数需要将所有的图像堆积到一个数组中;也就是说,如果有很多图像,该处理方式需要占用很多内存。我们将会在下一节中使用该函数。

    1.3.6 图像的主成分分析(PCA)

    PCA(Principal Component Analysis,主成分分析)是一个非常有用的降维技巧。它可以在使用尽可能少维数的前提下,尽量多地保持训练数据的信息,在此意义上是一个最佳技巧。即使是一幅 100×100 像素的小灰度图像,也有 10 000 维,可以看成 10 000 维空间中的一个点。一兆像素的图像具有百万维。由于图像具有很高的维数,在许多计算机视觉应用中,我们经常使用降维操作。PCA 产生的投影矩阵可以被视为将原始坐标变换到现有的坐标系,坐标系中的各个坐标按照重要性递减排列。

    为了对图像数据进行 PCA 变换,图像需要转换成一维向量表示。我们可以使用 NumPy 类库中的 flatten() 方法进行变换。

    将变平的图像堆积起来,我们可以得到一个矩阵,矩阵的一行表示一幅图像。在计算主方向之前,所有的行图像按照平均图像进行了中心化。我们通常使用 SVD(Singular Value Decomposition,奇异值分解)方法来计算主成分;但当矩阵的维数很大时,SVD 的计算非常慢,所以此时通常不使用 SVD 分解。下面就是 PCA 操作的代码:

    from PIL import Image
    from numpy import *
    
    def pca(X):
      """ 主成分分析:
        输入:矩阵X ,其中该矩阵中存储训练数据,每一行为一条训练数据
        返回:投影矩阵(按照维度的重要性排序)、方差和均值"""
    
      # 获取维数
      num_data,dim = X.shape
    
      # 数据中心化
      mean_X = X.mean(axis=0)
      X = X - mean_X
    
    if dim>num_data:
      # PCA- 使用紧致技巧
      M = dot(X,X.T) # 协方差矩阵
      e,EV = linalg.eigh(M) # 特征值和特征向量
      tmp = dot(X.T,EV).T # 这就是紧致技巧
      V = tmp[::-1] # 由于最后的特征向量是我们所需要的,所以需要将其逆转
      S = sqrt(e)[::-1] # 由于特征值是按照递增顺序排列的,所以需要将其逆转
      for i in range(V.shape[1]):
        V[:,i] /= S
    else:
      # PCA- 使用SVD 方法
      U,S,V = linalg.svd(X)
      V = V[:num_data] # 仅仅返回前nun_data 维的数据才合理
    
    # 返回投影矩阵、方差和均值
    return V,S,mean_X
    

    该函数首先通过减去每一维的均值将数据中心化,然后计算协方差矩阵对应最大特征值的特征向量,此时可以使用简明的技巧或者 SVD 分解。这里我们使用了 range() 函数,该函数的输入参数为一个整数 n,函数返回整数 0...(n-1) 的一个列表。你也可以使用 arange() 函数来返回一个数组,或者使用 xrange() 函数返回一个产生器(可能会提升速度)。我们在本书中贯穿使用 range() 函数。

    如果数据个数小于向量的维数,我们不用 SVD 分解,而是计算维数更小的协方差矩阵 XXT 的特征向量。通过仅计算对应前 kk 是降维后的维数)最大特征值的特征向量,可以使上面的 PCA 操作更快。由于篇幅所限,有兴趣的读者可以自行探索。矩阵 V 的每行向量都是正交的,并且包含了训练数据方差依次减少的坐标方向。

    我们接下来对字体图像进行 PCA 变换。fontimages.zip 文件包含采用不同字体的字符 a 的缩略图。所有的 2359 种字体可以免费下载 2。假定这些图像的名称保存在列表 imlist 中,跟之前的代码一起保存传在 pca.py 文件中,我们可以使用下面的脚本计算图像的主成分:

    2免费字体图像库由 Martin Solli 收集并上传(http://webstaff.itn.liu.se/~marso/)。

    from PIL import Image
    from numpy import *
    from pylab import *
    import pca
    
    im = array(Image.open(imlist[0])) # 打开一幅图像,获取其大小
    m,n = im.shape[0:2] # 获取图像的大小
    imnbr = len(imlist) # 获取图像的数目
    
    # 创建矩阵,保存所有压平后的图像数据
    immatrix = array([array(Image.open(im)).flatten()
                   for im in imlist],'f')
    
    # 执行 PCA 操作
    V,S,immean = pca.pca(immatrix)
    
    # 显示一些图像(均值图像和前 7 个模式)
    figure()
    gray()
    subplot(2,4,1)
    imshow(immean.reshape(m,n))
    for i in range(7):
      subplot(2,4,i+2)
      imshow(V[i].reshape(m,n))
    
    show()

    注意,图像需要从一维表示重新转换成二维图像;可以使用 reshape() 函数。如图 1-8 所示,运行该例子会在一个绘图窗口中显示 8 个图像。这里我们使用了 PyLab 库的 subplot() 函数在一个窗口中放置多个图像。

    图 1-8:平均图像(左上)和前 7 个模式(具有最大方差的方向模式)

    1.3.7 使用pickle模块

    如果想要保存一些结果或者数据以方便后续使用,Python 中的 pickle 模块非常有用。pickle 模块可以接受几乎所有的 Python 对象,并且将其转换成字符串表示,该过程叫做封装(pickling)。从字符串表示中重构该对象,称为拆封(unpickling)。这些字符串表示可以方便地存储和传输。

    我们来看一个例子。假设想要保存上一节字体图像的平均图像和主成分,可以这样来完成:

    # 保存均值和主成分数据
    f = open('font_pca_modes.pkl', 'wb')
    pickle.dump(immean,f)
    pickle.dump(V,f)
    f.close()

    在上述例子中,许多对象可以保存到同一个文件中。pickle 模块中有很多不同的协议可以生成 .pkl 文件;如果不确定的话,最好以二进制文件的形式读取和写入。在其他 Python 会话中载入数据,只需要如下使用 load() 方法:

    # 载入均值和主成分数据
    f = open('font_pca_modes.pkl', 'rb')
    immean = pickle.load(f)
    V = pickle.load(f)
    f.close()

    注意,载入对象的顺序必须和先前保存的一样。Python 中有个用 C 语言写的优化版本,叫做 cpickle 模块,该模块和标准 pickle 模块完全兼容。关于 pickle 模块的更多内容,参见 pickle 模块文档页 http://docs.python.org/library/pickle.html

    在本书接下来的章节中,我们将使用 with 语句处理文件的读写操作。这是 Python 2.5 引入的思想,可以自动打开和关闭文件(即使在文件打开时发生错误)。下面的例子使用 with() 来实现保存和载入操作:

    # 打开文件并保存
    with open('font_pca_modes.pkl', 'wb') as f:
      pickle.dump(immean,f)
      pickle.dump(V,f)

    # 打开文件并载入
    with open('font_pca_modes.pkl', 'rb') as f:
      immean = pickle.load(f)
      V = pickle.load(f)

    上面的例子乍看起来可能很奇怪,但 with() 确实是个很有用的思想。如果你不喜欢它,可以使用之前的 open 和 close函数。

    作为 pickle 的一种替代方式,NumPy 具有读写文本文件的简单函数。如果数据中不包含复杂的数据结构,比如在一幅图像上点击的点列表,NumPy 的读写函数会很有用。保存一个数组 x 到文件中,可以使用:

    savetxt('test.txt',x,'%i')

    最后一个参数表示应该使用整数格式。类似地,读取可以使用:

    x = loadtxt('test.txt')

    你可以从在线文档 http://docs.scipy.org/doc/numpy/reference/generated/numpy.loadtxt.html 了解更多内容。

    最后,NumPy 有专门用于保存和载入数组的函数。你可以在上面的在线文档里查看关于 save() 和 load() 的更多内容。

    1.4 SciPy

    SciPyhttp://scipy.org/) 是建立在 NumPy 基础上,用于数值运算的开源工具包。SciPy 提供很多高效的操作,可以实现数值积分、优化、统计、信号处理,以及对我们来说最重要的图像处理功能。接下来,本节会介绍 SciPy 中大量有用的模块。SciPy 是个开源工具包,可以从 http://scipy.org/Download 下载。

    1.4.1 图像模糊

    图像的高斯模糊是非常经典的图像卷积例子。本质上,图像模糊就是将(灰度)图像 I 和一个高斯核进行卷积操作:

    Iσ = I*

    其中 * 表示卷积操作; 是标准差为 σ 的二维高斯核,定义为 :

    G_\sigma=\frac{1}{2\pi\sigma}e^{-(x^2+y^2)/2\sigma^2}

    高斯模糊通常是其他图像处理操作的一部分,比如图像插值操作、兴趣点计算以及很多其他应用。

    SciPy 有用来做滤波操作的 scipy.ndimage.filters 模块。该模块使用快速一维分离的方式来计算卷积。你可以像下面这样来使用它:

    from PIL import Image
    from numpy import *
    from scipy.ndimage import filters
    
    im = array(Image.open('empire.jpg').convert('L'))
    im2 = filters.gaussian_filter(im,5)

    上面 guassian_filter() 函数的最后一个参数表示标准差。

    图 1-9 显示了随着 σ 的增加,一幅图像被模糊的程度。σ 越大,处理后的图像细节丢失越多。如果打算模糊一幅彩色图像,只需简单地对每一个颜色通道进行高斯模糊:

    im = array(Image.open('empire.jpg'))
    im2 = zeros(im.shape)
    for i in range(3):
      im2[:,:,i] = filters.gaussian_filter(im[:,:,i],5)
    im2 = uint8(im2)

    在上面的脚本中,最后并不总是需要将图像转换成 uint8 格式,这里只是将像素值用八位来表示。我们也可以使用:

    im2 = array(im2,'uint8')

    来完成转换。

    关于该模块更多的内容以及不同参数的选择,请查看 http://docs.scipy.org/doc/scipy/reference/ndimage.html 上 SciPy 文档中的 scipy.ndimage 部分。

    图 1-9:使用 scipy.ndimage.filters 模块进行高斯模糊:(a)原始灰度图像;(b)使用 σ=2 的高斯滤波器;(c)使用 σ=5 的高斯滤波器;(d)使用 σ=10 的高斯滤波器

    1.4.2 图像导数

    整本书中可以看到,在很多应用中图像强度的变化情况是非常重要的信息。强度的变化可以用灰度图像 I(对于彩色图像,通常对每个颜色通道分别计算导数)的 x 和 y 方向导数 Ix 和 Iy 进行描述。

    图像的梯度向量为∇I = [IxIy]T。梯度有两个重要的属性,一是梯度的大小

    \left|\boldsymbol{\nabla I}\right|=\sqrt{{\boldsymbol{I}_x}^2+{\boldsymbol{I}_y}^2}

    它描述了图像强度变化的强弱,一是梯度的角度

    α=arctan2(IyIx)

    描述了图像中在每个点(像素)上强度变化最大的方向。NumPy 中的 arctan2() 函数返回弧度表示的有符号角度,角度的变化区间为 -π...π。

    我们可以用离散近似的方式来计算图像的导数。图像导数大多数可以通过卷积简单地实现:

    Ix=I*Dx 和 Iy=I*Dy

    对于 Dx 和 Dy,通常选择 Prewitt 滤波器:

    D_x=\begin{vmatrix}-1&0&1\\-1&0&1\\ -1&0&1\end{vmatrix} 和 D_y=\begin{vmatrix}-1&-1&-1\\0&0&0\\ 1&1&1\end{vmatrix}

    或者 Sobel 滤波器:

    D_x=\begin{vmatrix}-1&0&1\\-2&0&2\\ -1&0&1\end{vmatrix} 和 D_y=\begin{vmatrix}-1&-2&-1\\0&0&0\\ 1&2&1\end{vmatrix}

    这些导数滤波器可以使用 scipy.ndimage.filters 模块的标准卷积操作来简单地实现,例如:

    from PIL import Image
    from numpy import *
    from scipy.ndimage import filters
    
    im = array(Image.open('empire.jpg').convert('L'))
    
    # Sobel 导数滤波器
    imx = zeros(im.shape)
    filters.sobel(im,1,imx)
    
    imy = zeros(im.shape)
    filters.sobel(im,0,imy)
    
    magnitude = sqrt(imx**2+imy**2)

    上面的脚本使用 Sobel 滤波器来计算 x 和 y 的方向导数,以及梯度大小。sobel() 函数的第二个参数表示选择 x 或者 y 方向导数,第三个参数保存输出的变量。图 1-10 显示了用 Sobel 滤波器计算出的导数图像。在两个导数图像中,正导数显示为亮的像素,负导数显示为暗的像素。灰色区域表示导数的值接近于零。

    图 1-10:使用 Sobel 导数滤波器计算导数图像:(a)原始灰度图像;(b)x 导数图像;(c)y 导数图像;(d)梯度大小图像

    上述计算图像导数的方法有一些缺陷:在该方法中,滤波器的尺度需要随着图像分辨率的变化而变化。为了在图像噪声方面更稳健,以及在任意尺度上计算导数,我们可以使用高斯导数滤波器:

    Ix=I*Gσx 和 Iy=I*Gσy

    其中,Gσx和 Gσy 表示  在 x 和 y 方向上的导数, 为标准差为 σ 的高斯函数。

    我们之前用于模糊的 filters.gaussian_filter() 函数可以接受额外的参数,用来计算高斯导数。可以简单地按照下面的方式来处理:

    sigma = 5 # 标准差
    
    imx = zeros(im.shape)
    filters.gaussian_filter(im, (sigma,sigma), (0,1), imx)
    
    imy = zeros(im.shape)
    filters.gaussian_filter(im, (sigma,sigma), (1,0), imy)

    该函数的第三个参数指定对每个方向计算哪种类型的导数,第二个参数为使用的标准差。你可以查看相应文档了解详情。图 1-11 显示了不同尺度下的导数图像和梯度大小。你可以和图 1-9 中做相同尺度模糊的图像做比较。

    图 1-11:使用高斯导数计算图像导数:x 导数图像(上),y 导数图像(中),以及梯度大小图像(下);(a)为原始灰度图像,(b)为使用 σ=2 的高斯导数滤波器处理后的图像,(c)为使 用 σ=5 的高斯导数滤波器处理后的图像,(d)为使用 σ=10 的高斯导数滤波器处理后的图像

    1.4.3 形态学:对象计数

    形态学(或数学形态学)是度量和分析基本形状的图像处理方法的基本框架与集合。形态学通常用于处理二值图像,但是也能够用于灰度图像。二值图像是指图像的每个像素只能取两个值,通常是 0 和 1。二值图像通常是,在计算物体的数目,或者度量其大小时,对一幅图像进行阈值化后的结果。你可以从http://en.wikipedia.org/wiki/Mathematical_morphology 大体了解形态学及其处理图像的方式。

    scipy.ndimage 中的 morphology 模块可以实现形态学操作。你可以使用 scipy.ndimage 中的 measurements 模块来实现二值图像的计数和度量功能。下面通过一个简单的例子介绍如何使用它们。

    考虑在图 1-12a3 里的二值图像,计算该图像中的对象个数可以通过下面的脚本实现:

    3这个图像实际上是图像“分割”后的结果。如果你想知道该图像是如何创建的,可以查看 9.3 节。

    from scipy.ndimage import measurements,morphology
    
    # 载入图像,然后使用阈值化操作,以保证处理的图像为二值图像
    im = array(Image.open('houses.png').convert('L'))
    im = 1*(im<128)
    
    labels, nbr_objects = measurements.label(im)
    print "Number of objects:", nbr_objects
    
    

    上面的脚本首先载入该图像,通过阈值化方式来确保该图像是二值图像。通过和 1 相乘,脚本将布尔数组转换成二进制表示。然后,我们使用 label() 函数寻找单个的物体,并且按照它们属于哪个对象将整数标签给像素赋值。图 1-12b 是labels 数组的图像。图像的灰度值表示对象的标签。可以看到,在一些对象之间有一些小的连接。进行二进制开(binary open)操作,我们可以将其移除:

    # 形态学开操作更好地分离各个对象
    im_open = morphology.binary_opening(im,ones((9,5)),iterations=2)
    
    labels_open, nbr_objects_open = measurements.label(im_open)
    print "Number of objects:", nbr_objects_open
    

    binary_opening() 函数的第二个参数指定一个数组结构元素。该数组表示以一个像素为中心时,使用哪些相邻像素。在这种情况下,我们在 y 方向上使用 9 个像素(上面 4 个像素、像素本身、下面 4 个像素),在 x 方向上使用 5 个像素。你可以指定任意数组为结构元素,数组中的非零元素决定使用哪些相邻像素。参数 iterations 决定执行该操作的次数。你可以尝试使用不同的迭代次数 iterations 值,看一下对象的数目如何变化。你可以在图 1-12c 与图 1-12d 中查看经过开操作后的图像,以及相应的标签图像。正如你想象的一样,binary_closing() 函数实现相反的操作。我们将该函数和在morphology 和 measurements 模块中的其他函数的用法留作练习。你可以从 scipy.ndimage 模块文档http://docs.scipy.org/doc/scipy/reference/ndimage.html 中了解关于这些函数的更多知识。

    图 1-12:形态学示例。使用二值开操作将对象分开,然后计算物体的数目:(a)为原始二值图像;(b)为对应原始图像的标签图像,其中灰度值表示物体的标签;(c)为使用开操作后的二值图像;(d)为开操作后图像的标签图像

    1.4.4 一些有用的SciPy模块

    SciPy 中包含一些用于输入和输出的实用模块。下面介绍其中两个模块:io 和 misc

    1. 读写.mat文件

      如果你有一些数据,或者在网上下载到一些有趣的数据集,这些数据以 Matlab 的 .mat 文件格式存储,那么可以使用 scipy.io 模块进行读取。

      data = scipy.io.loadmat('test.mat')

      上面代码中,data 对象包含一个字典,字典中的键对应于保存在原始 .mat 文件中的变量名。由于这些变量是数组格式的,因此可以很方便地保存到 .mat 文件中。你仅需创建一个字典(其中要包含你想要保存的所有变量),然后使用 savemat() 函数:

      data = {}
      data['x'] = x
      scipy.io.savemat('test.mat',data)

      因为上面的脚本保存的是数组 x,所以当读入到 Matlab 中时,变量的名字仍为 x。关于 scipy.io 模块的更多内容,请参见在线文档 http://docs.scipy.org/doc/scipy/reference/io.html

    2. 以图像形式保存数组

      因为我们需要对图像进行操作,并且需要使用数组对象来做运算,所以将数组直接保存为图像文件 4 非常有用。本书中的很多图像都是这样的创建的。

      imsave() 函数可以从 scipy.misc 模块中载入。要将数组 im 保存到文件中,可以使用下面的命令:

      from scipy.misc import imsave
      imsave('test.jpg',im)

      scipy.misc 模块同样包含了著名的 Lena 测试图像:

      lena = scipy.misc.lena()

      该脚本返回一个 512×512 的灰度图像数组。

    4所有 Pylab 图均可保存为多种图像格式,方法是点击图像窗口中的“保存”按钮。

    1.5 高级示例:图像去噪

    我们通过一个非常实用的例子——图像的去噪——来结束本章。图像去噪是在去除图像噪声的同时,尽可能地保留图像细节和结构的处理技术。我们这里使用 ROF(Rudin-Osher-Fatemi)去噪模型。该模型最早出现在文献 [28] 中。图像去噪对于很多应用来说都非常重要;这些应用范围很广,小到让你的假期照片看起来更漂亮,大到提高卫星图像的质量。ROF 模型具有很好的性质:使处理后的图像更平滑,同时保持图像边缘和结构信息。

    ROF 模型的数学基础和处理技巧非常高深,不在本书讲述范围之内。在讲述如何基于 Chambolle 提出的算法 [5] 实现 ROF 求解器之前,本书首先简要介绍一下 ROF 模型。

    一幅(灰度)图像 I 的全变差(Total Variation,TV)定义为梯度范数之和。在连续表示的情况下,全变差表示为:

    J(\boldsymbol{I})=\int\left|\nabla\boldsymbol{I}\right|\text{dx}            (1.1)

    在离散表示的情况下,全变差表示为:

    J(\boldsymbol{I})=\sum_{\text{x}}\left|\nabla\boldsymbol{I}\right|

    其中,上面的式子是在所有图像坐标 x=[x, y] 上取和。

    在 Chambolle 提出的 ROF 模型里,目标函数为寻找降噪后的图像 U,使下式最小:

    \min_U\left|\left|\boldsymbol{I}-\boldsymbol{U}\right|\right|^2+2\lambda J(\boldsymbol{U}),

    其中范数 ||I-U|| 是去噪后图像 U 和原始图像 I 差异的度量。也就是说,本质上该模型使去噪后的图像像素值“平坦”变化,但是在图像区域的边缘上,允许去噪后的图像像素值“跳跃”变化。

    按照论文 [5] 中的算法,我们可以按照下面的代码实现 ROF 模型去噪:

    from numpy import *
    
    def denoise(im,U_init,tolerance=0.1,tau=0.125,tv_weight=100):
      """ 使用A. Chambolle(2005)在公式(11)中的计算步骤实现Rudin-Osher-Fatemi(ROF)去噪模型
    
        输入:含有噪声的输入图像(灰度图像)、U 的初始值、TV 正则项权值、步长、停业条件
    
        输出:去噪和去除纹理后的图像、纹理残留"""
    
    m,n = im.shape # 噪声图像的大小
    
    # 初始化
    U = U_init
    Px = im # 对偶域的x 分量
    Py = im # 对偶域的y 分量
    error = 1
    
    while (error > tolerance):
      Uold = U
    
      # 原始变量的梯度
      GradUx = roll(U,-1,axis=1)-U # 变量U 梯度的x 分量
      GradUy = roll(U,-1,axis=0)-U # 变量U 梯度的y 分量
    
      # 更新对偶变量
      PxNew = Px + (tau/tv_weight)*GradUx
      PyNew = Py + (tau/tv_weight)*GradUy
      NormNew = maximum(1,sqrt(PxNew**2+PyNew**2))
    
      Px = PxNew/NormNew # 更新x 分量(对偶)
      Py = PyNew/NormNew # 更新y 分量(对偶)
    
      # 更新原始变量
      RxPx = roll(Px,1,axis=1) # 对x 分量进行向右x 轴平移
      RyPy = roll(Py,1,axis=0) # 对y 分量进行向右y 轴平移
    
      DivP = (Px-RxPx)+(Py-RyPy) # 对偶域的散度
      U = im + tv_weight*DivP # 更新原始变量
    
      # 更新误差
      error = linalg.norm(U-Uold)/sqrt(n*m);
    
    return U,
    展开全文
  • 什么是数字图像处理?历史、以及它所研究的内容。 说起图像处理,你会想到什么?你是否真的了解这个领域所研究的内容。 纵向来说,数字图像处理研究的历史相当悠久; 横向来说,数字图像处理研究的话题相当广泛。...
  • 图像处理经典文献

    千次阅读 2018-01-29 10:43:26
    我们所说的图像处理实际上就是数字图像处理,是把真实世界中的连续三维随机信号投影到传感器的二维平面上,采样并量化后得到二维矩阵。数字图像处理就是二维矩阵的处理,而从二维图像中恢复出三维场景就是计算机视觉...
  • python,opencv,计算机视觉、图像处理;代码,图片,注释,代码比较简单,均为20行左右,十分有趣哦!!!!haha镜、漫画、素描、马赛克、油画。
  • 本文主要介绍了一些简单易懂最常用的Python图像处理库。 当今世界充满了各种数据,而图像是其中高的重要组成部分。然而,若想其有所应用,我们需要对这些图像进行处理。图像处理是分析和操纵数字图像的过程,旨在...
  • 本文主要介绍了一些简单易懂最常用的Python图像处理库 当今世界充满了各种数据,而图像是其中高的重要组成部分。然而,若想其有所应用,我们需要对这些图像进行处理。图像处理是分析和操纵数字图像的过程,旨在提高...
  • Python中的图像处理

    千次阅读 2018-03-03 22:27:15
    第 1 章 基本的图像操作和处理本章讲解操作和处理图像的基础知识,将通过大量...1.1 PIL:Python图像处理类库PIL(Python Imaging Library Python,图像处理类库)提供了通用的图像处理功能,以及大量有用的基本图...
  • 十个Python图像处理工具

    千次阅读 2019-04-01 16:14:39
    这些Python库提供了一种简单直观的方法来转换图像并理解底层数据。 今天的世界充满了数据,图像是这些数据的重要组成部分。但是,在使用它们之前,必须...Python是这些图像处理任务的绝佳选择,因为它作为一种科学...
  • Python中最常用的十大图像处理

    千次阅读 2019-11-01 09:33:00
    图像处理是分析和操纵数字图像的过程,旨在提高其质量或从中提取一些信息,然后将其用于某些方面。 图像处理中的常见任务包括显示图像,基本操作(如裁剪、翻转、旋转等),图像分割,分类和特征提取,图像恢复和...
  • 神奇的图像处理算法

    2012-09-28 10:20:48
    神奇的图像处理算法 http://www.ruanyifeng.com/blog/2011/08/amazing_algorithms_of_image_processing.html 作者: 阮一峰 ...这是利用数学算法,进行高难度图像处理的一个例子。事实上
  • 图像处理的研究方向

    千次阅读 2013-05-25 16:44:41
     我虽然从事图像处理研究,但做的东西比较杂,也不是很深入。只能给你一些粗浅的建议。  我感觉图像处理现在的发展有两个层次,一个是算法研究,需要较多的数学基础,如偏微分方程(PDE)、各种空间变换(小波、...
  • 6个案例手把手教你用Python和OpenCV进行图像处理

    千次阅读 多人点赞 2020-08-25 00:05:58
    导读:图像是由若干个像素组成的,因此,图像处理可以看作计算机对像素的处理。在面向Python的OpenCV中,可以通过位置索引的方式对图像内的像素进行访问和处理。本文手把手教你图像处理的...
  • 本文主要介绍了一些简单易懂最常用的Python图像处理库 当今世界充满了各种数据,而图像是其中高的重要组成部分。然而,若想其有所应用,我们需要对这些图像进行处理。图像处理是分析和操纵数字图像的过程,旨在提高...
  • python 图像处理

    千次阅读 2018-06-19 14:51:01
    转自:点击打开链接第 1 章 基本的图像操作和处理本章讲解操作和处理图像的基础知识,将通过大量...1.1 PIL:Python图像处理类库PIL(Python Imaging Library Python,图像处理类库)提供了通用的图像处理功能,以...
  • 基于Web的图像处理

    千次阅读 2016-01-25 15:26:20
    原文地址:基于Web的图像处理作者:ENVI-IDL中国 在过去的几十年里,地理信息在互联网上的使用和展示经过了飞速发展。第一代互联网上展示的地图是嵌入到HTML中的静态图片,例如GIF、JPEG或者PNG格式,这是使用互联网...
  • 10个Python图像处理工具,非常全了!

    千次阅读 2019-05-30 00:05:17
    因此,图像处理是分析和操纵数字图像的过程,主要目的是提高数字图像的质量或从中提取一些信息,然后可以将其用于某种用途。 图像处理中常见的任务包括显示图像,基本操作,如裁剪、翻转、旋转等、图像分割、分类.....
  • 图像处理基础秋招复习(一)

    千次阅读 2018-09-02 13:47:34
    面试图像岗总是会有些传统的图像处理的基础知识问答面试,比较不足,搜集些基础问题来写写强化下。 收集的问题连接: https://blog.csdn.net/songzitea/article/details/48931151 1.彩色图像、灰度图像、二值图像...
1 2 3 4 5 ... 20
收藏数 27,839
精华内容 11,135
关键字:

图像处理的有趣例子