2009年10月30日星期五

浅谈C++中内存泄漏的检测

http://blog.csdn.net/phinecos/archive/2009/10/29/4745720.aspx

首先我们需要知道程序有没有内存泄露,然后定位到底是哪行代码出现内存泄露了,这样才能将其修复。

最简单的方法当然是借助于专业的检测工具,比较有名如BoundsCheck,功能非常强大,相信做C++开发的人都离不开它。此外就是不使用任何工具,而是自己来实现对内存泄露的监控,分如下两种情况:

一. 在 MFC 中检测内存泄漏

假如是用MFC的程序的话,很简单。默认的就有内存泄露检测的功能。

我们用VS2005生成了一个MFC的对话框的程序,发现他可以自动的检测内存泄露.不用我们做任何特殊的操作. 仔细观察,发现在每个CPP文件中,都有下面的代码:

#ifdef _DEBUG
#define new DEBUG_NEW
#endif

DEBUG_NEW 这个宏定义在afx.h文件中,就是它帮助我们定位内存泄漏。

    在含有以上代码的cpp文件中分配内存后假如没有删除,那么停止程序的时候,VisualStudio的Output窗口就会显示如下的信息了:

Detected memory leaks!
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 "stdafx.h"
#include 
<crtdbg.h>
int _tmain(int argc, _TCHAR* argv[])
{
    
int* p = new int();
    _CrtDumpMemoryLeaks();
    
return 0;
}

 运行后,在Output(输出)窗口,显示了如下的信息:

Detected memory leaks!
Dumping objects -
>
{
112} normal block at 0x003AA770, 4 bytes long.
 Data: 
<    > 00 00 00 00 
Object dump complete
.

 但是这个只是告诉我们程序有内存泄露,到底在哪泄露了一眼看不出来啊。

   看我们的检测内存泄露版本二:

#include "stdafx.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
int _tmain(int argc, _TCHAR* argv[])
{
    
int* p = new int();
    _CrtDumpMemoryLeaks();
    
return 0;
}

  该程序定义了几个宏,通过宏将Debug版本下的new给替换了,新的new记录下了调用new时的文件名和代码行.运行后,可以看到如下的结果:

Detected memory leaks!
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 _tmain(int argc, _TCHAR* argv[])
{
    
int* p = new int();
    _CrtDumpMemoryLeaks();
    delete p;
    
return 0;
}

运行后可以发现我们删除了指针,但是它仍然报内存泄露。所以可以想象,每调用一次new,程序内部都会将该调用记录下来,类似于有个数组记录,假如delete了,那么就将其从数组中删除,而_CrtDumpMemoryLeaks()就是把这个数组当前的状态打印出来。

所以除了在必要的时候Dump出内存信息外,最重要的就是在程序退出的时候需要掉用一次_CrtDumpMemoryLeaks();

假如程序有不止一个出口,那么我们就需要在多个地方都调用该函数。

更进一步,假如程序在类的析构函数里删除指针,怎么办?例如:

#include "stdafx.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
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;
}

  可以看到析构函数在程序退出的时候才调用,明明没有内存泄露,但是这样的写法还是报了。

  如何改进呢,看检测内存泄露版本三:

#include "stdafx.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
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 "stdafx.h"
#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中,该文件内容如下:

#pragma once
#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.

  1. 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.
  2. Code such as the following becomes suspect:

       try
       {
          TheFastButResourceHungryWay();
       }
       catch (...)
       {
          TheSlowButSureWay();
       }
       
    If 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!)
  3. 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.
  4. 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::exception
MFC_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年广东省高考状元

2009年8月12日星期三

广东:卡口系统年内启用可快速识别套牌车

http://news.QQ.com  2009年08月10日13:38   金羊网-羊城晚报   

  本报讯记者崔朝阳报道:广东省公安厅近日在对省人大代表的建议答复中表示,可快速发现、识别和截查套牌车的卡口系统有望年内在广东全省范围投入使用,为加大对套牌车的打击力度,公安厅还拟提请省人大常委会修订《广东省道路交通安全条例》,加大对查处套牌车的处罚力度。

       今年初的省“两会”期间,陈进德等6名省人大代表提交建议指出,套牌车难发现、难查处、轻处罚,全省交警系统没有形成有效的联动措施,是套牌车层出不穷的重要原因。

  省公安厅表示,针对目前对套牌车处罚过轻,公安厅目前正会同省政府法制办开展调研,拟在年内提请省人大常委会修改道路交通安全条例,增加限制违法行为人人身自由(拘留)、加大罚款额度、没收违法车辆、追究刑事责任等条款,增加违法人违法成本。

  此外,对套牌车,广东已建立全省范围的套牌车黑名单库,将套牌车号码都录入库中,部署全省警力查询。具有号牌识别功能、一旦发现盗抢机动车或假号牌车自动报警的卡口系统,预计年内可投入使用。

  代表在建议中还提出,应增加车牌科技含量,让真牌不易被仿造,假牌更易被识别。对此,公安厅表示,该厅还在研发电子车牌,未来的电子车牌中将置入电子芯片,建立车牌电子身份认证系统,确保车辆唯一性,从源头上根除套牌车。

  记者今晨从省公安厅交管局了解到,由于车牌制作涉及国家统一标准问题,公安厅已就此上报国家相关部门,目前还在等待批复。

2009年8月10日星期一

利用OpenCV进行图像连接

#include <cv.h>
#include <highgui.h>
#include <stdlib.h>

/* プロトタイプ宣言 */
IplImage *combine_image (int num, IplImage ** tmp);

/* メイン関数 */
int
main (int argc, char **argv)
{
  int i, img_num;
  IplImage **img;
  IplImage *combined_img;

  // (1)コマンド引数で指定された画像を全て読み込む
  if (argc < 2) {
    return 1;
  }
  else {
    img_num = argc - 1;
    img = (IplImage **) cvAlloc (sizeof (IplImage *) * img_num);
    for (i = 0; i < img_num; i++) {
      img[i] = cvLoadImage (argv[i + 1], CV_LOAD_IMAGE_COLOR);
      if (img[i] == 0)
        return -1;
    }
  }

  // (2)画像を連結する
  combined_img = combine_image (img_num, img);

  cvNamedWindow ("Image", CV_WINDOW_AUTOSIZE);
  cvShowImage ("Image", combined_img);
  cvWaitKey (0);

  cvDestroyWindow ("Image");
  cvReleaseImage (&combined_img);
  for (i = 0; i < img_num; i++) {
    cvReleaseImage (&img[i]);
  }
  cvFree (&img);

  return 0;
}

/* 画像を連結する関数 */
IplImage *
combine_image (int num, IplImage ** tmp)
{
  int i;
  int width = 0, height = 0;
  IplImage *cimg;
  CvRect roi = cvRect (0, 0, 0, 0);

  // (3)与えられた各画像から,連結後の幅と高さを求める
  for (i = 0; i < num; i++) {
    width += tmp[i]->width;
    height = height < tmp[i]->height ? tmp[i]->height : height;
  }
  cimg = cvCreateImage (cvSize (width, height), IPL_DEPTH_8U, 3);
  cvZero (cimg);

  // (4)ROIを利用して各画像をコピーする
  for (i = 0; i < num; i++) {
    roi.width = tmp[i]->width;
    roi.height = tmp[i]->height;
    cvSetImageROI (cimg, roi);
    cvCopy (tmp[i], cimg);
    roi.x += roi.width;
  }
  cvResetImageROI (cimg);

  return cimg;
}

// (1)コマンド引数で指定された画像を全て読み込む
コマンド引数で指定された画像を全て,カラー画像として読み込む.そのなかに1つでも読み込めない画像がある場合には終了する.

// (2)画像を連結する
あらかめ定義した関数combined_img()を呼び出して,画像を横方向に連結する.この例では単純に画像の上辺を揃えて横方向に連結を行うだけであるが,画像を折り返して連結したり,さらにいわゆる「矩形パッキング問題」をの解を求めることで無駄な領域が小さくなるように連結することもできる.また,このサンプルでは連結する関数combined_image()に画像のポインタの配列を渡しているが,可変引数を利用(stdarg.h)して,combine_image()を次のように定義する方法もある.

IplImage*
combine_image(int num, ...)
{
  va_list list;
  int i;
  int width = 0, height = 0;
  IplImage *tmp[num];
  IplImage *cimg;
  CvRect roi = cvRect(0, 0, 0, 0);
        
  va_start(list, num);
  for(i=0; iwidth;
    height = height < tmp[i]->height ? tmp[i]->height : height;
  }
  va_end(list);
  cimg = cvCreateImage(cvSize(width, height), IPL_DEPTH_8U, 3);
  cvZero(cimg);

  for(i=0; iwidth;
    roi.height = tmp[i]->height;
    cvSetImageROI(cimg, roi);
    cvCopy(tmp[i], cimg);
    roi.x += roi.width;
  }
  cvResetImageROI(cimg);
  
  return cimg;
}

