PLearn 0.1
RGBImage.cc
Go to the documentation of this file.
00001 // -*- C++ -*-
00002 
00003 // PLearn (A C++ Machine Learning Library)
00004 // Copyright (C) 1998 Pascal Vincent
00005 // Copyright (C) 1999-2002 Pascal Vincent, Yoshua Bengio and University of Montreal
00006 //
00007 
00008 // Redistribution and use in source and binary forms, with or without
00009 // modification, are permitted provided that the following conditions are met:
00010 // 
00011 //  1. Redistributions of source code must retain the above copyright
00012 //     notice, this list of conditions and the following disclaimer.
00013 // 
00014 //  2. Redistributions in binary form must reproduce the above copyright
00015 //     notice, this list of conditions and the following disclaimer in the
00016 //     documentation and/or other materials provided with the distribution.
00017 // 
00018 //  3. The name of the authors may not be used to endorse or promote
00019 //     products derived from this software without specific prior written
00020 //     permission.
00021 // 
00022 // THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
00023 // IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
00024 // OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
00025 // NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
00026 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
00027 // TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
00028 // PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
00029 // LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
00030 // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
00031 // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
00032 // 
00033 // This file is part of the PLearn library. For more information on the PLearn
00034 // library, go to the PLearn Web site at www.plearn.org
00035 
00036 
00037  
00038 
00039 /* *******************************************************      
00040    * $Id: RGBImage.cc 5176 2006-03-13 22:40:51Z lamblin $
00041    * AUTHORS: Pascal Vincent & Yoshua Bengio
00042    * This file is part of the PLearn library.
00043    ******************************************************* */
00044 
00045 #include <fstream>
00046 #include "RGBImage.h"
00047 #include <plearn/math/TMat_maths.h>
00048 
00049 namespace PLearn {
00050 using namespace std;
00051 
00052 const RGB RGB::BLACK(0,0,0);
00053 const RGB RGB::WHITE(255,255,255);
00054 const RGB RGB::RED(255,0,0);
00055 const RGB RGB::GREEN(0,255,0);
00056 const RGB RGB::BLUE(0,0,255);
00057 const RGB RGB::DISCOCOLORS[] = {RGB::RED, RGB::GREEN, RGB::BLUE};
00058 
00059 
00060 /************/
00061 /* RGBImage */
00062 /************/
00063 
00064 RGBImage::RGBImage(int the_height, int the_width)
00065   :height_(the_height), width_(the_width)
00066 {
00067   tmpnam(tmpfilename);
00068   if(height_>0 && width_>0)
00069     {
00070       storage = new Storage<RGB>(height_*width_);
00071       clear();
00072     }
00073 }
00074 
00075 RGBImage::RGBImage(Mat r, Mat g, Mat b)
00076   :height_(r.length()), width_(r.width())
00077 {
00078   tmpnam(tmpfilename);
00079   storage = new Storage<RGB>(height_*width_);
00080   for(int i=0; i<height_; i++)
00081     for(int j=0; j<width_; j++)
00082       setPixel(i,j,RGB((unsigned char)r(i,j), (unsigned char)g(i,j), (unsigned char)b(i,j)));
00083 }
00084 
00085 
00086 RGBImage::~RGBImage()
00087 {
00088   unlink(tmpfilename);
00089 }
00090 
00091 void
00092 RGBImage::resize(int newheight, int newwidth)
00093 {
00094   if(storage)
00095     storage->resize(newheight*newwidth);
00096   else
00097     storage = new Storage<RGB>(newheight*newwidth);
00098   height_ = newheight;
00099   width_ = newwidth;
00100 }
00101 
00102 void
00103 RGBImage::removeBorders(int top_border,int bottom_border,
00104                         int left_border,int right_border)
00105 {
00106   if(!storage)
00107     PLERROR("Empty RGBImage");
00108   int newwidth = width_ - (left_border + right_border);
00109   int newheight = height_ - (top_border + bottom_border);
00110   if (newheight < 1)
00111     PLERROR("RGBImage::removeBorders(%d,%d,%d,%d): can't remove more than height_(%d)",
00112           top_border,bottom_border,left_border,right_border,height_);
00113   if (newwidth < 1)
00114     PLERROR("RGBImage::removeBorders(%d,%d,%d,%d): can't remove more than width_(%d)",
00115           top_border,bottom_border,left_border,right_border,width_);
00116   PP< Storage<RGB> > newstorage = new Storage<RGB>(newheight*newwidth);
00117   RGB* d= newstorage->data;
00118   RGB* data = storage->data;
00119   for (int r=0;r<newheight;r++)
00120     for (int c=0;c<newwidth;c++,d++)
00121       *d = data[(top_border+r)*width_+(left_border+c)];
00122   height_=newheight;
00123   width_=newwidth;
00124   storage = newstorage;
00125 }
00126 
00127 void
00128 RGBImage::fill(RGB color)
00129 {
00130   if(!storage)
00131     PLERROR("Empty RGBImage");
00132   RGB* data = storage->data;
00133   for(int pos=0; pos<height_*width_; pos++)
00134     data[pos] = color;
00135 }
00136 
00137 void 
00138 RGBImage::loadPPM(const char* filename)
00139 {
00140   int ncolors;
00141   ifstream in(filename);
00142   if(!in)
00143     PLERROR("In RGBImage::loadPPM could not open file %s for reading",filename);
00144 
00145   char imagetype[20];
00146   int newheight, newwidth;
00147   in >> imagetype >> newwidth >> newheight >> ncolors;
00148   char tmp[100];
00149   in.getline(tmp,99); // skip until end of line
00150   if(strcmp(imagetype,"P6")!=0)
00151     PLERROR("In RGBImage::loadPPM unsupported format (not a P6 PPM)");
00152   resize(newheight, newwidth);
00153   RGB* data = storage->data;
00154   PLWARNING("In RGBImage::loadPPM - Code has been changed to compile, but hasn't been tested (remove this warning if it works)");
00155   // The following line:
00156   //  in.read((unsigned char*)data,height_*width_*3);
00157   // has been replaced by:
00158   in.read((char*)data,height_*width_*3);
00159 }
00160 
00161 void 
00162 RGBImage::savePPM(const char* filename) const
00163 {
00164   if(!storage)
00165     PLERROR("Empty RGBImage");
00166   RGB* data = storage->data;
00167   ofstream out(filename);
00168   if(!out)
00169     PLERROR("In RGBImage::savePPM could not open file for writing");
00170   out << "P6\n" << width_ << ' ' << height_ << ' ' << 255 << endl;
00171   PLWARNING("In RGBImage::loadPPM - Code has been changed to compile, but hasn't been tested (remove this warning if it works)");
00172   // The following line:
00173   //  out.write((unsigned char*)data, height_*width_*3);
00174   // has been replaced by:
00175   out.write((char*)data, height_*width_*3);
00176   out.flush();
00177 }
00178 
00179 void 
00180 RGBImage::display() const
00181 {
00182   savePPM(tmpfilename);
00183   char command[1000];
00184   sprintf(command,"xv %s &",tmpfilename);
00185   system(command);
00186 }
00187 
00188 void 
00189 RGBImage::loadJPEG(const char* filename, int scale)
00190 {
00191   char command[1000];
00192   sprintf(command,"djpeg -pnm -scale 1/%d %s > %s", scale, filename, tmpfilename);
00193   system(command);
00194   cout << command << endl;
00195   loadPPM(tmpfilename);
00196   unlink(tmpfilename);
00197 }
00198 
00199 void 
00200 RGBImage::displayAndWait() const
00201 {
00202   savePPM(tmpfilename);
00203   char command[1000];
00204   sprintf(command,"xv %s",tmpfilename);
00205   system(command);
00206   unlink(tmpfilename);
00207 }
00208 
00209 void 
00210 RGBImage::shrinkToIntersection(int& i, int& j, int& h, int& w) const
00211 {
00212   int istart = MAX(0,i);
00213   int iend = MIN(i+h,height_);
00214   int jstart = MAX(0,j);
00215   int jend = MIN(j+w,width_);
00216 
00217   i = istart;
00218   j = jstart;
00219   h = MAX(iend-istart,0);
00220   w = MAX(jend-jstart,0);
00221 }
00222 
00223 void 
00224 RGBImage::blit(int desti, int destj, const RGBImage srcim)
00225 {
00226   int blit_i = desti;
00227   int blit_j = destj;
00228   int blit_h = srcim.height_;
00229   int blit_w = srcim.width_;
00230 
00231   shrinkToIntersection(blit_i,blit_j,blit_h,blit_w);
00232   if(blit_h==0 || blit_w==0)
00233     return;
00234 
00235   int src_i = blit_i-desti;
00236   int src_j = blit_j-destj;
00237 
00238   for(int i=0; i<blit_h; i++)
00239     for(int j=0; j<blit_w; j++)
00240       setPixel(blit_i+i, blit_j+j, srcim.getPixel(src_i+i,src_j+j));
00241 }
00242 
00243 RGB
00244 RGBImage::computeAverage() const
00245 {
00246   if(!storage)
00247     PLERROR("Empty RGBImage");
00248   RGB* data = storage->data;
00249   int r = 0;
00250   int g = 0;
00251   int b = 0;
00252   int npixels = height_*width_;
00253   for(int pos=0; pos<npixels; pos++)
00254     {
00255       RGB& pixl = data[pos];
00256       r += pixl.r;
00257       g += pixl.g;
00258       b += pixl.b;
00259     }
00260   return RGB(r/npixels, g/npixels, b/npixels);
00261 }
00262 
00263 Vec 
00264 RGBImage::computeHistogram(int r_bins, int g_bins, int b_bins, bool do_normalize)
00265 {
00266   Vec histo(r_bins*g_bins*b_bins);
00267   for(int i=0; i<height_; i++)
00268     for(int j=0; j<width_; j++)
00269       {
00270         RGB pix = getPixel(i,j);
00271         int r_bin = pix.r*r_bins/256;
00272         int g_bin = pix.g*g_bins/256;
00273         int b_bin = pix.b*b_bins/256;
00274         int pos =  r_bin*g_bins*b_bins + g_bin*b_bins  + b_bin;
00275         histo[pos]++;
00276       }
00277   if(do_normalize)
00278     histo /= height_*width_;
00279   return histo;
00280 }
00281 
00282 /******************/
00283 /*** RGBImageDB ***/
00284 /******************/
00285 
00286 RGBImageDB::RGBImageDB(int the_subsample_factor, int the_remove_border, int the_max_n_images_in_memory)
00287   : subsample_factor(the_subsample_factor), remove_border(the_remove_border), 
00288     max_n_images_in_memory(the_max_n_images_in_memory), n_images_in_memory(0)
00289 {
00290 }
00291 
00292 RGBImageDB::RGBImageDB(char* dbfilename, int the_subsample_factor,int the_remove_border, int the_max_n_images_in_memory)
00293   : subsample_factor(the_subsample_factor), remove_border(the_remove_border),
00294     max_n_images_in_memory(the_max_n_images_in_memory), n_images_in_memory(0)    
00295 {
00296   load(dbfilename);
00297 }
00298 
00299 void 
00300 RGBImageDB::load(char* dbfile)
00301 {
00302   ifstream in(dbfile);
00303   if(!in)
00304     PLERROR("In RGBImageDB::load could not open dbfile %s for reading",dbfile);
00305   
00306   char filename[300];
00307   int classnum;
00308 
00309   int n = 0;
00310   in >> filename >> classnum;
00311   while(in)
00312     {
00313       int imageid = append(filename);
00314       n++;
00315       imageIdAndClass.resize(n,2);
00316       imageIdAndClass(n-1,0) = imageid;
00317       imageIdAndClass(n-1,1) = classnum;
00318       in >> filename >> classnum;
00319     }
00320 }
00321 
00322 int
00323 RGBImageDB::append(char* filename)
00324 {
00325   filenames.append(strdup(filename));
00326   images.append(0);
00327   return filenames.size()-1;
00328 }
00329 
00330 RGBImage
00331 RGBImageDB::getImage(int imageid)
00332 {
00333   if(!images[imageid])
00334     {
00335       RGBImage* newimage = new RGBImage;
00336       newimage->loadJPEG(filenames[imageid],subsample_factor);
00337       if (remove_border>0)
00338         newimage->removeBorders(remove_border);
00339       n_images_in_memory++;
00340       if (n_images_in_memory>max_n_images_in_memory)
00341         {
00342           for(int i=0; i<images.size(); i++)
00343             if(images[i]!=0)
00344               {
00345                 delete images[i];
00346                 images[i] = 0;
00347               }
00348         }
00349       images[imageid] = newimage;
00350     }
00351   return *images[imageid];
00352 }
00353 
00354 
00355 Mat 
00356 RGBImageDB::computeHistogramRepresentation(int r_bins, int g_bins, int b_bins, bool do_normalize)
00357 {
00358   Mat dataset(imageIdAndClass.length(), r_bins*g_bins*b_bins+1);
00359   Mat histograms = dataset.subMatColumns(0,r_bins*g_bins*b_bins);
00360   Mat classnums = dataset.lastColumn();
00361 
00362   for(int i=0; i<imageIdAndClass.length(); i++)
00363     {
00364       RGBImage im = getImage((int)imageIdAndClass(i,0));
00365       histograms(i) << im.computeHistogram(r_bins,g_bins,b_bins,do_normalize);
00366       classnums(i,0) = imageIdAndClass(i,0);
00367     }
00368   return dataset;
00369 }
00370 
00371 
00372 RGBImageDB::~RGBImageDB()
00373 {
00374   for(int i=0; i<filenames.size(); i++)
00375     {
00376       free(filenames[i]);
00377       if(images[i])
00378         delete images[i];
00379     }
00380 }
00381 
00382 
00383 
00384 
00385 /*****************************/
00386 /*** RGBImageVMatrix ***/
00387 /*****************************/
00388 
00389 RGBImageVMatrix::
00390 RGBImageVMatrix(RGBImage the_image,
00391                      const Vec& drow, const Vec& dcol,
00392                      real the_scale,
00393                      real the_offset)
00394   :image(the_image),
00395    delta_row(drow), delta_column(dcol),
00396    scale(the_scale), offset_(the_offset)
00397 {
00398   if (delta_row.length()!=delta_column.length())
00399     PLERROR("RGBImageVMatrix: delta_row(%d) and delta_column(%d) have"
00400           " differing sizes",delta_row.length(),delta_column.length());
00401   width_ = 3 * (delta_row.length() + 1);
00402   if (delta_row.length()>0)
00403     {
00404       max_delta_row = (int)max(delta_row);
00405       int min_delta_row = (int)min(delta_row);
00406       max_delta_column = (int)max(delta_column);
00407       int min_delta_column = (int)min(delta_column);
00408       first_row = MAX(0,-min_delta_row);
00409       first_column = MAX(0,-min_delta_column);
00410       bottom_border_row = MIN(image.height(),image.height()-max_delta_row);
00411       right_border_col = MIN(image.width(),image.width()-max_delta_column);
00412     }
00413   else
00414     {
00415       first_row = 0;
00416       first_column = 0;
00417       bottom_border_row = image.height();
00418       right_border_col = image.width();
00419     }
00420   n_cols = right_border_col - first_column;
00421   reset();
00422 }
00423 
00424 void RGBImageVMatrix::setImage(RGBImage new_image)
00425 {
00426   image = new_image;
00427   if (delta_row.length()>0)
00428     {
00429       bottom_border_row = 
00430         MIN(image.height(),image.height()-max_delta_row);
00431       right_border_col = 
00432         MIN(image.width(),image.width()-max_delta_column);
00433     }
00434   else
00435     {
00436       bottom_border_row = image.height();
00437       right_border_col = image.width();
00438     }
00439   n_cols = right_border_col - first_column;
00440   reset();
00441 }
00442 
00443 int RGBImageVMatrix::length()
00444 { return (bottom_border_row-first_row)*n_cols; }
00445 
00446 int RGBImageVMatrix::width() 
00447 { return width_; }
00448 
00449 void RGBImageVMatrix::reset()
00450 { 
00451   current_i=first_row;
00452   current_j=first_column;
00453 }
00454 
00455 void RGBImageVMatrix::seek(int position)
00456 {
00457   if (position>=length())
00458     PLERROR("RGBImageVMatrix::seek(%d > size=%d)",position,length());
00459   int i = position / n_cols;
00460   current_i = first_row + i;
00461   current_j = first_column + position - i*n_cols;
00462 }
00463 
00464 int RGBImageVMatrix::position()
00465 {
00466   return (current_j-first_column) + (current_i-first_row)*n_cols;
00467 }
00468 
00469 void RGBImageVMatrix::sample(Vec& samplevec)
00470 {
00471   samplevec.resize(width_);
00472   real *sample = samplevec.data();
00473   real *dr = delta_row.data();
00474   real *dc = delta_column.data();
00475   int np = delta_row.length();
00476   // copy the value of its neighbors
00477   for (int p=0; p<np; p++) {
00478     int offset_i=(int)dr[p];
00479     int offset_j=(int)dc[p];
00480     RGB parent_rgb = image.getPixel(current_i+offset_i,current_j+offset_j);
00481     *(sample++) = (parent_rgb.r+offset_)*scale;
00482     *(sample++) = (parent_rgb.g+offset_)*scale;
00483     *(sample++) = (parent_rgb.b+offset_)*scale;
00484   }   
00485 
00486   // copy the value of the pixel
00487   RGB target_rgb = image.getPixel(current_i,current_j);
00488   *(sample++) = (target_rgb.r+offset_)*scale;
00489   *(sample++) = (target_rgb.g+offset_)*scale;
00490   *(sample++) = (target_rgb.b+offset_)*scale;
00491 
00492   // update current_i and current_j, traversing in
00493   // the usual left-to-right / top-to-bottom order:
00494   current_j++;
00495   if (current_j==right_border_col)
00496     {
00497       current_j = first_column;
00498       current_i++;
00499       if (current_i==bottom_border_row)
00500         current_i = first_row;
00501     }
00502 }
00503 
00504 /*****************************/
00505 /*** RGBImagesVMatrix ***/
00506 /*****************************/
00507 
00508 RGBImagesVMatrix::
00509 RGBImagesVMatrix(RGBImageDB& imagesdb,
00510                       const Vec& delta_row,
00511                       const Vec& delta_col, 
00512                       bool appendclass,
00513                       real scale,
00514                       real offset_)
00515   :image_distr(imagesdb.getImage((int)imagesdb.imageIdAndClass(0,0)),delta_row,delta_col,scale,offset_),
00516    images_db(imagesdb), current_image(0), append_class(appendclass),
00517    length_(0), image_start(imagesdb.imageIdAndClass.length()) 
00518 { 
00519   width_ = 3*(delta_row.length()+1);
00520   if(append_class)
00521     width_++;
00522 
00523   for (int i=0;i<images_db.imageIdAndClass.length();i++)
00524     {
00525       image_start[i] = length_;
00526       image_distr.setImage(images_db.getImage((int)imagesdb.imageIdAndClass(i,0)));
00527       length_ += image_distr.length();
00528     }
00529   if (append_class)
00530     {
00531       int n = image_distr.width();
00532       pixelsAndClass.resize(n+1);
00533       pixels = pixelsAndClass.subVec(0,n);
00534     }
00535 }
00536 
00537 int RGBImagesVMatrix::length() 
00538 { return length_; }
00539 
00540 int RGBImagesVMatrix::width() 
00541 { return width_; }
00542 
00543 void RGBImagesVMatrix::reset() 
00544 { 
00545   current_image = 0;
00546   image_distr.setImage(images_db.getImage((int)images_db.imageIdAndClass(0,0)));
00547   image_distr.reset();
00548 }
00549 
00550 void RGBImagesVMatrix::sample(Vec& samplevec) 
00551 { 
00552   samplevec.resize(width_);
00553   if (append_class)
00554     {
00555       Vec tmpvec = samplevec.subVec(0,width_-1);
00556       image_distr.sample(tmpvec);
00557       samplevec[width_-1] = images_db.imageIdAndClass(current_image,1);
00558     }
00559   else
00560     image_distr.sample(samplevec);
00561       
00562   if (image_distr.first())
00563     {
00564       current_image++;
00565       if (current_image == images_db.imageIdAndClass.length())
00566         current_image = 0;
00567       image_distr.setImage(images_db.getImage((int)images_db.imageIdAndClass(current_image,0)));
00568     }
00569 }  
00570 
00571 
00572 void RGBImagesVMatrix::seek(int position)
00573 {
00574   int n=images_db.imageIdAndClass.length();
00575   int i;
00576   for (i=0;i<n && position>=image_start[i];i++);
00577   current_image = i-1;
00578   image_distr.setImage(images_db.getImage((int)images_db.imageIdAndClass(current_image,0)));
00579   image_distr.seek(int(position-image_start[current_image]));
00580 }
00581      
00582   
00583 int RGBImagesVMatrix::position()
00584 {
00585   return (int)image_start[current_image] + image_distr.position();
00586 }
00587 
00588 bool RGBImagesVMatrix::firstSampleOfObject()
00589 {
00590   return image_distr.first();
00591 }
00592 
00593 int RGBImagesVMatrix::nSamplesOfObject()
00594 {
00595   return image_distr.length();
00596 }
00597 
00598 
00599 int RGBImagesVMatrix::nObjects()
00600 {
00601   return images_db.imageIdAndClass.length(); 
00602 }
00603 
00604 
00605 /*** ImageVMatrix ***/
00606 
00607 /*
00608 
00609 ImageVMatrix::ImageVMatrix(const Mat& the_data, 
00610                                      int the_ncol_image, const Mat& the_parents,
00611                                      int the_border_size=1, real the_scale=1.0,
00612                                      real the_offset=0.0)
00613   :data(the_data), n_values_per_pixel(the_data.width()), ncol_image(the_ncol_image), 
00614    parents(the_parents), border_size(the_border_size), 
00615    scale(the_scale), offset(the_offset)
00616 {
00617      width_ = n_values_per_pixel * (the_parents.length() + 1);
00618      reset();
00619 }
00620 
00621 
00622 void ImageVMatrix::nextPixel()
00623 {
00624   // skip the first row and the first column in the image
00625   // this will be changed
00626   currentpos++;
00627   while ((currentpos % ncol_image < border_size) ||
00628          (currentpos % ncol_image >= ncol_image - border_size))
00629     currentpos++;
00630   if(currentpos>=data.length()-ncol_image*border_size) // cycling
00631     reset();
00632 }
00633 
00634 void ImageVMatrix::reset()
00635 { 
00636   currentpos = border_size*ncol_image; 
00637   nextPixel();
00638 }
00639 
00640 void ImageVMatrix::sample(Vec& samplevec)
00641 {
00642   samplevec.resize(width_);
00643   real *sample = samplevec.data();
00644   real *pixel_value;
00645 
00646   // copy the value of its parents
00647   for (int p=0; p<parents.length(); p++) 
00648     {
00649       int offset_x=(int)parents(p,0);
00650       int offset_y=(int)parents(p,1);
00651       pixel_value = data[currentpos+offset_x+ncol_image*offset_y]; 
00652       for (int i=0; i<n_values_per_pixel; i++)
00653         sample[i] = (pixel_value[i]+offset_) * scale;
00654     }   
00655 
00656   // copy the value of the pixel
00657   pixel_value=data[currentpos];
00658   for (int i=0; i<n_values_per_pixel; i++)
00659     sample[i + n_values_per_pixel * parents.length()]
00660       = (pixel_value[i]+offset_) * scale;
00661 
00662   nextPixel();
00663 }
00664 
00665 int ImageVMatrix::length()
00666 { return data.length() - 2*border_size*ncol_image -
00667     2*border_size*(data.length()/ncol_image-2*border_size); }
00668 
00669 int ImageVMatrix::width()
00670 { return width_; }
00671 
00672 */
00673 
00674 } // end of namespace PLearn
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines