Home | History | Annotate | Download | only in src
      1 /*M///////////////////////////////////////////////////////////////////////////////////////
      2 //
      3 //  IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
      4 //
      5 //  By downloading, copying, installing or using the software you agree to this license.
      6 //  If you do not agree to this license, do not download, install,
      7 //  copy or use the software.
      8 //
      9 //
     10 //                           License Agreement
     11 //                For Open Source Computer Vision Library
     12 //
     13 // Copyright (C) 2000-2008, Intel Corporation, all rights reserved.
     14 // Copyright (C) 2009, Willow Garage Inc., all rights reserved.
     15 // Third party copyrights are property of their respective owners.
     16 //
     17 // Redistribution and use in source and binary forms, with or without modification,
     18 // are permitted provided that the following conditions are met:
     19 //
     20 //   * Redistribution's of source code must retain the above copyright notice,
     21 //     this list of conditions and the following disclaimer.
     22 //
     23 //   * Redistribution's in binary form must reproduce the above copyright notice,
     24 //     this list of conditions and the following disclaimer in the documentation
     25 //     and/or other materials provided with the distribution.
     26 //
     27 //   * The name of the copyright holders may not be used to endorse or promote products
     28 //     derived from this software without specific prior written permission.
     29 //
     30 // This software is provided by the copyright holders and contributors "as is" and
     31 // any express or implied warranties, including, but not limited to, the implied
     32 // warranties of merchantability and fitness for a particular purpose are disclaimed.
     33 // In no event shall the Intel Corporation or contributors be liable for any direct,
     34 // indirect, incidental, special, exemplary, or consequential damages
     35 // (including, but not limited to, procurement of substitute goods or services;
     36 // loss of use, data, or profits; or business interruption) however caused
     37 // and on any theory of liability, whether in contract, strict liability,
     38 // or tort (including negligence or otherwise) arising in any way out of
     39 // the use of this software, even if advised of the possibility of such damage.
     40 //
     41 //M*/
     42 
     43 /*
     44  * Implementation of the paper Shape Matching and Object Recognition Using Shape Contexts
     45  * Belongie et al., 2002 by Juan Manuel Perez for GSoC 2013.
     46  */
     47 
     48 #include "precomp.hpp"
     49 #include "opencv2/core.hpp"
     50 #include "scd_def.hpp"
     51 #include <limits>
     52 
     53 namespace cv
     54 {
     55 class ShapeContextDistanceExtractorImpl : public ShapeContextDistanceExtractor
     56 {
     57 public:
     58     /* Constructors */
     59     ShapeContextDistanceExtractorImpl(int _nAngularBins, int _nRadialBins, float _innerRadius, float _outerRadius, int _iterations,
     60                                       const Ptr<HistogramCostExtractor> &_comparer, const Ptr<ShapeTransformer> &_transformer)
     61     {
     62         nAngularBins=_nAngularBins;
     63         nRadialBins=_nRadialBins;
     64         innerRadius=_innerRadius;
     65         outerRadius=_outerRadius;
     66         rotationInvariant=false;
     67         comparer=_comparer;
     68         iterations=_iterations;
     69         transformer=_transformer;
     70         bendingEnergyWeight=0.3f;
     71         imageAppearanceWeight=0.0f;
     72         shapeContextWeight=1.0f;
     73         sigma=10.0f;
     74         name_ = "ShapeDistanceExtractor.SCD";
     75     }
     76 
     77     /* Destructor */
     78     ~ShapeContextDistanceExtractorImpl()
     79     {
     80     }
     81 
     82     //! the main operator
     83     virtual float computeDistance(InputArray contour1, InputArray contour2);
     84 
     85     //! Setters/Getters
     86     virtual void setAngularBins(int _nAngularBins){CV_Assert(_nAngularBins>0); nAngularBins=_nAngularBins;}
     87     virtual int getAngularBins() const {return nAngularBins;}
     88 
     89     virtual void setRadialBins(int _nRadialBins){CV_Assert(_nRadialBins>0); nRadialBins=_nRadialBins;}
     90     virtual int getRadialBins() const {return nRadialBins;}
     91 
     92     virtual void setInnerRadius(float _innerRadius) {CV_Assert(_innerRadius>0); innerRadius=_innerRadius;}
     93     virtual float getInnerRadius() const {return innerRadius;}
     94 
     95     virtual void setOuterRadius(float _outerRadius) {CV_Assert(_outerRadius>0); outerRadius=_outerRadius;}
     96     virtual float getOuterRadius() const {return outerRadius;}
     97 
     98     virtual void setRotationInvariant(bool _rotationInvariant) {rotationInvariant=_rotationInvariant;}
     99     virtual bool getRotationInvariant() const {return rotationInvariant;}
    100 
    101     virtual void setCostExtractor(Ptr<HistogramCostExtractor> _comparer) { comparer = _comparer; }
    102     virtual Ptr<HistogramCostExtractor> getCostExtractor() const { return comparer; }
    103 
    104     virtual void setShapeContextWeight(float _shapeContextWeight) {shapeContextWeight=_shapeContextWeight;}
    105     virtual float getShapeContextWeight() const {return shapeContextWeight;}
    106 
    107     virtual void setImageAppearanceWeight(float _imageAppearanceWeight) {imageAppearanceWeight=_imageAppearanceWeight;}
    108     virtual float getImageAppearanceWeight() const {return imageAppearanceWeight;}
    109 
    110     virtual void setBendingEnergyWeight(float _bendingEnergyWeight) {bendingEnergyWeight=_bendingEnergyWeight;}
    111     virtual float getBendingEnergyWeight() const {return bendingEnergyWeight;}
    112 
    113     virtual void setStdDev(float _sigma) {sigma=_sigma;}
    114     virtual float getStdDev() const {return sigma;}
    115 
    116     virtual void setImages(InputArray _image1, InputArray _image2)
    117     {
    118         Mat image1_=_image1.getMat(), image2_=_image2.getMat();
    119         CV_Assert((image1_.depth()==0) && (image2_.depth()==0));
    120         image1=image1_;
    121         image2=image2_;
    122     }
    123 
    124     virtual void getImages(OutputArray _image1, OutputArray _image2) const
    125     {
    126         CV_Assert((!image1.empty()) && (!image2.empty()));
    127         _image1.create(image1.size(), image1.type());
    128         _image2.create(image2.size(), image2.type());
    129         _image1.getMat()=image1;
    130         _image2.getMat()=image2;
    131     }
    132 
    133     virtual void setIterations(int _iterations) {CV_Assert(_iterations>0); iterations=_iterations;}
    134     virtual int getIterations() const {return iterations;}
    135 
    136     virtual void setTransformAlgorithm(Ptr<ShapeTransformer> _transformer) {transformer=_transformer;}
    137     virtual Ptr<ShapeTransformer> getTransformAlgorithm() const {return transformer;}
    138 
    139     //! write/read
    140     virtual void write(FileStorage& fs) const
    141     {
    142         fs << "name" << name_
    143            << "nRads" << nRadialBins
    144            << "nAngs" << nAngularBins
    145            << "iters" << iterations
    146            << "img_1" << image1
    147            << "img_2" << image2
    148            << "beWei" << bendingEnergyWeight
    149            << "scWei" << shapeContextWeight
    150            << "iaWei" << imageAppearanceWeight
    151            << "costF" << costFlag
    152            << "rotIn" << rotationInvariant
    153            << "sigma" << sigma;
    154     }
    155 
    156     virtual void read(const FileNode& fn)
    157     {
    158         CV_Assert( (String)fn["name"] == name_ );
    159         nRadialBins = (int)fn["nRads"];
    160         nAngularBins = (int)fn["nAngs"];
    161         iterations = (int)fn["iters"];
    162         bendingEnergyWeight = (float)fn["beWei"];
    163         shapeContextWeight = (float)fn["scWei"];
    164         imageAppearanceWeight = (float)fn["iaWei"];
    165         costFlag = (int)fn["costF"];
    166         sigma = (float)fn["sigma"];
    167     }
    168 
    169 protected:
    170     int nAngularBins;
    171     int nRadialBins;
    172     float innerRadius;
    173     float outerRadius;
    174     bool rotationInvariant;
    175     int costFlag;
    176     int iterations;
    177     Ptr<ShapeTransformer> transformer;
    178     Ptr<HistogramCostExtractor> comparer;
    179     Mat image1;
    180     Mat image2;
    181     float bendingEnergyWeight;
    182     float imageAppearanceWeight;
    183     float shapeContextWeight;
    184     float sigma;
    185     String name_;
    186 };
    187 
    188 float ShapeContextDistanceExtractorImpl::computeDistance(InputArray contour1, InputArray contour2)
    189 {
    190     // Checking //
    191     Mat sset1=contour1.getMat(), sset2=contour2.getMat(), set1, set2;
    192     if (set1.type() != CV_32F)
    193         sset1.convertTo(set1, CV_32F);
    194     else
    195         sset1.copyTo(set1);
    196 
    197     if (set2.type() != CV_32F)
    198         sset2.convertTo(set2, CV_32F);
    199     else
    200         sset2.copyTo(set2);
    201 
    202     CV_Assert((set1.channels()==2) && (set1.cols>0));
    203     CV_Assert((set2.channels()==2) && (set2.cols>0));
    204     if (imageAppearanceWeight!=0)
    205     {
    206         CV_Assert((!image1.empty()) && (!image2.empty()));
    207     }
    208 
    209     // Initializing Extractor, Descriptor structures and Matcher //
    210     SCD set1SCE(nAngularBins, nRadialBins, innerRadius, outerRadius, rotationInvariant);
    211     Mat set1SCD;
    212     SCD set2SCE(nAngularBins, nRadialBins, innerRadius, outerRadius, rotationInvariant);
    213     Mat set2SCD;
    214     SCDMatcher matcher;
    215     std::vector<DMatch> matches;
    216 
    217     // Distance components (The output is a linear combination of these 3) //
    218     float sDistance=0, bEnergy=0, iAppearance=0;
    219     float beta;
    220 
    221     // Initializing some variables //
    222     std::vector<int> inliers1, inliers2;
    223 
    224     Ptr<ThinPlateSplineShapeTransformer> transDown = transformer.dynamicCast<ThinPlateSplineShapeTransformer>();
    225 
    226     Mat warpedImage;
    227     int ii, jj, pt;
    228 
    229     for (ii=0; ii<iterations; ii++)
    230     {
    231         // Extract SCD descriptor in the set1 //
    232         set1SCE.extractSCD(set1, set1SCD, inliers1);
    233 
    234         // Extract SCD descriptor of the set2 (TARGET) //
    235         set2SCE.extractSCD(set2, set2SCD, inliers2, set1SCE.getMeanDistance());
    236 
    237         // regularization parameter with annealing rate annRate //
    238         beta=set1SCE.getMeanDistance();
    239         beta *= beta;
    240 
    241         // match //
    242         matcher.matchDescriptors(set1SCD, set2SCD, matches, comparer, inliers1, inliers2);
    243 
    244         // apply TPS transform //
    245         if ( !transDown.empty() )
    246             transDown->setRegularizationParameter(beta);
    247         transformer->estimateTransformation(set1, set2, matches);
    248         bEnergy += transformer->applyTransformation(set1, set1);
    249 
    250         // Image appearance //
    251         if (imageAppearanceWeight!=0)
    252         {
    253             // Have to accumulate the transformation along all the iterations
    254             if (ii==0)
    255             {
    256                 if ( !transDown.empty() )
    257                 {
    258                     image2.copyTo(warpedImage);
    259                 }
    260                 else
    261                 {
    262                     image1.copyTo(warpedImage);
    263                 }
    264             }
    265             transformer->warpImage(warpedImage, warpedImage);
    266         }
    267     }
    268 
    269     Mat gaussWindow, diffIm;
    270     if (imageAppearanceWeight!=0)
    271     {
    272         // compute appearance cost
    273         if ( !transDown.empty() )
    274         {
    275             resize(warpedImage, warpedImage, image1.size());
    276             Mat temp=(warpedImage-image1);
    277             multiply(temp, temp, diffIm);
    278         }
    279         else
    280         {
    281             resize(warpedImage, warpedImage, image2.size());
    282             Mat temp=(warpedImage-image2);
    283             multiply(temp, temp, diffIm);
    284         }
    285         gaussWindow = Mat::zeros(warpedImage.rows, warpedImage.cols, CV_32F);
    286         for (pt=0; pt<sset1.cols; pt++)
    287         {
    288             Point2f p = sset1.at<Point2f>(0,pt);
    289             for (ii=0; ii<diffIm.rows; ii++)
    290             {
    291                 for (jj=0; jj<diffIm.cols; jj++)
    292                 {
    293                     float val = float(std::exp( -float( (p.x-jj)*(p.x-jj) + (p.y-ii)*(p.y-ii) )/(2*sigma*sigma) ) / (sigma*sigma*2*CV_PI));
    294                     gaussWindow.at<float>(ii,jj) += val;
    295                 }
    296             }
    297         }
    298 
    299         Mat appIm(diffIm.rows, diffIm.cols, CV_32F);
    300         for (ii=0; ii<diffIm.rows; ii++)
    301         {
    302             for (jj=0; jj<diffIm.cols; jj++)
    303             {
    304                 float elema=float( diffIm.at<uchar>(ii,jj) )/255;
    305                 float elemb=gaussWindow.at<float>(ii,jj);
    306                 appIm.at<float>(ii,jj) = elema*elemb;
    307             }
    308         }
    309         iAppearance = float(cv::sum(appIm)[0]/sset1.cols);
    310     }
    311     sDistance = matcher.getMatchingCost();
    312 
    313     return (sDistance*shapeContextWeight+bEnergy*bendingEnergyWeight+iAppearance*imageAppearanceWeight);
    314 }
    315 
    316 Ptr <ShapeContextDistanceExtractor> createShapeContextDistanceExtractor(int nAngularBins, int nRadialBins, float innerRadius, float outerRadius, int iterations,
    317                                                                         const Ptr<HistogramCostExtractor> &comparer, const Ptr<ShapeTransformer> &transformer)
    318 {
    319     return Ptr <ShapeContextDistanceExtractor> ( new ShapeContextDistanceExtractorImpl(nAngularBins, nRadialBins, innerRadius,
    320                                                                                        outerRadius, iterations, comparer, transformer) );
    321 }
    322 
    323 //! SCD
    324 void SCD::extractSCD(cv::Mat &contour, cv::Mat &descriptors, const std::vector<int> &queryInliers, const float _meanDistance)
    325 {
    326     cv::Mat contourMat = contour;
    327     cv::Mat disMatrix = cv::Mat::zeros(contourMat.cols, contourMat.cols, CV_32F);
    328     cv::Mat angleMatrix = cv::Mat::zeros(contourMat.cols, contourMat.cols, CV_32F);
    329 
    330     std::vector<double> logspaces, angspaces;
    331     logarithmicSpaces(logspaces);
    332     angularSpaces(angspaces);
    333     buildNormalizedDistanceMatrix(contourMat, disMatrix, queryInliers, _meanDistance);
    334     buildAngleMatrix(contourMat, angleMatrix);
    335 
    336     // Now, build the descriptor matrix (each row is a point) //
    337     descriptors = cv::Mat::zeros(contourMat.cols, descriptorSize(), CV_32F);
    338 
    339     for (int ptidx=0; ptidx<contourMat.cols; ptidx++)
    340     {
    341         for (int cmp=0; cmp<contourMat.cols; cmp++)
    342         {
    343             if (ptidx==cmp) continue;
    344             if ((int)queryInliers.size()>0)
    345             {
    346                 if (queryInliers[ptidx]==0 || queryInliers[cmp]==0) continue; //avoid outliers
    347             }
    348 
    349             int angidx=-1, radidx=-1;
    350             for (int i=0; i<nRadialBins; i++)
    351             {
    352                 if (disMatrix.at<float>(ptidx, cmp)<logspaces[i])
    353                 {
    354                     radidx=i;
    355                     break;
    356                 }
    357             }
    358             for (int i=0; i<nAngularBins; i++)
    359             {
    360                 if (angleMatrix.at<float>(ptidx, cmp)<angspaces[i])
    361                 {
    362                     angidx=i;
    363                     break;
    364                 }
    365             }
    366             if (angidx!=-1 && radidx!=-1)
    367             {
    368                 int idx = angidx+radidx*nAngularBins;
    369                 descriptors.at<float>(ptidx, idx)++;
    370             }
    371         }
    372     }
    373 }
    374 
    375 void SCD::logarithmicSpaces(std::vector<double> &vecSpaces) const
    376 {
    377     double logmin=log10(innerRadius);
    378     double logmax=log10(outerRadius);
    379     double delta=(logmax-logmin)/(nRadialBins-1);
    380     double accdelta=0;
    381 
    382     for (int i=0; i<nRadialBins; i++)
    383     {
    384         double val = std::pow(10,logmin+accdelta);
    385         vecSpaces.push_back(val);
    386         accdelta += delta;
    387     }
    388 }
    389 
    390 void SCD::angularSpaces(std::vector<double> &vecSpaces) const
    391 {
    392     double delta=2*CV_PI/nAngularBins;
    393     double val=0;
    394 
    395     for (int i=0; i<nAngularBins; i++)
    396     {
    397         val += delta;
    398         vecSpaces.push_back(val);
    399     }
    400 }
    401 
    402 void SCD::buildNormalizedDistanceMatrix(cv::Mat &contour, cv::Mat &disMatrix, const std::vector<int> &queryInliers, const float _meanDistance)
    403 {
    404     cv::Mat contourMat = contour;
    405     cv::Mat mask(disMatrix.rows, disMatrix.cols, CV_8U);
    406 
    407     for (int i=0; i<contourMat.cols; i++)
    408     {
    409       for (int j=0; j<contourMat.cols; j++)
    410       {
    411           disMatrix.at<float>(i,j) = (float)norm( cv::Mat(contourMat.at<cv::Point2f>(0,i)-contourMat.at<cv::Point2f>(0,j)), cv::NORM_L2 );
    412           if (_meanDistance<0)
    413           {
    414               if (queryInliers.size()>0)
    415               {
    416                   mask.at<char>(i,j)=char(queryInliers[j] && queryInliers[i]);
    417               }
    418               else
    419               {
    420                   mask.at<char>(i,j)=1;
    421               }
    422           }
    423       }
    424     }
    425 
    426     if (_meanDistance<0)
    427     {
    428       meanDistance=(float)mean(disMatrix, mask)[0];
    429     }
    430     else
    431     {
    432       meanDistance=_meanDistance;
    433     }
    434     disMatrix/=meanDistance+FLT_EPSILON;
    435 }
    436 
    437 void SCD::buildAngleMatrix(cv::Mat &contour, cv::Mat &angleMatrix) const
    438 {
    439     cv::Mat contourMat = contour;
    440 
    441     // if descriptor is rotationInvariant compute massCenter //
    442     cv::Point2f massCenter(0,0);
    443     if (rotationInvariant)
    444     {
    445         for (int i=0; i<contourMat.cols; i++)
    446         {
    447             massCenter+=contourMat.at<cv::Point2f>(0,i);
    448         }
    449         massCenter.x=massCenter.x/(float)contourMat.cols;
    450         massCenter.y=massCenter.y/(float)contourMat.cols;
    451     }
    452 
    453 
    454     for (int i=0; i<contourMat.cols; i++)
    455     {
    456         for (int j=0; j<contourMat.cols; j++)
    457         {
    458             if (i==j)
    459             {
    460                 angleMatrix.at<float>(i,j)=0.0;
    461             }
    462             else
    463             {
    464                 cv::Point2f dif = contourMat.at<cv::Point2f>(0,i) - contourMat.at<cv::Point2f>(0,j);
    465                 angleMatrix.at<float>(i,j) = std::atan2(dif.y, dif.x);
    466 
    467                 if (rotationInvariant)
    468                 {
    469                     cv::Point2f refPt = contourMat.at<cv::Point2f>(0,i) - massCenter;
    470                     float refAngle = atan2(refPt.y, refPt.x);
    471                     angleMatrix.at<float>(i,j) -= refAngle;
    472                 }
    473                 angleMatrix.at<float>(i,j) = float(fmod(double(angleMatrix.at<float>(i,j)+(double)FLT_EPSILON),2*CV_PI)+CV_PI);
    474             }
    475         }
    476     }
    477 }
    478 
    479 //! SCDMatcher
    480 void SCDMatcher::matchDescriptors(cv::Mat &descriptors1, cv::Mat &descriptors2, std::vector<cv::DMatch> &matches,
    481                                   cv::Ptr<cv::HistogramCostExtractor> &comparer, std::vector<int> &inliers1, std::vector<int> &inliers2)
    482 {
    483     matches.clear();
    484 
    485     // Build the cost Matrix between descriptors //
    486     cv::Mat costMat;
    487     buildCostMatrix(descriptors1, descriptors2, costMat, comparer);
    488 
    489     // Solve the matching problem using the hungarian method //
    490     hungarian(costMat, matches, inliers1, inliers2, descriptors1.rows, descriptors2.rows);
    491 }
    492 
    493 void SCDMatcher::buildCostMatrix(const cv::Mat &descriptors1, const cv::Mat &descriptors2,
    494                                  cv::Mat &costMatrix, cv::Ptr<cv::HistogramCostExtractor> &comparer) const
    495 {
    496     comparer->buildCostMatrix(descriptors1, descriptors2, costMatrix);
    497 }
    498 
    499 void SCDMatcher::hungarian(cv::Mat &costMatrix, std::vector<cv::DMatch> &outMatches, std::vector<int> &inliers1,
    500                            std::vector<int> &inliers2, int sizeScd1, int sizeScd2)
    501 {
    502     std::vector<int> free(costMatrix.rows, 0), collist(costMatrix.rows, 0);
    503     std::vector<int> matches(costMatrix.rows, 0), colsol(costMatrix.rows), rowsol(costMatrix.rows);
    504     std::vector<float> d(costMatrix.rows), pred(costMatrix.rows), v(costMatrix.rows);
    505 
    506     const float LOWV = 1e-10f;
    507     bool unassignedfound;
    508     int  i=0, imin=0, numfree=0, prvnumfree=0, f=0, i0=0, k=0, freerow=0;
    509     int  j=0, j1=0, j2=0, endofpath=0, last=0, low=0, up=0;
    510     float min=0, h=0, umin=0, usubmin=0, v2=0;
    511 
    512     // COLUMN REDUCTION //
    513     for (j = costMatrix.rows-1; j >= 0; j--)
    514     {
    515         // find minimum cost over rows.
    516         min = costMatrix.at<float>(0,j);
    517         imin = 0;
    518         for (i = 1; i < costMatrix.rows; i++)
    519         if (costMatrix.at<float>(i,j) < min)
    520         {
    521             min = costMatrix.at<float>(i,j);
    522             imin = i;
    523         }
    524         v[j] = min;
    525 
    526         if (++matches[imin] == 1)
    527         {
    528             rowsol[imin] = j;
    529             colsol[j] = imin;
    530         }
    531         else
    532         {
    533             colsol[j]=-1;
    534         }
    535     }
    536 
    537     // REDUCTION TRANSFER //
    538     for (i=0; i<costMatrix.rows; i++)
    539     {
    540         if (matches[i] == 0)
    541         {
    542             free[numfree++] = i;
    543         }
    544         else
    545         {
    546             if (matches[i] == 1)
    547             {
    548                 j1=rowsol[i];
    549                 min=std::numeric_limits<float>::max();
    550                 for (j=0; j<costMatrix.rows; j++)
    551                 {
    552                     if (j!=j1)
    553                     {
    554                         if (costMatrix.at<float>(i,j)-v[j] < min)
    555                         {
    556                             min=costMatrix.at<float>(i,j)-v[j];
    557                         }
    558                     }
    559                 }
    560                 v[j1] = v[j1]-min;
    561             }
    562         }
    563     }
    564     // AUGMENTING ROW REDUCTION //
    565     int loopcnt = 0;
    566     do
    567     {
    568         loopcnt++;
    569         k=0;
    570         prvnumfree=numfree;
    571         numfree=0;
    572         while (k < prvnumfree)
    573         {
    574             i=free[k];
    575             k++;
    576             umin = costMatrix.at<float>(i,0)-v[0];
    577             j1=0;
    578             usubmin = std::numeric_limits<float>::max();
    579             for (j=1; j<costMatrix.rows; j++)
    580             {
    581                 h = costMatrix.at<float>(i,j)-v[j];
    582                 if (h < usubmin)
    583                 {
    584                     if (h >= umin)
    585                     {
    586                         usubmin = h;
    587                         j2 = j;
    588                     }
    589                     else
    590                     {
    591                         usubmin = umin;
    592                         umin = h;
    593                         j2 = j1;
    594                         j1 = j;
    595                     }
    596                 }
    597             }
    598             i0 = colsol[j1];
    599 
    600             if (fabs(umin-usubmin) > LOWV) //if( umin < usubmin )
    601             {
    602                 v[j1] = v[j1] - (usubmin - umin);
    603             }
    604             else // minimum and subminimum equal.
    605             {
    606                 if (i0 >= 0) // minimum column j1 is assigned.
    607                 {
    608                     j1 = j2;
    609                     i0 = colsol[j2];
    610                 }
    611             }
    612             // (re-)assign i to j1, possibly de-assigning an i0.
    613             rowsol[i]=j1;
    614             colsol[j1]=i;
    615 
    616             if (i0 >= 0)
    617             {
    618                 //if( umin < usubmin )
    619                 if (fabs(umin-usubmin) > LOWV)
    620                 {
    621                     free[--k] = i0;
    622                 }
    623                 else
    624                 {
    625                     free[numfree++] = i0;
    626                 }
    627             }
    628         }
    629     }while (loopcnt<2); // repeat once.
    630 
    631     // AUGMENT SOLUTION for each free row //
    632     for (f = 0; f<numfree; f++)
    633     {
    634         freerow = free[f];       // start row of augmenting path.
    635         // Dijkstra shortest path algorithm.
    636         // runs until unassigned column added to shortest path tree.
    637         for (j = 0; j < costMatrix.rows; j++)
    638         {
    639             d[j] = costMatrix.at<float>(freerow,j) - v[j];
    640             pred[j] = float(freerow);
    641             collist[j] = j;        // init column list.
    642         }
    643 
    644         low=0; // columns in 0..low-1 are ready, now none.
    645         up=0;  // columns in low..up-1 are to be scanned for current minimum, now none.
    646         unassignedfound = false;
    647         do
    648         {
    649             if (up == low)
    650             {
    651                 last=low-1;
    652                 min = d[collist[up++]];
    653                 for (k = up; k < costMatrix.rows; k++)
    654                 {
    655                     j = collist[k];
    656                     h = d[j];
    657                     if (h <= min)
    658                     {
    659                         if (h < min) // new minimum.
    660                         {
    661                             up = low; // restart list at index low.
    662                             min = h;
    663                         }
    664                         collist[k] = collist[up];
    665                         collist[up++] = j;
    666                     }
    667                 }
    668                 for (k=low; k<up; k++)
    669                 {
    670                     if (colsol[collist[k]] < 0)
    671                     {
    672                         endofpath = collist[k];
    673                         unassignedfound = true;
    674                         break;
    675                     }
    676                 }
    677             }
    678 
    679             if (!unassignedfound)
    680             {
    681                 // update 'distances' between freerow and all unscanned columns, via next scanned column.
    682                 j1 = collist[low];
    683                 low++;
    684                 i = colsol[j1];
    685                 h = costMatrix.at<float>(i,j1)-v[j1]-min;
    686 
    687                 for (k = up; k < costMatrix.rows; k++)
    688                 {
    689                     j = collist[k];
    690                     v2 = costMatrix.at<float>(i,j) - v[j] - h;
    691                     if (v2 < d[j])
    692                     {
    693                         pred[j] = float(i);
    694                         if (v2 == min)
    695                         {
    696                             if (colsol[j] < 0)
    697                             {
    698                                 // if unassigned, shortest augmenting path is complete.
    699                                 endofpath = j;
    700                                 unassignedfound = true;
    701                                 break;
    702                             }
    703                             else
    704                             {
    705                                 collist[k] = collist[up];
    706                                 collist[up++] = j;
    707                             }
    708                         }
    709                         d[j] = v2;
    710                     }
    711                 }
    712             }
    713         }while (!unassignedfound);
    714 
    715         // update column prices.
    716         for (k = 0; k <= last; k++)
    717         {
    718             j1 = collist[k];
    719             v[j1] = v[j1] + d[j1] - min;
    720         }
    721 
    722         // reset row and column assignments along the alternating path.
    723         do
    724         {
    725             i = int(pred[endofpath]);
    726             colsol[endofpath] = i;
    727             j1 = endofpath;
    728             endofpath = rowsol[i];
    729             rowsol[i] = j1;
    730         }while (i != freerow);
    731     }
    732 
    733     // calculate symmetric shape context cost
    734     cv::Mat trueCostMatrix(costMatrix, cv::Rect(0,0,sizeScd1, sizeScd2));
    735     float leftcost = 0;
    736     for (int nrow=0; nrow<trueCostMatrix.rows; nrow++)
    737     {
    738         double minval;
    739         minMaxIdx(trueCostMatrix.row(nrow), &minval);
    740         leftcost+=float(minval);
    741     }
    742     leftcost /= trueCostMatrix.rows;
    743 
    744     float rightcost = 0;
    745     for (int ncol=0; ncol<trueCostMatrix.cols; ncol++)
    746     {
    747         double minval;
    748         minMaxIdx(trueCostMatrix.col(ncol), &minval);
    749         rightcost+=float(minval);
    750     }
    751     rightcost /= trueCostMatrix.cols;
    752 
    753     minMatchCost = std::max(leftcost,rightcost);
    754 
    755     // Save in a DMatch vector
    756     for (i=0;i<costMatrix.cols;i++)
    757     {
    758         cv::DMatch singleMatch(colsol[i],i,costMatrix.at<float>(colsol[i],i));//queryIdx,trainIdx,distance
    759         outMatches.push_back(singleMatch);
    760     }
    761 
    762     // Update inliers
    763     inliers1.reserve(sizeScd1);
    764     for (size_t kc = 0; kc<inliers1.size(); kc++)
    765     {
    766         if (rowsol[kc]<sizeScd1) // if a real match
    767             inliers1[kc]=1;
    768         else
    769             inliers1[kc]=0;
    770     }
    771     inliers2.reserve(sizeScd2);
    772     for (size_t kc = 0; kc<inliers2.size(); kc++)
    773     {
    774         if (colsol[kc]<sizeScd2) // if a real match
    775             inliers2[kc]=1;
    776         else
    777             inliers2[kc]=0;
    778     }
    779 }
    780 
    781 }
    782