2010年4月29日星期四
2010年4月14日星期三
面试前需考虑的25个问题-褪墨
面试前需考虑的25个问题
Thu, 08 Apr 2010 09:29:03 +0800
我曾经在The Simple Dollar上提到自己过去曾组织了大量面试工作。虽然我招聘的通常是技术类职位,但实际问到的问题(因此是有实际价值的)都是无关技术的。一个好的面试问题能使应聘者的本性显露出来――诚实,可信,反应敏锐等等。
长期以来,我收集了一些自己在面试中总会用到的问题,这里整理出25个最有价值的,附带一两个把每个问题回答好的技巧或怎么会把它弄糟的案例。希望这个总结能为面试官和应聘者提供一些有洞见的参考,若你能轻而易举回答所有问题,面试就不必担心了。最后,我将给出一份核对清单作为"家庭作业"给每个即将面临重要面试的应聘者。
首先,愚蠢地回答愚蠢的问题。
工作面试中有许多问题非常愚蠢,而且都有显而易见的答案。"你最大的弱点是什么?"这个问题从来不可能得到一个诚实的答案,而且多数时候只会招致一些例如"我是工作狂!"的虚伪回答。面试官问这些问题是因为这些都是"应该"被问的,但他们通常不会从中得到任何有效信息。"你认为自己成功吗?",答案总是肯定的;"你具有团队精神吗?"答案也总是肯定的;"你打算在这儿工作多久?"答案总是长期;"工作和薪水何者更重要?",答案总是工作比薪水更重要。
识别一个无聊的问题很简单――你是不是能很容易地给出一个放之四海皆准之而又无关痛痒的答案?如果是的,别为这问题费神,把精力放在解决具有实际意义的问题上。
- 1. 介绍你自己
这个问题基本是为了让应聘者放松,同时也给我自己判断他们谈吐的机会。这是一个在一切面试中都需要准备的问题,所以你需要有一个固定的答案。走进面试室前在头脑中对这个问题要有个清晰的答案。"最佳"答案要能使你充分展现自己与众不同之处,以使自己在众多应聘者中脱颖而出。列出自己的四五项最大特点,用三十秒陈述出来。
- 2. 谈谈你对我们的了解
这个问题直接考察了面试者是否做了准备功课。一个能讲出公司大量信息的面试者也许是出乎意料的,但连基本情况都不了解的人多数会被淘汰――那不是我们要的人。换言之,面试前,了解你将应聘的那个机构。
- 3. 什么使你区别于其他应聘者
面试官通常基于简历已经得到了这个问题的答案,但这是你真正自我推销的时候。多数面试官都会坐在一旁看你把自己推销得如何。偶尔惊喜是好的,但也可能显得狡猾――如果某些内容应该在简历上出现,却为什么未出现?你该知道自己简历的精华何在,然后将它们列出。
- 4. 描述你应聘的职位
这也是一道"作业"题,但通过应聘者当场给出的见解也能掌握一些信息。最佳准备是阅读职位描述并用自己的语言对自己复述出来,以便在面试时流利应答。
- 5. 为何对此职位感兴趣
这个问题实际上有些像一个小把戏,因为这是对第二个问题(你对公司的了解)以及第四个问题(描述你应聘的职位)的回问。这样问是因为它有助于判断:人们是轻率作答(像是"因为我就是合适人选"),还是考虑之后诚恳作答。对这个问题可以事先准备好一个程述式的答案――大致上,只要给出一些这个公司和职位吸引你的理由以及它们为何吸引你。
- 6. 这个职位的哪一方面使你感到最不适
多数人认为这个问题会涉及淘汰,但通常它并非如此。这其实是个诚实问题。没有人会对某项工作的每个方面都满意――这不是我们的天性。工作地点?工作时间?同事?公司规模太大?太小?诚实在此很重要――我希望听到一个感到不适的诚恳理由(尤其是真正从对公司的观察中得来),而不是一句没有任何不适的陈词滥调。好的回答可以是"我从未在如此大规模的公司工作过",或"在协作文化上我听说了一些奇怪的方面"、或"在起步阶段工作使我感到紧张"等。
- 7. 上一份工作中你最大的成功是什么?
- 8. 上一份工作中你最大的失败是什么?
这两个问题通常可以组成一组,但重要的是后者。最好的应聘者应该承认自己有过过失(他们诚实而敢于承认错误)并从中吸取了教训,这是一项无比重要的美德。
- 9. 说说你先前最好的上司
- 10. 说说你先前最糟的上司
这两个问题能直接试探出面试者适合于何种管理风格以及会如何管理他人。假设我就职于一个管理松散而需要自我驱动的机构,这种情况下,我希望听到的回答是"最佳"老板是万事不插手的,或者"最糟"老板是事事盯紧的。相反,若我在等级森严的机构,我希望听到的恰恰相反――"最佳"老板提供有强度的引导和交流,或"最糟"老板是让应聘者无所适从的。最佳办法是尽可能诚实应答――面试官会对协作文化有很好的认识,坦率地说,如果你侥幸进入了一家公司而并不匹配那里的文化,适应和取得成功将会很艰难。这些问题也可以以"你倾向于何种管理模式"的方式提问。
其他技巧:突出论及所有老板的优点,绝不要把面试变成一场针对任何人的批判会。最糟的老板会有一些小毛病而且都是处于对你的期望而不是有人格缺陷。面试中抱怨别人只会使你自己显得恶劣,所以别上当了。
- 11. 说说你曾遇到的最困难的项目
面试官通常不真正关注项目具体是什么,其实质是看看你是否经历过真正的困难并如何克服它。对大多数人而言,这并不是最大的成功或失败,而是将失败方面转化为成功的方面。
- 12. 对这个领域的未来趋势有何看法
这个问题对一些领域有用――技术类或领导岗位――对其他一些则没用。这个问题有用没用在你应聘的特定工作类型中是显而易见的。如果有用,答案的准备很简单――只要花上半小时阅读相关领域的一些博客文章你就能汲取所需信息。
- 13. 去年中是否在与这项工作有关要求方面学到了新的东西/提升了自己
这是一个很容易让人无所适从的问题,多少人就是想不出答案。最佳应对方式其实就是总是花一些时间以任何方式提升自己的技能。写写开源代码、实践一下当主持人、上上课等,如果你每年都投入经历提升自己,不但会有一份漂亮的简历,当然这个问题也就不算什么了。
- 14. 说说你梦想中的职业
绝不要说这项工作,绝不要说另一项具体的工作。这两种回答都实在糟糕――前者树起了警旗而后者说明你无心恋战。回答应该紧贴一些具体的特质――讲讲你梦想中工作的一些方面。可以的话其中一些应该契合你要应聘的单位,但不要完全契合是最好的。
- 15. 之前工作中遇到过严重冲突吗?它是如何解决的?
这个问题需要诚实,同时需意识到任何事物的矛盾都包含两方面。它同样可能会让那些不厚道的人开始抱怨前任雇主,以至于给面试官留下坏印象。最好的回答方式通常包括描述事实,但同时注意事件的两方面,而且你从中学会了从他人的角度考虑问题。
- 16. 从上份工作中学到了什么
虽然列出一些技术上的技能,尤其如果你的工作是很技术性的,那很好,但涉及一些非技术性内容很重要。"在多数时候单独工作后,我学到了怎样在一个团队中工作",像这样的回答就很好。任何工作中都可以学到些什么,面试官期待你能从上一份工作中学到些什么以助于你胜任新的工作。
- 17. 上份工作为何离职
多数时候,这是在考察一个稳定的性格。强有力且具体的回答,无论具体理由如何都是好的。"我想继续前进" 不算一个强有力的理由。裁员是个好理由,同样寻找一些特殊的新挑战也是好理由(但想要接受的挑战得要有特点)。在此,应该淡化对前任工作的具体描述,因为这样会很容易陷入对之前职位的抱怨之中。
- 18. 对上个工作岗位提一个可行的建议
虽然答案会很大程度上牵涉到上任工作的特殊性质,但其实那些特殊性质并不重要。最重要的是你确实给出了建议并使它富有成果,最好再加上一些成功的故事。这样做显得你也会对新职位有同样贡献,来提升整个机构。在这个问题上没有答案并不是大缺陷,不会是一个决定"生死"的问题。
- 19. 有被辞退过吗?说说这一经历
显而易见,能说"没有"最好,但答案是"有"其实也不会被一片否决。事实上,"有"也能转化为积极因素――这是表明你虽有过过错但从中学到了有价值的教训的良好途径。在这个问题上要诚实,无论如何别埋怨让你走人的人。即使对于发生的事感到气愤,也要带有敬意地提到他们。
- 20. 解雇过别人吗?说说这一经历
这个问题基本是考察你对人是否有宽容之心。回答时务必谨慎――这绝不是一个简单的选择题或经历叙述,而关系到你的去留。也别埋怨被你解雇的人――尽可能用理由冷静地回答。
- 21. 同时还在应征其他工作吗?
这是一个相关诚实的问题,我期待听到"是的",但太想迎合我胃口的人回答说"没有"。最佳回答方式是"是的,就像你们还在面试其他人那样。我们都在寻求最佳选择。"如果你的答案是"没有",那么这么说――"没有,其实我对现在的工作是满意的,但是你们提供的这个职位有些非常吸引我的地方驱使我来这儿应聘。 "然后列举出那些吸引你的地方。
- 22. 你认为这个职位的报酬应当如何
很多人可能会感到惊奇,这通常并非在协商工资。多数时候,面试你的人都不负责你能得到的工资。这个问题通常是一个现实的考量――如果招清洁工,他们想要80K美元工资,那你大概会立即把应聘书投进垃圾箱;同样,一个高技能的程序员只把自己卖到30k美元也是在敲警钟。好的回答通常要靠谱或稍稍高于实际而不是低得过分或高得疯狂。我在面试前都会了解好要价,然后在回答中多加30%。
- 23. 自认为五年后自己在职业中发展如何
这是个有点"垃圾"的问题,但就某些方面说它可以筛选出那些具有主观能动性的人。回答类似于"我将在我应聘的岗位中取得成功"的人既不够积极也不够诚实。我宁可得到包括提升或成为企业家――强大的机构之繁荣有赖于做事主动的人。对面试者来说唯一的问题是一些公司――通常是小公司――并不需要做事主动的人而且特别害怕那些梦想成为企业家的人。因此如果对企业文化不熟悉,谈及升职通常是最安全的。但我个人欣赏在面试中谈到企业家的人――那意味他们对成功怀有热情的那类人。
- 24. 你的长远目标是什么――比如,从事这项事业十五年后?
这是一个迟来的好问题,因为它能帮你判断面试者是否是长远计划者。因为有长远计划的人通常有一个良好而成熟的思维状态,而且会始终绷紧弦努力工作,胜于没有长远计划的人。
- 25. 对这项工作还有何疑问?
是,你确实该对这项工作有疑问,没有问题意味着你并未真正对这个职位产生兴趣。因此,面试者必须把在面试前准备的一些问题作为一项任务来对待。大多数面试官都乐于回答一切问题――只要确保你的问题是理智的就行。
准备功课!
以下功课在任何面试前都需要准备,以帮助你应对上述绝大多数问题。
- 准备十分简洁的自我描述:以便在任何面试中都可以用。最好的技巧是提到自己与众不同甚至独一无二的地方,同时兼顾不突出或(最坏的话)中庸之处――保留自己的缺陷,除非它和一个重要的优点有关联。连续的三十秒陈述即可。
- 研究公司:通过访问他们的网页了解他们究竟做些什么。最好阅读公司近些年的年度报告和它在维基上的描述(如果它足够大),或者用Google搜索公司名字和地点(如果是小公司)。如果它处于起步阶段,找尽可能多的资料,如果相关资料实在太少,就别再多花精力了。
- 研究职位:认真阅读招聘启事,对任何不懂之处都要查阅。如果还不甚熟悉,你还必须让自己深入阅读招聘启事以让自己清楚这一领域的重要信息何在――从博客和新闻网页开始是不错的选择。你还应该在生活范围附近调查一下类似工作,以对这类工作的常规起步工资心中有数。
- 了解自己适合这一职位的原因:收集你能找到的公司信息和招聘启事,将它们与自己的技能作对照。这样做五份,它们在面试中会有奇效。同时,找出至少一项自己对公司或职位不满之处然后思考为什么这使你不满。
- 不断努力提升工作技能:参与能使你提高相关领域关键技能的活动。任职人际关系领域吗?加入一个主持人协会;任职行政助理吗?加入为某个机构服务的志愿工作,以不同方式锻炼你的能力(同样适用于从事贸易者);任职程序员吗?那就为一项开源项目作点贡献。
- 在头脑中储备一些对应聘职位的问题:当你进入面试室。这能给人一个强烈的印象,说明你确实对那个职位感兴趣,这将大有益处。各类问题都可以,但最好涉及工作中的协作文化和特定技术。
- 千万不要抱怨之前的工作:如果先前的工作中确实有让你懊恼之处,花些时间试着想想它的积极方面。要知道面试时先前工作至少在某种程度上会被提及,因此准备好不带消极情绪谈论它,寻找积极因素,同时尽可能冷静陈述离职理由。
- 诚实:首当其冲。如果你在面试时编造了什么还出了错,面试官会把你的应聘书投进垃圾箱。你只要做的是,集中于自己本身具有的优点上,如果把它们都在面试中陈述出来,这已经就是那个机构喜欢你的那些因素。别浪费时间捏造东西来说。
原文:25 Questions to Think About Before Your Next Job Interview � The Simple Dollar
译者:Sylvia(GTD翻译小组核心成员)
2010年3月21日星期日
开源的车牌识别工程
建在google code 上
地址是 : http://code.google.com/p/lprbuaa/
LicensePlateRecognition
而 GraphicsEngine.是:
“一个简单的数字图像处理引擎,提供各种基本图像处理算法、连通域分析和内存管理。API为两层结构,并在不断完善中。来自北京航空航天大学机械工程及自动化学院。张以成”
2010年3月15日星期一
低效能人士的七个坏习惯-褪墨
来自"褪墨"的最新文章,
低效能人士的七个坏习惯
Sun, 14 Mar 2010 12:44:00 +0800
是否真有幸福并非取决于天性,而是取决于人的习惯。褪墨上曾分享过哈佛大学企业管理硕士,杨百翰大学博士史蒂芬・柯维(Stephen R.Covey)的《高效能人士的七个习惯》上几篇文章:《学会积极主动》和《要是第一》,而本文将列举七个低效能人士的坏习惯。欢迎大家参加褪墨号召的《一个月培养一个好习惯》活动!
-
缺席
也许你曾经听过伍迪·艾伦所说的这句话:"百分之八十的成功来自于出席。"更多的出席——这是在生活中保证更多成功要做的最重要也是最简单的事情之一。例如,如果想要改善自己的健康状况,最有效的事情就是每天按时出现在健身房里,无论是天气是多么不好、你是多么不想出门、你是多么的繁忙,只要你能坚持在积极性不高的时候能出席健身房,你就会开始改善健康,而不是每天躺在沙发上幻想自己的身体忽然变得更健康了。如果你想提高写作水平或绘画水平,你就要经常练习。如果你想交更多的朋友,就需要出席更多的活动。学会出席,积极主动的参与,这将使你的生活受益匪浅。
-
拖拖拉拉
我最喜欢的三种摆脱拖拉情况的方法,列举如下:
- 在一天的最开始就完成那些最重要的工作。早上良好的开始会让你一天都保持高昂的情绪和积极的动力。这通常会使你这一天都十分高效。
- 一项的一项去完成。想想,你如何吃掉一头大象呢?不要打算一口吃成胖子,这会使你感到过多负担以至于产生拖延的念头。把一项工作分为若干可付诸于行动的小步骤,然后仅仅关注第一步直到把它完成,接下来再继续下一步。
- 说服自己。我发现这种向导型调整十分有效。我时常花上一段时间躺在床上反复默念"在这几天里我都会十分高效"之后,我就觉得自己又有动力,又有激情地开始工作了。
拓展阅读:《防止拖延的积极步骤》
-
做无关紧要的事情
除了拖拖拉拉以外,另外一个容易陷入的不良习惯就是深陷无关紧要的事情之中。为了提高效率你也许需要某种时间管理方法。比如这套极为简单的时间管理方法,使用80/20法则。80/20法则,也就是我们通常所熟悉的帕累托法则,认为80%的收获源自20%的努力。所以为求高效,你应该将大部分精力集中在那些极少数重要的事情上。你只需按优先顺序写下这一天你需要做的三件最重要的事情,然后从头做起。即使你只能完成其中的一件事,你仍然完成了今天最重要的事情。也许你也会偏爱其他诸如GTD等方法,不过无论你如何组织工作,最关键的还是寻找那些最重要的工作,这样你就不必花费几天,几个星期甚至几个月的时间去忙于那些并不是很重要的事情。如果这些事情无关紧要,那么即使你快速的完成它们也是没有多大用处的。
拓展阅读:《放置大石头的艺术:让你的效率翻倍》
-
多虑
因为多虑而使我们很少采取行动,陷于无穷的分析之中只会使虚度光阴。行动之前加以思考是没有错的,做一些调查研究,制定一个计划,探究可能存在的积极以及不利因素。但是强制性地反复思考就会成为另外一种浪费时间的做法了。在尝试之前你没有必要去从每一个角度检查每一件事情。而且你也不可以等到一个最完美的时间再去做事,因为这样的时间从来不会出现。如果你继续这样思考就只会使自己陷的越来越深,从而使采取行动变得越来越难。相反,虽然思考在一定程度上对你有所帮助,但你现在需要做的就只是停止思考,然后去做那些你应该做的事情。
-
凡事过于消极
当你凡事都从消极方面考虑时,你的积极性就会被大大打击。你会发现到处都是问题和错误,而这些问题和错误可能是本不存在的,所以不要抓住细节不放。当你从一个消极角度看问题时,每次你都可能找出十个借口来逃避问题,因此你几乎一事无成。你向任何愿意倾听的人诉苦(也许很多人并不想听),抱怨你的工作,生活和领导有多么的差劲。其实,你的生活取决于你如何看待这个世界。对此的一个解决方法就是了解消极方面的限度,认识到你的消极思考并不是这个世界的真实写照。然后不妨尝试一些其他的角度。举例来说,你可以尝试着培养一下凡事从更为积极和乐观的角度思考的习惯,这会对你大有帮助。通过这种方式,你也许就会开始尝试这种积极性的挑战。这并不容易,然而如果你接受了这种挑战,连续7天都只从积极方面思考,ä �就会突然意识到你看问题的角度和想法是如此深刻地影响着你对世界的理解和你所得到的成果。
拓展阅读:《如何打破消极思维模式》
-
固执己见,与世隔绝
我们很难去承认自己的想法不是最佳选择,因此我们通常过于执着自己的想法,变得闭目塞听,而这会让你很难取得进步。在这种情况下,即使认真思考改变人生的可能性都会变得很难。显然,解决方法之一就是打开心胸,开阔视野,从他人和自己的错误中汲取教训,从书籍等资源中获取知识。与任何事一样,这事说起来容易做起来难。正如前面所说,对此我的建议就是认识到你的知识领域毕竟是有限的,而你做事的方式也会存在不足。那么不妨就尝试一下新事物吧。 而我的另一条建议就是,阅读一下埃克哈特·托利的《新天地》,特别是有关Ego的章节。正如托利所建议的,如果你不再像Ego那样思考,你就会更加容易接受新思想,抛弃那些已经无用的旧思想。另外我想要补充说明的就是:不要迷信书本,也不要盲目追求新的信息,否则你就会成为一个沉迷于自我帮助的人。在行动中运用那些新信息和你学到的事情,然后加以尝试。
-
持续信息过剩
信息过剩并不是说你过多的阅读,我所指的是所有输入信息的过剩。如果你让所有的信息都涌进大脑,这当然会导致难于清晰思考,因为刺激源太多了。以下就是这种习惯可能会存在的弊端:
- 你所接受的一些信息也许会是消极的。媒体和周围环境会因种种原因提供一种消极的信息。如果你没有根据需要对信息加以选择,也许你就会陷入消极之中,从而影响到你的所思,所感,所为。
- 这会使你急于追赶当今发生的事情。每时每刻都有十几件事同时发生,想要追赶上它们几乎是不可能的事情。你的生活会因此充满压力。
- 如果你持续被信息轰炸,并且还试图将所有信息分类,那么你将很难做出决定并采取行动。就我个人而言,如果我得到过多的信息,就会造成某种形式上的瘫痪。如果你已被这种习惯所困,终日急于忙碌在一些非重要的事情上。为了可以集中精力,清晰思考并付诸行动,你就需要在吸取信息时更有选择性。当你工作时尽可能的避免那些分散注意力的事物,如关掉电话,断开网络,关上大门。久而久之,你就会发现,当你没有每隔五分钟就被打扰一次,没有机会因浏览RSS-feeds或喜爱的网站而拖延时,居然可以完成这么多的事情。我并不是建议你们停止阅读所有的博客或报纸,但是一定要清楚哪些是你真正想要阅读的,哪些只是用来打发时间的。学会拥有一扇心灵之门,关上它而去关注更为重要的事情,这样你就没有必要陷入那些来自周围环境的 ��极情绪。要知道,如果周围的所有人都在拖延或者焦急的忙于各种非重要的事情时,你会很容易被这种情绪所影响的。
原文:7 Habits of Highly Ineffective People – The Positivity Blog
如下实现,用CString时,是肯定会造成内存泄露
2010年3月4日星期四
2010年3月1日星期一
车牌识别中一种新的直接基于彩色图像的二值化简化方法[转]
车牌识别包括预处理、定位、字符分割和字符识别等几部分。在本文中,我们仅讨论预处理的二值化过程。二值化的好坏决定着车牌识别的精度。事实上,要提高车牌识别精度必须要有好的二值化方法。本文提出了一种新的直接基于彩色图像的二值化方法。
车牌识别,一直存在两种思路。一是将彩色图像灰度化,然后二值化等等;二是直接基于彩色图像。
由于直接基于彩色图像的方法大都未取得很好的效果,因此,造成灰度化、二值化成为目前车牌识别的主流。但是,采用灰度化的方法,有个根本的问题就是,灰度化是有损的。灰度化难在阈值选取。尽管没有理论证明,车牌识别的精度很难超过90%,这或许就与阈值的选取有关。
要跳出阈值选取的怪圈,我认为车牌识别就还是应该回到彩色模式上来。其实,彩色图像为什么要灰度化?其依据是彩色图像需要占用大量的处理时间。老师上课的时候是这样在讲,教科书上也是这样在写。
从广泛的意义上讲,彩色图像确实会占用大量的处理时间,但是,针对车牌识别而言,我们可以找到一种非常简单的方法。通过这种方法,彩色图像会被极大地简化,就是说可以简化到无需考虑处理时间的占用问题。
注意,我说的是简化。就是说,我们要通过简化的方法,达到通过灰度化实现二值化同样的目标,即将彩色图像直接简化成二值化图像,简化过程不使用灰度化方法,仅通过空间映射方式。这样做的好处是什么呢?它克服了灰度化的有损性,最大限度地保持了图像的原貌。传统的方法经灰度化后得到的二值化结果,已经完全丧失原有的色彩信息,而新的通过空间映射的方法,同样得到了二值化图像,但是,图像中每个像素的原色彩信息依然保留着。
直接基于彩色图像的二值化简化,其核心思想是通过:黄色 =(红色+绿色)/ 2 ,依此将红、绿、蓝三原色构成的色彩空间映射到由黄、蓝两种颜色构成的色彩空间;映射后的色彩值有黑色、蓝色、灰色、黄色和白色五种。具体的简化过程如下:
1、建立公式:黄色 =(红色+绿色)/ 2 ,取值0..255。
2、建立公式:灰度 =(黄色+蓝色)/ 2 ,取值0..255。
3、将黄色和蓝色都大于187的视同白色。
4、将黄色和蓝色都小于153的视同黑色。
5、去除白色和黑色剩下的中间色中,如果黄色<0.9×蓝色(意味着颜色偏蓝),则需进一步进行蓝色细分;如果蓝色<0.9×黄色(意味着颜色偏黄),则需进一步进行黄色细分;否则则需进一步进行灰色细分。
6、蓝色细分:如果黄色<0.8×蓝色,说明颜色中蓝色成分明显多于黄色成分,视同蓝色;否则,如果灰度<187,视同蓝色,反之视同灰色。
7、黄色细分:如果蓝色<0.8×黄色,说明颜色中黄色成分明显多于蓝色成分,视同黄色;否则,如果灰度>153,视同黄色,反之视同灰色。
8、灰色细分:如果灰度<153,视同黑色;如果灰度>187,视同白色,否则视同灰色。
经过空间映射,现在的空间由黑色、蓝色、灰色、黄色和白色五种颜色构成。显然,这个空间仍然可以被看成是彩色空间,它包括中国车牌所需要的各种底色和字符色(红字需要再特殊处理一下),所有色彩信息都没有丢失。注意,这里有灰色,它正好解决了对“脏”车牌的识别。
进一步我们可以将黑色和蓝色统一归并为蓝色,黄色和白色统一归并为黄色。灰色可根据底色视同蓝色或黄色。这样我们就完成了直接基于彩色图像的二值化简化。
上述直接基于彩色图像的二值化简化方法,最关键的是把空间简化了,把RGB三维空间简化成YB二维,然后,继续简化成直线,再继续简化成五个值,最后简化成二值,节省了计算时间。它与其他的彩色模式的差异在哪里呢?其他的彩色模式大都没有降低维数,减少计算量,即尽管采用了彩色模式,但是最后还是要回到灰度化上面去。这是不对的。
实际应用时,参数会略有调整,测试请到 http://www.yulaohui.com/color5_2/ 。
2009年12月21日星期一
2009年10月30日星期五
浅谈C++中内存泄漏的检测
http://blog.csdn.net/phinecos/archive/2009/10/29/4745720.aspx
首先我们需要知道程序有没有内存泄露,然后定位到底是哪行代码出现内存泄露了,这样才能将其修复。
最简单的方法当然是借助于专业的检测工具,比较有名如BoundsCheck,功能非常强大,相信做C++开发的人都离不开它。此外就是不使用任何工具,而是自己来实现对内存泄露的监控,分如下两种情况:
一. 在 MFC 中检测内存泄漏
假如是用MFC的程序的话,很简单。默认的就有内存泄露检测的功能。
我们用VS2005生成了一个MFC的对话框的程序,发现他可以自动的检测内存泄露.不用我们做任何特殊的操作. 仔细观察,发现在每个CPP文件中,都有下面的代码:
#define new DEBUG_NEW
#endif
DEBUG_NEW 这个宏定义在afx.h文件中,就是它帮助我们定位内存泄漏。
在含有以上代码的cpp文件中分配内存后假如没有删除,那么停止程序的时候,VisualStudio的Output窗口就会显示如下的信息了:
Dumping objects ->
d:\code\mfctest\mfctest.cpp(80) : {157} normal block at 0x003AF170, 4 bytes long.
Data: < > 00 00 00 00
Object dump complete.
在Output窗口双击粗体字那一行,那么IDE就会打开该文件,定位到该行,很容易看出是哪出现了内存泄露。
二.检测纯C++的程序内存泄露
我试了下用VisualStudio建立的Win32 Console Application和Win32 Project项目,结果都不能检测出内存泄露。
下面一步一步来把程序的内存泄露检测的机制建立起来。
首先,我们需要知道C运行库的Debug版本提供了许多检测功能,使得我们更容易的Debug程序。在MSDN中有专门的章节讲这个,叫做Debug Routines,建议大家先看看里面的内容吧。
我们会用到里面很重要的几个函数。其中最重要的是 _CrtDumpMemoryLeaks();自己看MSDN里的帮助吧。使用这个函数,需要包含头文件crtdbg.h
该函数只在Debug版本才有用,当在调试器下运行程序时,_CrtDumpMemoryLeaks 将在“Output(输出)”窗口中显示内存泄漏信息.写段代码试验一下吧,如下:
检测内存泄露版本一:
#include <crtdbg.h>
int _tmain(int argc, _TCHAR* argv[])
{
int* p = new int();
_CrtDumpMemoryLeaks();
return 0;
}
运行后,在Output(输出)窗口,显示了如下的信息:
Dumping objects ->
{112} normal block at 0x003AA770, 4 bytes long.
Data: < > 00 00 00 00
Object dump complete.
但是这个只是告诉我们程序有内存泄露,到底在哪泄露了一眼看不出来啊。
看我们的检测内存泄露版本二:
#ifdef _DEBUG
#define DEBUG_CLIENTBLOCK new( _CLIENT_BLOCK, __FILE__, __LINE__)
#else
#define DEBUG_CLIENTBLOCK
#endif
#define _CRTDBG_MAP_ALLOC
#include <crtdbg.h>
#ifdef _DEBUG
#define new DEBUG_CLIENTBLOCK
#endif
int _tmain(int argc, _TCHAR* argv[])
{
int* p = new int();
_CrtDumpMemoryLeaks();
return 0;
}
该程序定义了几个宏,通过宏将Debug版本下的new给替换了,新的new记录下了调用new时的文件名和代码行.运行后,可以看到如下的结果:
Dumping objects ->
d:\code\consoletest\consoletest.cpp(21) : {112} client block at 0x003A38B0, subtype 0, 4 bytes long.
Data: < > 00 00 00 00
Object dump complete.
呵呵,已经和MFC程序的效果一样了,但是等一等。看下如下的代码吧:
{
int* p = new int();
_CrtDumpMemoryLeaks();
delete p;
return 0;
}
运行后可以发现我们删除了指针,但是它仍然报内存泄露。所以可以想象,每调用一次new,程序内部都会将该调用记录下来,类似于有个数组记录,假如delete了,那么就将其从数组中删除,而_CrtDumpMemoryLeaks()就是把这个数组当前的状态打印出来。
所以除了在必要的时候Dump出内存信息外,最重要的就是在程序退出的时候需要掉用一次_CrtDumpMemoryLeaks();
假如程序有不止一个出口,那么我们就需要在多个地方都调用该函数。
更进一步,假如程序在类的析构函数里删除指针,怎么办?例如:
#ifdef _DEBUG
#define DEBUG_CLIENTBLOCK new( _CLIENT_BLOCK, __FILE__, __LINE__)
#else
#define DEBUG_CLIENTBLOCK
#endif
#define _CRTDBG_MAP_ALLOC
#include <crtdbg.h>
#ifdef _DEBUG
#define new DEBUG_CLIENTBLOCK
#endif
class Test
{
public:
Test() { _p = new int(); }
~Test() { delete _p; }
int* _p;
};
int _tmain(int argc, _TCHAR* argv[])
{
int* p = new int();
delete p;
Test t;
_CrtDumpMemoryLeaks();
return 0;
}
可以看到析构函数在程序退出的时候才调用,明明没有内存泄露,但是这样的写法还是报了。
如何改进呢,看检测内存泄露版本三:
#ifdef _DEBUG
#define DEBUG_CLIENTBLOCK new( _CLIENT_BLOCK, __FILE__, __LINE__)
#else
#define DEBUG_CLIENTBLOCK
#endif
#define _CRTDBG_MAP_ALLOC
#include <crtdbg.h>
#ifdef _DEBUG
#define new DEBUG_CLIENTBLOCK
#endif
class Test
{
public:
Test() { _p = new int(); }
~Test() { delete _p; }
int* _p;
};
int _tmain(int argc, _TCHAR* argv[])
{
_CrtSetDbgFlag ( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF );
int* p = new int();
delete p;
Test t;
return 0;
}
_CrtSetDbgFlag ( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF );该语句在程序退出时自动调用 _CrtDumpMemoryLeaks。必须同时设置 _CRTDBG_ALLOC_MEM_DF 和 _CRTDBG_LEAK_CHECK_DF.
这样,该版本已经达到了MFC一样的效果了,但是我觉得光这样还不够,因为我们只是在Output窗口中输出信息,对开发人员的提醒还不明显,经常会被遗漏,而且很多人就算发现了内存泄露,但是不好修复,不会严重影响到程序外在表现,都不会修复。怎么样能让开发人员主动的修复内存泄露的问题呢?记得曾经和人配合写程序,我的函数参数有要求,不能为空,但是别人老是传空值,没办法了,只好在函数开始验证函数参数,给他assert住,这样程序运行时老是不停的弹出assert,调试程序那个烦压,最后其他程序员烦了,就把这个问题给改好了,输入参数就正确了。所以我觉得咱要让程序员主动去做一件事,首先要让他觉得做这个事是能减轻自己负担,让自己工作轻松的。呵呵,那咱们也这样,当程序退出时,检测到内存泄露就让程序提示出来。
看检测内存泄露版本四:
#include <assert.h>
#ifdef _DEBUG
#define DEBUG_CLIENTBLOCK new( _CLIENT_BLOCK, __FILE__, __LINE__)
#else
#define DEBUG_CLIENTBLOCK
#endif
#define _CRTDBG_MAP_ALLOC
#include <crtdbg.h>
#ifdef _DEBUG
#define new DEBUG_CLIENTBLOCK
#endif
void Exit()
{
int i = _CrtDumpMemoryLeaks();
assert( i == 0);
}
int _tmain(int argc, _TCHAR* argv[])
{
atexit(Exit);
int* p = new int();
return 0;
}
该版本会在程序退出时检查内存泄露,假如存在就会弹出提示对话框.
atexit(Exit);设置了在程序退出时执行Exit()函数。Exit()函数中,假如存在内存泄露,_CrtDumpMemoryLeaks()会返回非0值,就会被assert住了。
到这个版本已经达到可以使用的程度了。但是我们还可以做些改进,因为真要准确的检测到代码中所有的内存泄露,需要把代码中的#define……拷贝到所有使用new的文件中。不可能每个文件都拷贝这么多代码,所以我们可以将他提取出来,放在一个文件中,比如我是放在KDetectMemoryLeak.h中,该文件内容如下:
#ifdef _DEBUG
#define DEBUG_CLIENTBLOCK new( _CLIENT_BLOCK, __FILE__, __LINE__)
#else
#define DEBUG_CLIENTBLOCK
#endif
#define _CRTDBG_MAP_ALLOC
#include <stdlib.h>
#include <crtdbg.h>
#ifdef _DEBUG
#define new DEBUG_CLIENTBLOCK
#endif
然后将KDetectMemoryLeak.h包含在项目的通用文件中,例如用VS建的项目就将其包含在stdafx.h中。或者我自己建的一个Common.h文件中,该文件包含一些通用的,基本所有文件都会用到的代码。
2009年9月9日星期三
A Visual C++ Exception FAQ
A Visual C++ Exception FAQ
Copyright © 2001-2007 Doug Harrison (http://members.cox.net/doug_web/eh.htm)
This document answers some common questions concerning catch(...) and exceptions in general as implemented by Visual C++. It's structured mainly as a conversation, in which one question and answer leads to the next, so you'll get the most out of it if you read it as a whole. To give you a quick idea of what I'm going to talk about, the questions are:
| Q1 | I wrote the following, and I don't understand why catch(...) doesn't catch the Win32 structured exception in a release build, or in general, when compiling with optimizations (e.g. /O1 or /O2). |
| Q2 | I also wrote the code above, and I don't understand why the Win32 structured exception (SE) is caught in a debug build or when I compile with /EHa. Also, I sometimes find that catch(...) catches SEs even in release builds when I use /GX. Isn't catch(...) supposed to catch only C++ exceptions? |
| Q3 | So what are the consequences of catching Win32 Structured Exceptions (SEs) in catch(...) ? |
| Q4 | What about _set_se_translator? |
| Q5 | How do I deal with all this? |
| Q6 | How do I safely use _com_error, std::exception, and other non-MFC exception classes in MFC programs? |
This document applies to Visual C++ 5 through Visual C++ .NET 2003 and beyond. The upcoming Visual C++ 2005 release, known also as "Whidbey", corrects one of the problems discussed below, and this partly affects questions Q1, Q2, and Q5, which are updated accordingly. The remaining questions and answers apply in full to Visual C++ 5 and later.
Q1. I wrote the following, and I don't understand why catch(...) doesn't catch the Win32 structured exception in a release build, or in general, when compiling with optimizations (e.g. /O1 or /O2).
#include <stdio.h>
int main()
{
try
{
int* p = 0;
*p = 0; // Cause access violation
}
catch (...)
{
puts("Caught access violation");
}
return 0;
}
A. In Visual C++5 through Visual C++ .NET 2003, you're compiling with /GX or /EHs, which enables the compiler's synchronous exception model. This model is defined to catch only exceptions resulting from a C++ throw statement, and there is no such statement in the above. If you were to examine the assembly language emitted for this program, you would find the compiler has optimized all the exception handling machinery out of the function, because the optimizer can determine the tried code cannot throw a C++ exception. This is a great optimization! It's especially appreciated when writing template code. Unfortunately, there is a bug that causes catch(...) to catch Win32 structured exceptions in some scenarios, which leads to the next question.
Q2. I also wrote the code above, and I don't understand why the Win32 structured exception (SE) is caught in a debug build or when I compile with /EHa. Also, I sometimes find that catch(...) catches SEs even in release builds when I use /GX. Isn't catch(...) supposed to catch only C++ exceptions?
A. According to Stroustrup, C++ exception handling (EH) is not intended to handle signals or other low-level, OS-specific events such as arithmetic exceptions. Win32 Structured Exceptions (SEs) clearly fall into this category, and it should not be possible to catch SEs in catch(...). However, the C++ Standard doesn't specifically forbid this, and because anytime you raise an SE you invoke undefined behavior, it's "legal" for catch(...) to catch SEs, in a very technical sense, because the C++ Standard imposes no requirements on the behavior of a program that does something undefined, such as dereferencing a NULL pointer. That said, while it may seem convenient to catch truly everything in catch(...), catching SEs there is the source of numerous problems. Before discussing why I say that, let's consider how Visual C++ is documented to behave.
Visual C++ 5 and later define two EH models, called synchronous and asynchronous. The model chosen is determined by the /EH command line option. /EHs specifies the synchronous model, while /EHa specifies the asynchronous model. There is also /GX, which is defined by default for MFC and other AppWizard applications. /GX is equivalent to /EHsc, so it selects the synchronous model. (The c indicates that extern "C" functions do not throw exceptions.) The VC++ documentation defines the asynchronous model as follows:
In previous versions of Visual C++, the C++ exception handling mechanism supported asynchronous (hardware) exceptions by default. Under the asynchronous model, the compiler assumes any instruction may generate an exception.
Under the asynchronous model, catch(...) catches SEs, and you must use /EHa if this is what you really want. You must also use /EHa if you're expecting to catch SEs that have been translated into C++ exceptions with the help of _set_se_translator(). (See Q4.)
The synchronous model is described as follows:
With the new synchronous exception model, now the default, exceptions can be thrown only with a throw statement. Therefore, the compiler can assume that exceptions happen only at a throw statement or at a function call. This model allows the compiler to eliminate the mechanics of tracking the lifetime of certain unwindable objects, and to significantly reduce the code size, if the objects' lifetimes do not overlap a function call or a throw statement.
The synchronous model is intended to provide C++ EH as Stroustrup intended, but unfortunately, in Visual C++ 5 through Visual C++ .NET 2003, it doesn't behave exactly as documented, and it's still possible to catch SEs in catch(...) if you compile without optimizations, or you compile with optimizations, and the optimizer is unable to determine the tried code cannot throw a C++ exception. For example, in VC5, if the tried code calls a function, the optimizer assumes it can throw, while in VC6, the function may need to live in another translation unit (source file) to cause the optimizer to be pessimistic. Visual C++ .NET 2005 at last corrects this problem for the synchronous model.
Q3. So what are the consequences of catching Win32 Structured Exceptions (SEs) in catch(...) ?
A. In order to answer this question, we first need to discuss what C++ exceptions and SEs represent. According to Stroustrup, C++ exception handling is error handling. For example, failure to acquire a resource such as memory or running out of disk space while writing to a file is an error that is often best reported by throwing an exception, especially when the resource is normally expected to be available. This greatly simplifies code by eliminating the need to check function return codes, and it helps you centralize error handling. This sort of error can occur in a correctly written program, and that is what C++ EH is intended to address.
On the other hand, SEs typically represent program bugs. Everyone is familiar with access violations resulting from dereferencing NULL pointers. The hardware detects this and traps, and Windows turns the hardware event into an SE. In general, SEs represent programmer errors, and correctly written programs have no such errors. SEs are also used in the normal operation of the system. For example, it's possible to use VirtualAlloc() to reserve a region of your address space and dynamically commit pages as a program accesses uncommitted memory and causes page faults. The program catches the SE in an __except clause, commits the memory, and resumes execution with the instruction that caused the fault. This should be invisible to C++ EH, which should not be able to interfere with it.
C++ exceptions and Win32 structured exceptions represent very different things. Problems caused by homogenizing them in catch(...) include the following.
- If catch(...) is able to catch SEs, it's impossible to write the following with any confidence:
// Begin exception-free code
... Update critical data structure
// End exception-free code
If the critical code has a bug that results in an SE, an outer catch(...) block may catch the SE, creating a completely unanticipated program state. The program may hobble along, further corrupting its state. If you're lucky, a subsequent uncaught SE will bring the program down before it does any serious damage, but debugging the problem may be much more difficult than if catch(...) hadn't swallowed the initial SE, because the secondary SE may occur in code far removed from the source of the actual bug. The OS will report the uncaught secondary SE and give you the opportunity to debug it, but it will lead you to the source of this SE, not the source of the actual problem. - Code such as the following becomes suspect:
tryIf a program bug or compiler code generation bug causes an access violation in the tried function, your discovery of the bug is hindered by catch(...) swallowing the SE. The only manifestation of the bug may be an inexplicable slowness, which may not be apparent in your testing, while if catch(...) hadn't caught the SE, you certainly would have discovered the bug while testing. (OS error boxes are pretty hard to miss!)
{
TheFastButResourceHungryWay();
}
catch (...)
{
TheSlowButSureWay();
}
- The normal operation of the system is impaired. For example, the MFC CPropertySheet::DoModal() documentation describes a scenario in which you should not use catch(...). The exception raised by the DebugBreak API can be caught by catch(...), rendering DebugBreak useless. Also, if you're using __try/__except to handle SEs properly, you may have trouble if an interior catch(...) is present, even if it rethrows. You almost certainly will have trouble if your SE handler resumes execution with the faulting instruction. You may find the catch(...) block was entered and local variables destroyed, which is very bad if execution is resumed in its complementary try block. And if that try block subsequently throws a C++ exception, you may find yourself in an infinite loop with your SE filter function.
- Application frameworks are taking a chance if they guard your code with catch(...), which they normally should do. For example, MFC does not use catch(...), and as a result, an uncaught C++ exception terminates an MFC application.
Q4. What about _set_se_translator?
A. _set_se_translator is a function used to register another function which translates Win32 structured exceptions into true C++ exceptions. It allows you to partially avoid the catch(...) problems described in Q3 by writing the following, where se_t is the type of object thrown by the translator function:
catch (se_t) { throw; }
catch (...) { ... } This is not a great workaround, because it's easy to forget to augment every catch(...) as shown above, and you would have to establish a translator in every thread you create that runs code which uses this method, because SE handlers are attributes of a thread, and calling _set_se_translator in one thread has no effect on other threads. Also, the translator function isn't inherited by new threads; thus, _set_se_translator has no effect on threads created after it's called. Besides being difficult and error-prone to implement, this workaround can't account for code you didn't write and can't modify, and this can be an issue for library users.
Finally, the documentation does not make it clear that to use _set_se_translator reliably, you must select the asynchronous EH model discussed in Q2, and that tends to bloat your object code. If you don't do this, your code is subject to the optimization discussed in Q1.
Q5. How do I deal with all this?
A. When using Visual C++ 5 through Visual C++ .NET 2003, the best course is to avoid catch(...) whenever possible. If you must use catch(...), be aware of all the issues described in the preceding questions. If you're using Visual C++ .NET 2005, the /EHs option behaves as documented, the synchronous model works correctly, and you don't have to worry about catch(...) catching SEs.
Q6. How do I safely use _com_error, std::exception, and other non-MFC exception classes in MFC programs?
A. MFC was designed before Visual C++ supported C++ exception handling. The original MFC implementation was based on macros such as TRY and CATCH and used setjmp and longjmp to simulate C++ exception handling. To simplify this initial implementation, MFC threw pointers to CException objects and pointers to objects of classes derived from CException, and CException* was the only exception type supported by early versions of MFC. Though MFC was updated to use C++ exceptions in Visual C++ 2.0, it was never made aware of other exception types, and the MFC source code continues to use the macros, which are now defined in terms of C++ EH. For example, MFC defines CATCH_ALL in terms of:
catch (CException* e)
Clearly, this doesn't catch all exceptions if the tried code uses the C++ Standard Library, compiler COM support, or other libraries that define their own exception types. MFC does not itself use any exception type other than CException*, but in many places, it wraps your code as follows:
TRY
{
// Call your code
}
CATCH_ALL(e)
{
// Clean up and perhaps report the error to the user
}
END_CATCH_ALL
For example, an MFC WindowProc is guarded this way, because exceptions aren't allowed to cross Windows message boundaries. However, CATCH_ALL catches only MFC exceptions, and if you fail to catch a non-MFC exception yourself, your program will be terminated due to an uncaught exception. Even if you do catch the exception yourself, where you catch it is still very important, because there are a number of functions within MFC that expect to catch all exceptions so they can clean up or return an error code to the caller through a normal function return statement. Now, if the try blocks within these functions call into your code, and you don't translate non-MFC exceptions into MFC exceptions right then and there, you allow non-MFC exceptions to propagate through MFC code that expects to catch everything, and as just described, it can't, and it doesn't. You may end up skipping some important clean-up code, and even though you catch your non-MFC exception at some outer level, it may be too late. This suggests the following rule of thumb:
Never allow a non-MFC exception to pass through MFC code
At a minimum, this means protecting every message handler that could exit via a non-MFC exception with try/catch. Now, if a message handler can't do anything about an exception, and you want it to be reported to the user, it's often appropriate for the handler to exit via an exception, because MFC will present the user with a nice message box describing the error, provided it can catch it. To achieve this result, you need to translate non-MFC exceptions into MFC exceptions. Macros can help here. For example, consider the code sketched below:
class MfcGenericException : public CException
{
public:
// CException overrides
BOOL GetErrorMessage(
LPTSTR lpszError,
UINT nMaxError,
PUINT pnHelpContext = 0)
{
ASSERT(lpszError != 0);
ASSERT(nMaxError != 0);
if (pnHelpContext != 0)
*pnHelpContext = 0;
_tcsncpy(lpszError, m_msg, nMaxError-1);
lpszError[nMaxError-1] = 0;
return *lpszError != 0;
}
protected:
explicit MfcGenericException(const CString& msg)
: m_msg(msg)
{
}
private:
CString m_msg;
};
class MfcStdException : public MfcGenericException
{
public:
static MfcStdException* Create(const std::exception& ex)
{
return new MfcStdException(ex);
}
private:
explicit MfcStdException(const std::exception& ex)
: MfcGenericException(ex.what())
{
}
};
#define MFC_STD_EH_PROLOGUE try {
#define MFC_STD_EH_EPILOGUE \
} catch (std::exception& ex) { throw MfcStdException::Create(ex); }
The code above defines a class, MfcGenericException, which is derived from MFC's CException, and which serves as the base class for MfcStdException and other non-MFC exception types. (We need this base class because MFC does not provide a generic exception type that encapsulates a message string.) The macros at the bottom are intended to surround your message handlers and other code called from MFC that can throw non-MFC exceptions. You use it like this:
void MyWnd::OnMyCommand()
{
MFC_STD_EH_PROLOGUE... your code which can throw std::exceptionMFC_STD_EH_EPILOGUE
}
Together, the macros guard your code in a try block, and the MFC_STD_EH_EPILOGUE macro translates std::exception into something MFC can catch, in this case, MfcStdException. Note that MfcStdException has a private constructor and defines a static Create function, and the latter provides the only way to create an MfcStdException. It ensures the exception object is created on the heap, which we must do, because each object maintains state information in the form of its error message. We can't simply throw a pointer to a static instance, as AfxThrowMemoryException does, because that wouldn't be thread-safe due to our state information, and it's also possible to throw and catch an exception while handling another, which is ultimately rethrown, and that would tend to overwrite the first message. We can't take any shortcuts here! Whoever catches our exception is responsible for calling its Delete member function, inherited from CException. This function will delete the MfcStdException object, and it's good to disallow mistakes such as throwing a pointer to a local object by preventing the creation of local objects altogether.
Using a technique such as the above is essential to creating MFC programs which are robust in the presence of heterogeneous exception types. It's much easier than writing explicit try/catch blocks, and it allows exceptions to propagate to whoever can best handle them. In fact, explicit try/catch blocks are relatively rare in well-designed programs, because the code is written in such a way that the automatic stack unwinding and local variable destruction does the right thing. Consequently, the final step of handling an exception often amounts to simply letting the user know something went wrong, and by translating your non-MFC exceptions into MFC exceptions, MFC can handle that just fine.
Comments
To comment on this page, please send email to dsh@mvps.org.
2009年8月13日星期四
简单有效的图像去雾技术
| 标签:微软亚洲研究院 图像去雾 cvpr 最佳论文 研究心得 论文背后故事 | 分类:技术 |
作者:何恺明
那是2009年4月24日的早上,我收到了一封不同寻常的email。发信人是CVPR 2009的主席们,他们说我的文章获得了CVPR 2009的最佳论文奖(Best Paper Award)。我反复阅读这封邮件以确认我没有理解错误。这真是一件令人难以置信的事情。
北京灰霾照片的去雾结果
CVPR的中文名是计算机视觉与模式识别会议,是计算机视觉领域最顶尖 的国际会议之一。今年的CVPR共收到约1450篇投稿,其中393篇文章被接收,接收率为26%。只有一篇文章被选为今年的最佳论文。这是CVPR创立 25年以来首次由中国人获得这个奖项。这篇文章是我在微软亚洲研究院形象计算组实习的时候完成的,也是我个人真正意义上写的第一篇论文。
简单有效的图像去雾技术
这篇论文研究的问题是图像的去雾技术,它可 以还原图像的颜色和能见度,同时也能利用雾的浓度来估计物体的距离,这些在计算机视觉上都有重要应用(例如三维重建,物体识别)。但是之前人们还没找到简 单有效的方法来达到这个目的。在这篇论文里,我们找到了一个非常简单的,甚至说令人惊讶统计规律,并提出了有效的去雾方法。
与之前的方法不同,我们把注意力放到了无雾图像的统计特征上。我们发现,在无雾图像中,每一个局部区域都很有可能会有阴影,或者是纯颜色的东西,又或者是黑色的东西。因此,每一个局部区域都很有可能有至少一个颜色通道会有很低的值。我们把这个统计规律叫做Dark Channel Prior。直观来说,Dark Channel Prior认为每一个局部区域都总有一些很暗的东西。这个规律很简单,但在我们研究的去雾问题上却是本质的基本规律。
由于雾总是灰白色的,因此一旦图像受到雾的影响,那么这些本来应该很暗的东西就会变得灰白。不仅如此,根据物理上雾的形成公式,我们还能根据这些东西的灰白程度来判断雾的浓度。因此,我们提出的Dark Channel Prior能很有效地去除雾的影响,同时利用物的浓度来估算物体的距离。
电脑游戏带来的灵感
这个想法的产生来自于两个偶然的观察。
第一个观察来自一个3D游戏。这个游戏有很多带有雾的场景,但这些场景 都是虚构的不实在的东西。计算机生成的3D图像会与自然图像的统计规律有很大区别,但人的视觉系统却仍然能感觉到虚拟图像中存在的雾。这让我相信,人的视 觉系统一定有一种有效的机制来感知有雾的图像,而且这种机制一定与现存的去雾方法不一样。前人提出的去雾方法都把重点放在图像的对比度上,但虚拟场景和现 实场景在对比度上的统计规律会很不一样。人的视觉系统仍然能够感知虚拟场景中的雾,说明除了对比度以外,人眼一定还在利用别的东西来感知雾。所以我觉得, 这个问题里一定有人们未曾发现的更接近本质的东西。
第二个观察来自对前人的去雾方法的研究。之前最有效的去雾方法是Fattal在2008年的Siggraph文章《Single Image Dehazing》中提出来的,这篇文章是我们首要超越的目标。这篇文章里给出的比较结果中,我发现一种叫做Dark Object Subtraction的方法有时候会有更好的效果。这种方法利用了全图最暗的点来去除全局均匀的雾。如果雾的确是均匀的,这种方法就会更有效。其缺点在于它无法处理不均匀的雾,而这却正是去雾问题中的难点。因此自然的想法就是局部利用Dark Object Subtraction处理图像。而恰巧这样做并不需要利用对比度,说明它与之前的方法有了本质的区别。让人吃惊的是,在大量的实验中,我发现这么简单的想法,其效果却非常好。
但我们论文中最重要的观点却形成在我动笔写文章之后。在文章的前几稿 中,我在形象计算组的mentor孙剑一直追问我,我们的方法能成功的本质原因是什么,背后有什么我们没有充分理解透彻的“真知灼见”。尽管我们有很简单 的方法,也有很漂亮的实验结果,但我们却无法让人对这种方法的有效性感到信服。这是因为我们还讲不出个道理来。带着这个问题,我又回到了实验和观察之中。 我发现,既然大量实验结果证实局部做Dark Object Subtraction的做法是成功的,那么就说明去雾之后的图像的每个局部的确是有暗的物体存在的。也就是说,在这个方法成功的背后,其实有一个关于无 雾图像的统计规律。我的mentor孙剑让我去先去研究一个无雾图像的数据库。通过大量的实验,我们发现这个统计规律是客观存在的。这就是我们所提出的 Dark Channel Prior。
这是我写的第一篇论文
2007年,我从清华大学基础科学班本科毕业,之后就读于香港中文大 学。在基础科学班的主修课程是数学和物理,因此在本科阶段,我并没有系统地学习过计算机方面的相关知识。出于兴趣,我选修了计算机图形和图像方面的一些相 关课程。但是在进入微软亚洲研究院实习的初期,这些基础课程远远不足以应付我面对的研究工作。背景知识的缺乏使我在入门的路上举步维艰。在阅读文章的时 候,我常常都不知道哪些是大家都在用的方法,哪些才是作者的贡献。对我来说,我看见的每一样东西都是新的。
在面试的时候,我的导师汤晓鸥就跟我说过,他并不在意我没有相关的背景 知识,因为所有相关的东西都是可以学的。在进入微软亚洲研究院实习的头一年里,我在mentor孙剑的指导下做过几个不同的课题,虽然都没有成功,但从中 学习了不少知识。其中我花了大量时间研究的image matting问题 (半透明物体边界提取),就对这次的文章有很大帮助。在刚开始研究去雾的时候,我就发现雾的方程和matting的方程非常相似,而我之前所研究的 matting框架可以给去雾带来帮助。利用这个框架,我只需要寻找一个能局部估算雾的浓度的方法就行了。这个框架使得我能专心的寻找这样的方法并且最后 提出了Dark Channel Prior。
纽约、北京灰霾照片的去雾结果
即使有了想法和实验结果,第一次写文章也使我觉得非常困难。我经常陷入 自己和自己吵架的角色当中。在每一段话写好之后,我常常会质问自己事情到底是不是这样的,这其中有没有漏洞。我也会问自己,如果我是评委,或者是读者,那 么我能看懂这篇文章吗,我怎么写才能让思路更加流畅。就在这样的挣扎中,一稿通常要写好几天。而即使是这样,起初的几稿也远没能让孙剑满意。一开始,他只 在文章的结构、思路和观点的提出上给我建议,而不去具体修改我的文章。于是我又回去继续和自己吵架。但每当我把自己说服了,孙剑还是总能提出新的质疑。就 在这样的循环中,终于有一天孙剑说文章已经写得不错了,他才开始具体的修改。正是这样的苛刻要求,才会有后来高质量的文章。
大道之行在于简
我们这篇文章的三个审稿人都给出了最高的评分。他们认为我们的方法简单 而有效。其中一位评委说,Dark Channel Prior的想法听起来很不可思议,但我们却证明了其真实性。另一位评委认为很少有文章能够用如此简单的方法使实验结果获得如此大的提升。还有一位评委甚 至亲自实现了我们的方法并确认其可行。孙剑说阅读这样的评审结果是一件让人快乐的事情。而汤老师认为,这篇文章的成功在于三个方面。第一,方法非常简单; 第二,对于一个很困难的问题,给出了很好的结果;第三,发现了一个基本的自然规律并且应用在实际的问题中。在迈阿密的演讲结束后,观众也给予了很高的评 价。他们跟我说,这是这次CVPR上最有趣的一个演讲。
一位与会的研究员说,最好的idea,往往就是那些看起来很简单,但说 出来大家都会觉得怎么没有人想到过的idea。而我们的idea正好就符合了这一点。我们论文摘要的第一句话是这么说的,“我们提出了一个简单而有效的方 法”。或许,这就是对我们这次工作最好的概括——简单的,就是美的。
作者介绍
何恺明:微软亚洲研究院视觉计算组实习生,现就读于香港中文大学讯息工程多媒体实验室,本科毕业于清华大学基础科学班。他是2006年微软小学者奖获得者,2003年广东省高考状元Google一下本博客
-
[原]Linux下编译使用boost库 - Boost库是一个可移植、提供源代码的C++库,作为标准库的后备,是C++标准化进程的开发引擎之一。 Boost库由C++标准委员会库工作组成员发起,其中有些内容有望成为下一代C++标准库内容。在C++社区中影响甚大,是不折不扣的“准”标准库。Boost由于其对跨平台的强调,对标准C++的强调,与...8 年前
-
[原]猎头、培训与咨询的价值(2)【补1】——北漂18年(93) - 【上期用手机写的,同时用语音输入转化成文字,错字较多,经好友霍师傅提醒本期重写,并增加一部分新内容】 简单谈下我对猎头、培训与咨询的看法。三样都干过,算是有些浅见。 猎头 简单的说就是人才中介。虽然在公司看来是可以直接解决现有企业问题的一个直接方法,但很多时候都不太管用。 猎头费一般是人才的一个月月...9 年前
-
我的时间管理道与术(三) - 本系列来自 水中颉 原创投稿。 本文续上篇《我的时间管理道与术(一):接受现实和感知时间》和《我的时间管理道与术(二):目标与计划》。 建立至上而下的检视机制 六个关注层面和检视周期 宗旨和使命、关键路径是云端;关键点和平衡点是方向指导层;项目是最接地气的现实目标层;下一步行动 是非常具体的待执行事务层...10 年前
-
OpenCV統計應用-Mahalanobis距離 - Mahalanobis距離是一個可以準確找出資料分布上面極端值(Outliers)的統計方法,使用線性迴歸的概念,也就是說他使用的是共變數矩陣以及該資料分布的平均數來找尋極端值的產生,而可以讓一群資料系統具有穩健性(Robust),去除不必要的雜訊訊息,這邊拿前面共變數矩陣的資料為例,並且新增了兩個點座標向量來做...17 年前
-
努力推进模式识别实际产品的开发与应用 - Salu 无论是手写体识别、文档处理、人脸识别、基于内容的图片搜索、嵌入人工智能的搜索技术、虚拟网络社区、还是其它相关新科技下的信息整合领域,现在都在努力实用化。 前两年、即使现在还有很多人在抱怨说人脸的方法都不能用,但是就今年出现的和正在做的有关人脸识别实际应用的各种形式的产品可以说如雨后春笋。这是一个趋...18 年前
-
-
-
-
-
-
-
- 《Learning OpenCV》 (1)
- 《Learning OpenCV》免费下载 (1)
- 标准车牌图片 (1)
- 博客最近30天访问情况 (1)
- 车辆号牌识别率合理评价 (1)
- 车辆牌照定位技术研究初探 (1)
- 车辆牌照相似字符识别研究 (1)
- 车牌定位MATLAB程序 (1)
- 车牌定位MATLAB代码,车牌定位代码 (1)
- 车牌定位MATLAB代码免费下载 (1)
- 车牌模糊识别 (1)
- 车牌模糊识别技术 (1)
- 车牌目标的自动定位技术 (1)
- 车牌识别 (1)
- 车牌识别程序VIN.rar (1)
- 车牌识别概述 (1)
- 车牌识别及验证码识别的一般思路 (1)
- 车牌识别技术(LPR)简介 (1)
- 车牌识别技术的研究 (1)
- 车牌识别技术的研究和实现(四川大学博士论文) (1)
- 车牌识别技术在城市交通管理中的应用 (1)
- 车牌识别论文下载 (1)
- 车牌识别视频 (2)
- 车牌识别搜索引擎 (1)
- 车牌识别所需的图片要求 (1)
- 车牌识别系统 (2)
- 车牌识别系统在智能交通系统中的应用 (1)
- 车牌识别学位论文下载 (1)
- 车牌识别英文论文(免费下载) (1)
- 车牌识别中二值化方法研究 (1)
- 车牌识别综述 (2)
- 车牌字符的识别常用的一些算法 (1)
- 车牌字符识别 (1)
- 车牌自动识别产品概述 (1)
- 初入职场两件事:入对行,跟对人 (1)
- 第一份工作一定要选一家好公司 (1)
- 电子眼摄录 (1)
- 多神经网络在车牌字符识别中的应用 (1)
- 高性能的车牌识别系统 (1)
- 关于CvvImage的CopyOf()函数的问题 (1)
- 国内外车辆牌照识别技术综述 (1)
- 国外很好的MATLAB车牌定位、识别代码,免费下载 (1)
- 汉字模板 (1)
- 号牌识别原理 (1)
- 机动车辆牌照识别系统技术指标测试及说明 (1)
- 基于笔画描述的文字识别OCR系统——技术说明 (1)
- 基于边缘颜色对的车牌定位新方法 (1)
- 基于彩色分割的车牌自动识别技术 (1)
- 基于车牌识别的交通流结构分析 (1)
- 基于粗糙集和BP网络的车牌字符识别 (1)
- 基于分形维数的车牌识别二值化算法 (1)
- 基于改进Sobel算子的车牌定位方法 (1)
- 基于计算机视觉的智能交通监控系统的研究 (1)
- 基于模板匹配和神经网络的车牌字符识别方法 (1)
- 基于内容的图像检索技术研究 (1)
- 基于神经网络的车牌自动识别系统研究 (1)
- 基于水平灰度跳边和垂直投影的车牌定位方法 (1)
- 基于图像处理的车牌识别系统的研究 (1)
- 基于图像处理与神经网络的车牌识别技术的研究 (1)
- 基于小波多尺度和熵在图像字符特征提取方法的改进 (1)
- 基于小波分析的车牌识别关键方法研究 (1)
- 基于小波分析的车牌字符识别 (2)
- 基于颜色和纹理分析的车牌定位方法 (2)
- 基于AdaBoost的车牌字符识别模型研究 (1)
- 基于K-L变换改进算法的车牌自动识别技术研究 (1)
- 基于OCR的车牌辨识系统的系统结构分析 (1)
- 几种车牌字符识别算法的比较 (1)
- 交通图象检测与处理方法研究 (1)
- 卷积神经网络的研究及其在车牌识别系统中的应用 (1)
- 科技转型谈何容易! 张五常 (1)
- 汽车牌照自动识别技术初探 (1)
- 汽车牌照自动识别技术研究 (1)
- 浅谈车牌识别技术应用现状 (1)
- 什么是车牌自动识别产品? (1)
- 什么是库(library) (1)
- 神经网络进行字符识别MATLAB程序 (1)
- 史蒂夫•乔布斯与禅宗美学 (1)
- 使我的新年目标成功! (1)
- 手写数字识别的原理及应用 (1)
- 手写体字符识别程序免费下载 (1)
- 图像匹配程序,有代码,可以下载 (1)
- 脱机手写签名识别系统.rar (1)
- 向比尔盖兹学习三抓三放简化工作 (1)
- 小波:看森林,也看树木 (1)
- 小波神经网络程序 (1)
- 徐家骏:我在华为工作十年的感悟 (1)
- 一篇好文,以在迷茫时阅读 (1)
- 一种改进的车牌图像二值化方法 (1)
- 一种基于模板匹配的车牌识别方法 (1)
- 一种完整的汽车牌照识别算法 (1)
- 印刷汉字识别方法综述 (1)
- 印刷体汉字识别系统 (1)
- 应届生求职全程指南应届生求职全程指南 (1)
- 英文字母字符模板 (1)
- 智能识别市场现状及未来几年发展趋势 (1)
- 中国智能交通系统发展框架构想 (1)
- 字符分割 (1)
- 字符分割MATLAB代码 (1)
- 字符结构知识在车牌识别中的应用 (1)
- 自己制作的车牌字符模板图片 (1)
- Complete License Plate Recognition Systems (1)
- CvvImage的CopyOf()单通道解决 (1)
- License Plate Recognition (1)
- License Plate Recognition Algorithms and Technology (1)
- License Plate Recognition Applications (1)
- MATLAB图像处理命令 (1)
- Matlab在图像处理与目标识别方面的应用实验 (1)
- OCR字符识别源代码 (1)
- OpenCV 编程入门 (1)
- OpenCV 关于adaboost的一些说明 (1)
- opencv中创建Iplimage的方法 (1)
- SVM在车牌字符识别中的应用 (1)
- The full "Square Detector" program. (1)
- The IplImage Structure (1)
- This is all what you want to know about OCR (1)
- Visual C++开发工具与调试技巧整理 (1)
- Visual C++数字图像处理典型算法及实现 (1)
- Vlpr Introduction (1)