|
@@ -8,6 +8,7 @@
|
|
#include <core/image/FilterT.h>
|
|
#include <core/image/FilterT.h>
|
|
#include <core/image/CircleT.h>
|
|
#include <core/image/CircleT.h>
|
|
#include <core/image/Convert.h>
|
|
#include <core/image/Convert.h>
|
|
|
|
+#include <core/imagedisplay/ImageDisplay.h>
|
|
#include <core/vector/VectorT.h>
|
|
#include <core/vector/VectorT.h>
|
|
|
|
|
|
//vislearning
|
|
//vislearning
|
|
@@ -18,6 +19,7 @@
|
|
#include <vislearning/features/localfeatures/LFWriteCache.h>
|
|
#include <vislearning/features/localfeatures/LFWriteCache.h>
|
|
//
|
|
//
|
|
#include <vislearning/math/cluster/KMeans.h>
|
|
#include <vislearning/math/cluster/KMeans.h>
|
|
|
|
+#include <vislearning/math/cluster/KMedian.h>
|
|
#include <vislearning/math/cluster/GMM.h>
|
|
#include <vislearning/math/cluster/GMM.h>
|
|
|
|
|
|
using namespace std;
|
|
using namespace std;
|
|
@@ -40,6 +42,10 @@ void FeatureLearningPrototypes::setClusterAlgo( const std::string & _clusterAlgo
|
|
{
|
|
{
|
|
this->clusterAlgo = new OBJREC::KMeans(this->initialNumberOfClusters);
|
|
this->clusterAlgo = new OBJREC::KMeans(this->initialNumberOfClusters);
|
|
}
|
|
}
|
|
|
|
+ else if (_clusterAlgoString.compare("kmedian") == 0)
|
|
|
|
+ {
|
|
|
|
+ this->clusterAlgo = new OBJREC::KMedian(this->initialNumberOfClusters);
|
|
|
|
+ }
|
|
else if (_clusterAlgoString.compare("GMM") == 0)
|
|
else if (_clusterAlgoString.compare("GMM") == 0)
|
|
{
|
|
{
|
|
this->clusterAlgo = new OBJREC::GMM(this->conf, this->initialNumberOfClusters);
|
|
this->clusterAlgo = new OBJREC::GMM(this->conf, this->initialNumberOfClusters);
|
|
@@ -433,7 +439,6 @@ NICE::FloatImage FeatureLearningPrototypes::evaluateCurrentCodebookByDistance (
|
|
|
|
|
|
//convert float to RGB
|
|
//convert float to RGB
|
|
NICE::ColorImage noveltyImageRGB ( xsize, ysize );
|
|
NICE::ColorImage noveltyImageRGB ( xsize, ysize );
|
|
-// ICETools::convertToRGB ( noveltyImageGaussFiltered, noveltyImageRGB );
|
|
|
|
if ( beforeComputingNewFeatures )
|
|
if ( beforeComputingNewFeatures )
|
|
{
|
|
{
|
|
imageToPseudoColorWithRangeSpecification( noveltyImageGaussFiltered, noveltyImageRGB, 0 /* min */, maxValForVisualization /* maxFiltered*/ /* max */ );
|
|
imageToPseudoColorWithRangeSpecification( noveltyImageGaussFiltered, noveltyImageRGB, 0 /* min */, maxValForVisualization /* maxFiltered*/ /* max */ );
|
|
@@ -461,36 +466,96 @@ NICE::FloatImage FeatureLearningPrototypes::evaluateCurrentCodebookByDistance (
|
|
noveltyImageRGB.writePPM( destination );
|
|
noveltyImageRGB.writePPM( destination );
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ ImageDisplay imgDisp;
|
|
|
|
|
|
// now look where the closest features for the current cluster indices are
|
|
// now look where the closest features for the current cluster indices are
|
|
int tmpProtCnt ( 0 );
|
|
int tmpProtCnt ( 0 );
|
|
for (NICE::VVector::const_iterator protIt = prototypes.begin(); protIt != prototypes.end(); protIt++, tmpProtCnt++)
|
|
for (NICE::VVector::const_iterator protIt = prototypes.begin(); protIt != prototypes.end(); protIt++, tmpProtCnt++)
|
|
{
|
|
{
|
|
- double distToNewCluster ( std::numeric_limits<double>::max() );
|
|
|
|
|
|
+ double distToCurrentCluster ( std::numeric_limits<double>::max() );
|
|
int indexOfMostSimFeat( 0 );
|
|
int indexOfMostSimFeat( 0 );
|
|
double tmpDist;
|
|
double tmpDist;
|
|
- int tmpCnt ( 0 );
|
|
|
|
|
|
+ int featureCnt ( 0 );
|
|
|
|
|
|
for ( NICE::VVector::iterator i = features.begin();
|
|
for ( NICE::VVector::iterator i = features.begin();
|
|
i != features.end();
|
|
i != features.end();
|
|
- i++, tmpCnt++)
|
|
|
|
|
|
+ i++, featureCnt++)
|
|
{
|
|
{
|
|
tmpDist = this->distFunction->calculate( *i, *protIt );
|
|
tmpDist = this->distFunction->calculate( *i, *protIt );
|
|
- if ( tmpDist < distToNewCluster )
|
|
|
|
|
|
+ if ( tmpDist < distToCurrentCluster )
|
|
{
|
|
{
|
|
- distToNewCluster = tmpDist;
|
|
|
|
- indexOfMostSimFeat = tmpCnt;
|
|
|
|
|
|
+ distToCurrentCluster = tmpDist;
|
|
|
|
+ indexOfMostSimFeat = featureCnt;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
int posX ( ( positions[indexOfMostSimFeat] ) [0] );
|
|
int posX ( ( positions[indexOfMostSimFeat] ) [0] );
|
|
int posY ( ( positions[indexOfMostSimFeat] ) [1] );
|
|
int posY ( ( positions[indexOfMostSimFeat] ) [1] );
|
|
- NICE::Circle circ ( Coord( posX, posY), 2*(tmpProtCnt+1) /* radius*/, Color(200,0,255 ) );
|
|
|
|
|
|
+
|
|
|
|
+ //position (for OpponentSIFT of van de Sande): x y scale orientation cornerness
|
|
|
|
+
|
|
|
|
+ /*What is the interpretation of scale?
|
|
|
|
+
|
|
|
|
+ The scale parameter was implemented to correspond with the Gaussian filter sigma at which points were detected. Therefore, the
|
|
|
|
+ scale is not directly interpretable as the size of the region described in terms of number of pixels. However, it is linearly
|
|
|
|
+ related the radius of the circular area described. To capture the area of the Gaussian originally used, we have a 3x
|
|
|
|
+ magnification factor. But, remember that SIFT has 4x4 cells, and this is a measure for a single cell. So, because we are
|
|
|
|
+ working with a radius, multiply by 2. Due to the square shape of the region, we need to extend the outer radius even further
|
|
|
|
+ by sqrt(2), otherwise the corners of the outer cells are cut off by our circle. So, the largest outer radius is
|
|
|
|
+ Round(scale * 3 * 2 * sqrt(2)). The area from which the SIFT descriptor is computed is a square which fits within a circle
|
|
|
|
+ of this radius. Also, due to the Gaussian weighting applied within SIFT, the area that really matters is much, much smaller:
|
|
|
|
+ the outer parts get a low weight.
|
|
|
|
+
|
|
|
|
+ For the default scale of 1.2, we get a outer circle radius of 10. The potential sampling area then becomes -10..10, e.g. a
|
|
|
|
+ 21x21patch. However, the square area which fits inside this circle is smaller: about 15x15. The corners of this 15x15square
|
|
|
|
+ touch the outer circle. */
|
|
|
|
+
|
|
|
|
+ /*Why is the direction (angle) field always 0?
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ Estimating the dominant orientation of a descriptor is useful in matching scenarios. However, in an object/scene categorization
|
|
|
|
+ setting, the additional invariance reduces accuracy. Being able to discriminate between dominant directions of up and right
|
|
|
|
+ is very useful here, and rotated down images are quite rare in an object categorization setting. Therefore, orientation
|
|
|
|
+ estimation is disabled in the color descriptor software. The subsequent rotation of the descriptor to achieve
|
|
|
|
+ rotation-invariance is still possible by supplying your own regions and angles for an image (through --loadRegions). However,
|
|
|
|
+ by default, no such rotation is performed, since the default angle is 0. */
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ //adapt the pseudo color transformation as done in Convert.cpp
|
|
|
|
+ size_t seg = ( size_t ) ( tmpProtCnt/(float)prototypes.size() * 6.0 );
|
|
|
|
+ double y = ( 6 * tmpProtCnt/(float)prototypes.size() - seg );
|
|
|
|
+
|
|
|
|
+ Color circleColor;
|
|
|
|
+ switch ( seg ) {
|
|
|
|
+ case 0:
|
|
|
|
+ circleColor = Color( 0,0,(int)round(y*255) );
|
|
|
|
+ break;
|
|
|
|
+ case 1:
|
|
|
|
+ circleColor = Color( 0,(int)round(y*255),255 );
|
|
|
|
+ break;
|
|
|
|
+ case 2:
|
|
|
|
+ circleColor = Color( 0,255,(int)round((1-y)*255) );
|
|
|
|
+ break;
|
|
|
|
+ case 3:
|
|
|
|
+ circleColor = Color( (int)round(y*255),255,0 );
|
|
|
|
+ break;
|
|
|
|
+ case 4:
|
|
|
|
+ circleColor = Color( 255,(int)round((1-y)*255),0 );
|
|
|
|
+ break;
|
|
|
|
+ case 5:
|
|
|
|
+ circleColor = Color( 255,(int)round(y*255),(int)round(y*255) );
|
|
|
|
+ break;
|
|
|
|
+ default:
|
|
|
|
+ circleColor = Color( 255,255,255 );
|
|
|
|
+ break;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ NICE::Circle circ ( Coord( posX, posY), (int) round(2*3*sqrt(2)*( positions[indexOfMostSimFeat] )[2]) /* radius*/, circleColor );
|
|
img.draw(circ);
|
|
img.draw(circ);
|
|
}
|
|
}
|
|
-
|
|
|
|
|
|
+
|
|
if ( b_showResults )
|
|
if ( b_showResults )
|
|
- showImage(img, "Current image and most similar features for current cluster");
|
|
|
|
|
|
+ showImage(img, "Current image and most similar features for current prototypes");
|
|
else
|
|
else
|
|
{
|
|
{
|
|
std::vector< std::string > list2;
|
|
std::vector< std::string > list2;
|
|
@@ -532,34 +597,46 @@ NICE::ImageT< int > FeatureLearningPrototypes::evaluateCurrentCodebookByAssignme
|
|
i->normalizeL1();
|
|
i->normalizeL1();
|
|
}
|
|
}
|
|
|
|
|
|
- std::cerr << "normalization done - now look for nearest clusters for every extracted feature" << std::endl;
|
|
|
|
-
|
|
|
|
|
|
+ //this is the image we will return finally
|
|
NICE::ImageT< int > clusterImage ( xsize, ysize );
|
|
NICE::ImageT< int > clusterImage ( xsize, ysize );
|
|
clusterImage.set ( 0 );
|
|
clusterImage.set ( 0 );
|
|
-
|
|
|
|
|
|
+
|
|
|
|
+ // after iterating over all features from the new image, this vector contains
|
|
|
|
+ // distances to the most similar feature for every prototype
|
|
|
|
+ NICE::Vector minDistances ( this->prototypes.size() );
|
|
|
|
+
|
|
NICE::VVector::const_iterator posIt = positions.begin();
|
|
NICE::VVector::const_iterator posIt = positions.begin();
|
|
for ( NICE::VVector::const_iterator i = features.begin();
|
|
for ( NICE::VVector::const_iterator i = features.begin();
|
|
i != features.end();
|
|
i != features.end();
|
|
- i++, posIt++)
|
|
|
|
|
|
+ i++, posIt++ )
|
|
{
|
|
{
|
|
|
|
|
|
//loop over codebook representatives
|
|
//loop over codebook representatives
|
|
double minDist ( std::numeric_limits<double>::max() );
|
|
double minDist ( std::numeric_limits<double>::max() );
|
|
- int indexOfNearestCluster ( 0 );
|
|
|
|
- int clusterCounter ( 0 );
|
|
|
|
- for (NICE::VVector::const_iterator it = this->prototypes.begin(); it != this->prototypes.end(); it++, clusterCounter++)
|
|
|
|
|
|
+ int indexOfNearestPrototype ( 0 );
|
|
|
|
+ int prototypeCounter ( 0 );
|
|
|
|
+ for (NICE::VVector::const_iterator it = this->prototypes.begin(); it != this->prototypes.end(); it++, prototypeCounter++)
|
|
{
|
|
{
|
|
//compute distance
|
|
//compute distance
|
|
double tmpDist ( this->distFunction->calculate(*i,*it) );
|
|
double tmpDist ( this->distFunction->calculate(*i,*it) );
|
|
|
|
+ //check what the closest prototype is
|
|
if (tmpDist < minDist)
|
|
if (tmpDist < minDist)
|
|
{
|
|
{
|
|
minDist = tmpDist;
|
|
minDist = tmpDist;
|
|
- indexOfNearestCluster = clusterCounter;
|
|
|
|
|
|
+ indexOfNearestPrototype = prototypeCounter;
|
|
}
|
|
}
|
|
|
|
+
|
|
|
|
+ //check whether we found a feature for the current prototype which is more similar then the previous best one
|
|
|
|
+ if ( minDistances[ prototypeCounter ] > tmpDist )
|
|
|
|
+ minDistances[ prototypeCounter ] = tmpDist;
|
|
}
|
|
}
|
|
|
|
+
|
|
|
|
|
|
|
|
+
|
|
|
|
+
|
|
//take minimum distance and store in in a float image
|
|
//take minimum distance and store in in a float image
|
|
- //TODO hard coded!!!
|
|
|
|
|
|
+ // for nice visualization, we plot the cluster index into a square of size 3 x 3
|
|
|
|
+ //TODO currently hard coded!!!
|
|
int noProtoTypes ( this->prototypes.size() -1 );
|
|
int noProtoTypes ( this->prototypes.size() -1 );
|
|
|
|
|
|
for ( int tmpY = (*posIt)[1] - 1; tmpY < (*posIt)[1] + 1; tmpY++)
|
|
for ( int tmpY = (*posIt)[1] - 1; tmpY < (*posIt)[1] + 1; tmpY++)
|
|
@@ -569,35 +646,59 @@ NICE::ImageT< int > FeatureLearningPrototypes::evaluateCurrentCodebookByAssignme
|
|
if ( _binaryShowLatestPrototype )
|
|
if ( _binaryShowLatestPrototype )
|
|
{
|
|
{
|
|
//just a binary image - 1 if newest prototype is nearest - 0 if not
|
|
//just a binary image - 1 if newest prototype is nearest - 0 if not
|
|
- if ( indexOfNearestCluster == noProtoTypes)
|
|
|
|
|
|
+ if ( indexOfNearestPrototype == noProtoTypes)
|
|
clusterImage ( tmpX, tmpY ) = 1;
|
|
clusterImage ( tmpX, tmpY ) = 1;
|
|
else
|
|
else
|
|
clusterImage ( tmpX, tmpY ) = 0;
|
|
clusterImage ( tmpX, tmpY ) = 0;
|
|
}
|
|
}
|
|
else
|
|
else
|
|
//as many different values as current prototypes available
|
|
//as many different values as current prototypes available
|
|
- clusterImage ( tmpX, tmpY ) = indexOfNearestCluster;
|
|
|
|
|
|
+ clusterImage ( tmpX, tmpY ) = indexOfNearestPrototype;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
-// clusterImage ( (*posIt)[0], (*posIt)[1] ) = indexOfNearestCluster;
|
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ std::cerr << "Codebook evaluation by assignments... min distances in image for every prototype: " << std::endl << " " << minDistances << std::endl;
|
|
|
|
+
|
|
//show how many clusters we have
|
|
//show how many clusters we have
|
|
if ( !_binaryShowLatestPrototype )
|
|
if ( !_binaryShowLatestPrototype )
|
|
{
|
|
{
|
|
int tmpCnt ( 0 );
|
|
int tmpCnt ( 0 );
|
|
for (NICE::VVector::const_iterator protoIt = prototypes.begin(); protoIt != prototypes.end(); protoIt++, tmpCnt++)
|
|
for (NICE::VVector::const_iterator protoIt = prototypes.begin(); protoIt != prototypes.end(); protoIt++, tmpCnt++)
|
|
{
|
|
{
|
|
- for ( int tmpY = 1 + 2 - 2; tmpY < (2 + 2); tmpY++)
|
|
|
|
- {
|
|
|
|
- for ( int tmpX = 1 + 5*tmpCnt - 2; tmpX < (1 + 5*tmpCnt + 2); tmpX++)
|
|
|
|
|
|
+ for ( int tmpY = 1 + 2 - 2; tmpY < (2 + 2); tmpY++)
|
|
{
|
|
{
|
|
- //Take care, this might go "over" the image
|
|
|
|
- clusterImage ( tmpX, tmpY ) = (Ipp8u) tmpCnt;
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
|
|
+ for ( int tmpX = 1 + 4*tmpCnt ; tmpX < (1 + 4*tmpCnt + 3); tmpX++)
|
|
|
|
+ {
|
|
|
|
+ //Take care, this might go "over" the image
|
|
|
|
+ clusterImage ( tmpX, tmpY ) = (Ipp8u) tmpCnt;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
}
|
|
}
|
|
}
|
|
}
|
|
- std::cerr << " evaluateCurrentCodebookByAssignments done" << std::endl;
|
|
|
|
|
|
+
|
|
return clusterImage;
|
|
return clusterImage;
|
|
}
|
|
}
|
|
|
|
+
|
|
|
|
+void FeatureLearningPrototypes::evaluateCurrentCodebookByConfusionMatrix( NICE::Matrix & _confusionMat )
|
|
|
|
+{
|
|
|
|
+ _confusionMat.resize ( this->prototypes.size(), this->prototypes.size() );
|
|
|
|
+ _confusionMat.set( 0.0 );
|
|
|
|
+
|
|
|
|
+ double tmpDist ( 0.0 );
|
|
|
|
+ NICE::VVector::const_iterator protoItJ = prototypes.begin();
|
|
|
|
+ for ( int j = 0; j < prototypes.size(); j++, protoItJ++)
|
|
|
|
+ {
|
|
|
|
+ NICE::VVector::const_iterator protoItI = protoItJ;
|
|
|
|
+ for ( int i = j; i < prototypes.size(); i++, protoItI++)
|
|
|
|
+ {
|
|
|
|
+ tmpDist = this->distFunction->calculate( *protoItJ, *protoItI );
|
|
|
|
+
|
|
|
|
+ //assuming symmetric distances
|
|
|
|
+ _confusionMat ( i, j ) = tmpDist;
|
|
|
|
+// if ( i != j )
|
|
|
|
+// _confusionMat ( j, i ) = tmpDist;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+
|