/* * mp_BHAT.cpp * * A class for loading and holding BHAT weights. * * Created by Andrew Salamon on Tues Jan 30 2007. * * Copyright (c) 2007 Machine Perception Laboratory * University of California San Diego. * Please read the license information at the end of this file. */ #include "mp_BHAT.h" #include #include #include #include #include #include // For system independent path support #include #ifdef WIN32 #include #else extern "C" { #include } #define xmlFree(x) free(x) #endif #include "xmlSmartPtr.h" using namespace std; static bool getIntProperty( xmlNodePtr cur, const char *name, int *val ); mp_BHAT::mp_BHAT( const std::string &_filename ) : weights(NULL), count(0), filename(_filename), loadError(false) { weights = loadWeights(); // If we failed to find or load a file, set our error flag if( !weights ) loadError = true; } mp_BHAT::~mp_BHAT() { clearWeights(); } void mp_BHAT::clearWeights() { free( (double *)weights ); } /** Sets the path to the directory containing the BHAT Weight files. * We will attemmpt to load BHAT Weights from files in the given directory. Otherwise * we only search the current directory. * * This string will be used via Boost's * filesystem::path * library and should work with system dependent paths. * For example: /Users/Shared/Weights would work on a Mac OS X machine * and C:\\AFolder\\SVMWeights should work on a Window's machine. * That hasn't been properly tested yet. * Both relative and absolute paths should work. */ void mp_BHAT::setDirectory( const std::string &dir ) { directory=dir; } const string & mp_BHAT::getDirectory() { return directory; } bool mp_BHAT::getLoadError() { return loadError; } const double * mp_BHAT::loadWeights() { return loadWeightsXML(); } const double * mp_BHAT::loadWeightsXML() { double *comp = NULL; // build an XML tree from the file; // We may want to switch to an event based parser (SAX) to limit memory use, but it would need to be // synchronous, or the app would at least need to insure that all files were loaded before trying to // access any. Error handling might be easier/better when using SAX, too. // We can use this alternate API to suppress error and warning messages, so we can do our own error handling xmlParserCtxtPtr xmlCtxt = xmlNewParserCtxt(); // Make sure the parser context get's deleted no matter how we exit the method xmlSmartPtr ctxtDel( xmlCtxt, (xmlSmartPtr::deleter)xmlFreeParserCtxt ); if( xmlInitParserCtxt( xmlCtxt ) ) { cerr << "Couldn't init Parser context" << endl; return comp; } if( xmlCtxtUseOptions( xmlCtxt, (XML_PARSE_NOERROR | XML_PARSE_NOWARNING) ) ) { cerr << "Couldn't set Parser context options" << endl; return comp; } string fname; try { boost::filesystem::path xmlPath( filename ); if( !directory.empty() ) { boost::filesystem::path xmlDir( directory, boost::filesystem::native ); xmlPath = xmlDir / xmlPath; } fname = xmlPath.native_file_string(); } catch ( const boost::filesystem::filesystem_error & ) { cerr << "Couldn't build valid path for file: " << fname << " with directory: " << directory << endl; return comp; } xmlDocPtr doc = xmlCtxtReadFile( xmlCtxt, fname.c_str(), NULL, (XML_PARSE_NOERROR | XML_PARSE_NOWARNING) ); if (doc == NULL) { // Does libxml2 provide any error messages for us to access? // cerr << "Couldn't parse (or open): " << fname << endl; return comp; } // Make sure the doc get's free'd no matter what xmlSmartPtr docDel( doc, xmlFreeDoc ); xmlNodePtr cur = xmlDocGetRootElement(doc); if( !cur ) { cerr << "Empty Document in file: " << fname << endl; return comp; } if(xmlStrcmp(cur->name, (const xmlChar*) "bhat")) { cerr << "Invalid root element in file: " << fname << endl; return comp; } int colcount=0; int rowcount=0; if( !getIntProperty( cur, "colcount", &colcount ) || (colcount <= 0) || (colcount > 12) ) { cerr << "Missing or invalid 'colcount' attribute in file: " << fname << endl; return comp; } if( !getIntProperty( cur, "rowcount", &rowcount ) || (rowcount <= 0) ) { cerr << "Missing or invalid 'rowcount' attribute in file: " << fname << endl; return comp; } count = rowcount / 72; const char *rowFormat = NULL; std::string rowString; // Build the format string we will use when reading in each row. // Old format was: "%lf %lf %lf %lf %lf %lf %lf %lf" for( unsigned int cnt = colcount; cnt > 0; --cnt ) { rowString += "%lf "; } rowFormat = rowString.c_str(); // Get weights creation related properties // file="FFD06_EF06_W_norm_mod6g2.mat" date="24-Oct-2006 13:48:32" cert="258:270M" char *tmpAttr = (char*)xmlGetProp( cur, (const xmlChar *)"file" ); if( tmpAttr ) sourceFile = tmpAttr; tmpAttr = (char*)xmlGetProp( cur, (const xmlChar *)"date" ); if( tmpAttr ) createDate = tmpAttr; tmpAttr = (char*)xmlGetProp( cur, (const xmlChar *)"cert" ); if( tmpAttr ) certVersion = tmpAttr; comp = (double *)malloc( sizeof(double) * rowcount * colcount ); int row=0, index=0; for( cur = cur->xmlChildrenNode; cur != NULL; cur = cur->next ) { if(!xmlStrcmp(cur->name, (const xmlChar*)"row")) { // Number of children should only be one, but we ought to check it char *rowData = (char *)xmlNodeGetContent( cur ); if( !rowData ) { cerr << "Bad row data, row " << row << ", in file: " << fname << endl; return comp; } xmlSmartPtr rowDel( rowData, (xmlSmartPtr::deleter)xmlFree ); #ifdef WIN32 double d1[12]; // this needs to be at least as large a the number of columns in the sscanf below. #else double d1[colcount]; #endif // This needs to be generalized, but will currently work for anything up to 12 columns. Dangerous, though. int cnt = sscanf( rowData, rowFormat, &d1[0],&d1[1],&d1[2],&d1[3],&d1[4],&d1[5],&d1[6],&d1[7],&d1[8],&d1[9],&d1[10],&d1[11] ); if( colcount != cnt ) { cerr << "Bad row " << row << " in file: " << fname << endl; return comp; } for( int i=0; i < colcount; ++i ) { comp[index++] = d1[i]; } ++row; } } if( row != rowcount ) { cerr << "Document's rowcount does not match actual data: " << fname << endl; return comp; } return comp; } const double * mp_BHAT::loadWeightsBinary() { double *comp = NULL; return comp; } static bool getIntProperty( xmlNodePtr cur, const char *name, int *val ) { char *prop = (char*)xmlGetProp( cur, (const xmlChar *)name ); bool good=true; if(!prop) return false; if( sscanf( prop, "%d", val ) != 1 ) good = false; xmlFree( prop ); return good; }