2009年7月1日星期三

车牌识别 - Ask.com Search

http://www.ask.com/web?q=%E8%BD%A6%E7%89%8C%E8%AF%86%E5%88%AB&search=search&qsrc=0&o=0&l=dir

2009年5月12日星期二

为何美国没有史玉柱而中国没有乔布斯

  1.变脸 Face Off

  2.超我 Superego

  3.小王国与大盗

  4.羊驼

作者:申音 《创业家》杂志前执行主编

时光倒流20年。

那是1989,柏林墙倒塌的时刻。整个世界都在关心着冷战的终结、民主的胜利、意识形态的巨变。

但有两个家伙根本不关心政治。

一 个是34岁的史蒂夫。乔布斯,这位"硅谷金童"的人生正处于低谷。在被自己参与缔造的苹果电脑公司赶出来后,他为了复仇创办的Next和低价买下的 Pixar都处于入不敷出、岌岌可危的境地。这位昔日全美最富有的人之一(远远超过那时的比尔。盖茨),私人财产只剩下2500万美元。

而另一个是27岁的史玉柱。刚刚硕士毕业的他瘦得可以被风吹倒。跟那个年代大多数的热血学子们相比,史玉柱没有狂热的政治理想,他一心想的是如何找梯子登上商业舞台。

20年的跌宕过后,乔布斯和史玉柱已分别成为各自国家商界的标志性人物。而当年关心政治的人们如今只关心经济了。

不过,还没有人去认真地比较一下乔布斯和史玉柱。更没有人去思考:为什么美国没有史玉柱,中国没有乔布斯?

这其实是一件很有意思的事情。

精彩摘录:

・两人还是洞悉人性的营销天才。只不过一个靠发掘人性的美好赚钱,而另一个利用人性的弱点来抢金。

・两个理想主义的青年,一个变成了真正的商业梦想家,为了创造未来他不惜调动一切资源。而另一个人转变为纯粹而现实的商人,精确地计算着自己的付出与回报,不再做那些费力不讨好的事。

・中国并不缺乏伟大的商人,比如胡雪岩、张謇、卢作孚,但他们死了,人们如今只记得一些轶事;而熊彼得定义的企业家过去了,他们还能留下激动人心的产品和伟大的公司。

・美国梦的真正含义是什么?是给一个理想主义者提供梦想成真的舞台。那么中国梦呢?是不是逼着一个理想主义者向现实妥协,最终自己成为这个环境的一部分,才能成功?

 变脸 Face Off

  在我看来,乔布斯和史玉柱的人生经历就像是同一个剧本大纲拍出的两部电影,只不过更换了不同的导演和时空场景。

  两人都是本国知识青年里的佼佼者,脑子好使还天不怕地不怕,早早投身高科技领域创业,并在三十岁左右就达到了事业的第一次高峰。

  1983年,年仅28岁的乔布斯登上了《时代》周刊的封面,他被看成是"个人电脑的真正发明者"。1992年,在一个国内十大城市万名青年的问卷调查中,史玉柱名列"最受崇拜的青年人物"第二位,第一名是比尔。盖茨。

  两人都习惯于蔑视陈规、特立独行,带着点邪性的"范",一个永远的黑色套头衫配牛仔裤,而另一个光头红白运动衣。

  他俩都有着与生俱来的商业敏感,是真正的跨界高手。乔布斯在PC、音乐、电影、手机等多个领域都证明了自己的能力,而史玉柱则在软件、房地产、保健品、金融投资、网游等战场纵横驰骋。

  两人都是执着的完美主义者,都能把一个产品做到惊天地泣鬼神。卖了10年的脑白金仍是中国最畅销的保健品,而iPod则占据了数字音乐播放器市场的70%.此外,两人还是洞悉人性的营销天才。只不过一个靠发掘人性的美好赚钱,而另一个利用人性的弱点来抢金。

  或许是因为成功来得太过迅速,他们先后导演过自毁式的高速坠落,幡然醒悟之后,又表现出惊人的自我纠错能力,并重新勾画一条不断向上的弧线。

  曾经有那么一段时间,史玉柱看起来就要成为中国的"乔布斯"。从1989年起,他每一年都要推出一款自主开发的产品,从M-6401桌面排版软件、M-6402文字处理软件到巨人汉卡,再到巨人中文手写电脑、巨人财务软件等等。巨人飞快地成长为一个年产值10亿、利润数千万的高科技集团公司,其年度销售商大会更成为全国规模最大的电脑盛会。

   几乎与此同时,乔布斯只剩下几千万美元的现金,和两个面临着裁员重组的问题公司,他完全可能成为美国版的"史玉柱",比如在加州搞房地产,去拉斯维加斯 开赌场,到中东倒腾石油,或者变成一个精明的股权投资人。但他什么都没有干,只是继续掏自己和别人的腰包来支持两个长期赔钱的公司,固执地等着它们出现转 机。

  某种程度上,乔布斯是幸运的。在美国,他的理想主义总能找到合适的欣赏者,所以他可以咬牙不向现实妥协。像罗斯。佩罗这样的前辈创业家愿意签出支票,并与其分担风险。迪斯尼这样的大公司也同意投资给小公司Pixar,让其制作动画长片并参与分成。

  而史玉柱之所以蜕变成今天的史玉柱,是因为他发现,做软件公司为他意外地赢得了名声,而在中国,名声就意味着许多新的机会。

  在邓小平南巡讲话之后,史玉柱恰逢其时地被媒体和政府发现。很快,他被评为"中国十大改革风云人物"、"广东省十大优秀科技企业家"等,获得了珠海市第二届科技进步特殊贡献奖,一位总把裤带提到胸下的最高领导视察后题词说"中国就应该做巨人"。

  你可以不关心政治,但政治会来关心你。

   为了支持这个"高科技楷模",当地政府以不到当时市价1/4的价格给了史玉柱一块市中心地皮盖楼(350/平米地价,2007年珠海的平均地价已经是 3500/平米)。而在若干位更高层领导的直接鼓励下,他把计划一改再改,从原来的自用变成了商用,从最早的19层变成72层(全中国第一高楼),预算从 2亿变成了12亿元。

  单纯软件业务的回报根本不足以支撑这样高的资金投入,于是史玉柱中断了技术创新,开始卖楼花、运作保健品、搞服装,杀入更加暴利的行业。最后因为由于工程拖期、宏观环境的恶化,导致整个公司的资金链断裂,巨人大厦从此沦为中国最著名的"烂尾楼",直到今天。

  有意思的是,1998年,背负着2亿巨债的史玉柱想要东山再起。他又算了一笔账,搞软件虽然利润很高,但市场相对有限,如果要还清2亿元,估计要10年;保健品不仅市场大而且刚起步,做脑白金最多5年(实际上只用了3年)。

  事实证明,他的选择居然又是对的。

 超我 Superego

  一个人的性格,会成为他今后命运方向的指针吗?

  乔布斯,一个私生子,在中产阶级养父母的宠爱下长大,自卑与自大的双重人格。他的理想主义是与强烈的领地意识、以及咄咄逼人的攻击性混合在一起的。

  乔布斯早年的密友、曾经一起吸毒、一起去印度朝圣的丹尼尔。科特克说:"史蒂夫心中总是装着他的苹果电脑。从更深层次上分析,他的成功是由于其 内心总有一种深切的不安全感,正是这种不安全感使他必须出去闯荡以证明自己存在的价值。另外,由于他从小就知道自己是被收养的孩子,他的行为并不被大多数 人理解……"

  就像《星球大战》中安纳金天行者。这种内心深处的"原力",与加州的硅片、晶体管结合起来,苹果公司诞生了。正如伊甸园里蛇诱惑亚当和夏娃吃下的那个苹果,甜美而带有一点"邪恶"。

  事实上,在苹果电脑早期的发展中,沃兹的技术、马库拉的资本运作、斯科特的管理和乔布斯的远见几乎一样重要,但作为公司董事局主席,乔布斯的个人魅力,让他轻易成为了公众关注的中心,并几乎独享了上市成功后的光环。

  随着创业伙伴们因为各种原因一个个隐退,他越来越认为自己才是"点石成金"的超级人物,可以将个人凌驾于公司之上。硅谷最著名的投资人之一、当时苹果的董事会成员阿瑟。洛克回忆说:"那时,乔布斯刚愎自用,满脑子主意,肆意而为,毫不考虑公司的前途。"

  按照佛洛依德的理论分析,乔布斯的自我(ego)逐渐被本我(id)所控制,他被原力的黑暗面所吸引,并一步步走向公司的对立面,终于被董事会放逐。

  而那个来自安徽小镇怀远,独自闯荡大城市的青年同样拥有着强大的内心世界,激励他的是一种英雄主义情结。

  事实上,史玉柱出生的环境里根本连个晶体管都找不到,当乔布斯和沃兹开始组装第一台计算机的时候,他还只能对着《十万个为什么》自制土炸药。更不用说预判到"一人一台电脑"的产业未来呢?

  对于以嬉皮士自居的乔布斯而言,电脑恰好是能帮助他释放旺盛的Libido(欲望)的媒介罢了,但对于史玉柱来说,电脑则是帮助他改变命运的唯一工具。否则他只能像刘震云小说《单位》里的大学生小林一样,过着按部就班的生活。

  1984年,在乔布斯策划的那则著名广告里,苹果的女模特挥锤砸烂了"Big Brother老大哥"。在PC世界里,谁都知道"老大哥"就是IBM. 1990年,身高1米80、体重不到120斤的史玉柱给自己的新公司起了一个非常响亮的名字――"巨人",他宣布,巨人要成为中国的IBM,东方的巨人。

  这很容易让人联想起2000多年前的刘邦,他在见到秦皇车驾时的感叹,"大丈夫当如是也"。而另一个叛逆者项羽,说的是"彼可取而代之"。

  这种自我赋予又被外界不断强化的使命感贯穿了史玉柱创业的前半程。熟读毛选,青年楷模,大跃进式的增长目标,军事化的企业管理,为自己为城市树立一座固化的丰碑……

  终于有一天,这些使命变成了他再也承受不起的包袱,庞大的"巨人"分崩离析。三年后,在经历了对人性和坏境的彻底反思,一次次自杀的念头和被追杀的恐惧之后,一个完全理性的、成熟的、内心更强大的史玉柱回来了。

  他彻底放弃了珠海时期的企业文化"做中国的IBM","这是非常害人的一个空想,制定了一个很虚的目标。它在鼓励什么,鼓励大家搞大跃进、搞浮夸,让大家心里面很浮躁,对于现实没有什么帮助,现在我就不搞那个宏伟的目标了,我觉得实用最关键。"

  新的文化很直白,"说到做到、只认功劳、严己宽人、敢担责任、艰苦奋斗。"这是在黄山太平湖,柳传志教给他的。

  史玉柱不再把自己当成一个"知识分子",他坦言"胆子越来越小","只做好商人必须做的事情"。在一个只有"锦上添花"没有"雪中送炭"的现实 环境里,他找到了正确的生存策略。让企业永远保持充沛的现金流,负债率低于30%;只做高利润率又不跟政府沾边的生意(这样的生意常常处于灰色地带);既 然银行不肯借钱,就干脆参股银行;及时把公司变现,玩资本财技(上海健特借壳、巨人上市融资近10亿美金);跟有合法伤害权的媒体保持恰当的距离又给予充 分的好处。唯一遗憾地是,他对创新不再有兴趣。

  乔布斯在"荒野"中被放逐的时间更长。尽管他的坏脾气改变有限,但他学会了宽容和谦卑,知道怎么信任和授权给优秀的人,比如Pixar总裁约 翰。拉赛特,乔纳森。埃弗。这个设计白色马桶的英国人,后来在苹果公司担当设计副总裁。他学会了合作与开放,让微软入股,用英特尔的芯片,建立了 iPhone开放平台。

  人们的第一次成功往往根源于欲望和运气,而第二次乃至更多的成功则需要智慧和自控。

  无论乔布斯还是史玉柱,最终都通过克制本我,实现了自我超越(Superego),这才是他们真正的魅力所在……

  不同在于,两个理想主义的青年,一个变成了真正的商业梦想家,为了创造未来他不惜调动一切资源。而另一个人转变为纯粹而现实的商人,精确地计算着自己的付出与回报,不再做哪些费力不讨好的事。

