libeblearntools
/home/rex/ebltrunk/tools/libeblearntools/include/pascal_dataset.hpp
00001 /***************************************************************************
00002  *   Copyright (C) 2009 by Pierre Sermanet   *
00003  *   pierre.sermanet@gmail.com   *
00004  *   All rights reserved.
00005  *
00006  * Redistribution and use in source and binary forms, with or without
00007  * modification, are permitted provided that the following conditions are met:
00008  *     * Redistributions of source code must retain the above copyright
00009  *       notice, this list of conditions and the following disclaimer.
00010  *     * Redistributions in binary form must reproduce the above copyright
00011  *       notice, this list of conditions and the following disclaimer in the
00012  *       documentation and/or other materials provided with the distribution.
00013  *     * Redistribution under a license not approved by the Open Source 
00014  *       Initiative (http://www.opensource.org) must display the 
00015  *       following acknowledgement in all advertising material:
00016  *        This product includes software developed at the Courant
00017  *        Institute of Mathematical Sciences (http://cims.nyu.edu).
00018  *     * The names of the authors may not be used to endorse or promote products
00019  *       derived from this software without specific prior written permission.
00020  *
00021  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED 
00022  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
00023  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
00024  * DISCLAIMED. IN NO EVENT SHALL ThE AUTHORS BE LIABLE FOR ANY
00025  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
00026  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
00027  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
00028  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
00029  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
00030  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
00031  ***************************************************************************/
00032 
00033 #ifndef PASCAL_DATASET_HPP_
00034 #define PASCAL_DATASET_HPP_
00035 
00036 #include <algorithm>
00037 
00038 #include "xml_utils.h"
00039 #include "tools_utils.h"
00040 
00041 #ifdef __BOOST__
00042 #define BOOST_FILESYSTEM_VERSION 2
00043 #include "boost/filesystem.hpp"
00044 #include "boost/regex.hpp"
00045 using namespace boost::filesystem;
00046 using namespace boost;
00047 #endif
00048 
00049 using namespace std;
00050 
00051 namespace ebl {
00052   
00054   // constructors & initializations
00055 
00056   template <class Tdata>
00057   pascal_dataset<Tdata>::pascal_dataset(const char *name_, const char *inroot_,
00058                                         bool ignore_diff, bool ignore_trunc,
00059                                         bool ignore_occl,
00060                                         const char *annotations,
00061                                         const char *ignore_path,
00062                                         bool ignore_bb)
00063     : dataset<Tdata>(name_, inroot_),
00064       min_aspect_ratio(-1), max_aspect_ratio(-1), minborders(),
00065       max_jitter_match(1.0) {
00066     // initialize pascal-specific members
00067     // if (inroot_) {
00068     //   annroot = inroot;
00069     //   annroot += "/Annotations/"; // look for xml files in annotations
00070     //   imgroot = inroot;
00071     //   imgroot += "JPEGImages/"; // image directory
00072     // }
00073     imgroot = inroot;
00074     if (annotations && strcmp(annotations, ""))
00075       annroot = annotations;
00076     else
00077       eblerror("expected annotations folder, please specify with -annotations");
00078     if (ignore_path && strcmp(ignore_path, ""))
00079       ignore_root = ignore_path;
00080     ignore_difficult = ignore_diff;
00081     ignore_bbox = ignore_bb;
00082     ignore_truncated = ignore_trunc;
00083     ignore_occluded = ignore_occl;
00084 #ifndef __XML__ // return error if xml not enabled
00085     eblerror("XML libraries not available, install libxml++ and recompile");
00086 #endif /* __XML__ */
00087     extension = XML_PATTERN;
00088     cout << "Image search extension pattern: " << extension << endl;
00089   }
00090 
00091   template <class Tdata>
00092   pascal_dataset<Tdata>::~pascal_dataset() {
00093   }
00094 
00096   // data extraction
00097 
00098   template <class Tdata>
00099   bool pascal_dataset<Tdata>::extract() {
00100 #ifdef __BOOST__
00101 #ifdef __XML__    
00102     if (!allocated && !strcmp(save_mode.c_str(), DATASET_SAVE))
00103       return false;
00104     cout << "Extracting samples from PASCAL files into dataset..." << endl;
00105     // adding data to dataset using all xml files in annroot
00106     path p(annroot);
00107     if (!exists(p))
00108       eblerror("Annotation path " << annroot << " does not exist.");
00109     xtimer.start();
00110     processed_cnt = 0;
00111     // find all xml files recursively
00112     list<string> *files = find_fullfiles(annroot, XML_PATTERN, NULL, true,
00113                                          true);
00114     if (!files || files->size() == 0)
00115       eblerror("no xml files found in " << annroot << " using file pattern "
00116                << XML_PATTERN);
00117     cout << "Found " << files->size() << " xml files." << endl;
00118     for (list<string>::iterator i = files->begin(); i != files->end(); ++i) {
00119       this->process_xml(*i);
00120       processed_cnt++;
00121       if (full())
00122         break;
00123     }
00124     cout << "Extracted " << data_cnt << " elements into dataset." << endl;
00125     cout << "Extraction time: " << xtimer.elapsed() << endl;
00126     print_stats();
00127     if (files) delete files;
00128 #endif /* __XML__ */
00129 #endif /* __BOOST__ */
00130     return true;
00131   }
00132 
00133   template <class Tdata>
00134   void pascal_dataset<Tdata>::set_min_aspect_ratio(float minar) {
00135     cout << "Setting minimum aspect ratio to " << minar << endl;
00136     min_aspect_ratio = minar;
00137   }
00138 
00139   template <class Tdata>
00140   void pascal_dataset<Tdata>::set_max_aspect_ratio(float maxar) {
00141     cout << "Setting maximum aspect ratio to " << maxar << endl;
00142     max_aspect_ratio = maxar;
00143   }
00144 
00145   template <class Tdata>
00146   void pascal_dataset<Tdata>::set_minborders(idxdim &d) {
00147     minborders = d;
00148     cout << "Setting minimum distances allowed to image borders to " 
00149          << minborders << endl;
00150   }
00151 
00152   template <class Tdata>
00153   void pascal_dataset<Tdata>::set_max_jitter_match(float match) {
00154     max_jitter_match = match;
00155     cout << "Setting maximum jitter match between jittered rects and other "
00156          << "objects in an image to " << max_jitter_match << endl;
00157   }
00158 
00160   // data
00161 
00162 #ifdef __XML__ // disable some derived methods if XML not available
00163 #ifdef __BOOST__
00164 
00165   template <class Tdata>
00166   bool pascal_dataset<Tdata>::included(const string &class_name,
00167                                        uint difficult, uint truncated,
00168                                        uint occluded) {
00169     return dataset<Tdata>::included(class_name)
00170       && !(ignore_difficult && difficult)
00171       && !(ignore_truncated && truncated) 
00172       && !(ignore_occluded && occluded);
00173   }
00174 
00175   template <class Tdata>
00176   intg pascal_dataset<Tdata>::count_samples() {
00177     total_difficult = 0;
00178     total_truncated = 0;
00179     total_occluded = 0;
00180     total_ignored = 0;
00181     total_samples = 0;
00182     string xmlpath;
00183     path p(annroot);
00184     if (!exists(p)) 
00185       eblthrow("Annotation path " << annroot << " does not exist.");
00186     cout << "Counting number of samples in " << annroot << " ..." << endl;
00187     // find all xml files recursively
00188     list<string> *files = find_fullfiles(annroot, XML_PATTERN, NULL, false,
00189                                          true);
00190     if (!files || files->size() == 0)
00191       eblthrow("no xml files found in " << annroot << " using file pattern "
00192                << XML_PATTERN);
00193     cout << "Found " << files->size() << " xml files." << endl;
00194     for (list<string>::iterator i = files->begin(); i != files->end(); ++i) {
00195       xmlpath = *i;
00196       // parse xml
00197       DomParser parser;
00198       parser.parse_file(xmlpath);
00199       if (parser) {
00200         // initialize root node and list
00201         const Node* pNode = parser.get_document()->get_root_node();
00202         Node::NodeList list = pNode->get_children();
00203         // parse all objects in image
00204         for(Node::NodeList::iterator iter = list.begin();
00205             iter != list.end(); ++iter) {
00206           if (!strcmp((*iter)->get_name().c_str(), "object")) {
00207             // check for difficult flag in object node
00208             Node::NodeList olist = (*iter)->get_children();
00209             count_sample(olist);
00210           }
00211         }
00212       }
00213     }
00214     if (files) delete files;
00215     cout << "Found: " << total_samples << " samples, including ";
00216     cout << total_difficult << " difficult, " << total_truncated;
00217     cout << " truncated and " << total_occluded << " occluded." << endl;
00218     ignore_difficult ? cout << "Ignoring" : cout << "Using";
00219     cout << " difficult samples." << endl;
00220     ignore_truncated ? cout << "Ignoring" : cout << "Using";
00221     cout << " truncated samples." << endl;
00222     ignore_occluded ? cout << "Ignoring" : cout << "Using";
00223     cout << " occluded samples." << endl;
00224     total_samples = total_samples - total_ignored;
00225     return total_samples;
00226   }
00227 
00228   template <class Tdata>
00229   void pascal_dataset<Tdata>::count_sample(Node::NodeList &olist) {
00230     uint difficult = 0, truncated = 0, occluded = 0;
00231     string obj_classname, pose;
00232     bool pose_found = false;
00233     Node::NodeList::iterator oiter;
00234       
00235     for(oiter = olist.begin(); oiter != olist.end(); ++oiter) {
00236       if (!strcmp((*oiter)->get_name().c_str(), "difficult"))
00237         difficult = xml_get_uint(*oiter);
00238       else if (!strcmp((*oiter)->get_name().c_str(), "truncated"))
00239         truncated = xml_get_uint(*oiter);
00240       else if (!strcmp((*oiter)->get_name().c_str(), "occluded"))
00241         occluded = xml_get_uint(*oiter);
00242       else if (!strcmp((*oiter)->get_name().c_str(), "name"))
00243         xml_get_string(*oiter, obj_classname);
00244       else if (!strcmp((*oiter)->get_name().c_str(), "pose")) {
00245         xml_get_string(*oiter, pose);
00246         pose_found = true;
00247       }
00248     }
00249     
00251     // object
00252     if (!usepartsonly) {
00253       // add object's class to dataset
00254       if (included(obj_classname, difficult, truncated, occluded)) {
00255         if (usepose && pose_found) { // append pose to class name
00256           obj_classname += "_";
00257           obj_classname += pose;
00258         }
00259         if (included(obj_classname, difficult, truncated, occluded))
00260           this->add_class(obj_classname);
00261       }
00262     }
00263     // increment samples numbers
00264     total_samples++;
00265     if (difficult) total_difficult++;
00266     if (truncated) total_truncated++;
00267     if (occluded) total_occluded++;
00268     if ((difficult && ignore_difficult)
00269         || (truncated && ignore_truncated)
00270         || (occluded && ignore_occluded))
00271       total_ignored++;
00272     
00274     // parts
00275     if (useparts || usepartsonly) {
00276       string part_classname;
00277       
00278       // add part's class to dataset
00279       for(oiter = olist.begin();oiter != olist.end(); ++oiter) {
00280         if (!strcmp((*oiter)->get_name().c_str(), "part")) {
00281           // get part's name
00282           Node::NodeList plist = (*oiter)->get_children();
00283           for(Node::NodeList::iterator piter = plist.begin();
00284               piter != plist.end(); ++piter) {
00285             if (!strcmp((*piter)->get_name().c_str(), "name")) {
00286               xml_get_string(*piter, part_classname);
00287               // found a part and its name, add it
00288               if (included(part_classname, difficult, truncated, occluded)) {
00289                 if (usepose && pose_found) { // append pose to class name
00290                   part_classname += "_";
00291                   part_classname += pose;
00292                 }
00293                 if (dataset<Tdata>::included(part_classname)) {
00294                   this->add_class(part_classname);
00295                   // increment samples numbers
00296                   this->total_samples++;
00297                   if (difficult) total_difficult++;
00298                   if (truncated) total_truncated++;
00299                   if (occluded) total_occluded++;
00300                   if ((difficult && ignore_difficult)
00301                       || (truncated && ignore_truncated)
00302                       || (occluded && ignore_occluded))
00303                     total_ignored++;
00304                 }
00305               }
00306             }
00307           }
00308         }
00309       }
00310     }
00311   }
00312   
00314   // process xml
00315 
00316   template <class Tdata>
00317   bool pascal_dataset<Tdata>::process_xml(const string &xmlfile) {
00318     string image_filename, image_fullname, folder;
00319     int height = -1, width = -1, depth = -1;
00320     rect<int> *cropr = NULL;
00321 
00322     // get image's properties
00323     if (!pascal_xml::get_properties(imgroot, xmlfile, image_filename,
00324                                     image_fullname, folder, height, width,
00325                                     depth, objects, &cropr, false))
00326       return false;
00327     // get ignored boxes if present
00328     if (!ignore_root.empty()) {
00329       string bname = ebl::basename(xmlfile.c_str());
00330       string dname = ebl::dirname(image_filename.c_str());
00331       string xml, filename, fullname, folder;
00332       int h = -1, w = -1, d = -1;
00333       xml << ignore_root << "/" << dname << "/" << bname;
00334       if (file_exists(xml)) {
00335         if (!pascal_xml::get_properties(imgroot, xml, filename, fullname,
00336                                         folder, h, w, d, objects, &cropr, true))
00337           return false;
00338         // check that ignored properties matches original properties
00339         if (fullname.compare(image_fullname) || (height != h) || (width != w))
00340           eblerror("mistmatch between orignal image (" << image_fullname << ", "
00341                    << height << "x" << width << ") and ignored infos ("
00342                    << fullname << ", " << h << "x" << w << ")");
00343       } else // ignore file doesnt exist
00344         cerr << "warning: ignore xml not found: " << xml << endl;
00345     }
00346     // process objects
00347     process_objects(objects, height, width, image_fullname,
00348                     image_filename, cropr);
00349     // delete all objects
00350     for (uint i = 0; i < objects.size(); ++i)
00351       delete objects[i];
00352     objects.clear();
00353     if (cropr) delete cropr;
00354     return true;
00355   }
00356 
00358   // process all objects
00359 
00360   template <class Tdata>
00361   bool pascal_dataset<Tdata>::
00362   process_objects(const vector<object*> &objs, int height, int width,
00363                   const string &image_fullname, const string &image_filename,
00364                   const rect<int> *cropr) {
00365     idx<Tdata> *img = NULL;
00366     // process all objects
00367     for (iobj = 0; iobj < objs.size(); ++iobj) {
00368       object &o = *(objs[iobj]);
00369       if (!o.ignored) { // only process non-ignored objects
00370         if (!usepartsonly) {
00371           // check that minimum visibility ratio is respected
00372           if (minvisibility > 0.0 && o.visible) {
00373             float match = o.match(*o.visible);
00374             if (match < minvisibility) {
00375               cout << "Rejecting object " << o.id << " from " 
00376                    << image_filename << " because visible ratio ("
00377                    << match << ") is lower than "
00378                    << "limit (" << minvisibility << ")" << endl;
00379               continue ; // skip this object
00380             }
00381           }
00382           // check that minimum aspect ratio is respected
00383           if (o.height == 0) continue ; // skip this object
00384           float ar = o.width / (float) o.height;
00385           if (min_aspect_ratio >= 0.0 && ar < min_aspect_ratio) {
00386             cout << "Rejecting object " << o.id << " from " << image_filename 
00387                  << " because aspect ratio of " << o << " (" << ar 
00388                  << ") is lower than " << "limit (" << min_aspect_ratio << ")" 
00389                  << endl;
00390             continue ; // skip this object        
00391           }
00392           if (max_aspect_ratio >= 0.0 && ar > max_aspect_ratio) {
00393             cout << "Rejecting object " << o.id << " from " << image_filename 
00394                  << " because aspect ratio of " << o << " (" << ar 
00395                  << ") is bigger than " << "limit (" << max_aspect_ratio << ")" 
00396                  << endl;
00397             continue ; // skip this object
00398           }
00399           // check that object is larger than minimum size
00400           if (o.height < mindims.dim(0) || o.width < mindims.dim(1)) {
00401             cout << "Rejecting object " << o.id << " from " 
00402                  << image_filename << " because object's size ("
00403                  << o << ") is smaller than minimum size "
00404                  << "(" << mindims << ")" << endl;
00405             continue ; // skip this object
00406           }
00407           // check that bbox is not below image border distances
00408           if (!minborders.empty()) {
00409             if (height < 0 || width < 0) { // we don't know the image sizes
00410               eblerror("image sizes not found in xml");
00411             } // from now on assume all images have the same size (TODO)
00412             int hmax = height - minborders.dim(0);
00413             int wmax = width - minborders.dim(1);
00414             if (o.h0 < (int) minborders.dim(0) || o.w0 < minborders.dim(1)
00415                 || o.h0 + o.height > hmax || o.w0 + o.width > wmax) {
00416               cout << "Rejecting object " << o.id << " from " 
00417                    << image_filename << " because (cropped) bbox " << o
00418                    << " is closer to image borders (" << height << "x" << width
00419                    << ") than allowed (" << minborders << ")" << endl;
00420               continue ; // skip this object
00421             }
00422           }
00423           // process image  
00424           if (included(o.name, o.difficult, o.truncated, o.occluded)) {
00425             // load image if not already loaded
00426             if (!img) {
00427               // check that image exists
00428               if (!file_exists(image_fullname)) {
00429                 cerr << "Error: image not found " << image_fullname << endl;
00430                 return false;
00431               }
00432               input_height = height;
00433               input_width = width;
00434               //load image
00435               load_data(image_fullname);
00436             }
00437             // remove jitters that overlap with other objects
00438             if (max_jitter_match > 0)
00439               remove_jitter_matches(objs, iobj, max_jitter_match);
00440             // extract object from image
00441             process_image(load_img, o, o.name, o.difficult, 
00442                           image_fullname, o.centroid, o.visible, cropr);
00443             load_img.clear();
00444           }
00445         }
00446       }
00447     }
00448     if (img) delete img;
00449     return true;
00450   }
00451 
00452   template <class Tdata>
00453   void pascal_dataset<Tdata>::load_data(const string &fname) {
00454     // load image
00455     dataset<Tdata>::load_data(fname);
00456     // check that image matches expected sizes
00457     if (load_img.get(0).dim(0) != input_height 
00458         || load_img.get(0).dim(1) != input_width) {
00459       eblerror( "Error: expected image of size is different \
00460                  from loaded image size");
00461     }
00462   }
00463   
00465   // process object's image
00466 
00467   template <class Tdata>
00468   void pascal_dataset<Tdata>::
00469   process_image(midx<Tdata> &imgs, const rect<int> &r,
00470                 string &obj_class, uint difficult, const string &image_filename,
00471                 pair<int,int> *center, const rect<int> *visr,
00472                 const rect<int> *cropr) {
00473     t_label label = this->get_label_from_class(obj_class);
00474     rect<int> rr = r;
00475     if (ignore_bbox)
00476       rr = rect<int>(0, 0, imgs.get(0).dim(0), imgs.get(0).dim(1));
00477     add_data(imgs, label, &obj_class, image_filename.c_str(), &rr, center,
00478              visr, cropr, &objects);
00479     imgs.clear();
00480   }
00481 
00483 #endif /* __XML__ */
00484 #endif /* __BOOST__ */
00485   
00486   template <class Tdata>
00487   void pascal_dataset<Tdata>::compute_random_jitter() {
00488     // recompute all possible jitters
00489     dataset<Tdata>::compute_random_jitter();
00490     // remove jitters overlapping with other boxes given current box
00491     remove_jitter_matches(objects, iobj, max_jitter_match);
00492   }
00493 
00494   template <class Tdata>
00495   void pascal_dataset<Tdata>::
00496   remove_jitter_matches(const vector<object*> &objs,
00497                         uint iobj, float max_match) {
00498     rect<int> ri = *objs[iobj];
00499     // apply w/h ratio to object
00500     if (this->bbox_woverh > 0)
00501       ri.scale_width(this->bbox_woverh);
00502     // find ratio between current box and target
00503     float ratio = std::max(ri.height / (float) this->height,
00504                            ri.width / (float) this->width);
00505     EDEBUG("jitter ratio is " << ratio);
00506     // check overlaps with other objects
00507     vector<jitter>::iterator i = random_jitter.begin();
00508     for ( ; i != random_jitter.end(); ++i) {
00509       rect<int> rijitt = i->get_rect(ri, ratio);
00510       for (uint j = 0; j < objs.size(); ++j) {
00511         rect<int> rj = *objs[j];
00512         // scale object the same way jitter has been scaled
00513         rj.scale_centered(i->s, i->s);
00514         // apply w/h ratio to object
00515         if (this->bbox_woverh > 0)
00516           rj.scale_width(this->bbox_woverh);
00517         if (rj.match(rijitt) > max_match) {
00518           // there is a match, remove this jitter
00519           // note: erasing in a vector is not efficient because it has to move
00520           // all elements, but this should be rare enough to be better than
00521           // creating a new vector to which we add all elements.
00522           cout << "(removing jitter) match with jitter is more than "
00523                << max_match << ": " << rj << " and " << rijitt
00524                << " match by " << rj.match(rijitt) << endl;
00525           i = random_jitter.erase(i);
00526           if (i == random_jitter.end())
00527             return ;
00528           break ; // stop looking for match
00529         }
00530       }
00531     }
00532   }
00533 
00534   template <class Tdata>
00535   void pascal_dataset<Tdata>::extract_statistics() {
00536     path p(annroot);
00537     if (!exists(p))
00538       eblerror("Annotation path " << annroot << " does not exist.");
00539     // find all xml files recursively
00540     list<string> *files = find_fullfiles(annroot, XML_PATTERN, NULL, true,
00541                                          true);
00542     if (!files || files->size() == 0)
00543       eblerror("no xml files found in " << annroot << " using file pattern "
00544                << XML_PATTERN);
00545     cout << "Found " << files->size() << " xml files." << endl;
00546     // open an output file
00547     string fname;
00548     mkdir_full(outdir.c_str());
00549     fname << outdir << "/stats.csv";
00550     ofstream *fp = new ofstream(fname.c_str());
00551     if (!fp) eblerror("failed to open " << fname);
00552     cout << "Extracting statistics into " << fname << " ..." << endl;
00553     // write header
00554     *fp << "filename; height; width; h/w ratio; bbox height; bbox width; bbox h/w ratio; "
00555         << "max context;" << endl;
00556     // write stats
00557     for (list<string>::iterator i = files->begin(); i != files->end(); ++i)
00558       this->write_statistics(*i, *fp);
00559     if (files) delete files;
00560     delete fp;
00561   }
00562   
00563   template <class Tdata>
00564   void pascal_dataset<Tdata>::write_statistics(string &xml, ofstream &fp) {
00565     string image_filename, image_fullname, folder;
00566     int height = -1, width = -1, depth = -1;
00567     rect<int> *cropr = NULL;
00568 
00569     // get image's properties
00570     if (!pascal_xml::get_properties(imgroot, xml, image_filename,
00571                                     image_fullname, folder, height, width,
00572                                     depth, objects, &cropr, false))
00573       eblerror("error while getting properties of " << xml);
00574     // loop on objects
00575     for (uint i = 0; i < objects.size(); ++i) {
00576       object &o = *objects[i];
00577       // compute maximum context ratio of object
00578       float maxh = (float) std::max(0, std::min(o.h0, height - o.h0 +o.height));
00579       float maxw = (float) std::max(0, std::min(o.w0, width - o.w0 + o.width));
00580       float cratio = std::min((maxh * 2 + o.height) / o.height,
00581                               (maxw * 2 + o.width) / o.width);
00582       fp << xml << "; " << height << "; " << width 
00583          << "; " << height / (float) width << "; "
00584          << o.height << "; " << o.width << "; " << o.height / (float) o.width
00585          << "; " << cratio << ';' << endl;
00586     }
00587     // delete all objects
00588     for (uint i = 0; i < objects.size(); ++i)
00589       delete objects[i];
00590     objects.clear();
00591     if (cropr) delete cropr;    
00592   }
00593   
00594 } // end namespace ebl
00595 
00596 #endif /* PASCAL_DATASET_HPP_ */