#include <fstream>
#include <iostream>
#include "libmv/base/scoped_ptr.h"
#include "libmv/base/vector.h"
#include "libmv/base/vector_utils.h"
#include "libmv/correspondence/feature.h"
#include "libmv/detector/detector.h"
#include "libmv/detector/fast_detector.h"
#include "libmv/detector/star_detector.h"
#include "libmv/detector/surf_detector.h"
#include "libmv/image/image.h"
#include "libmv/image/image_converter.h"
#include "libmv/image/image_drawing.h"
#include "libmv/image/image_io.h"
#include "libmv/tools/tool.h"
using namespace libmv;
using namespace std;
void usage() {
LOG(ERROR) << " repreatability ImageReference ImageA ImageB ... " <<std::endl
<< " ImageReference : the input image on which features will be extrated,"
<< std::endl
<< " ImageA : an image to test repeatability with ImageReference"
<< std::endl
<< " ImageB : an image to test repeatability with ImageReference"
<< std::endl
<< " ... : images names to test repeatability with ImageReference"
<< std::endl
<< " Gound truth transformation between ImageReference and ImageX is named\
in ImageX.txt"
<< " INFO : !!! experimental !!! ." << std::endl;
}
enum eDetectorType
{
FAST_DETECTOR = 0,
SURF_DETECTOR = 1,
STAR_DETECTOR = 2
};
bool detectFeatures(const eDetectorType DetectorType,
const Image & im,
libmv::vector<libmv::Feature *> * featuresOut);
bool testRepeatability(const libmv::vector<libmv::Feature *> & featuresA,
const libmv::vector<libmv::Feature *> & featuresB,
const string & ImageNameB,
const Image & imageB,
libmv::vector<double> * exportedData = NULL,
ostringstream * stringStream = NULL);
int main(int argc, char **argv) {
libmv::Init("Extract features from on images series and test detector \
repeatability", &argc, &argv);
if (argc < 3 ||
(GetFormat(argv[1])==Unknown && GetFormat(argv[2])==Unknown)) {
usage();
LOG(ERROR) << "Missing parameters or errors in the command line.";
return 1;
}
const string sImageReference = argv[1];
ByteImage byteImage;
if ( 0 == ReadImage( sImageReference.c_str(), &byteImage) ) {
LOG(ERROR) << "Invalid inputImage.";
return 1;
}
Image imageReference(new ByteImage(byteImage));
if( byteImage.Depth() == 3)
{
ByteImage byteImageGray;
Rgb2Gray(byteImage, &byteImageGray);
imageReference=Image(new ByteImage(byteImageGray));
}
eDetectorType DETECTOR_TYPE = FAST_DETECTOR;
libmv::vector<libmv::Feature *> featuresRef;
if ( !detectFeatures( DETECTOR_TYPE,
imageReference,
&featuresRef))
{
LOG(ERROR) << "No feature found on Reference Image.";
return 1;
}
libmv::vector<double> repeatabilityStat;
for(int i = 2; i < argc; ++i)
{
const string sImageToCompare = argv[i];
libmv::vector<libmv::Feature *> featuresToCompare;
if ( 0 == ReadImage( sImageToCompare.c_str(), &byteImage) ) {
LOG(ERROR) << "Invalid inputImage (Image to compare to reference).";
return 1;
}
Image imageToCompare(new ByteImage(byteImage));
if( byteImage.Depth() == 3)
{
ByteImage byteImageGray;
Rgb2Gray(byteImage, &byteImageGray);
imageToCompare = Image(new ByteImage(byteImageGray));
}
if (detectFeatures(DETECTOR_TYPE,
imageToCompare,
&featuresToCompare))
{
testRepeatability( featuresRef,
featuresToCompare,
sImageToCompare + string(".txt"),
imageToCompare,
&repeatabilityStat);
}
else
{
LOG(INFO) << "Image : " << sImageToCompare
<< " have no feature detected with the choosen detector.";
}
DeleteElements(&featuresToCompare);
}
ofstream fileStream("Repeatability.xls");
fileStream << "RepeatabilityStats" << endl;
fileStream << "ImageName \t Feature In Reference \t Features In ImageName \
\t Position Repeatability \t Position Accuracy" << endl;
int cpt=0;
for (int i = 2; i < argc; ++i) {
fileStream << argv[i] << "\t";
for (int j=0; j < repeatabilityStat.size()/(argc-2); ++j) {
fileStream << repeatabilityStat[cpt] << "\t";
cpt++;
}
fileStream << endl;
}
DeleteElements(&featuresRef);
fileStream.close();
}
bool detectFeatures(const eDetectorType DetectorType,
const Image & im,
libmv::vector<libmv::Feature *> * featuresOut)
{
using namespace detector;
switch (DetectorType) {
case FAST_DETECTOR:
{
scoped_ptr<Detector> detector(CreateFastDetector(9, 30));
detector->Detect( im, featuresOut, NULL);
}
break;
case SURF_DETECTOR:
{
scoped_ptr<Detector> detector(CreateSURFDetector());
detector->Detect( im, featuresOut, NULL);
}
break;
case STAR_DETECTOR:
{
scoped_ptr<Detector> detector(CreateStarDetector());
detector->Detect( im, featuresOut, NULL);
}
break;
default:
{
scoped_ptr<Detector> detector(CreateFastDetector(9, 30));
detector->Detect( im, featuresOut, NULL);
}
}
return (featuresOut->size() >= 1);
}
bool testRepeatability(const libmv::vector<libmv::Feature *> & featuresA,
const libmv::vector<libmv::Feature *> & featuresB,
const string & ImageNameB,
const Image & imageB,
libmv::vector<double> * exportedData,
ostringstream * stringStream)
{
const double distThreshold = 1.5;
Mat3 transfoMatrix;
if(ImageNameB.size() > 0 ) {
ifstream file( ImageNameB.c_str());
if(file.is_open()) {
for(int i=0; i<3*3; ++i) {
file>>transfoMatrix(i);
}
Mat3 temp = transfoMatrix.transpose();
transfoMatrix = temp;
}
else {
LOG(ERROR) << "Invalid input transformation file.";
if (stringStream) {
(*stringStream) << "Invalid input transformation file.";
}
if(exportedData) {
exportedData->push_back(featuresA.size());
exportedData->push_back(featuresB.size());
exportedData->push_back(0);
exportedData->push_back(0);
}
return 0;
}
}
int nbRepeatable = 0;
int nbRepeatablePossible = 0;
double localisationError = 0;
for (int iA=0; iA < featuresA.size(); ++iA) {
const libmv::PointFeature * featureA =
dynamic_cast<libmv::PointFeature *>( featuresA[iA] );
Vec3 pos; pos << featureA->x(), featureA->y(), 1.0;
Vec3 transformed = transfoMatrix * pos;
transformed/=transformed(2);
double distanceSearch = std::numeric_limits<double>::max();
int indiceFound = -1;
if ( imageB.AsArray3Du()->Contains(pos(0), pos(1)) ) {
++nbRepeatablePossible; for (int iB=0; iB < featuresB.size(); ++iB) {
const libmv::PointFeature * featureB =
dynamic_cast<libmv::PointFeature *>( featuresB[iB] );
Vec3 posB; posB << featureB->x(), featureB->y(), 1.0;
double distance = DistanceL2(transformed,posB);
if ( distance <= distThreshold ) {
distanceSearch = distance;
indiceFound = iB;
}
}
}
if ( indiceFound != -1 ) {
++nbRepeatable;
const libmv::PointFeature * featureB =
dynamic_cast<libmv::PointFeature *>( featuresB[indiceFound] );
Vec3 posB; posB << featureB->x(), featureB->y(), 1.0;
double distance = DistanceL2(transformed,posB);
localisationError += distance;
}
}
if( nbRepeatable >0 ) {
localisationError/= nbRepeatable;
}
else {
localisationError = 0;
}
nbRepeatablePossible = min(nbRepeatablePossible, featuresB.size());
ostringstream os;
os<< endl << " Feature Repeatability " << ImageNameB << endl
<< " ---------------------- " << endl
<< " Image A get\t" << featuresA.size() << "\tfeatures" << endl
<< " Image B get\t" << featuresB.size() << "\tfeatures" << endl
<< " ---------------------- " << endl
<< " Position repeatability :\t" <<
nbRepeatable / (double) nbRepeatablePossible * 100 << endl
<< " Position mean error :\t" << localisationError << endl;
cout << os.str();
if (stringStream) {
(*stringStream) << os.str();
}
if(exportedData) {
exportedData->push_back(featuresA.size());
exportedData->push_back(featuresB.size());
exportedData->push_back(nbRepeatable/(double) nbRepeatablePossible * 100);
exportedData->push_back(localisationError);
}
return (nbRepeatable != 0);
}