小王国与大盗

  其实,道德感与一个人的成功并无太大关系。

  在硅谷,与乔布斯的天才同样闻名的是他的傲慢自负、喜怒无常、抠门小气、巧取豪夺、冷漠无情。前时代周刊的记者,日后红杉投资的大佬迈克。莫里茨,曾把在乔布斯威权统治下的苹果称之为"小王国"(Little Kindom),这是一个非常准确的描述。

  专制带来效率,难得乔布斯的个人品味和商业直觉又皆属一流。不过,如果乔布斯生在中国的话,一个传统意义上的"臣民社会"而非"公民社会"里,他的自大会惯坏他,人们会完全屈服于他的权威,听从他的指令,直到他最终把公司毁灭,就像牟其中、唐万新、孙宏斌这些人一样。

  但他生在美国,民主社会和资本主义机制里有种天生的"对冲"力量。董事会、股东大会、机构投资者像一道道紧箍咒约束着他。默认的游戏规则是,只要你有能力做大公司的价值,就算你是个暴君也无妨;反之,等待你的就是自动下台或者政变逼宫。

  在中国,大多数民营企业和其创始人的命运,缠绕得太紧密了。权力的垄断和资本的垄断密不可分。我们没有发明出一套"防火墙"的制度,能将创始人的个人行为、个人信用与企业分离开。

  于是,考察企业家自身的道德品质,变成了一件无比严肃又异常困难的事情。

  相比大多数国内同行,史玉柱,堪称私德无缺。他信守承诺、有情有义、高度放权、慷慨分利,在公司破产之后,其核心团队成员仍不离不弃,甚至拿自己的钱来补贴公司。而他二次创业成功后,主动还债两亿的壮举,也足以表率。

  在后来的巨人集团内部,史玉柱甚至成功地创造了一个"民主管理"的小环境。他成立了七人执委会,任何决策都必须多数票才能通过。除了核心产品,日常人财物管理全部放手,文秘出身的总裁刘伟,事实上已扮演了CEO的角色。

  在《庄子。外篇》中,大盗盗跖(传说是贤人柳下惠的弟弟)与他的徒弟谈论"盗亦有道"。跖曰:"夫妄意室中之藏,圣也。入先,勇也。出后,义也。知可否,智也。分均,仁也。五者不备而能成大盗者,天下未之有也。"

  五者皆备的史玉柱,在现实的商业世界里扮演一个"Big Brother"的角色,通过排山倒海的营销攻势,淹没一切反对声音。居高临下地制订规则,利用消费者的无知、贪婪和权力欲,还有既懒惰而又想过瘾的心 理,设计出各种圈钱的工具(详见《南方周末》的文章《系统》)。

  史玉柱直言,"商业是什么?商业的本质就是在法律法规许可范围内获取最大利益。我是一个商人,做的事情就是在不危害社会的前提下为企业赚取更多利润。要一个商人又要赚钱又要宣扬道德,那不是商人,而是慈善家。"

  而视道德为无物、几乎从未在慈善事业上捐出一毛钱的乔布斯,从iMac、iPod到iPhone,他所推动的一切都是在迎合"YOU"这个消费 者主权的时代。过去唯我独尊、四面树敌的苹果,开始走向一种新的理念:用户要的不是技术,甚至不是电脑,而是利益的最大化和体验的最优化。

  他不再把苹果视作一家电脑公司,归根结底,"我们骨子里就是一家消费品公司,你的生死存亡掌握在消费者的手中。他们才是我们关注的对象。我们觉得自己的工作就是对整个用户体验负责。如果表现不及格,那就是我们的错。错就一个字。"

  这是不是"民主"精神在商业里的最好体现?

 羊驼

  1995年,史玉柱名列《福布斯》中国百富榜第8位,当时他是唯一一个搞高科技的企业家。

  2009年,史玉柱仍然是《新财富》500富人榜中,排名最高的与新经济有关的富豪。在他后面的是李彦宏、马化腾、施正荣等。

  看《枪炮、病菌与钢铁――人类社会的命运》一书,美国生物历史学家贾雷德。戴蒙德谈到了一个有趣的问题:为什么14种被驯化的大型哺乳动物有13种来自欧亚大陆,为什么欧亚大陆的牛、马、绵羊、山羊、猪,而不是南美洲的羊驼,成为最终广泛分布于全世界的牲畜?

  他的答案是环境。最主要的原因是,欧亚大陆由于其广大的面积和生态的多样性,拥有最多的可驯化的候补动物。而这些驯化动物又促进了欧亚大陆民族的繁荣,进而帮助后者征服全世界,而这些动物跟随着征服者遍及其它大陆。

  在商业世界里,存在着相似的问题。在过去100年中,为什么以美国为首的少数4-5个国家,而不是剩下的100多个国家,集中产生了几乎所有对社会进步至关重要的创新?几乎定义了这个星球所有成功的商业模式?

  史玉柱和乔布斯,两个同样具有商业天赋,且性格背景相似的创业者,最终一个变成垃圾商品的成功贩卖者,而另一个成为这个星球上最具创造力的企业家。

  当乔布斯在斯坦福的演讲"Stay hungry,Stay foolish"可以影响全世界青年的时候,史玉柱在《赢在中国》里的语录却只能供本土创业者琢磨。

  每个社会里都可以孕育出潜在的商业天才。而真正的商业天才,在任何环境下都有可能成功的。

  但在某些环境中成长出来的商业,就像南美安第斯高原的羊驼。看着很可爱很善良,经济价值很高,但是却无法大规模的繁衍和推广。

  美国梦的真正含义是什么?是给一个理想主义者提供梦想成真的舞台。那么中国梦呢?是不是逼着一个理想主义者向现实妥协,最终自己成为这个环境的一部分,才能成功?

  "当巨人一步步成长壮大的时候,我最喜欢看的是有关成功者的书,当巨人跌倒之后,我看的全是失败者的书,希望能从中找出站起来的力量。"史玉柱说。

  在2000年CCTV的一次《对话》节目中,史玉柱谈到了所谓"理想的状态,就是说今后市场经济发育到一定时候,法制环境建立,然后就是政企脱 钩,我最希望的是一个什么样的环境:就是一个政府包括国家领导人,省级领导人,包括地方领导人,他做他的事,我们企业做我们企业的事。就等于你这个领导人 你定游戏规则,然后我们这些人就按你游戏规则做事。最好是不要有什么太多的接触,我就是这个意思。巨人大厦这个问题上,不管哪一级的领导人没有任何的责 任,责任全是我的。"

  当一个人把所有的问题都归咎于内因的时候,是因为他意识到自己改变不了外在环境,能改变的只有自己。

  其实近代以来,中国并不缺乏伟大的商人,比如胡雪岩、张謇、卢作孚,但他们死了,人们如今只记得一些轶事;而熊彼得定义的企业家过去了,他们还能留下激动人心的产品和伟大的公司。

  今天,中国可能是唯一一个创业热情甚至比美国更高的国家。各级政府鼓励"以创业带动就业",启动创业板,政府成立风险投资基金。这些都是好事。 但必须承认,我们仍然是一个缺少知识产权保护,国家垄断资源和与之相关的分配权,资本更青睐与权力结合而非知识,优秀的年轻人希望跻身公务员之列的社会。

  哪些被乔布斯所激励的创业者们,最终会不会变成为史玉柱的信徒呢?

  这是一个问题。



2009年5月8日星期五

C++笔记-指针和自由存储空间[转]

作者:Ackarlix
 
1.申明和初始化指针
int * p1;
这表明 *p1的类型为int 。由于*操作符被用于指针,因此p1变量本身必须是指针。可以这样说,p1是指针(也即是地址空间),*p1是int,而不是指针;
顺便说一下,*操作符两边的空格是可选的.传统上c程序员用int *p1,而很多c++程序员用int* p1;
可以在申明语句中初始化指针,在这种情况下,被初始化的是指针,而不是它所指向的值.也就是说下面的语句:
int p1=5;
int *pt=&p1;
将pt(而不是*pt)的值设置为&p1.
2.指针的危险
极其重要的一点:在c++中创建指针时,计算机将分配用来存储地址的内存,但不会分配用来存储指针所指向的数据的内存.为数据提供空间是一个独立的过程,忽略这一步无疑是自找麻烦:
int *p1;
*p1=12;
p1确实是一个指针,但他指向那里呢,上叙代码并没有将地址赋给p1,那么12将被放在哪里呢,我们不知道.
警告:一定要在对指针应用解除引用操作符*之前,将指针初始化为一个确定的适当的地址.这是关于使用指针的金科玉律.
3.用new来分配内存
指针真正的用武之地在于,在运行阶段分配未命名的内存以存储值,在这种情况下只能用指针来访问内存.
下面是这样的一个范例:
int *p1=new int;
*p1=1000;
数组:
int *p1=new int[10];
p1代表的是地址,而*p1则代表一个值;
对于数组p1来讲,new操作符返回第一个元素的地址,在这个例子中,该地址被赋给指针p1;
计算机可能会由于没有足够的内存而无法满足new的请求,在这种情况下,new将返回0;
4.用delete来释放内存
delete操作符,他使得在用完内存后,能够将其归还给内存池,这是通向最有效的使用内存的的关键的一步
int *ps = new int;
...
delete ps;
这将释放ps指向的内存,但不会删除指针ps本身,也就意味着可以将指针ps重新指向另外一个新分配的内存块;
注意:一定要配对的使用new和delete,否则将发生内存泄露,也就是说被分配的内存再也无法使用了.
int *p1=new int[10];
...
delete [] p1;
5.指针与数组
#include<iostream.h>
int main()
{
   
double *p3 =new double
 [3];
   p3[0]=0.2;
   p3[1]=0.3;
   p3[2]=0.5;
   cout<<"p3[1] is "<<p3[1]<<endl;
   p3=p3+1;
   cout<<"now p3[0] is "<<p3[0]<<endl;
   p3=p3-1;
   delete [] p3;
   
return
 0;

}
下面是程序的输出:
p3[1] is 0.3
now p3[0] is 0.3

2009年5月6日星期三

谷歌创始佩人奇密歇根大学毕业典礼演讲全文

Google的共同创始人之一Larry Page本科毕业于密歇根大学计算机系,是2009年密歇根大学毕业典礼的演讲嘉宾。他说:I know it seems like the world is crumbling out there, but it is actually a great time in your life to get a little crazy, follow your curiosity, and be ambitious about it. Don't give up on your dreams.

演讲全文及录像可在Google新闻稿网页上获得。

