opencv 使用cvload加载xml出现错误原因解析及方法

前端之家收集整理的这篇文章主要介绍了opencv 使用cvload加载xml出现错误原因解析及方法前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。

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

我一再强调过基本功的重要、细心的重要、认真的重要、代码的重要。。。。。。为什么,因为我从来没有用过opencv,也从来不懂什么图像算法,也从来没看过opencv的代码,也从没用C++写过20行以上的程序。
好吧,我就来分析一下这个问题的形成和解决方案,我没有windows环境,所以下面均以linux环境进行描述。
我用的opencv版本是sf上下载的opencv-1.1pre1.tar.gz
解开之后,编译opencv代码库就不用说了,然后在sample/c/facedetect.c的基础上进行简化,生成以下测试文件

    1. t;../../data/haarcascades/haarcascade_frontalface_alt.xml";
    2. staticCvHaarClassifierCascade*cascade=0;
    3. intmain(intargc,char**argv)
    4. {
    5. cascade=(CvHaarClassifierCascade*)cvLoad(cascade_name,0);
    6. if(!cascade)
    7. {
    8. fprintf(stderr,"ERROR:Couldnotloadclassifiercascade\n");
    9. fprintf(stderr,&quot;Usage:facedetect&#91;--cascade=\&quot;<cascade_path>\&quot;&#93;\n&quot;
    10. &quot;&#91;--nested-cascade&#91;=\&quot;nested_cascade_path\&quot;&#93;&#93;\n&quot;
    11. &quot;&#91;--scale&#91;=<imagescale>\n&quot;
    12. &quot;&#91;filename|camera_index&#93;\n&quot;);
    13. return-1;
    14. }
    15. return0;
    16. }
    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. constchar*cascade_name=
    7. &quo

编译

    1. gcc-L/work/books/opencv/dist/lib-lcv-lhighgui-lcxcore-ofacedetectfacedetect.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-ofacedetectfacedetect.o


执行

    1. LD_LIBRARY_PATH=/work/books/opencv/dist/lib./facedetect
    2. OpenCVERROR:Unspecifiederror(Thenodedoesnotrepresentauserobject(unknowntype?))
    3. infunctioncvRead,cxpersistence.cpp(5061)
    4. Terminatingtheapplication...
  1. 错误



竟然,出现了传说中的错误。为了确认这一点,我再修改编译命令:

    1. gcc-L/work/books/opencv/dist/lib-lcv-ofacedetectfacedetect.o


执行

    1. LD_LIBRARY_PATH=/work/books/opencv/dist/lib./facedetect

这说明了什么?说明如果程序直接链接libcxcore库就会报错,而通过libcv或是libhighgui间接的链接libcxcore就没事。为什么会出现如此神奇的现象呢?
我们来看看编译生成的libcxcore.la和libcv.la两个文件

    1. tethisfile!
    2. #Itisnecessaryforlinkingthelibrary.
    3. #Thenamethatwecandlopen(3).
    4. dlname='libcxcore.so.2'
    5. #Namesofthislibrary.
    6. library_names='libcxcore.so.2.0.0libcxcore.so.2libcxcore.so'
    7. #Thenameofthestaticarchive.
    8. old_library=''
    9. #Librariesthatthisonedependsupon.
    10. dependency_libs='-lpthread-ldl'
    11. #Versioninformationforlibcxcore.
    12. #Directorythatthislibraryneedstobeinstalledin:
    13. libdir='/work/books/opencv/dist/lib'
    14. current=2
    15. age=0
    16. revision=0
    1. #libcxcore.la-alibtoollibraryfile
    2. #Generatedbyltmain.sh-GNUlibtool1.5.26Debian1.5.26-1ubuntu1(1.1220.2.493
    3. #Isthisanalreadyinstalledlibrary?
    4. installed=yes
    5. #Shouldwewarnaboutportabilitywhenlinkingagainst-modules?
    6. shouldnotlink=no
    7. #Filestodlopen/dlpreopen
    8. dlopen=''
    9. dlpreopen=''
    10. 2008/02/0116:58:18)
    11. #
    12. #PleaseDONOTdele


    1. library_names='libcv.so.2.0.0libcv.so.2libcv.so'
    2. #Thenameofthestaticarchive.
    3. old_library=''
    4. #Librariesthatthisonedependsupon.
    5. dependency_libs='/work/books/opencv/dist/lib/libcxcore.la-lpthread-ldl'
    1. #libcv.la-alibtoollibraryfile
    2. #Generatedbyltmain.sh-GNUlibtool1.5.26Debian1.5.26-1ubuntu1(1.1220.2.4932008/02/0116:58:18)
    3. #
    4. #PleaseDONOTdeletethisfile!
    5. #Itisnecessaryforlinkingthelibrary.
    6. #Thenamethatwecandlopen(3).
    7. dlname='libcv.so.2'
    8. #Versioninformationforlibcv.
    9. current=2
    10. age=0
    11. revision=0
    12. #Isthisanalreadyinstalledlibrary?
    13. installed=yes
    14. #Shouldwewarnaboutportabilitywhenlinkingagainst-modules?
    15. shouldnotlink=no
    16. #Filestodlopen/dlpreopen
    17. dlopen=''
    18. dlpreopen=''
    19. #Directorythatthislibraryneedstobeinstalledin:
    20. libdir='/work/books/opencv/dist/lib'


