From: <hug...@li...> - 2010-10-05 17:43:43
|
branch: details: http://hugin.hg.sourceforge.net/hgweb/hugin/hugin/hgrepo/h/hu/hugin/hugin/rev/4fd1620e445a changeset: 4438:4fd1620e445a user: tmodes date: Mon Oct 04 21:54:17 2010 +0200 description: Refactoring celeste code for integration into cpfind * Some speed improvements, smaller memory footprint * Integrated into cpfind * Speed improvements for using celeste from inside Hugin GUI diffstat: src/celeste/CMakeLists.txt | 6 +- src/celeste/Celeste.cpp | 817 ++++++++++--------------- src/celeste/Celeste.h | 67 ++- src/celeste/CelesteGlobals.cpp | 19 +- src/celeste/CelesteGlobals.h | 20 +- src/celeste/ContrastFilter.cpp | 4 +- src/celeste/ContrastFilter.h | 5 +- src/celeste/Gabor.cpp | 8 +- src/celeste/Gabor.h | 6 +- src/celeste/GaborFilter.cpp | 3 + src/celeste/GaborFilter.h | 4 +- src/celeste/GaborGlobal.h | 3 - src/celeste/GaborJet.cpp | 6 +- src/celeste/GaborJet.h | 5 +- src/celeste/ImageFile.cpp | 3 + src/celeste/ImageFile.h | 4 +- src/celeste/LogPolar.cpp | 5 +- src/celeste/LogPolar.h | 4 +- src/celeste/Main.cpp | 634 +++++++------------ src/celeste/PGMImage.cpp | 4 +- src/celeste/PGMImage.h | 4 +- src/celeste/Utilities.cpp | 3 + src/celeste/Utilities.h | 55 +- src/celeste/svm.cpp | 4 + src/celeste/svm.h | 4 +- src/foreign/vigra/vigra/inspectimage.hxx | 183 +++++- src/hugin1/hugin/CPEditorPanel.cpp | 122 +-- src/hugin1/hugin/CPEditorPanel.h | 5 +- src/hugin1/hugin/ImagesPanel.cpp | 167 +--- src/hugin1/hugin/MainFrame.cpp | 29 + src/hugin1/hugin/MainFrame.h | 6 + src/hugin_base/panodata/ControlPoint.h | 4 +- src/hugin_base/panodata/Panorama.cpp | 22 + src/hugin_base/panodata/Panorama.h | 4 + src/hugin_cpfind/cpfind/CMakeLists.txt | 4 +- src/hugin_cpfind/cpfind/PanoDetector.cpp | 83 ++- src/hugin_cpfind/cpfind/PanoDetector.h | 12 + src/hugin_cpfind/cpfind/PanoDetectorLogic.cpp | 121 +++- src/hugin_cpfind/cpfind/main.cpp | 13 + src/hugin_shared.h | 9 +- 40 files changed, 1310 insertions(+), 1171 deletions(-) diffs (truncated from 3321 to 500 lines): diff -r 77ed3451d5f2 -r 4fd1620e445a src/celeste/CMakeLists.txt --- a/src/celeste/CMakeLists.txt Mon Oct 04 17:58:13 2010 +0200 +++ b/src/celeste/CMakeLists.txt Mon Oct 04 21:54:17 2010 +0200 @@ -54,7 +54,7 @@ #message("Using these CMAKE_CXX_FLAGS : ${CMAKE_CXX_FLAGS}") ENDIF (NOT WIN32) ADD_LIBRARY(celeste SHARED ${CELESTE_SRC}) - TARGET_LINK_LIBRARIES(celeste huginbasewx ${common_libs}) + TARGET_LINK_LIBRARIES(celeste huginbase ${common_libs}) set_target_properties(celeste PROPERTIES VERSION ${HUGIN_LIB_VERSION}) IF(WIN32) install(TARGETS celeste RUNTIME DESTINATION ${BINDIR}) @@ -67,13 +67,13 @@ #message("Using these CMAKE_CXX_FLAGS : ${CMAKE_CXX_FLAGS}") ENDIF (NOT WIN32) ADD_LIBRARY(celeste STATIC ${CELESTE_SRC}) - TARGET_LINK_LIBRARIES(celeste huginbasewx ${common_libs}) + TARGET_LINK_LIBRARIES(celeste huginbase ${common_libs}) set_target_properties(celeste PROPERTIES VERSION ${HUGIN_LIB_VERSION}) ENDIF (${HUGIN_SHARED_LIBS}) # standalone ADD_EXECUTABLE(celeste_standalone ../hugin_config.h Main.cpp ) -target_link_libraries( celeste_standalone celeste ${image_libs} ${Boost_LIBRARIES} ${wxWidgets_LIBRARIES}) +target_link_libraries( celeste_standalone celeste ${image_libs}) SET (CELESTE_MODEL data/celeste.model diff -r 77ed3451d5f2 -r 4fd1620e445a src/celeste/Celeste.cpp --- a/src/celeste/Celeste.cpp Mon Oct 04 17:58:13 2010 +0200 +++ b/src/celeste/Celeste.cpp Mon Oct 04 21:54:17 2010 +0200 @@ -19,9 +19,12 @@ ***************************************************************************/ #include <iostream> -#include "vigra/stdimage.hxx" -#include "vigra/resizeimage.hxx" -#include "vigra/impex.hxx" +#include <vigra/stdimage.hxx> +#include <vigra/resizeimage.hxx> +#include <vigra/inspectimage.hxx> +#include <vigra/copyimage.hxx> +#include <vigra/transformimage.hxx> +#include <vigra/initimage.hxx> #include "vigra/colorconversions.hxx" #include <sys/types.h> #include <sys/stat.h> @@ -38,515 +41,377 @@ using namespace vigra; using namespace std; +namespace celeste +{ + typedef vigra::BRGBImage::PixelType RGB; -void get_gabor_response(string& imagefile, unsigned int& mask, string& model_file, double& threshold,string& -mask_format,vector<double>& svm_responses){ +// load SVM model +bool loadSVMmodel(struct svm_model*& model, string& model_file) +{ + if((model = svm_load_model(model_file.c_str())) == 0) + { + cout << "Couldn't load model file '" << model_file << "'" << endl << endl; + return false; + } + else + { + cout << "Loaded model file:\t" << model_file << endl; + return true; + } +}; - // Open SVM model file - struct svm_model* model; - - if((model = svm_load_model(model_file.c_str())) == 0){ - cout << "Couldn't load model file '" << model_file << "'" << endl << endl; - for (int j = 0; j < gNumLocs; j++){ - svm_responses.push_back(0); - } - return; - }else{ - cout << "Loaded model file:\t" << model_file << endl; - } - - // Integers and containers for libsvm - int nr_class=svm_get_nr_class(model); - int max_nr_attr = 56; - struct svm_node *gabor_responses = (struct svm_node *) malloc(max_nr_attr*sizeof(struct svm_node)); - double *prob_estimates = (double *) malloc(nr_class*sizeof(double)); - - // Open image using Vigra - try{ +// destroy SVM model +void destroySVMmodel(struct svm_model*& model) +{ + svm_destroy_model(model); +}; - cout << "Opening image file:\t" << imagefile << endl; +// prepare image for use with celeste (downscale, converting in Luv) +void prepareCelesteImage(vigra::UInt16RGBImage& rgb,vigra::UInt16RGBImage& luv, int& resize_dimension, double& sizefactor,bool verbose) +{ + // Max dimension + sizefactor = 1; + int nw=rgb.width(); + int nh=rgb.height(); - // Read image given as first argument - // File type is determined automatically - vigra::ImageImportInfo info(imagefile.c_str()); + if(nw >= nh) + { + if (resize_dimension >= nw ) + { + resize_dimension = nw; + } + } + else + { + if (resize_dimension >= nh) + { + resize_dimension = nh; + } + } + //cout << "Re-size dimenstion:\t" << resize_dimension << endl; + if(verbose) + cout << "Image dimensions:\t" << rgb.width() << " x " << rgb.height() << endl; - // Create RGB images of appropriate size - //vigra::FRGBImage in(info.width(), info.height()); - vigra::UInt16RGBImage in(info.width(), info.height()); + // Re-size to max dimension + vigra::UInt16RGBImage temp; - // Import the image - importImage(info, destImage(in)); + if (rgb.width() > resize_dimension || rgb.height() > resize_dimension) + { + if (rgb.width() >= rgb.height()) + { + sizefactor = (double)resize_dimension/rgb.width(); + // calculate new image size + nw = resize_dimension; + nh = static_cast<int>(0.5 + (sizefactor*rgb.height())); + } + else + { + sizefactor = (double)resize_dimension/rgb.height(); + // calculate new image size + nw = static_cast<int>(0.5 + (sizefactor*rgb.width())); + nh = resize_dimension; + } - // Max dimension - double sizefactor = 1; - int nw = info.width(), nh = info.height(); + if(verbose) + { + cout << "Scaling by:\t\t" << sizefactor << endl; + cout << "New dimensions:\t\t" << nw << " x " << nh << endl; + }; - // In case we want to save filters - // Create this directory and change option in Globals.cpp - char basename[] = "gabor_filters/celeste"; - - if (info.width() >= info.height()){ - if (resize_dimension >= info.width() ){ - resize_dimension = info.width(); - } - }else{ - if (resize_dimension >= info.height()){ - resize_dimension = info.height(); - } - } - //cout << "Re-size dimenstion:\t" << resize_dimension << endl; - - cout << "Image dimensions:\t" << info.width() << " x " << info.height() << endl; - - // Re-size to max dimension - if (info.width() > resize_dimension || info.height() > resize_dimension){ - - if (info.width() >= info.height()){ + // create a RGB image of appropriate size + temp.resize(nw,nh); + // resize the image, using a bi-cubic spline algorithm + vigra::resizeImageNoInterpolation(srcImageRange(rgb),destImageRange(temp)); + //exportImage(srcImageRange(out), ImageExportInfo("test.tif").setPixelType("UINT16")); + } + else + { + temp.resize(nw,nh); + vigra::copyImage(srcImageRange(rgb),destImage(temp)); + }; - sizefactor = (double)resize_dimension/info.width(); + // Convert to Luv colour space + luv.resize(temp.width(),temp.height()); + vigra::transformImage(srcImageRange(temp), destImage(luv), RGB2LuvFunctor<double>() ); + //exportImage(srcImageRange(luv), ImageExportInfo("test_luv.tif").setPixelType("UINT16")); + temp.resize(0,0); +}; - // calculate new image size - nw = resize_dimension; - nh = static_cast<int>(0.5 + (sizefactor*info.height())); - - }else{ - sizefactor = (double)resize_dimension/info.height(); +// converts the given Luv image into arrays for Gabors filtering +void prepareGaborImage(vigra::UInt16RGBImage& luv, float**& pixels) +{ + pixels = CreateMatrix( (float)0, luv.height(), luv.width() ); + // Prepare framebuf for Gabor API, we need only L channel + for (int i = 0; i < luv.height(); i++ ) + { + for (int j = 0; j < luv.width(); j++ ) + { + pixels[i][j] = luv(j,i)[0]; + //cout << i << " " << j << " = " << k << " - " << frameBuf[k] << endl; + } + } +}; +//classify the points with SVM +vector<double> classifySVM(struct svm_model* model, int gNumLocs,int**& gLocations,int width,int height,int vector_length, float*& response,int gRadius,vigra::UInt16RGBImage& luv,bool needsMoreIndex=false) +{ + std::vector<double> svm_response; + // Integers and containers for libsvm + int max_nr_attr = 56; + int nr_class=svm_get_nr_class(model); + struct svm_node *gabor_responses = (struct svm_node *) malloc(max_nr_attr*sizeof(struct svm_node)); + double *prob_estimates = (double *) malloc(nr_class*sizeof(double)); - // calculate new image size - nw = static_cast<int>(0.5 + (sizefactor*info.width())); - nh = resize_dimension; - - } - - cout << "Scaling by:\t\t" << sizefactor << endl; - cout << "New dimensions:\t\t" << nw << " x " << nh << endl; - - // create a RGB image of appropriate size - //vigra::FRGBImage out(nw, nh); - vigra::UInt16RGBImage out(nw, nh); - - // resize the image, using a bi-cubic spline algorithm - resizeImageNoInterpolation(srcImageRange(in),destImageRange(out)); + for (int j = 0; j < gNumLocs; j++) + { + unsigned int feature = 1; - in = out; + if(needsMoreIndex) + { + if(j >= max_nr_attr - 1) + { + max_nr_attr *= 2; + gabor_responses = (struct svm_node *) realloc(gabor_responses,max_nr_attr*sizeof(struct svm_node)); + } + }; - } - //exportImage(srcImageRange(in), ImageExportInfo("test.tif").setPixelType("UINT16")); - - // Convert to LUV colour space - UInt16RGBImage luv(in.width(),in.height()); - transformImage(srcImageRange(in), destImage(luv), RGB2LuvFunctor<double>() ); - //transformImage(srcImageRange(in), destImage(luv), RGBPrime2LuvFunctor<double>() ); + for ( int v = (j * vector_length); v < ((j + 1) * vector_length); v++) + { + gabor_responses[feature-1].index = feature; + gabor_responses[feature-1].value = response[v]; + //cout << feature << ":" << response[v] << " "; + feature++; + } - //exportImage(srcImageRange(luv), ImageExportInfo("test_luv.tif").setPixelType("UINT16")); + // Work out average colour and variance + vigra::FindAverageAndVariance<vigra::UInt16RGBImage::PixelType> average; + vigra::inspectImage(srcIterRange( + luv.upperLeft()+vigra::Diff2D(gLocations[j][0]-gRadius,gLocations[j][1]-gRadius), + luv.upperLeft()+vigra::Diff2D(gLocations[j][0]+gRadius,gLocations[j][1]+gRadius) + ),average); + // Add these colour features to feature vector - // Prepare Gabor API array - float *frameBuf = new float[in.width()*in.height()]; - float *u_values = new float[in.width()*in.height()]; - float *v_values = new float[in.width()*in.height()]; - float** pixels = CreateMatrix( (float)0, in.height(), in.width() ); + gabor_responses[feature-1].index = feature; + gabor_responses[feature-1].value = average.average()[1]; + //cout << feature << ":" << u_ave << " "; + feature++; + gabor_responses[feature-1].index = feature; + gabor_responses[feature-1].value = sqrt(average.variance()[1]); + //cout << feature << ":" << std_u << " "; + feature++; + gabor_responses[feature-1].index = feature; + gabor_responses[feature-1].value = average.average()[2]; + //cout << feature << ":" << v_ave << " "; + feature++; + gabor_responses[feature-1].index = feature; + gabor_responses[feature-1].value = sqrt(average.variance()[2]); + //cout << feature << ":" << std_v << " "; + feature++; + gabor_responses[feature-1].index = feature; + gabor_responses[feature-1].value = luv(gLocations[j][0],gLocations[j][1])[1]; + //cout << feature << ":" << u_values[pixel_number] << " "; + feature++; + gabor_responses[feature-1].index = feature; + gabor_responses[feature-1].value = luv(gLocations[j][0],gLocations[j][1])[2]; + //cout << feature << ":" << v_values[pixel_number] << " " << endl; + gabor_responses[feature].index = -1; - // Do something with each pixel... - unsigned int counter = 0; - vigra::UInt16RGBImage::iterator img_iter(luv.begin()),end(luv.end()); - //vigra::FRGBImage::iterator img_iter(luv.begin()),end(luv.end()); - - for(; img_iter != end; ++img_iter) { + svm_predict_probability(model,gabor_responses,prob_estimates); + svm_response.push_back(prob_estimates[0]); + } + // Free up libsvm stuff + free(gabor_responses); + free(prob_estimates); + return svm_response; +}; - // [0] is L, [1] is U, [2] is V - // We only want L for Gabor filtering - frameBuf[counter] = (*img_iter)[0]; - - u_values[counter] = (*img_iter)[1]; - v_values[counter] = (*img_iter)[2]; - - //cout << "Pixel " << counter << " - L: " << (*img_iter)[0] << endl; - //cout << "Pixel " << counter << " - U: " << (*img_iter)[1] << endl; - //cout << "Pixel " << counter << " - V: " << (*img_iter)[2] << endl; - counter++; - } +// create a grid of control points for creating masks +void createGrid(int& gNumLocs,int**& gLocations,int gRadius,int width, int height) +{ + int spacing=(gRadius*2)+1; + for (int i = gRadius; i < height - gRadius; i += spacing ) + { + for (int j = gRadius; j < width - gRadius; j += spacing ) + { + gNumLocs++; + } + // Add extra FP at the end of each row in case width % gRadius + gNumLocs++; + } - // Prepare framebuf for Gabor API - unsigned int k = 0; - for (int i = 0; i < in.height(); i++ ){ - for (int j = 0; j < in.width(); j++ ){ - pixels[i][j] = frameBuf[k]; - //cout << i << " " << j << " = " << k << " - " << frameBuf[k] << endl; - k++; - } - } + // Add extra FP at the end of each row in case nh % gRadius + for (int j = gRadius; j < width - gRadius; j += spacing ) + { + gNumLocs++; + } - if (gNumLocs){ + // Create the storage matrix + gLocations = CreateMatrix( (int)0, gNumLocs, 2); + gNumLocs = 0; + for (int i = gRadius; i < height - gRadius; i += spacing ) + { + for (int j = gRadius; j < width - gRadius; j += spacing ) + { + gLocations[gNumLocs][0] = j; + gLocations[gNumLocs][1] = i; + //cout << "fPoint " << gNumLocs << ":\t" << i << " " << j << endl; + gNumLocs++; + } - float *response = NULL; - int len = 0; - - // Scale control points by sizefactor - for (int j = 0; j < gNumLocs; j++){ - - //cout << sizefactor << ": " << gLocations[j][0] << "," << gLocations[j][1] << " ---> "; - gLocations[j][0] = int(gLocations[j][0] * sizefactor); - gLocations[j][1] = int(gLocations[j][1] * sizefactor); - //cout << gLocations[j][0] << "," << gLocations[j][1] << endl; + // Add extra FP at the end of each row in case width % spacing + if (width % spacing) + { + gLocations[gNumLocs][0] = width - gRadius - 1; + gLocations[gNumLocs][1] = i; + //cout << "efPoint " << gNumLocs << ":\t" << i << " " << nw - gRadius - 1 << endl; + gNumLocs++; + } + } - // Move CPs to border if the filter radius is out of bounds - if (gLocations[j][0] <= gRadius){ - //cout << "Moving CP to border" << endl; - gLocations[j][0] = gRadius + 1; - } - if (gLocations[j][1] <= gRadius){ - //cout << "Moving CP to border" << endl; - gLocations[j][1] = gRadius + 1; - } - if (gLocations[j][0] >= nw - gRadius){ - //cout << "Moving CP to border" << endl; - gLocations[j][0] = nw - gRadius - 1; - } - if (gLocations[j][1] >= nh - gRadius){ - //cout << "Moving CP to border" << endl; - gLocations[j][1] = nh - gRadius - 1; - } - } + // Add extra FP at the end of each row in case height % spacing + if (height % spacing) + { + for (int j = gRadius; j < width - gRadius; j += spacing ) + { + gLocations[gNumLocs][0] = j; + gLocations[gNumLocs][1] = height - gRadius - 1; + //cout << "efPoint " << gNumLocs << ":\t" << nh - gRadius - 1 << " " << j << endl; + gNumLocs++; + } + } +}; - // Do Gabor filtering - cout << "Generating feature vector by Gabor filtering..." << endl; - response = ProcessChannel( pixels, in.height(), in.width(), response, &len, basename ); - - // Turn the response into SVM vector, and add colour features - int vector_length = (int)len/gNumLocs; - - cout << "Classifying control points using SVM..." << endl; - for (int j = 0; j < gNumLocs; j++){ +//generates the celeste mask on base of given responses and locations +void generateMask(vigra::BImage& mask,int& gNumLocs, int**& gLocations,std::vector<double> svm_responses,int gRadius, double threshold) +{ + for ( int j = 0; j < gNumLocs; j++ ) + { + if (svm_responses[j] >= threshold) + { + unsigned int sub_x0 = gLocations[j][0] - gRadius; + unsigned int sub_y0 = gLocations[j][1] - gRadius; + unsigned int sub_x1 = gLocations[j][0] + gRadius + 1; + unsigned int sub_y1 = gLocations[j][1] + gRadius + 1; + //cout << sub_x0 << ","<< sub_y0 << " - " << sub_x1 << "," << sub_y1 << endl; - int pixel_number = gLocations[j][0] + (in.width() * (gLocations[j][1] - 1)) - 1; - unsigned int feature = 1; - double score = 0; - - //cout << "0 "; - for ( int v = (j * vector_length); v < ((j + 1) * vector_length); v++){ - gabor_responses[feature-1].index = feature; - gabor_responses[feature-1].value = response[v]; - //cout << feature << ":" << response[v] << " "; - feature++; - } + // Set region to black + vigra::initImage(srcIterRange(mask.upperLeft() + vigra::Diff2D(sub_x0, sub_y0), + mask.upperLeft() + vigra::Diff2D(sub_x1, sub_y1)), 0); + } + else + { + //cout << "Non-cloud\t(score " << prob_estimates[0] << " <= " << threshold << ")" << endl; + } + } +}; - // Work out average colour - U + V channels - float u_sum = 0, v_sum = 0; - unsigned int pixels_in_region = (gRadius * 2)*(gRadius * 2); - - for (int t = 1 - gRadius; t <= gRadius; t++){ - - unsigned int this_y_pixel = pixel_number + (t * in.width()); +vigra::BImage getCelesteMask(struct svm_model* model, vigra::UInt16RGBImage& input, int radius, float threshold, int resize_dimension,bool adaptThreshold,bool verbose) +{ + vigra::UInt16RGBImage luv; + double sizefactor=1.0; + prepareCelesteImage(input,luv,resize_dimension,sizefactor,verbose); - for (int r = 1 - gRadius; r <= gRadius; r++){ - |