http://www.google.com/intl/en/press/annc/20090502-page-commencement.html


  以下是演讲全文:

  09级的同学,首先我希望大家站起来,向支持你们的亲朋好友挥手致意!展示你们的爱!

  今天来到这里我很荣幸。

  请等一下。我知道,刚才那句陈词滥调,大家可能在想:每一位毕业典礼的演讲人都会说――很荣幸,但就我而言,的确字真意深,因为我来到这里,要比你们所知的大多数人都更为特别而亲切。我想告诉大家个中缘由。



  很久以前,1962年9月,这座校园里有一家名为史蒂文的消费合作社。该合作社有一间厨房,学生志愿者每十年左右来打扫厨房顶。当年有位名叫格洛莉亚的女大学生,爬上了高高的梯子,努力打扫肮脏的天花板,地板上站着一位名叫卡尔的寄宿生,他钦佩地看着这位姑娘,这就是他俩的第一次见面。他俩就是我的父母亲。我想你们会想,我就是厨房"化学实验"的直接成果,就在这里,密歇根大学。我的母亲今天和我们在一起,我们应该去找到他们的相遇之处,并且在天花板上镶嵌一块铭牌,上刻"感谢爸爸、妈妈!"

  今天我们全家都来到了密歇根大学:我、我哥、我父母亲。我父亲在密歇根大学获得了三个半学位,他的博士学位是通信科学,因为他们认为计算机只是一时热门,他是44年前获得的。他与我母亲为之作出了巨大牺牲。在抚养我刚出生的哥哥时,他俩经常一分钱掰成两半使。母亲用手敲出了父亲的论文,我戴的这顶天鹅绒帽是我父亲的。这张文凭,和你们即将拿到的一样,是我父亲的。

  我的祖父在密歇根弗林特的雪佛兰工厂工作,他是装配线上的一名工人。他曾开车带着他的两个孩子来到这里,并告诉他们:这是你们今后要上的大学。他的两个孩子都是从密歇根大学毕业的,这就是"美国梦"。他的女儿,贝芙丽,今天也和我们在一起。我的祖母经常扛着一个大铁锤,那时工人们静坐罢工用以保护自己。当我小的时候的时候,我就用那个大铁锤在地上砸树桩什么的。现在人们不再需要扛着个笨重的大家伙保护自己了,这很好。但今天万一出了状况,反正我带着它来了。

  我父亲后来成为密歇根州立大学的教授,我也是个非常有运气的孩子。教授的生活是非常有弹性的,他有大量的时间培养我,哪里有比在学校培养孩子更好的地方呢?

  我想告诉大家的是,我来这里不仅仅是回家看看。我难以表达我来这里的自豪感,与我母亲、我哥哥、我的妻子露西,还有你们大家。我为你们感到骄傲,为你的亲朋好友骄傲,我们都加入了伟大的、大密歇根家庭,我感觉它是我生命的一部分。

  我想告诉大家的是,我很清楚你们坐在座椅上,听着一个老家伙废话连篇地做毕业典礼演讲的感受,不要急,我会简短地说。

  我有个关于追寻梦想的故事,更准确地说是一个发现梦想成真之路的故事。

  你们知道,午夜甜梦中醒来是什么感觉吗?如果床边没有纸笔把梦记下,而第二天一早忘个精光又会怎样吗?

  当我23岁的时候,我就做过这么一个梦。我猛地醒来,我想:如果我能把整个互联网下载下来,仅保存着链接......,我抓起一支笔开始写,有时候从梦中醒来是非常重要的。我花了一个午夜的时间描绘了细节,并确信它将所有作为。不久后,我就告诉我的导师,特里・温诺格拉德(Terry Winograd),要花两周时间下载整个网络,当时他点了点头,其实他完全知道要花更长的时间,但他很睿智,并没有告诉我。年轻人的乐观主义通常不可低估!令人吃惊的是,我没有想过要打造一个搜索引擎。这一概念甚至没有进入我脑海。但后来,我突然想到了更好的给网页排序的方式,以形成真正的搜索引擎,谷歌

  就这样诞生了。当有伟大的梦想出现时,抓住它。

  我在密歇根大学上学时,老师教导我如何梦想成真。我知道这听起来有些滑稽可笑,但我是从一次名为领导力成长的培训项目中得到了启发。该项目的口号就是"漠视不可能"。这个项目激励着我追寻一个疯狂的想法:我想在校园内建造一套个人快速运输系统以代替公交。这是这种解决我们交通问题的未来方式。我直到现在还想着很多有关交通的问题,你不要放任梦想,而要把它当作一种习惯去培育。现在人们花大力气干的很多事情,如做饭、保洁、开车,今后只会占用很少的时间。也就是说,如果我们"漠视不可能",就能找到解决方案。

  我认为,实现雄心勃勃的梦想更为容易,我知道这听起来是一派胡言。既然没有人能疯狂到做这件事情,你也不可能完成。但最优秀的人就希望挑战。这就是谷歌所面临的。我们的使命就是组织全球的信息,并且让它到处能接受,并发挥作用。这难道不会让大家兴奋吗?但是我们真的不想启动谷歌,因为谢林和我都太担心拿不到博士学位。不过我们后来刷爆了三张信用卡,从一辆敞篷货车中买了硬盘,这也是谷歌最早的硬件。如果用一句话总结,如何改变世界,那就是在某种极度兴奋的事业上发奋努力。

  当我做博士的时候,我像做三个方面的项目。感谢我的导师,他对我说,"为什么你不做做网络呢?"他给了我一些非常好的建议,因为即使在1995年,网络正随着人们和活动的增长而增长。技术和网络能使你变懒。变懒?我的意思是三人组合写的软件就可以让数以百计的人使用并受益,但三个人可以一天接上百万次电话吗?找到撬起地球的杠杆,你就能变得更懒。

  总而言之,我知道这个世界看起来已支离破碎,但这是一个伟大的时代,在你的一生中可以疯狂些,跟随你的好奇心,积极进取。不要放弃你的梦想。世界需要你们。

  如果我的父亲能活到今天,我想他最开心的莫过于看到我、露西和我们的孩子在一起。我向他会因为我没能拿到博士学位而恼怒。我的父亲是一位对新事物充满洞悉力和激情的人,如果他活到今天,他一定会有新的想法,如果他今天也能来到这里,将是他一生中最为荣耀的一天之一。

  感谢妈妈,感谢露西,感谢你们大家。

  拉里・佩奇

  拉里・佩奇(Larry Page) 1968 年出生在美国密执根州,是Google的创始首席执行官,带领公司发展成为了拥有 200 多名员工的盈利企业;2001年4月转任现职(产品总裁)。他目前仍与Eric Schmidt和 Sergey Brin 一起共同负责 Google的日常运作。

2009年5月5日星期二

2009年5月2日星期六

Fwd: 激励系列――立即开始-褪墨


  • 立即开始

总有些时候你不想出门跑步、做预算或做其他当日计划中的任务。好了,别再想它有多难,过程将有多漫长,只要告诉自己该开始行动了。

我有一条原则(非原创),马上穿好跑鞋,关上门,出发!如此,一切就水到渠成。只有当你坐在房间里想着跑步就感到累时它才是难的,一旦行动,永远不像你想的那样难。这一条技巧我屡试不爽。

弥缝添:我还记得希拉里在去年的演讲中说过:"时间太精确,每当你回头望时你就浪费了继续前进。"又还记得乔布斯在斯坦福大学做演讲中说:

当我十七岁时,我读到一则格言:"把每一天都当成生命中的最后一天,你就会轻松自在。"这对我影响深远,在过去33年里,我每天早上都会照镜 子,自问:"如果今天是此生最后一日,我今天要干些什么?"每当我连续太多天都得到一个"没事做"的答案时,我就知道我必须有所改变了。

提醒自己快死了,是我在人生中下重大决定时,所用过最重要的工具。因为几乎每件事-所有外界期望、所有名誉、所有对困窘或失败的恐惧-在面对死亡 时,都消失了,只有最重要的东西才会留下。提醒自己快死了,是我所知避免掉入自己有东西要失去了的陷阱里最好的方法。人生不带来,死不带去,没什么道理不 顺心而为。

人生苦短,人应该要在有限的时间里去抓紧实现自己的目标!保尔・柯察金说:"人的一生应当这样度过:当他回首往事时,不会因虚度年华而悔恨,也不会因碌碌无为而羞耻。这样,临终前他就可以自豪地说:'我已经把自己整个生命和全部精力都献给了世界上最壮丽的事业――为人类的解放而奋斗。'"那么,你呢?

原文:Top 20 Motivation Hacks - #15 - Zenhabits

译者:Sylvia(GTD翻译小组核心成员)

2009年4月29日星期三

An Approach to Korean License Plate Recognition

申明:来源于网络,引用请注明原作者!

论文,下载

firefox和IE下,下载后,是没有扩展名的一个文件(Chrome下正常),拖到你的PDF浏览器后即可打开。
部分内容来自网络,所引内容并不代表认同其观点。本博内容如有不当或侵犯权益,请来信告知!



A Novel Binarization Approach for License Plate

申明:来源于网络,引用请注明出处!

论文:下载
firefox和IE下,下载后,是没有扩展名的一个文件(Chrome下正常),拖到你的PDF浏览器后即可打开。
部分内容来自网络,所引内容并不代表认同其观点。本博内容如有不当或侵犯权益,请来信告知!




2009年4月28日星期二

Google Chrome 背后的天才:不在美国工作的Google员工[转帖]

导读:本文为《金融时报》网站发表的一篇人物特写,文章的主角是 Google Chrome V8引擎的开发者拉斯巴克(Lars Bak)。他是一个编程天才,却远离计算机世界的核心,在丹麦为Google工作。

Lars Bak在他的农场

远离计算机世界核心

奥尔胡斯(Aarhus)是丹麦第二大城市,也是日德兰半岛(Jutland)的非正式首府。在该市郊外5英里的地方,有一座改造过的农舍。里面有 宽敞的木地板和拱形的顶(曾经是马厩的一部分),在距离DVD播放机不远的地方摆着一个大的棕色皮沙发。从外面看,这座房子看上去仍略显陈旧:粗糙的石子 路,凹陷得小窗;但正是这里孕育了互联网未来的关键部分之一。

从哥本哈根开车到这里的路程比预期的更漫长,房子也很难找。房子的主人叫拉斯巴克,是一个年轻的编程天才,他之所以把家安在这里是因为他非常不愿意让别人找到自己。他的阿尔萨斯牧羊犬,米奇,见到我们也有些受惊:主人把它看住之后我才敢下车。

那是丹麦寒冷十二月的一天。这地方比爱丁堡还靠北,但天色如同东英格兰沼泽地的一样,灰蒙蒙一片。我们握手的时候,巴克看上去很不自在,我觉得他好 像压根不想让我靠近他的居所。不过,我们还是走进了放有棕色皮沙发的拱形顶房子里。现在这里是家庭影院,之前曾是他编程的办公室。温度只比外面稍高一点。 我拿出笔记本的时候还在瑟瑟发抖。好吧,您想了解些什么呢?巴克发问了。我们有四个小时的采访时间。

