(CvHaarClassifierCascade*)cvLoad( cascade_name, 0, 0, 0 ); 时出现以下错误

const char* cascade_name ="haarcascade_frontalface_alt.xml";
CvHaarClassifierCascade* cascade = (CvHaarClassifierCascade*)cvLoad( cascade_name, 0, 0, 0 );
时出现以下错误 
Unspecified error (The node does not represent a user object (unknown type?)) in
function cvRead, C:\User\VP\opencv\cxcore\src\cxpersistence.cpp(5061)

 

出错的原因:程序里面没有cvHaarDetectObjects()的调用,如果不调用此函数,可能编译器在链接程序的时候就少了些东西。 
解决方法:在程序void CTestDlg::OnButton1()函数的最后面加入代码: 
IplImage * img;
CvSeq* faces = cvHaarDetectObjects( img, cascade, storage,
                                            1.1, 2, 0,
                                            cvSize(30, 30) );



“加载分类器的错误,简直让我抓狂”的原因分析及解决方

原始问题来自:
<!-- l --><a class=\"postlink-local\" href=\"http://www.opencv.org.cn/forum/viewtopic.php?f=1&t=5672\">viewtopic.php?f=1&t=5672</a><!-- l -->

顺便google了一下,发现这几年来,碰到这个问题的人并不少,但却没有一个人愿意深入进去好好分析一下原因的,包括[url:7ez2t04s]http://opencv.willowgarage.com/wiki/FaceDetection[/url:7ez2t04s]给出的解决方案也是凑合,没能找出根本原因,说难听点,是扯蛋。对自己使用的工具如此的不熟悉,试问又怎么可能用的好,就更别说精益求精了。

我一再强调过基本功的重要、细心的重要、认真的重要、代码的重要。。。。。。为什么,因为我从来没有用过opencv,也从来不懂什么图像算法,也从来没看过opencv的代码,也从没用C++写过20行以上的程序。

好吧,我就来分析一下这个问题的形成和解决方案,我没有windows环境,所以下面均以linux环境进行描述。

我用的opencv版本是sf上下载的opencv-1.1pre1.tar.gz