显而易见,libcv已经显式依赖于libcxcore了,所以直接链接libcv,就会导致间接的对libcxcore的链接
继综合使用gdb、strace、ldd、readelf、objdump一系列工具对两种情况下生成的目标文件分别进行分析、比较之后,没有什么有价值的发现。
那么,究竟是什么原因导致了该现象呢?
到这里,似乎陷入了一种僵局。事实真的如此吗?不。我在之前的回帖中已经一再强调过了,哪里报错就到哪里去查,哪里跌倒就要在哪里爬起来,这是基本的常识。可惜不论是这个论坛里还是google所找到的所有链接里,却没有一个人愿意从错误的最初起源地去追溯,几乎所有人都是一味的问、猜、试,像[url:3jrjx97k]http://opencv.willowgarage.com/wiki/FaceDetection[/url:3jrjx97k]里这种试出来的所谓“解决方案”,没有任何的理论支撑,能站得住脚吗?这里很多同学都是搞算法,这个道理应该都明白。
言归正传,我们找到打印出

  1. OpenCVERROR:Unspecifiederror(Thenodedoesnotrepresentauserobject(unknowntype?))
  2. infunctioncvRead,cxpersistence.cpp(5061)
  3. Terminatingtheapplication...

这行错误信息的源代码,在

    1. void*obj=0;
    2. CV_FUNCNAME(&quot;cvRead&quot;);
    3. __BEGIN__;
    4. CV_CHECK_FILE_STORAGE(fs);
    5. if(!node)
    6. EXIT;
    7. if(!CV_NODE_IS_USER(node->tag)||!node->info)
    8. CV_ERROR(CV_StsError,&quot;Thenodedoesnotrepresentauserobject(unknowntype?)&quot;);
    9. CV_CALL(obj=node->info->read(fs,node));
    10. __END__;
    11. if(list)
    12. *list=cvAttrList(0,0);
    13. returnobj;
    14. }
    1. /*readsmatrix,image,sequence,graphetc.*/
    2. CV_IMPLvoid*
    3. cvRead(CvFileStorage*fs,CvFileNode*node,CvAttrList*list)
    4. {


那么,出现这个错误的原因到底是什么,我们看看CV_NODE_IS_USER(node->tag)的定义:

    1. #defineCV_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. elseif(strcmp(type_name,&quot;map&quot;)==0)
    6. elem_type=CV_NODE_MAP;
    7. elseif(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. intis_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_IMPLCvTypeInfo*
    3. cvFindType(constchar*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. returninfo;
    10. }



很清楚,这里就是在list里找看有没有哪个类型的名字跟传进来的type_name相同而已。
回过头,我们需要知道现在正在查找的type_name是什么,这很简单,加个打印就知道原来是opencv-haar-classifier,再去看看xml文件就知道来自于xml文件的这一行:

    1. <haarcascade_frontalface_alt2type_id=&quot;opencv-haar-classifier&quot;>



再下来就更清楚了,就是要弄明白,为什么这时候在list中找不到这个类型。看看CvType的构造函数就明白了:

    1. CvType::CvType(constchar*type_name,CvIsInstanceFuncis_instance,CvReleaseFuncrelease,CvReadFuncread,CvWriteFuncwrite,CvCloneFuncclone)
    2. {
    3. CvTypeInfo_info;
    4. _info.flags=0;
    5. _info.header_size=sizeof(_info);
    6. _info.type_name=type_name;
    7. _info.prev=_info.next=0;
    8. _info.is_instance=is_instance;
    9. _info.release=release;
    10. _info.clone=clone;
    11. _info.read=read;
    12. _info.write=write;
    13. cvRegisterType(&_info);
    14. info=first;
    15. }


也就是说每构造一个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. CvTypehaar_type(CV_TYPE_NAME_HAAR,icvIsHaarClassifier,(CvReleaseFunc)cvReleaseHaarClassifierCascade,icvReadHaarClassifier,icvWriteHaarClassifier,icvCloneHaarClassifier);



而其中CV_TYPE_NAME_HAAR的定义就是

    1. #defineCV_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-ofacedetectfacedetect.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-ofacedetectfacedetect.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-ofacedetectfacedetect.o
    2. anders@anders-laptop:/work/books/opencv/opencv-1.1.0/samples/c$LD_LIBRARY_PATH=/work/books/opencv/dist/lib./facedetect
    3. OpenCVERROR:Unspecifiederror(Thenodedoesnotrepresentauserobject(unknowntype?))
    4. infunctioncvRead,cxpersistence.cpp(5061)
    5. Terminatingtheapplication...
  1. 错误



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

猜你在找的XML相关文章