拉斯巴克并非家喻户晓的名字至少这个拉斯巴克不是。在丹麦还有一个拉斯巴克更出名,那是一位职业自行车选手。但是这位巴克可比任何运动员对你生活的影响更加深远。他最近的计算机软件程序V8是Chrome浏览器(谷歌商业计划的关键)的一部分。

网页浏览器都不赚钱,无论是IE、Safari、还是火狐。那么,新浏览器对谷歌的重要性何在?为什么要投入时间和精力在免费产品上,而这些又不能 给公司带来收入?答案不在浏览器本身,而在于它能访问的内容:也就是网络应用程序。这些才是给谷歌带来收入的东西。比如,该公司对Google文档寄予厚 望,这是一套在线办公软件,和微软的当代企业工具桌面版Office程序(Word, Excel和PowerPoint)类似。但为了更好地体验获得这些程序和其他在线应用,用户需要更好的浏览器,以便更好地运行相关代码。我们很多人已经 在使用网络程序了,比如Hotmail, Yahoo邮箱或Gmail,不过它们都相对简单,比起一般的桌面软件,它们的复杂性相形见绌。

浏览器已经无法处理日益复杂的网络应用。就好比是很多高性能的跑车跑在辙颠簸不平的路上一样。但是没有哪家跑车制造商会出钱修路,毕竟自己修路对手 也会沾光,更不要提高昂的费用。软件开发的成本不高,但依然存在竞争问题。不过谷歌说它不在乎,它说:没有好的浏览器,大家伙儿都会遭殃。

通过网络访问复杂程序的能力被称为云计算,并且谷歌并非唯一一家声称领先的公司。即使微软,桌面应用软件和操作系统的代名词,也在跃跃欲试。史蒂夫鲍尔默,微软的首席执行官,已经承诺研发在互联网中运行的操作系统他称之为视窗云,不过要想真正实现云计算,必须改进浏览器。

恰在此时,巴克出现了。这个丹麦人首次在加州硅谷引起人们的注意是在1991年,那时他在Sun公司工作,后来成为业界最佳程序员之一。1994 年,他离开Sun,帮助创建了Animorphic系统,该公司后来被Sun收购。再次回到Sun之后,巴克开发了后来成为Java HotSpot(行业标准计算系统之一)的程序。

可是2000年初,他却离开了计算机世界的核心,回到了丹麦,搬家是为了幸福生活,为了他的女儿们(他想让她们上丹麦语学校),为了自己的身心健 康。美国的开发者社区工作很紧张,生活方式不健康。当巴克回到丹麦时,两个月之内他减了20斤(多亏了美国的阿式饮食疗法【Atkins diet】),而且再也没有反弹。

享受工作和生活两个世界中最好的东西

2002年,巴克在奥尔胡斯创建了一家名为OOVM的公司。2004年,他将公司卖给了一家瑞士公司Esmertec,然后又在该公司干了两年,帮 助两个公司的融合。离开Esmertec时,他并不特别想找新项目:他有足够的钱养家糊口,也有各种打发时间的方式,包括粉刷农舍的计划。他估计得要一年 时间。

然后Google的电话就来了。对于Google,巴克是不二选择他编写了JavaScript引擎(Chrome的核心部分)。对于巴克,为 Google工作就是 小菜一碟。我不在乎当什么高级经理。我在乎的是推动技术边界。巴克接受了这份工作,但不会回到加州。事实上他从没打算再次回加州虽然谷歌的人性化办公室闻 名远近,餐厅里的美食,还可以免费理发,巴克却宁可在家工作离总部5000英里,相差9个时区。谷歌做好了信任我的准备。他们知道我不会消磨时间。重新装 修农场的计划要搁置一下。最后,他说,我只有14天的时间(粉刷)。涂料还留着呢。

巴克在日德兰半岛的居家办公室,他和妻子,孩子还有宠物阿尔萨斯牧羊犬米奇住在一起

巴克开始工作,在现在是家庭影院的地方建立了办公室。农舍是围绕院子修的,家就在办公室对面。每天,他走过石子路到办公室,然后开始写代码。每天结 束时,他又穿过院子,走回家,把工作彻底放下。在这些行走之间,他投入到浏览器开发中,有了这种浏览器,其他人就有更多机会做他所做的事:在家工作,与总 部连接,所需要的全部工具就是互联网的力量。

巴克也许是个计算天才,但他是上大学之后才开始接触计算机的。高中的计算机室又黑又臭,只有书呆子才去,他说,我喜欢运动。弹板跳水特酷。我后来才成为书呆子的。

在《局外人》中,Malcolm Gladwell探讨了天才和成功的本源,作者陈述了这样的观点,你需要苦练10000小时才能在所选择的领域里真正成功。我问巴克,你做足10000小 时了么?我能看出来他觉得这压根不相关;他对此也不感兴趣。我只是很高兴大器晚成,他再次强调,而不是年少就成名。

也许是因为大器晚成,巴克从不热衷于传说中那种靠咖啡提神彻夜写编程代码的故事。不过这可能也缘于巴克精通的程序类型:虚拟机,这是计算机科学家 Gerald Popek 和他的项目伙伴Robert Goldberg早在1970年代所探讨的一个想法。虚拟机器名副其实真正机器的计算机世界版,能够在单机程序或整套程序上使用。Chrome浏览器属于 后者。V8,巴克的虚拟机器,编辑不同程序通用的代码,以便减少冗余,让网络应用程序更快运行。

虚拟机是头怪兽,Bak说,没有完美的解决方案,你只能为最佳时机进行优化。有很多技术含量在其中。这是个漫长的游戏,你玩不完的。

工作量是恒定的,他补充说,所以我总要停下来吃饭。你可以有正常生活。对巴克而言,这意味这家庭和隐私。工作/生活平衡的问题在谈话中一再冒出来虽 然他不反对美式生活,但显然他更喜欢丹麦的生活方式。在美国,需要进取和格外自信。欧洲的生活方式则不然。而在美国,你能有晋升机会,能与技术发展保持同 步。在欧洲,你就只能做办公室当经理,没有活可干。通过在丹麦为谷歌工作,巴克意在一箭双雕,同时享受两个世界中最好的东西。

巴克将他的V8项目成为笑话:V8引擎就好像汽车镀铬发动机罩下面安装的那个东西 而Google就那么酷不论项目最初的名字是什么,以后不会改变。不会因适应市场营销而改变名字。

独立并从空白开始开发工作

从开展谷歌项目的第一天,巴克就征募了他以前的学生卡斯帕伦德的帮助。伦德年轻也更外向,他同意来农舍上班。他还有其他作用让巴克娱乐。伦德和他那 竞争力很强的老板习惯于用乒乓和Wii网球来调剂工作。谁的乒乓能赢,我在想;巴克回答说:问卡斯帕。哦,那么卡斯帕能赢喽。不是。稍后,他透了口风:他 打得比我好,不过还是我赢。他们的关系已经从师生变得更像同事,但又不完全是;有朋友把这比作巫师和学徒。

许多程序都是用以前的版本或相关代码创建的,但V8是白手起家一片空白。后来当我在丹麦之旅中遇见伦德时,他很乐于强调这一点:这是最纯粹的编码形式。巴克显然有共识在伦德说话的时候,他不停地微笑。

随着V8深入,项目扩展,巴克和伦德把编写代码从农舍搬到奥尔胡斯的一座大学,巴克在此任教。骑车需要30分钟,把通勤当作锻炼和巴克将工作和生活平衡的哲学不谋而合。

大学办公室虽然在距离上与加州山景Googleplex总部遥遥相隔,不过一看就是谷歌的风格。在进门的地方,五颜六色的椅子和小布袋到处都是,然 后是两个主要房间,有10来个人在这里工作。不过没有餐厅,只有厨房,里面有坚果贩卖机,还有一冰箱的瓶装水和健怡可乐。巴克引入了无糖政策:不是完全无 糖你可以从水果里得到自然糖分。不过没有巧克力,没有明目张胆的糖果而已。

每个人愿意接受这里的管理制度,见证了办公室的忠诚度。好像所有人都骑自行车来上班,下午5点左右就离开。我们开始的也早,累了还工作没有意义,所 以我们就回家,伦德说。这是两小时前巴克在他的农场上给我讲述的工作生涯的逐字描述。这令人心服口服。但和谷歌的人沟通怎么办?当巴克、伦德和其他成员在 奥尔胡斯时间下午5点下班时,旧金山还不过早上9九点。他们是利用硅谷24小时工作的趋势吗,还是并不需要和总部保持联络?伦德说大多数时候用电子邮件就 可以了。我采访的当天,在其他同事下班之后,巴克需要参加电话会议。他努力不把情绪流露出来,但很显然,他并不想参加。

