src/qvdta/qvsampleconsensus.h

00001 /*
00002  *      Copyright (C) 2007, 2008. PARP Research Group.
00003  *      <http://perception.inf.um.es>
00004  *      University of Murcia, Spain.
00005  *
00006  *      This file is part of the QVision library.
00007  *
00008  *      QVision is free software: you can redistribute it and/or modify
00009  *      it under the terms of the GNU Lesser General Public License as
00010  *      published by the Free Software Foundation, version 3 of the License.
00011  *
00012  *      QVision is distributed in the hope that it will be useful,
00013  *      but WITHOUT ANY WARRANTY; without even the implied warranty of
00014  *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00015  *      GNU Lesser General Public License for more details.
00016  *
00017  *      You should have received a copy of the GNU Lesser General Public
00018  *      License along with QVision. If not, see <http://www.gnu.org/licenses/>.
00019  */
00020 
00021 #ifndef QVRANSAC_H
00022 #define QVRANSAC_H
00023 
00024 #include <iostream>
00025 #include <QMap>
00026 
00027 #include <qvdefines.h>
00028 
00122 template <typename Element, typename Model> class QVRANSAC
00123         {
00124         protected:
00125                 int sampleSetSize, minInliers, iteration;
00126 
00127                 // To use after initialization
00128                 QList< Element > elements;
00129                 bool continueCondition;
00130 
00131                 // To return to the user.
00132                 QList<Element> inliersSet, bestInliers;
00133                 Model maybeModel, consensusModel, bestModel;
00134 
00135                 #ifndef DOXYGEN_IGNORE_THIS
00136                 virtual bool init()
00137                         {
00138                         // Check if there are enough in-liners to work.
00139                         if (elements.size() < sampleSetSize)
00140                                 return continueCondition = false;
00141                         else
00142                                 return continueCondition = true;
00143                         }
00144 
00145 
00146                 virtual const QList< Element > getNextMaybeInliersSet()
00147                         {
00148                         QList< Element > result;
00149 
00150                         for (int i = 0; i < this->sampleSetSize; i++)
00151                                 result.append(this->elements.at( RANDOM(0, this->elements.size()-1) ));
00152 
00153                         return result;
00154                         }
00155                 #endif
00156 
00157         public:
00164                 QVRANSAC(const int sampleSetSize, const int minInliers):sampleSetSize(sampleSetSize), minInliers(minInliers) { }
00165 
00173                 virtual const bool fit(const QList<Element> &elementList, Model &model) = 0;
00174 
00182                 virtual const bool test(const Model &model, const Element &element) = 0;
00183 
00189                 void addElement(const Element &element)                                 { elements.append(element); }
00190 
00196                 const Model & getBestModel() const                                      { return bestModel; }
00197 
00203                 const QList<Element> & getBestInliers() const                           { return bestInliers; }
00204 
00210                 const int getIterations() const                                         { return iteration; }
00211 
00219                 const bool iterate(const int maxIterations)
00220                         {
00221                         if (!init())
00222                                 return false;
00223 
00224                         iteration = 0;
00225 
00226                         while(continueCondition && (iteration++) < maxIterations)
00227                                 {
00228                                 // Get feasible maybe model, and maybe inliers set.
00229                                 const QList<Element> maybeInliers = getNextMaybeInliersSet();
00230                 
00231                                 // If no feasible model was found, return the continue condition.
00232                                 if (!fit(maybeInliers, maybeModel))
00233                                         continue;
00234                 
00235                                 // Else, get consensus set.
00236                                 inliersSet.clear();
00237                 
00238                                 foreach (Element element, elements)
00239                                         if (test(maybeModel, element))
00240                                                 inliersSet.append(element);
00241                 
00242                                 // Get consensus model.
00243                                 fit(inliersSet, consensusModel);
00244                 
00245                                 if (inliersSet.size() >= minInliers && inliersSet.size() > bestInliers.size() )
00246                                         {
00247                                         bestModel = consensusModel;
00248                                         bestInliers = inliersSet;
00249                                         return true;
00250                                         }
00251                                 }
00252                         return (inliersSet.size() >= minInliers);
00253                         }
00254         };
00255 
00279 template <typename Element, typename Model> class QVPROSAC: public QVRANSAC<Element, Model>
00280         {
00281         protected:
00282                 QMap<double, Element > elementsMap;
00283 
00284                 #ifndef DOXYGEN_IGNORE_THIS
00285                 virtual const QList< Element > getNextMaybeInliersSet()
00286                         {
00287                         const int maxSize = 2* log(this->getIterations()) / log(2);
00288                         QList< Element > result;
00289 
00290                         for (int i = 0; i < this->sampleSetSize; i++)
00291                                 result.append(this->elements.at( RANDOM(0, MAX(this->sampleSetSize, maxSize-1)) ));
00292 
00293                         return result;
00294                         }
00295 
00296                 bool init()
00297                         {
00298                         const QList<double> preferenceList = elementsMap.uniqueKeys();
00299                         for (int i = 0; i < preferenceList.size(); i++)
00300                                 this->elements += elementsMap.values(preferenceList.at(i));
00301 
00302                         return QVRANSAC<Element, Model>::init();
00303                         }
00304                 #endif
00305 
00306         public:
00313                 QVPROSAC(const int sampleSetSize, const int minInliers): QVRANSAC<Element, Model>(sampleSetSize, minInliers) { }
00314 
00322                 void addElement(const Element &element, const double preference = 0)
00323                         { elementsMap.insertMulti(preference, element); }
00324         };
00325 
00326 #endif