Skip to content
Snippets Groups Projects
Select Git revision
  • 0bad1f8bf75f4126fce6ac86ade63cb0a3b41bcf
  • master default protected
  • ia2
  • adql2.1-ia2
  • private_rows
5 results

adqlGrammar201.jj

Blame
  • usgscsm_cam_test.cc 8.55 KiB
    // A tool to perform some basic tests and operations on a CSM camera model.
    // 
    // Functionality:
    //--------------
    //
    // - Load a CSM model in ISD format or model state format, via:
    //   --model <model file>
    //
    // - Save the model state if invoked with:
    //   --output-model-state <model state .json file>
    // 
    // - Test projecting rays from the camera to ground, then back,
    //   and compare with the original pixel values.  
    
    #include <UsgsAstroPlugin.h>
    #include <RasterGM.h>
    #include <UsgsAstroLsSensorModel.h>
    
    #include <iostream>
    #include <fstream>
    
    struct Options {
      std::string model;              // the .json file in isd or model state format
      std::string output_model_state; // the output model state in .json format
      int sample_rate;
      double subpixel_offset, height_above_datum;
      Options(): sample_rate(0), subpixel_offset(0.0), height_above_datum(0.0) {}
    };
    
    void printUsage(std::string const& progName) {
      std::cout << "Usage: " << progName << " --model <model file> [other options]"
                << "\nSee the documentation for more information.\n";
    }
    
    // Do some naive parsing. Every option is assumed to start with two
    // dashes and have a string value.  The --help option is handled
    // separately.
    bool parseOptions(int argc, char **argv, Options & opt) {
    
      std::vector<std::string> params;
      for (int it = 1; it < argc; it++) {
        if (std::string(argv[it]) == std::string("--help")) {
          printUsage(argv[0]);
          return false;
        }
        params.push_back(argv[it]);
      }
    
      if (params.size() %2 != 0 || params.empty()) {
        std::cout << "Could not parse correctly the input arguments.\n";
        printUsage(argv[0]);
        return false;
      }
    
      // Collect the parsed options in a map
      std::map<std::string, std::string> parsed_options;
      int num = params.size() / 2;
      for (int it = 0; it < num; it++) {
        std::string opt = params[2*it + 0];
        std::string val = params[2*it + 1];
        if (opt.size() <= 2 || opt[0] != '-' || opt[1] != '-' || val.empty() || val[0] == '-' ) {
          std::cout << "Could not parse correctly the input arguments.\n";
          printUsage(argv[0]);
          return false;
        }
        opt = opt.substr(2); // wipe the dashes
        parsed_options[opt] = val;
      }
      
      // It is safe to access non-existent values from a map, the result
      // will be an empty string
      opt.model = parsed_options["model"];
      if (opt.model == "") {
        std::cout << "The input model file is empty.\n";
        printUsage(argv[0]);
        return false;
      }
    
      // Enforce that the sample rate is an integer. Later the user can add
      // a subpixel offset to each sampled pixel, if desired.
      double sample_rate_double = atof(parsed_options["sample-rate"].c_str());
      if (sample_rate_double != round(sample_rate_double)) {
        std::cout << "The value of --sample-rate must be an integer.\n";
        printUsage(argv[0]);
        return false;
      }
    
      // Collect all other option values. If not set, the values will default to 0.
      opt.output_model_state = parsed_options["output-model-state"];
      opt.sample_rate        = sample_rate_double;
      opt.subpixel_offset    = atof(parsed_options["subpixel-offset"].c_str());
      opt.height_above_datum = atof(parsed_options["height-above-datum"].c_str());
    
      return true;
    }
    
    // Read a file's content in a single string
    bool readFileInString(std::string const& filename, std::string & str) {
    
      str.clear(); // clear the output
    
      std::ifstream ifs(filename.c_str());
      if (!ifs.is_open()) {
        std::cout << "Cannot open file: " << filename << std::endl;
        return false;
      }
      
      ifs.seekg(0, std::ios::end);   
      str.reserve(ifs.tellg());
      ifs.seekg(0, std::ios::beg);
      str.assign((std::istreambuf_iterator<char>(ifs)),
                 std::istreambuf_iterator<char>());
      ifs.close();
    
      return true;
    }
    
    // Sort the errors and print some stats
    void printErrors(std::vector<double> & errors) {
      std::sort(errors.begin(), errors.end());
    
      if (errors.empty()) {
        std::cout << "Empty list of errors.\n";
        return;
      }
      
      std::cout << "Norm of pixel errors after projecting from camera to ground and back.\n";
      std::cout << "Min:    " << errors[0] << "\n";
      std::cout << "Median: " << errors[errors.size()/2] << "\n";
      std::cout << "Max:    " << errors.back() << "\n";
      std::cout << "Count:  " << errors.size() << "\n";
    }
    
    double pixDiffNorm(csm::ImageCoord const& a, csm::ImageCoord const& b) {
      return sqrt((a.line - b.line) * (a.line - b.line) + (a.samp - b.samp) * (a.samp - b.samp));
    }
    
    // Load a CSM camera model from an ISD or state file. Return true on success.
    bool loadCsmCameraModel(std::string const& model_file,
                           std::shared_ptr<csm::RasterGM> & model) {
    
      // This is needed to trigger loading libusgscsm.so. Otherwise 0
      // plugins are detected.
      UsgsAstroLsSensorModel lsModel;
    
      // Try to read the model as an ISD
      csm::Isd isd(model_file);
      
      // Read the model in a string, for potentially finding parsing the
      // model state from it.
      std::string model_state;
      if (!readFileInString(model_file, model_state))
        return false;
      
      // Check if loading the model worked
      bool success = false;
    
      // Try all detected plugins and all models for each plugin.
      csm::PluginList plugins = csm::Plugin::getList();
      for (auto iter = plugins.begin(); iter != plugins.end(); iter++) {
        
        const csm::Plugin* csm_plugin = (*iter);
        std::cout << "Detected CSM plugin: " << csm_plugin->getPluginName()  << "\n";
    
        size_t num_models = csm_plugin->getNumModels();
        std::cout << "Number of models for this plugin: " << num_models << "\n";
        
        // First try to construct the model from isd, and if that fails, from the state
        csm::Model *csm = NULL;
        csm::WarningList* warnings = NULL;
        for (size_t i = 0; i < num_models; i++) {
          
          std::string model_name = (*iter)->getModelName(i);
          if (csm_plugin->canModelBeConstructedFromISD(isd, model_name, warnings)) {
            // Try to construct the model from the isd
            csm = csm_plugin->constructModelFromISD(isd, model_name, warnings);
            std::cout << "Loaded a CSM model of type " << model_name << " from ISD file "
                      << model_file << ".\n";
            success = true;
          } else if (csm_plugin->canModelBeConstructedFromState(model_name, model_state, warnings)) {
            // Try to construct it from the model state
            csm = csm_plugin->constructModelFromState(model_state, warnings);
            std::cout << "Loaded a CSM model of type " << model_name << " from model state file "
                      << model_file << ".\n";
            success = true;
          } else {
            // No luck so far
            continue;
          }
    
          csm::RasterGM *modelPtr = dynamic_cast<csm::RasterGM*>(csm);
          if (modelPtr == NULL) {
            // Normally earlier checks should be enough and this should not happen
            std::cerr << "Could not load correctly a CSM model.\n";
            return false;
          } else {
            // Assign to a smart pointer which will handle deallocation
            model = std::shared_ptr<csm::RasterGM>(modelPtr);
            break;
          }
        }
      }
      
      if (!success) {
        std::cerr << "Failed to load a CSM model from: " << model_file << ".\n";
        return false;
      }
      
      return true;
    }
    
    int main(int argc, char **argv) {
    
      Options opt;
      if (!parseOptions(argc, argv, opt))
        return 1;
    
      // Keep the model as smart pointer to the class from which the
      // specific model types inherit.
      std::shared_ptr<csm::RasterGM> model;
    
      if (!loadCsmCameraModel(opt.model, model))
        return 1;
    
      if (opt.output_model_state != "") {
        std::cout << "Writing model state: " << opt.output_model_state << "\n";
        std::ofstream ofs(opt.output_model_state.c_str());
        ofs << model->getModelState() << "\n";
        ofs.close();
      }
    
      if (opt.sample_rate > 0) {
        csm::ImageVector image_size = model->getImageSize();
        std::cout << "\n";
        std::cout << "Camera image rows and columns: "
                  << image_size.line << ' ' << image_size.samp << "\n";
        std::cout << "Row and column sample rate: " << opt.sample_rate << "\n";
        std::cout << "Subpixel offset for each pixel: " << opt.subpixel_offset << "\n";
        std::cout << "Ground height (relative to datum): " << opt.height_above_datum << "\n";
        std::vector<double> errors;
        for (int samp = 0; samp < image_size.samp; samp += opt.sample_rate) {
          for (int line = 0; line < image_size.line; line += opt.sample_rate) {
            csm::ImageCoord c(line + opt.subpixel_offset, samp + opt.subpixel_offset);
            csm::EcefCoord ground = model->imageToGround(c, opt.height_above_datum);
            csm::ImageCoord d = model->groundToImage(ground);
            double error = pixDiffNorm(c, d);
            errors.push_back(error);
          }   
        }
        printErrors(errors);
      }
        
      return 0;
    }