编程可以是很单调的事情。虽然巴克和伦德紧密合作,但还是有种遗世独立的感觉。你写代码,测试,修改,再写,周而复始,直到你得到自己需要的。对于巴克,这很简单,也很隐蔽。然后,出于某种原因,外界就想进来干扰想要认识你,想要了解你的工作。

巴克很看重自己的隐私。在家给他照相也让他明显不自在。但当我告诉他,把他的名字输入谷歌搜索引擎里,得到的都是一页又一页和他同名的自行车手,这肯定遮挡了他本该有的光彩,他耸耸肩,我老了,不在乎别人说我什么。

真正让他恼火的是大家误会他的工作或按他的说法,技术。他举了一个例子,在有篇关于他的文章里,记者混淆了Java和 JavaScript(前者是可以在线进入的独立程序,而后者是依靠浏览器的脚本语言)。对此我们都笑起来,在那么一瞬间,我觉得巴克和我有了默契。

世界以非同寻常的方式发现了Chrome。故事是通过谷歌早前发布的专门漫画传出去的显然犯了个错误。公关忙成一团来支持这个消息,匆匆忙忙地举行了电话会议,发表博客日志解释到底发生了什么,随后又是新闻发布会,然后在Googleplex举办产品展示。

在群情激动地讨论为什么谷歌要发布新浏览器,泄密是不是有意而为之时,认真看漫画的人寥寥无几。虽然是由Scott McCloud(也算是漫画界的传奇人物)编撰的漫画,这可是艰难的工作。讨论的是Java Script (不是Java哟!),CPU和存储器漏洞大多数都不想知道的计算细节。但是这漫画却显示了Chrome特性背后的思想理念,还有独立小组解决拼图各个部 分的方式。巴克和伦德最初出现在第二页上,不过是到了大约三分之一的时候,在第13页,才有对丹麦V8团队的介绍,解释了这个虚拟机器的开发,不过没有提 到为什么V8距离谷歌总部那么远,隔着大陆和海洋。

伦德和巴克喜欢这个漫画。在奥尔胡斯办公室里,整幅漫画都用相框框起,挂在墙上。开始,我觉得这想法很怪异,巴克说。但是我意识到这太有才了。与其他白皮书相比,这要强10倍。大家都觉得这很有趣。

我怀疑他们俩都喜欢它的原因是它平等地对待巴克和伦德虽然与巴克相比,伦德可算是个大块头。让我看上去像个15岁的男孩,巴克说。他矜持地微笑了。

在刚推出的100天内,Chrome就已经吸引了一千万用户。虽然这个数字很震撼,可也只代表在线浏览器使用的百分之一。它还需要假以时日方能与火 狐、IE及其他产品抗衡。去年12月,谷歌宣布Chrome已经结束研发或Beta(测试)阶段,准备在某些个人电脑上作为预选安装的浏览器发行。这样可 以迅速增加用户数量。而且,欧洲委员会与微软就其IE浏览器如何并入视窗(Windows)操作系统所产生的反托拉斯战争以及其他纷争可能会给谷歌这样的 竞争者占领市场的机会。

抛开法律和市场份额不说,技术挑战已经存在了。微软不得不创建比V8更好的东西,巴克说。很多技术观察家很怀疑他们能够在短时间内完成:在测试 中,V8处理JavaScript的速度比最常见的IE浏览器要快56倍。我们一开始就是高标准,巴克说。随后谦虚地加了一句:还算成功啦。

即使Chrome漫画没有让巴克迅速成名,他的知名度在这几个月中也迅速增加。越来越多的人想了解Chrome背后的这个人。他宁可写虚拟机器,也 不愿意管理奥尔胡斯办公室,而且他宁可做其他所有事情,而不是和记者谈话,可那是游戏的一部分,他接受了。话虽如此,他离Facebook的创建者马克 扎克伯格或比尔盖兹还有很远的距离。我问,在漫画出来之后,你有没有收到粉丝的邮件?他笑了:没。不过我也没有收到抱怨的邮件。

背景知识介绍:浏览器大战

互联网在其短短历史中,已经看到各种浏览器的产生,但是主角只有四个:网景(Netscape)导航器,IE浏览器,火狐(Firefox)和Chrome。

说到普遍使用,网景浏览器是第一。每个人都用过导航器部分原因是没有其他正儿八经的替代者而且它用起来不错。然后是微软的IE浏览器,该产品是公司主导产品Windows操作系统的一部分,从而带动着它的增长。截止1998年,IE在使用方面已经取代了网景。

微软被指控违反了反托拉斯法,但那时已经太迟。IE控制了90%的市场份额,虽然有官司在身,时至今日,它还是以默认浏览器的身份安装在世界上绝大多数个人电脑上。网景被美国在线(AOL)收购了,在历经波折之后,于2007年停止研发。

要不是开源社区,事情到此就结束了。开源软件曾是(而且在某种程度上仍然是)微软之鞭。总裁史蒂夫鲍尔默曾说Linux的开源操作系统是癌症。此类 软件由软件开发者团队的人不断改善,他们这么做基本上是什么都不为。劳动果实通常都免费分发。1998年,网景将导航器的代码转换为名为 Mozilla的开源项目火狐正是从此演变而来的。

火狐占了IE市场份额的20%,而且仍在增长。它的众多属性如分页浏览和读取设置在其他产品如浏览器Opera上出现得其实还更早,但是得益于口碑 相传、出色的市场营销和火狐的对于技术水平较高用户的吸引力,它已然成了IE最强劲的挑战者。而且,它是开源的,第三方开发者可以通过创建新应用程序扩展 火狐的容量,让它更加强大。

在2008年谷歌推出Chrome之前,浏览器市场好像成了IE和火狐二者之间的竞争,当然也得提及以Mac为基础的Safari。

Chrome仍然只有百分之一的浏览器份额,但这会增长。有了拉斯巴克的V8引擎,Chrome简直如虎添翼。该浏览器使用许多开源代码和开放标 准,但也引入了一些重要的创新,如独立页面的使用。这听起来很无聊,但其实很至关重要。原因如下:通常,通过浏览器运行几个网络应用程序会导致崩溃。而且 当一个浏览器页面崩溃时,整个程序都需要重启,其他页面上的工作或活动都会丢失。

Chrome的运作方式意味着任何浏览器的崩溃都仅限于当页,所以,如果你在一个页面上写邮件,而另一页面的视频崩溃了,你的邮件并不受影响。你可以把崩溃的页面关掉,继续工作。让浏览器以这样的方式工作好像桌面一样对于未来网络应用程序至关重要。

当然,Chrome的运行速度也很关键。为了网络应用程序能够成功,它们需要反应迅速,否则用户会郁闷。速度、稳定、安全这些都是我们未来在线活动的关键方面。而浏览器则是大门。

注:本文转载自:1986 To Present

原文链接:http://www.1986tp.cn/2009/04/the-genius-behind-google-chrome/



2009年4月27日星期一

MFC中应用OpenCV教程

申明:来源于网络,引用请注明出处!

论文:下载
下载后,是没有扩展名的一个文件,拖到你的word后即可打开。
部分内容来自网络,所引内容并不代表认同其观点。本博内容如有不当或侵犯权益,请来信告知!

2009年4月26日星期日

自信是怎样炼成的[转帖]

转自

本文摘自文汇报《开放你的人生》

关于如何培养自信,很多的书中都有介绍各种理论。但自信是怎样炼成的,用案例最有说服力,这里有几个小案例供大家分享。

  • 有才华就要大胆地展示

1984年,现中国科技部部长、当时还是同济大学力学系研究生的万钢,登上了前往德国的飞机。在德国第一件事,就是跟同伴一起到克劳斯塔尔大学的外办,了解学习计划的安排。当时,外办的老师提出,作为外国留学生,首先必须参加PNDS考试(德语入学考试)。考试半年举行一次,他必须等到秋季参加考试通过后才能开始他的博士学业。万钢当场就很自信地表示:自己的德语这么好,为什么一定要考试?一定要等这么久才开始正式的学习?

外办的老师表示怀疑,来该校就读的留学生不知道有多少,还从来没人敢说自己德语好到不需要考试的地步。但经过万钢用德语毫不妥协的"攻关"之后,外办老师最后只能表态:"如果你真觉得不需要考试,那去跟几个教授谈谈,看看是否可以免试。"

后来,万钢就和外办指定的几个教授聊了整整一个小时,用德语回答各种问题,接受各种语言测试,最终让教授们心服口服――同意免试。万钢也因此成了克劳斯塔尔大学历史上唯一一个没有参加PNDS考试而直接入学的外国留学生。他自己总结说:在该自信的地方就一定要敢于自信,不过分谦虚就是实事求是的精神,有才华就要大胆地展示。

许多人都有过这样的亲身经历,一旦认定某件事不可能取得成功,那么,你就算尽力而为,成功的可能性也大大降低。因为你戴上了心理的镣铐,拒绝了超常的努力,所以也拒绝了奇迹的发生。而你相信自己在擅长和具有优势的方向上的努力,并坚持行动,虽然不一定能够迎来转机,但奇迹发生的可能性肯定会大大增加。

  • 潘杰客初到美国为什么会被看不起?

北京华商会监事潘杰客的人生事业非常开放,他做过国家建设部党委宣传部副部长,当过凤凰卫视的主持人,还创业当过老板,也做过职业经理人。但是他初到美国时,却被身边的同学所看不起。

潘杰客刚去美国时,住在美国的父母家里,吃、穿、用都依赖父母。这在中国人看来,是再习以为常不过了。但是,潘杰客晚上去英文夜校补习外语时,却发现周围的同学都看不起他,并且孤立他。

"我特别骄傲地跟人说我父亲是康奈尔大学毕业的,英文特别好,我还说我父亲教我英文。结果这些同学对我不屑一顾,说我们也英语不行,可我们一样能打工,然后到夜校读书,你竟然还有优越感?你都是靠别人啊!"

最后,潘杰客不得不改变自己:"我在家里住了三个月就搬出来,住在纽约的地下室,因为地下室便宜,并且还在快餐店送外卖养活自己。我觉得,如果我再要兄弟姐妹和父母的帮助,是一种耻辱,因为美国的文化是18岁就开始靠自己,我那时30岁了,所以我必须靠自己。"

这样来看西方国家的家庭教育就很好理解了:一方面,他们淡化家庭之间互相依附的关系,培养孩子独立、自立的意识和能力,个性的文化正是其教育观的体现;另一方面,他们尊重普遍性的人性和人权,实际上也就尊重了孩子的基本权益,并引导孩子养成这样的价值观。

自立并不意味着不需要别人的帮助,而是强调自主,不成为他人的附属物。独立也不意味着排他性或者封闭性,不去学习和借鉴成功者,只是强调要有自己的思考和判断。

  • 克服消极浮躁的心态

前亚洲开发银行驻中国首席经济学家汤敏曾回忆道,1973年,他被派到广西南宁第四中学当数学老师。他高中都没有毕业,但他必须去给一群跟自己差不多大的学生上课。有人这样问他:"你在讲台上如何能够自信?"

汤敏的回答是:克制情绪,保持乐观,然后自学。他经常是"上周自己刚自学完,这周就要给学生上课"。

1977年10月恢复高考,广西考区对考生的年龄限制是25岁,汤敏已经24岁了。他很想参加高考,可有三个原因在向他说"NO":第一,没有把握,除了数学自学过外,英语、物理、化学都没学过;第二,朋友家人不支持,因为他已经有份稳定的教师工作,普通师范大学毕业也不过当一个中学老师;第三,面子问题,和学生一起考试,"压力很大,如果考不过学生或者考砸了,肯定很丢脸"。

人要做成什么事,就不能有那么多的疑虑,人生难免有些尴尬,关键在于保持心态的良好和积极,汤敏这样认为。最后,他参加了考试,并且改变了自己的一生,最终成为中国著名的经济学家。

对于刚毕业的大学生以及初入职场的年轻人来说,最常见的心态问题有两种,一是眼高手低;二是有些自卑,不敢争取机会。

年轻人在找工作之前,应该结合自己的具体情况,包括学历、专业、年龄、籍贯、学习、工作、兴趣、理想等各方面因素,进行综合考虑,进行恰当定位,这样心态的积极才有落点和杠杆。

如何培养良好的心态和情绪,坚定自己的自信,在对200多位成功人士调研的基础上,我归纳出如下建议:

    1. 永远不要有绝对不可能的消极想法;
    2. 确立切实可行的目标,然后树立必胜的信念;
    3. 罗列自己的特长和优点以及能够在竞争中取胜的优势;
    4. 学会宽容该宽容的人包括自己的过失,也欣赏别人以及自己的成就;
    5. 学会感激别人的帮助,培养双赢的合作思维;
    6. 学会乐观地看待身边的人和事,哪怕是一些不如意的。
    • 敢于放弃也是一种冒险

    美国凯雷投资集团董事总经理何欣曾提到过自己一次没有"敢于放弃"的经历。2001年,何欣从INTEL投资部加盟凯雷投资集团担任亚太投资部副总裁。年底,他接触一个韩国"下一代OLED"的有机显示屏项目。何欣看上了这个团队的技术能力和OLED的市场潜力,但这也是一个非常典型的早期项目," 我们开始看这个项目时,只有专利和图纸。"

    第一轮融资,凯雷投入500万美元。随后问题来了:"OLED技术的实现,需要周边配套技术。"技术发明人是个姓金的博士,而金博士团队只有产品研发能力和生产流程的设计,没有生产技术设备的制造能力。凯雷和其他VC第二轮于是又投入1500万美元,可问题又来了,原材料价格上升,公司需要降低生产成本等等。凯雷再次联合众多VC进行了第三轮融资。

    第三次投资之后,问题还是不断。这时,金博士说:"我想辞职。"何欣了解到作为发起人的金博士还有另外一个公司,他的心已经不在这儿了。"在凯雷的带动下有三轮融资,最后项目融资总额接近1亿美元。但第四轮时,我决定不投了。我对团队很失望。"

    最后的损失当然很大。凯雷的亚太基金负责人祖文萃评价说:第一笔投资没有问题,经过8个月的调研,事先的风险都想过。错就错在第三笔投资不应该再追加,出现这么多的问题,说明团队的执行力不行。不能一错再错了,这时放弃也是值得去冒险的行为。

    我们有胆略去冒险,其实就意味着把冒险当作一门战略艺术。我归纳了一些成功人士的经验教训,希望能为年轻人作些参考:

      1. 首先要确立可以专注的核心目标。冒险不是无头苍蝇一般乱飞乱闯,或者飞蛾扑火一般莽撞,千万不要异想天开,没有企业能够什么都做得好。
      2. 评估风险。行动前要明确自己所能承受的风险范围,有时不可不做最坏的估算,不要相信这个世界上有大收益却没有风险的好事,天上不会掉馅饼。
      3. 不要拒绝盟友,也切勿替别人去冒险,替别人冒险通常不是件吃力就能讨好的事。
      4. 制定合适的行动计划。不要怀着试试看的心态在重要的事上冒险。如果是为了失败而行动,那又是何苦呢?最可怕的事永远是你既不全力去争取成功,又继续投入精力和成本。
      5. 注意随机应变。勇敢不是没有恐惧和担心,问题也绝不会忽视你,到头来你仍然要逐一解决,因此,应该选择最有利的时机做最有利的事。
        • 刘江南立足阿尔卡特公司的"非常道"策划

        阿尔卡特公司中国区副总裁刘江南刚进公司时,他发现公司里每个人都忙忙碌碌,像机器一样定期把自己的工作和业绩变成表格上的数字,逐级上报直至变成公司的报表。而公司的几个核心人物再根据这些数字,把好的分出来拿到市场上去卖了。而他来公司快一个月了,"还不知道自己应该干什么。参加了几次公司的业务会,满篇的专有名词,弄得我一头雾水,不知所云。我甚至怀疑,阿尔卡特是否找错人了。"

        刘江南认为"在这样的公司里,命运不是掌握在自己手里的"。他曾找到公司总裁,但是"总裁说他很忙,顾不上细想我的事……"刘江南意识到,"自己不是这个专业出身,在这样技术很强的公司里,能有什么作为?"经过思考,刘江南认为"要以非常态的作为使自己进入常态",就必须策划一件"公司上下都认为不可能的大事"。

        刘江南的计划就是让中国的国家主席会见公司的最高领导――这是一个"乍看起来有点像铤而走险,但仔细想想实际上是一项风险极小而获利极高的策划"。后来,当CCTV播放中国国家主席接见阿尔卡特公司董事长的新闻时,刘江南一下子受到了公司高层的认可。

        许多人的人生之所以失败,就是因为对自己的人生缺乏主动策划,或者缺乏有效的策划,因此总是被命运和生活所奴役,不能自主人生。

        缺少策划者多半是自己都不知道自己在做什么、不为自己而是为现实生活主控的人,也是"当一天和尚,撞一天钟"随波逐流混日子的人。当然,我们也不会知道他能做出什么来。

        欢迎访问、交流!对本博客有何建议,请
        来信告知!
        本博内容来源于网络,如有不当或侵犯权益,请来信告知,将及时撤除!
        如引用博客内容、论文,请注明原作者!

        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 年前