10ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong// Ceres Solver - A fast non-linear least squares minimizer
20ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong// Copyright 2012 Google Inc. All rights reserved.
30ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong// http://code.google.com/p/ceres-solver/
40ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong//
50ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong// Redistribution and use in source and binary forms, with or without
60ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong// modification, are permitted provided that the following conditions are met:
70ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong//
80ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong// * Redistributions of source code must retain the above copyright notice,
90ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong//   this list of conditions and the following disclaimer.
100ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong// * Redistributions in binary form must reproduce the above copyright notice,
110ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong//   this list of conditions and the following disclaimer in the documentation
120ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong//   and/or other materials provided with the distribution.
130ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong// * Neither the name of Google Inc. nor the names of its contributors may be
140ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong//   used to endorse or promote products derived from this software without
150ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong//   specific prior written permission.
160ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong//
170ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
180ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
190ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
200ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
210ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
220ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
230ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
240ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
250ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
260ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
270ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong// POSSIBILITY OF SUCH DAMAGE.
280ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong//
290ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong// Author: strandmark@google.com (Petter Strandmark)
300ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong//
310ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong// Denoising using Fields of Experts and the Ceres minimizer.
320ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong//
330ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong// Note that for good denoising results the weighting between the data term
340ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong// and the Fields of Experts term needs to be adjusted. This is discussed
350ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong// in [1]. This program assumes Gaussian noise. The noise model can be changed
360ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong// by substituing another function for QuadraticCostFunction.
370ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong//
380ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong// [1] S. Roth and M.J. Black. "Fields of Experts." International Journal of
390ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong//     Computer Vision, 82(2):205--229, 2009.
400ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong
410ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong#include <algorithm>
420ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong#include <cmath>
430ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong#include <iostream>
440ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong#include <vector>
450ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong#include <sstream>
460ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong#include <string>
470ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong
480ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong#include "ceres/ceres.h"
490ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong#include "gflags/gflags.h"
500ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong#include "glog/logging.h"
510ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong
520ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong#include "fields_of_experts.h"
530ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong#include "pgm_image.h"
540ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong
550ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus KongDEFINE_string(input, "", "File to which the output image should be written");
560ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus KongDEFINE_string(foe_file, "", "FoE file to use");
570ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus KongDEFINE_string(output, "", "File to which the output image should be written");
580ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus KongDEFINE_double(sigma, 20.0, "Standard deviation of noise");
590ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus KongDEFINE_bool(verbose, false, "Prints information about the solver progress.");
601d2624a10e2c559f8ba9ef89eaa30832c0a83a96Sascha HaeberlingDEFINE_bool(line_search, false, "Use a line search instead of trust region "
611d2624a10e2c559f8ba9ef89eaa30832c0a83a96Sascha Haeberling            "algorithm.");
620ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong
630ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kongnamespace ceres {
640ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kongnamespace examples {
650ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong
660ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong// This cost function is used to build the data term.
670ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong//
680ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong//   f_i(x) = a * (x_i - b)^2
690ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong//
700ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kongclass QuadraticCostFunction : public ceres::SizedCostFunction<1, 1> {
710ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong public:
720ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong  QuadraticCostFunction(double a, double b)
730ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong    : sqrta_(std::sqrt(a)), b_(b) {}
740ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong  virtual bool Evaluate(double const* const* parameters,
750ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong                        double* residuals,
760ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong                        double** jacobians) const {
770ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong    const double x = parameters[0][0];
780ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong    residuals[0] = sqrta_ * (x - b_);
790ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong    if (jacobians != NULL && jacobians[0] != NULL) {
800ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong      jacobians[0][0] = sqrta_;
810ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong    }
820ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong    return true;
830ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong  }
840ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong private:
850ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong  double sqrta_, b_;
860ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong};
870ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong
880ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong// Creates a Fields of Experts MAP inference problem.
890ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kongvoid CreateProblem(const FieldsOfExperts& foe,
900ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong                   const PGMImage<double>& image,
910ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong                   Problem* problem,
920ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong                   PGMImage<double>* solution) {
930ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong  // Create the data term
940ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong  CHECK_GT(FLAGS_sigma, 0.0);
950ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong  const double coefficient = 1 / (2.0 * FLAGS_sigma * FLAGS_sigma);
960ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong  for (unsigned index = 0; index < image.NumPixels(); ++index) {
970ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong    ceres::CostFunction* cost_function =
980ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong        new QuadraticCostFunction(coefficient,
990ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong                                  image.PixelFromLinearIndex(index));
1000ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong    problem->AddResidualBlock(cost_function,
1010ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong                              NULL,
1020ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong                              solution->MutablePixelFromLinearIndex(index));
1030ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong  }
1040ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong
1050ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong  // Create Ceres cost and loss functions for regularization. One is needed for
1060ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong  // each filter.
1070ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong  std::vector<ceres::LossFunction*> loss_function(foe.NumFilters());
1080ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong  std::vector<ceres::CostFunction*> cost_function(foe.NumFilters());
1090ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong  for (int alpha_index = 0; alpha_index < foe.NumFilters(); ++alpha_index) {
1100ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong    loss_function[alpha_index] = foe.NewLossFunction(alpha_index);
1110ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong    cost_function[alpha_index] = foe.NewCostFunction(alpha_index);
1120ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong  }
1130ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong
1140ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong  // Add FoE regularization for each patch in the image.
1150ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong  for (int x = 0; x < image.width() - (foe.Size() - 1); ++x) {
1160ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong    for (int y = 0; y < image.height() - (foe.Size() - 1); ++y) {
1170ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong      // Build a vector with the pixel indices of this patch.
1180ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong      std::vector<double*> pixels;
1190ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong      const std::vector<int>& x_delta_indices = foe.GetXDeltaIndices();
1200ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong      const std::vector<int>& y_delta_indices = foe.GetYDeltaIndices();
1210ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong      for (int i = 0; i < foe.NumVariables(); ++i) {
1220ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong        double* pixel = solution->MutablePixel(x + x_delta_indices[i],
1230ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong                                               y + y_delta_indices[i]);
1240ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong        pixels.push_back(pixel);
1250ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong      }
1260ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong      // For this patch with coordinates (x, y), we will add foe.NumFilters()
1270ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong      // terms to the objective function.
1280ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong      for (int alpha_index = 0; alpha_index < foe.NumFilters(); ++alpha_index) {
1290ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong        problem->AddResidualBlock(cost_function[alpha_index],
1300ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong                                  loss_function[alpha_index],
1310ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong                                  pixels);
1320ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong      }
1330ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong    }
1340ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong  }
1350ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong}
1360ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong
1370ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong// Solves the FoE problem using Ceres and post-processes it to make sure the
1380ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong// solution stays within [0, 255].
1390ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kongvoid SolveProblem(Problem* problem, PGMImage<double>* solution) {
1400ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong  // These parameters may be experimented with. For example, ceres::DOGLEG tends
1410ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong  // to be faster for 2x2 filters, but gives solutions with slightly higher
1420ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong  // objective function value.
1430ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong  ceres::Solver::Options options;
1440ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong  options.max_num_iterations = 100;
1450ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong  if (FLAGS_verbose) {
1460ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong    options.minimizer_progress_to_stdout = true;
1470ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong  }
1481d2624a10e2c559f8ba9ef89eaa30832c0a83a96Sascha Haeberling
1491d2624a10e2c559f8ba9ef89eaa30832c0a83a96Sascha Haeberling  if (FLAGS_line_search) {
1501d2624a10e2c559f8ba9ef89eaa30832c0a83a96Sascha Haeberling    options.minimizer_type = ceres::LINE_SEARCH;
1511d2624a10e2c559f8ba9ef89eaa30832c0a83a96Sascha Haeberling  }
1521d2624a10e2c559f8ba9ef89eaa30832c0a83a96Sascha Haeberling
1530ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong  options.linear_solver_type = ceres::SPARSE_NORMAL_CHOLESKY;
1540ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong  options.function_tolerance = 1e-3;  // Enough for denoising.
1550ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong
1560ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong  ceres::Solver::Summary summary;
1570ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong  ceres::Solve(options, problem, &summary);
1580ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong  if (FLAGS_verbose) {
1590ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong    std::cout << summary.FullReport() << "\n";
1600ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong  }
1610ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong
1620ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong  // Make the solution stay in [0, 255].
1630ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong  for (int x = 0; x < solution->width(); ++x) {
1640ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong    for (int y = 0; y < solution->height(); ++y) {
1650ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong      *solution->MutablePixel(x, y) =
1660ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong          std::min(255.0, std::max(0.0, solution->Pixel(x, y)));
1670ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong    }
1680ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong  }
1690ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong}
1700ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong}  // namespace examples
1710ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong}  // namespace ceres
1720ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong
1730ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kongint main(int argc, char** argv) {
1740ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong  using namespace ceres::examples;
1750ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong  std::string
1760ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong      usage("This program denoises an image using Ceres.  Sample usage:\n");
1770ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong  usage += argv[0];
1780ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong  usage += " --input=<noisy image PGM file> --foe_file=<FoE file name>";
1790ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong  google::SetUsageMessage(usage);
1800ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong  google::ParseCommandLineFlags(&argc, &argv, true);
1810ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong  google::InitGoogleLogging(argv[0]);
1820ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong
1830ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong  if (FLAGS_input.empty()) {
1840ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong    std::cerr << "Please provide an image file name.\n";
1850ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong    return 1;
1860ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong  }
1870ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong
1880ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong  if (FLAGS_foe_file.empty()) {
1890ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong    std::cerr << "Please provide a Fields of Experts file name.\n";
1900ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong    return 1;
1910ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong  }
1920ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong
1930ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong  // Load the Fields of Experts filters from file.
1940ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong  FieldsOfExperts foe;
1950ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong  if (!foe.LoadFromFile(FLAGS_foe_file)) {
1960ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong    std::cerr << "Loading \"" << FLAGS_foe_file << "\" failed.\n";
1970ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong    return 2;
1980ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong  }
1990ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong
2000ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong  // Read the images
2010ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong  PGMImage<double> image(FLAGS_input);
2020ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong  if (image.width() == 0) {
2030ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong    std::cerr << "Reading \"" << FLAGS_input << "\" failed.\n";
2040ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong    return 3;
2050ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong  }
2060ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong  PGMImage<double> solution(image.width(), image.height());
2070ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong  solution.Set(0.0);
2080ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong
2090ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong  ceres::Problem problem;
2100ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong  CreateProblem(foe, image, &problem, &solution);
2110ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong
2120ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong  SolveProblem(&problem, &solution);
2130ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong
2140ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong  if (!FLAGS_output.empty()) {
2150ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong    CHECK(solution.WriteToFile(FLAGS_output))
2160ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong        << "Writing \"" << FLAGS_output << "\" failed.";
2170ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong  }
2180ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong
2190ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong  return 0;
2200ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong}
221