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的基礎上進行簡化,生成以下測試文件:
t;../../data/haarcascades/haarcascade_frontalface_alt.xml";
static?CvHaarClassifierCascade*?cascade?=?0;
int?main(?int?argc,?char**?argv?)
{cascade?=?(CvHaarClassifierCascade*)cvLoad(?cascade_name,?0,?0,?0?);if(?!cascade?){fprintf(?stderr,?"ERROR:?Could?not?load?classifier?cascade\n"?);fprintf(?stderr,"Usage:?facedetect?[--cascade=\"<cascade_path>\"]\n""???[--nested-cascade[=\"nested_cascade_path\"]]\n""???[--scale[=<p_w_picpath?scale>\n""???[filename|camera_index]\n"?);return?-1;}return?0;
}#include?"cxcore.h"
#include?"cvtypes.h"
#include?<stdio.h>
#include?<stdlib.h>
#include?<string.h>
const?char*?cascade_name?=&quo
編譯
gcc?-L/work/books/opencv/dist/lib?-lcv?-lhighgui?-lcxcore?-o?facedetect?facedetect.o
執行
LD_LIBRARY_PATH=/work/books/opencv/dist/lib?./facedetect
結果正常,沒有報錯。
然后,我看了一下cvLoad函數在哪個庫里邊,顯然,是cxcore,而我的facedetect.c又只用到了opencv的這一個函數,因此,我精簡編譯命令,變成
gcc?-L/work/books/opencv/dist/lib?-lcxcore?-o?facedetect?facedetect.o
執行
LD_LIBRARY_PATH=/work/books/opencv/dist/lib?./facedetect
OpenCV?ERROR:?Unspecified?error?(The?node?does?not?represent?a?user?object?(unknown?type?))in?function?cvRead,?cxpersistence.cpp(5061)
Terminating?the?application...段錯誤
竟然,出現了傳說中的錯誤。為了確認這一點,我再修改編譯命令:
gcc?-L/work/books/opencv/dist/lib?-lcv?-o?facedetect?facedetect.o
執行
LD_LIBRARY_PATH=/work/books/opencv/dist/lib?./facedetect結果正常,沒有報錯。
這說明了什么?說明如果程序直接鏈接libcxcore庫就會報錯,而通過libcv或是libhighgui間接的鏈接libcxcore就沒事。為什么會出現如此神奇的現象呢?
我們來看看編譯生成的libcxcore.la和libcv.la兩個文件:
te?this?file!
#?It?is?necessary?for?linking?the?library.
#?The?name?that?we?can?dlopen(3).
dlname='libcxcore.so.2'
#?Names?of?this?library.
library_names='libcxcore.so.2.0.0?libcxcore.so.2?libcxcore.so'
#?The?name?of?the?static?archive.
old_library=''
#?Libraries?that?this?one?depends?upon.
dependency_libs='?-lpthread?-ldl'
#?Version?information?for?libcxcore.
#?Directory?that?this?library?needs?to?be?installed?in:
libdir='/work/books/opencv/dist/lib'
current=2
age=0
revision=0#?libcxcore.la?-?a?libtool?library?file
#?Generated?by?ltmain.sh?-?GNU?libtool?1.5.26?Debian?1.5.26-1ubuntu1?(1.1220.2.493
#?Is?this?an?already?installed?library?
installed=yes
#?Should?we?warn?about?portability?when?linking?against?-modules?
shouldnotlink=no
#?Files?to?dlopen/dlpreopen
dlopen=''
dlpreopen=''
2008/02/01?16:58:18)
#
#?Please?DO?NOT?dele
library_names='libcv.so.2.0.0?libcv.so.2?libcv.so'
#?The?name?of?the?static?archive.
old_library=''
#?Libraries?that?this?one?depends?upon.
dependency_libs='?/work/books/opencv/dist/lib/libcxcore.la?-lpthread?-ldl'#?libcv.la?-?a?libtool?library?file
#?Generated?by?ltmain.sh?-?GNU?libtool?1.5.26?Debian?1.5.26-1ubuntu1?(1.1220.2.493?2008/02/01?16:58:18)
#
#?Please?DO?NOT?delete?this?file!
#?It?is?necessary?for?linking?the?library.
#?The?name?that?we?can?dlopen(3).
dlname='libcv.so.2'
#?Version?information?for?libcv.
current=2
age=0
revision=0
#?Is?this?an?already?installed?library?
installed=yes
#?Should?we?warn?about?portability?when?linking?against?-modules?
shouldnotlink=no
#?Files?to?dlopen/dlpreopen
dlopen=''
dlpreopen=''
#?Directory?that?this?library?needs?to?be?installed?in:
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]里這種試出來的所謂“解決方案”,沒有任何的理論支撐,能站得住腳嗎?這里很多同學都是搞算法,這個道理應該都明白。
言歸正傳,我們找到打印出
OpenCV?ERROR:?Unspecified?error?(The?node?does?not?represent?a?user?object?(unknown?type?))in?function?cvRead,?cxpersistence.cpp(5061)
Terminating?the?application...
這行錯誤信息的源代碼,在
?void*?obj?=?0;CV_FUNCNAME(?"cvRead"?);__BEGIN__;CV_CHECK_FILE_STORAGE(?fs?);if(?!node?)EXIT;if(?!CV_NODE_IS_USER(node->tag)?||?!node->info?)CV_ERROR(?CV_StsError,?"The?node?does?not?represent?a?user?object?(unknown?type?)"?);CV_CALL(?obj?=?node->info->read(?fs,?node?));__END__;if(?list?)*list?=?cvAttrList(0,0);return?obj;
}/*?reads?matrix,?p_w_picpath,?sequence,?graph?etc.?*/
CV_IMPL?void*
cvRead(?CvFileStorage*?fs,?CvFileNode*?node,?CvAttrList*?list?)
{
那么,出現這個錯誤的原因到底是什么,我們看看CV_NODE_IS_USER(node->tag)的定義:
#define?CV_NODE_IS_USER(flags)???????(((flags)?&?CV_NODE_USER)?!=?0)
顯然,是因為node->tag不滿足這個判斷條件。繼續溯流而上,為什么不滿足這個條件,再看代碼,找到這個node->tag在哪里、滿足什么條件,才會具備CV_NODE_USER這個條件。
很容易找到,只有icvXMLParseValue函數中的這一段,也就是說是在解析xml文件時根據文件內容賦值的:
????????????if(?type_name?){if(?strcmp(?type_name,?"str"?)?==?0?)elem_type?=?CV_NODE_STRING;else?if(?strcmp(?type_name,?"map"?)?==?0?)elem_type?=?CV_NODE_MAP;else?if(?strcmp(?type_name,?"seq"?)?==?0?)elem_type?=?CV_NODE_MAP;else{CV_CALL(?info?=?cvFindType(?type_name?));if(?info?)elem_type?=?CV_NODE_USER;}}
細心的人會注意到,這里只是把CV_NODE_USER賦值給了elem_type,那它又是怎么到node->tag里去的呢,這需要綜合分析這段xml解析代碼,我這里簡單說一下。
elem_type會在下面幾行的位置被當做參數來遞歸調用icvXMLParseValue自身:
CV_CALL(?ptr?=?icvXMLParseValue(?fs,?ptr,?elem,?elem_type));
在icvXMLParseValue函數的入口處有變量聲明:
int?is_user_type?=?CV_NODE_IS_USER(value_type);
在icvXMLParseValue函數的最后有:
node->tag?|=?is_user_type???CV_NODE_USER?:?0;
所以,必須cvFindType( type_name )返回非空值,也就是說能夠找到這個type_name所對應的類型,才能夠滿足后面node->tag的要求。
接下來,沒啥好說的,繼續看cvFindType,因為從代碼來看,只有cvFindType返回非空值,才能使得elem_type滿足CV_NODE_USER這個條件。
CvTypeInfo?*CvType::first?=?0,?*CvType::last?=?0;
CV_IMPL?CvTypeInfo*
cvFindType(?const?char*?type_name?)
{CvTypeInfo*?info?=?0;for(?info?=?CvType::first;?info?!=?0;?info?=?info->next?)if(?strcmp(?info->type_name,?type_name?)?==?0?)break;return?info;
}
很清楚,這里就是在list里找看有沒有哪個類型的名字跟傳進來的type_name相同而已。
回過頭,我們需要知道現在正在查找的type_name是什么,這很簡單,加個打印就知道原來是opencv-haar-classifier,再去看看xml文件就知道來自于xml文件的這一行:
<haarcascade_frontalface_alt2?type_id="opencv-haar-classifier">
再下來就更清楚了,就是要弄明白,為什么這時候在list中找不到這個類型。看看CvType的構造函數就明白了:
CvType::CvType(?const?char*?type_name,CvIsInstanceFunc?is_instance,?CvReleaseFunc?release,CvReadFunc?read,?CvWriteFunc?write,?CvCloneFunc?clone?)
{CvTypeInfo?_info;_info.flags?=?0;_info.header_size?=?sizeof(_info);_info.type_name?=?type_name;_info.prev?=?_info.next?=?0;_info.is_instance?=?is_instance;_info.release?=?release;_info.clone?=?clone;_info.read?=?read;_info.write?=?write;cvRegisterType(?&_info?);info?=?first;
}
也就是說每構造一個CvType實例,就會調用cvRegisterType一次,看看cvRegisterType的代碼就知道,該函數就是在把新的CvTypeInfo實例掛到list上去。
說到這里,比較熟悉C++的人應該已經能夠有自己的想法了,我們無非就是要搞清楚兩點:
1、為什么直接鏈接cxcore的時候,list中就沒有opencv-haar-classifier
2、為什么通過鏈接cv來間接引用cxcore的時候,list中就會有opencv-haar-classifier
這時候,很自然就會聯想到全局對象的實例化了。我們只需要搜索一下代碼中所有會實例化CvType對象的地方,很容易就會看到cv/src/cvhaar.cpp的最后這一行:
CvType?haar_type(?CV_TYPE_NAME_HAAR,?icvIsHaarClassifier,(CvReleaseFunc)cvReleaseHaarClassifierCascade,icvReadHaarClassifier,?icvWriteHaarClassifier,icvCloneHaarClassifier?);
而其中CV_TYPE_NAME_HAAR的定義就是
#define?CV_TYPE_NAME_HAAR????"opencv-haar-classifier"
這就是一切的罪魁禍首。
分析到這里,一切都是明白的了,再也毫無任何疑問和任何一個無法解釋的角落了。
整理一下結論吧:
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參數,最后該庫也不會被鏈接,這樣做的好處顯而易見,是減小目標程序的尺寸??赡躹c對release版本庫的行為也是如此。
具體來說,就是使用vc時,即使你在options中寫了cv、cxcore、highgui,但是,如果是使用release版本的庫的話,而你的主程序又只調用了cxcore中的符號,那么,vc只會給你鏈接cxcore這一個庫。但使用debug版本庫的時候,可能就會強行全部鏈接、強行加載了,這也造成debug版本的程序會很大。
4、為什么網上還有一種說法是,在主程序中調用一個cvHaarDetectObjects函數,就可以解決問題。那是因為這個函數定義在cv庫中,所以會引起cv庫被加載,從而也會導致全局對象haar_type被搶先實例化。
5、網上其他什么調這個函數調那個函數之類的所謂“解決方案”,一概都跟4是一個道理。
好,原因分析完了,但問題如何解決呢?相信有的人應該已經有了自己的想法了,不外乎以下幾種:
1、在自己的程序中強行鏈接cv庫中聲明了該全局對象的目標文件,但該文件又依賴于其他一些東西,所以最后會導致下面的情況:
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
anders@anders-laptop:/work/books/opencv/opencv-1.1.0/samples/c$?LD_LIBRARY_PATH=/work/books/opencv/dist/lib?./facedetect
anders@anders-laptop:/work/books/opencv/opencv-1.1.0/samples/c$復制代碼
也就是說必須鏈接好幾個cv庫中的目標文件才行。
2、指定直接鏈接cv庫:
anders@anders-laptop:/work/books/opencv/opencv-1.1.0/samples/c$?gcc?-L/work/books/opencv/dist/lib?-lcv?-o?facedetect?facedetect.o
anders@anders-laptop:/work/books/opencv/opencv-1.1.0/samples/c$?LD_LIBRARY_PATH=/work/books/opencv/dist/lib?./facedetect
anders@anders-laptop:/work/books/opencv/opencv-1.1.0/samples/c$復制代碼
3、自己去實現一套cvhaar.cpp中的東西。
最后,讓我們再來回味一下這個錯誤信息吧:
anders@anders-laptop:/work/books/opencv/opencv-1.1.0/samples/c$?gcc?-L/work/books/opencv/dist/lib?-lcxcore?-o?facedetect?facedetect.o
anders@anders-laptop:/work/books/opencv/opencv-1.1.0/samples/c$?LD_LIBRARY_PATH=/work/books/opencv/dist/lib?./facedetect
OpenCV?ERROR:?Unspecified?error?(The?node?does?not?represent?a?user?object?(unknown?type?))in?function?cvRead,?cxpersistence.cpp(5061)
Terminating?the?application...段錯誤
能耐心從頭看到尾的人,應該會有所收獲吧,也不枉我寫的這么詳細了
轉載于:https://blog.51cto.com/aslonely/1766271
總結
以上是生活随笔為你收集整理的opencv 使用cvload加载xml出现错误原因解析及方法的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。