解开之后,编译opencv代码库就不用说了,然后在sample/c/facedetect.c的基础上进行简化,生成以下测试文件:
  1. #include &quot;cxcore.h&quot;
  2. #include &quot;cvtypes.h&quot;
  3. #include <stdio.h>
  4. #include <stdlib.h>
  5. #include <string.h>
  6. const char* cascade_name =
  7.     &quot;../../data/haarcascades/haarcascade_frontalface_alt.xml&quot;;
  8. static CvHaarClassifierCascade* cascade = 0;
  9. int main( int argc, char** argv )
  10. {
  11.     cascade = (CvHaarClassifierCascade*)cvLoad( cascade_name, 0, 0, 0 );
  12.     if( !cascade )
  13.     {
  14.         fprintf( stderr, &quot;ERROR: Could not load classifier cascade\\n&quot; );
  15.         fprintf( stderr,
  16.         &quot;Usage: facedetect &#91;--cascade=\\&quot;<cascade_path>\\&quot;&#93;\\n&quot;
  17.         &quot;   &#91;--nested-cascade&#91;=\\&quot;nested_cascade_path\\&quot;&#93;&#93;\\n&quot;
  18.         &quot;   &#91;--scale&#91;=<image scale>\\n&quot;
  19.         &quot;   &#91;filename|camera_index&#93;\\n&quot; );
  20.         return -1;
  21.     }
  22.     return 0;
  23. }
复制代码

编译
  1. gcc -L/work/books/opencv/dist/lib -lcv -lhighgui -lcxcore -o facedetect facedetect.o
复制代码

执行
  1. LD_LIBRARY_PATH=/work/books/opencv/dist/lib ./facedetect
复制代码
结果正常,没有报错。

然后,我看了一下cvLoad函数在哪个库里边,显然,是cxcore,而我的facedetect.c又只用到了opencv的这一个函数,因此,我精简编译命令,变成
  1. gcc -L/work/books/opencv/dist/lib -lcxcore -o facedetect facedetect.o
复制代码

执行
  1. LD_LIBRARY_PATH=/work/books/opencv/dist/lib ./facedetect
  2. OpenCV ERROR: Unspecified error (The node does not represent a user object (unknown type?))
  3.         in function cvRead, cxpersistence.cpp(5061)
  4. Terminating the application...
  5. 段错误
复制代码

竟然,出现了传说中的错误。为了确认这一点,我再修改编译命令:
  1. gcc -L/work/books/opencv/dist/lib -lcv -o facedetect facedetect.o
复制代码

执行
  1. LD_LIBRARY_PATH=/work/books/opencv/dist/lib ./facedetect
复制代码
结果正常,没有报错。

这说明了什么?说明如果程序直接链接libcxcore库就会报错,而通过libcv或是libhighgui间接的链接libcxcore就没事。为什么会出现如此神奇的现象呢?
我们来看看编译生成的libcxcore.la和libcv.la两个文件:
  1. # libcxcore.la - a libtool library file
  2. # Generated by ltmain.sh - GNU libtool 1.5.26 Debian 1.5.26-1ubuntu1 (1.1220.2.493 2008/02/01 16:58:18)
  3. #
  4. # Please DO NOT delete this file!
  5. # It is necessary for linking the library.
  6. # The name that we can dlopen(3).
  7. dlname=\'libcxcore.so.2\'
  8. # Names of this library.
  9. library_names=\'libcxcore.so.2.0.0 libcxcore.so.2 libcxcore.so\'
  10. # The name of the static archive.
  11. old_library=\'\'
  12. # Libraries that this one depends upon.
  13. dependency_libs=\' -lpthread -ldl\'
  14. # Version information for libcxcore.
  15. current=2
  16. age=0
  17. revision=0
  18. # Is this an already installed library?
  19. installed=yes
  20. # Should we warn about portability when linking against -modules?
  21. shouldnotlink=no
  22. # Files to dlopen/dlpreopen
  23. dlopen=\'\'
  24. dlpreopen=\'\'
  25. # Directory that this library needs to be installed in:
  26. libdir=\'/work/books/opencv/dist/lib\'
复制代码
  1. # libcv.la - a libtool library file
  2. # Generated by ltmain.sh - GNU libtool 1.5.26 Debian 1.5.26-1ubuntu1 (1.1220.2.493 2008/02/01 16:58:18)
  3. #
  4. # Please DO NOT delete this file!
  5. # It is necessary for linking the library.
  6. # The name that we can dlopen(3).
  7. dlname=\'libcv.so.2\'
  8. # Names of this library.
  9. library_names=\'libcv.so.2.0.0 libcv.so.2 libcv.so\'
  10. # The name of the static archive.
  11. old_library=\'\'
  12. # Libraries that this one depends upon.
  13. dependency_libs=\' /work/books/opencv/dist/lib/libcxcore.la -lpthread -ldl\'
  14. # Version information for libcv.
  15. current=2
  16. age=0
  17. revision=0
  18. # Is this an already installed library?
  19. installed=yes
  20. # Should we warn about portability when linking against -modules?
  21. shouldnotlink=no
  22. # Files to dlopen/dlpreopen
  23. dlopen=\'\'
  24. dlpreopen=\'\'
  25. # Directory that this library needs to be installed in:
  26. libdir=\'/work/books/opencv/dist/lib\'
复制代码

显而易见,libcv已经显式依赖于libcxcore了,所以直接链接libcv,就会导致间接的对libcxcore的链接。

继综合使用gdb、strace、ldd、readelf、objdump一系列工具对两种情况下生成的目标文件分别进行分析、比较之后,没有什么有价值的发现。
那么,究竟是什么原因导致了该现象呢?

到这里,似乎陷入了一种僵局。事实真的如此吗?不。我在之前的回帖中已经一再强调过了,哪里报错就到哪里去查,哪里跌倒就要在哪里爬起来,这是基本的常识。可惜不论是这个论坛里还是google所找到的所有链接里,却没有一个人愿意从错误的最初起源地去追溯,几乎所有人都是一味的问、猜、试,像[url:7ez2t04s]http://opencv.willowgarage.com/wiki/FaceDetection[/url:7ez2t04s]里这种试出来的所谓“解决方案”,没有任何的理论支撑,能站得住脚吗?这里很多同学都是搞算法,这个道理应该都明白。

言归正传,我们找到打印出
  1. OpenCV ERROR: Unspecified error (The node does not represent a user object (unknown type?))
  2.         in function cvRead, cxpersistence.cpp(5061)
  3. Terminating the application...
复制代码
这行错误信息的源代码,在
  1. /* reads matrix, image, sequence, graph etc. */
  2. CV_IMPL void*
  3. cvRead( CvFileStorage* fs, CvFileNode* node, CvAttrList* list )
  4. {
  5.     void* obj = 0;
  6.     CV_FUNCNAME( &quot;cvRead&quot; );
  7.     __BEGIN__;
  8.     CV_CHECK_FILE_STORAGE( fs );
  9.     if( !node )
  10.         EXIT;
  11.     if( !CV_NODE_IS_USER(node->tag) || !node->info )
  12.         CV_ERROR( CV_StsError, &quot;The node does not represent a user object (unknown type?)&quot; );
  13.     CV_CALL( obj = node->info->read( fs, node ));
  14.     __END__;
  15.     if( list )
  16.         *list = cvAttrList(0,0);
  17.     return obj;
  18. }
复制代码

那么,出现这个错误的原因到底是什么,我们看看CV_NODE_IS_USER(node->tag)的定义:
  1. #define CV_NODE_IS_USER(flags)       (((flags) & CV_NODE_USER) != 0)
复制代码

显然,是因为node->tag不满足这个判断条件。继续溯流而上,为什么不满足这个条件,再看代码,找到这个node->tag在哪里、满足什么条件,才会具备CV_NODE_USER这个条件。
很容易找到,只有icvXMLParseValue函数中的这一段,也就是说是在解析xml文件时根据文件内容赋值的:
  1.             if( type_name )
  2.             {
  3.                 if( strcmp( type_name, &quot;str&quot; ) == 0 )
  4.                     elem_type = CV_NODE_STRING;
  5.                 else if( strcmp( type_name, &quot;map&quot; ) == 0 )
  6.                     elem_type = CV_NODE_MAP;
  7.                 else if( strcmp( type_name, &quot;seq&quot; ) == 0 )
  8.                     elem_type = CV_NODE_MAP;
  9.                 else
  10.                 {
  11.                     CV_CALL( info = cvFindType( type_name ));
  12.                     if( info )
  13.                         elem_type = CV_NODE_USER;
  14.                 }
  15.             }
复制代码

细心的人会注意到,这里只是把CV_NODE_USER赋值给了elem_type,那它又是怎么到node->tag里去的呢,这需要综合分析这段xml解析代码,我这里简单说一下。
elem_type会在下面几行的位置被当做参数来递归调用icvXMLParseValue自身:
  1. CV_CALL( ptr = icvXMLParseValue( fs, ptr, elem, elem_type));
复制代码

在icvXMLParseValue函数的入口处有变量声明:
  1. int is_user_type = CV_NODE_IS_USER(value_type);
复制代码

在icvXMLParseValue函数的最后有:
  1. node->tag |= is_user_type ? CV_NODE_USER : 0;
复制代码

所以,必须cvFindType( type_name )返回非空值,也就是说能够找到这个type_name所对应的类型,才能够满足后面node->tag的要求。

接下来,没啥好说的,继续看cvFindType,因为从代码来看,只有cvFindType返回非空值,才能使得elem_type满足CV_NODE_USER这个条件。
  1. CvTypeInfo *CvType::first = 0, *CvType::last = 0;
  2. CV_IMPL CvTypeInfo*
  3. cvFindType( const char* type_name )
  4. {
  5.     CvTypeInfo* info = 0;
  6.     for( info = CvType::first; info != 0; info = info->next )
  7.         if( strcmp( info->type_name, type_name ) == 0 )
  8.             break;
  9.     return info;
  10. }
复制代码

很清楚,这里就是在list里找看有没有哪个类型的名字跟传进来的type_name相同而已。

回过头,我们需要知道现在正在查找的type_name是什么,这很简单,加个打印就知道原来是opencv-haar-classifier,再去看看xml文件就知道来自于xml文件的这一行:
  1. <haarcascade_frontalface_alt2 type_id=&quot;opencv-haar-classifier&quot;>
复制代码

再下来就更清楚了,就是要弄明白,为什么这时候在list中找不到这个类型。看看CvType的构造函数就明白了:
  1. CvType::CvType( const char* type_name,
  2.                 CvIsInstanceFunc is_instance, CvReleaseFunc release,
  3.                 CvReadFunc read, CvWriteFunc write, CvCloneFunc clone )
  4. {
  5.     CvTypeInfo _info;
  6.     _info.flags = 0;
  7.     _info.header_size = sizeof(_info);
  8.     _info.type_name = type_name;
  9.     _info.prev = _info.next = 0;
  10.     _info.is_instance = is_instance;
  11.     _info.release = release;
  12.     _info.clone = clone;
  13.     _info.read = read;
  14.     _info.write = write;
  15.     cvRegisterType( &_info );
  16.     info = first;
  17. }
复制代码
也就是说每构造一个CvType实例,就会调用cvRegisterType一次,看看cvRegisterType的代码就知道,该函数就是在把新的CvTypeInfo实例挂到list上去。

说到这里,比较熟悉C++的人应该已经能够有自己的想法了,我们无非就是要搞清楚两点:
1、为什么直接链接cxcore的时候,list中就没有opencv-haar-classifier
2、为什么通过链接cv来间接引用cxcore的时候,list中就会有opencv-haar-classifier

这时候,很自然就会联想到全局对象的实例化了。我们只需要搜索一下代码中所有会实例化CvType对象的地方,很容易就会看到cv/src/cvhaar.cpp的最后这一行:
  1. CvType haar_type( CV_TYPE_NAME_HAAR, icvIsHaarClassifier,
  2.                   (CvReleaseFunc)cvReleaseHaarClassifierCascade,
  3.                   icvReadHaarClassifier, icvWriteHaarClassifier,
  4.                   icvCloneHaarClassifier );
复制代码

而其中CV_TYPE_NAME_HAAR的定义就是
  1. #define CV_TYPE_NAME_HAAR    &quot;opencv-haar-classifier&quot;
复制代码
这就是一切的罪魁祸首。

分析到这里,一切都是明白的了,再也毫无任何疑问和任何一个无法解释的角落了。
整理一下结论吧:

1、为什么会报这个错,就是因为cvLoad时,解析出xml文件中有个type_id定义为opencv-haar-classifier,但这个opencv-haar-classifier类型却找不到定义,因此报错。

2、为什么链接cv的时候不保错,因为cv库中有个全局对象haar_type,因为它是全局对象,所以会在cvLoad函数被调用之前就被实例化(对静态库来说,在程序启动之后进入main函数之前会实例化所有的全局对象,而对于动态库来说,由于cvLoad函数调用会引起libcxcore动态库的加载,而在该库加载之后,会立即完成所有全局对象的实例化,然后加载动态库的动作才完成,也才会进入cvLoad函数),而该对象的实例化会注册一个opencv-haar-classifier类型,因此就不会报错了。

3、那么,网上有些说法说换成highguid等调试版本的库,为什么可以,那是因为vc做了特殊处理,对debug版本的库,不论三七二十一,不管有没有调用都强行链接,便于调试。说到这里,声明一点,对gcc来说,如果主程序中没有调用某个库中的任何符号,那么就算指定了-l参数,最后该库也不会被链接,这样做的好处显而易见,是减小目标程序的尺寸。可能vc对release版本库的行为也是如此。
具体来说,就是使用vc时,即使你在options中写了cv、cxcore、highgui,但是,如果是使用release版本的库的话,而你的主程序又只调用了cxcore中的符号,那么,vc只会给你链接cxcore这一个库。但使用debug版本库的时候,可能就会强行全部链接、强行加载了,这也造成debug版本的程序会很大。

4、为什么网上还有一种说法是,在主程序中调用一个cvHaarDetectObjects函数,就可以解决问题。那是因为这个函数定义在cv库中,所以会引起cv库被加载,从而也会导致全局对象haar_type被抢先实例化。

5、网上其他什么调这个函数调那个函数之类的所谓“解决方案”,一概都跟4是一个道理。

好,原因分析完了,但问题如何解决呢?相信有的人应该已经有了自己的想法了,不外乎以下几种:

1、在自己的程序中强行链接cv库中声明了该全局对象的目标文件,但该文件又依赖于其他一些东西,所以最后会导致下面的情况:
  1. anders@anders-laptop:/work/books/opencv/opencv-1.1.0/samples/c$ gcc -L/work/books/opencv/dist/lib -lcxcore -o facedetect facedetect.o ../../cv/src/.libs/cvhaar.o ../../cv/src/.libs/cvcolor.o ../../cv/src/.libs/cvimgwarp.o  ../../cv/src/.libs/cvsumpixels.o ../../cv/src/.libs/cvcanny.o ../../cv/src/.libs/cvtables.o ../../cv/src/.libs/cvderiv.o ../../cv/src/.libs/cvfilter.o ../../cv/src/.libs/cvutils.o ../../cv/src/.libs/cvtemplmatch.o ../../cv/src/.libs/cvaccum.o
  2. anders@anders-laptop:/work/books/opencv/opencv-1.1.0/samples/c$ LD_LIBRARY_PATH=/work/books/opencv/dist/lib ./facedetect
  3. anders@anders-laptop:/work/books/opencv/opencv-1.1.0/samples/c$
复制代码
也就是说必须链接好几个cv库中的目标文件才行。

2、指定直接链接cv库:
  1. anders@anders-laptop:/work/books/opencv/opencv-1.1.0/samples/c$ gcc -L/work/books/opencv/dist/lib -lcv -o facedetect facedetect.o
  2. anders@anders-laptop:/work/books/opencv/opencv-1.1.0/samples/c$ LD_LIBRARY_PATH=/work/books/opencv/dist/lib ./facedetect
  3. anders@anders-laptop:/work/books/opencv/opencv-1.1.0/samples/c$
复制代码

3、自己去实现一套cvhaar.cpp中的东西。

最后,让我们再来回味一下这个错误信息吧:
  1. anders@anders-laptop:/work/books/opencv/opencv-1.1.0/samples/c$ gcc -L/work/books/opencv/dist/lib -lcxcore -o facedetect facedetect.o
  2. anders@anders-laptop:/work/books/opencv/opencv-1.1.0/samples/c$ LD_LIBRARY_PATH=/work/books/opencv/dist/lib ./facedetect
  3. OpenCV ERROR: Unspecified error (The node does not represent a user object (unknown type?))
  4.         in function cvRead, cxpersistence.cpp(5061)
  5. Terminating the application...
  6. 段错误
复制代码

能耐心从头看到尾的人,应该会有所收获吧,也不枉我写的这么详细了。


你可能感兴趣的:((CvHaarClassifierCascade*)cvLoad( cascade_name, 0, 0, 0 ); 时出现以下错误)