|
Packit |
ea1746 |
// Ceres Solver - A fast non-linear least squares minimizer
|
|
Packit |
ea1746 |
// Copyright 2015 Google Inc. All rights reserved.
|
|
Packit |
ea1746 |
// http://ceres-solver.org/
|
|
Packit |
ea1746 |
//
|
|
Packit |
ea1746 |
// Redistribution and use in source and binary forms, with or without
|
|
Packit |
ea1746 |
// modification, are permitted provided that the following conditions are met:
|
|
Packit |
ea1746 |
//
|
|
Packit |
ea1746 |
// * Redistributions of source code must retain the above copyright notice,
|
|
Packit |
ea1746 |
// this list of conditions and the following disclaimer.
|
|
Packit |
ea1746 |
// * Redistributions in binary form must reproduce the above copyright notice,
|
|
Packit |
ea1746 |
// this list of conditions and the following disclaimer in the documentation
|
|
Packit |
ea1746 |
// and/or other materials provided with the distribution.
|
|
Packit |
ea1746 |
// * Neither the name of Google Inc. nor the names of its contributors may be
|
|
Packit |
ea1746 |
// used to endorse or promote products derived from this software without
|
|
Packit |
ea1746 |
// specific prior written permission.
|
|
Packit |
ea1746 |
//
|
|
Packit |
ea1746 |
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
Packit |
ea1746 |
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
Packit |
ea1746 |
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
Packit |
ea1746 |
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
|
Packit |
ea1746 |
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
Packit |
ea1746 |
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
Packit |
ea1746 |
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
Packit |
ea1746 |
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
Packit |
ea1746 |
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
Packit |
ea1746 |
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
Packit |
ea1746 |
// POSSIBILITY OF SUCH DAMAGE.
|
|
Packit |
ea1746 |
//
|
|
Packit |
ea1746 |
// Author: strandmark@google.com (Petter Strandmark)
|
|
Packit |
ea1746 |
//
|
|
Packit |
ea1746 |
// Simple class for accessing PGM images.
|
|
Packit |
ea1746 |
|
|
Packit |
ea1746 |
#ifndef CERES_EXAMPLES_PGM_IMAGE_H_
|
|
Packit |
ea1746 |
#define CERES_EXAMPLES_PGM_IMAGE_H_
|
|
Packit |
ea1746 |
|
|
Packit |
ea1746 |
#include <algorithm>
|
|
Packit |
ea1746 |
#include <cstring>
|
|
Packit |
ea1746 |
#include <fstream>
|
|
Packit |
ea1746 |
#include <iostream>
|
|
Packit |
ea1746 |
#include <sstream>
|
|
Packit |
ea1746 |
#include <string>
|
|
Packit |
ea1746 |
#include <vector>
|
|
Packit |
ea1746 |
|
|
Packit |
ea1746 |
#include "glog/logging.h"
|
|
Packit |
ea1746 |
|
|
Packit |
ea1746 |
namespace ceres {
|
|
Packit |
ea1746 |
namespace examples {
|
|
Packit |
ea1746 |
|
|
Packit |
ea1746 |
template<typename Real>
|
|
Packit |
ea1746 |
class PGMImage {
|
|
Packit |
ea1746 |
public:
|
|
Packit |
ea1746 |
// Create an empty image
|
|
Packit |
ea1746 |
PGMImage(int width, int height);
|
|
Packit |
ea1746 |
// Load an image from file
|
|
Packit |
ea1746 |
explicit PGMImage(std::string filename);
|
|
Packit |
ea1746 |
// Sets an image to a constant
|
|
Packit |
ea1746 |
void Set(double constant);
|
|
Packit |
ea1746 |
|
|
Packit |
ea1746 |
// Reading dimensions
|
|
Packit |
ea1746 |
int width() const;
|
|
Packit |
ea1746 |
int height() const;
|
|
Packit |
ea1746 |
int NumPixels() const;
|
|
Packit |
ea1746 |
|
|
Packit |
ea1746 |
// Get individual pixels
|
|
Packit |
ea1746 |
Real* MutablePixel(int x, int y);
|
|
Packit |
ea1746 |
Real Pixel(int x, int y) const;
|
|
Packit |
ea1746 |
Real* MutablePixelFromLinearIndex(int index);
|
|
Packit |
ea1746 |
Real PixelFromLinearIndex(int index) const;
|
|
Packit |
ea1746 |
int LinearIndex(int x, int y) const;
|
|
Packit |
ea1746 |
|
|
Packit |
ea1746 |
// Adds an image to another
|
|
Packit |
ea1746 |
void operator+=(const PGMImage& image);
|
|
Packit |
ea1746 |
// Adds a constant to an image
|
|
Packit |
ea1746 |
void operator+=(Real a);
|
|
Packit |
ea1746 |
// Multiplies an image by a constant
|
|
Packit |
ea1746 |
void operator*=(Real a);
|
|
Packit |
ea1746 |
|
|
Packit |
ea1746 |
// File access
|
|
Packit |
ea1746 |
bool WriteToFile(std::string filename) const;
|
|
Packit |
ea1746 |
bool ReadFromFile(std::string filename);
|
|
Packit |
ea1746 |
|
|
Packit |
ea1746 |
// Accessing the image data directly
|
|
Packit |
ea1746 |
bool SetData(const std::vector<Real>& new_data);
|
|
Packit |
ea1746 |
const std::vector<Real>& data() const;
|
|
Packit |
ea1746 |
|
|
Packit |
ea1746 |
protected:
|
|
Packit |
ea1746 |
int height_, width_;
|
|
Packit |
ea1746 |
std::vector<Real> data_;
|
|
Packit |
ea1746 |
};
|
|
Packit |
ea1746 |
|
|
Packit |
ea1746 |
// --- IMPLEMENTATION
|
|
Packit |
ea1746 |
|
|
Packit |
ea1746 |
template<typename Real>
|
|
Packit |
ea1746 |
PGMImage<Real>::PGMImage(int width, int height)
|
|
Packit |
ea1746 |
: height_(height), width_(width), data_(width*height, 0.0) {
|
|
Packit |
ea1746 |
}
|
|
Packit |
ea1746 |
|
|
Packit |
ea1746 |
template<typename Real>
|
|
Packit |
ea1746 |
PGMImage<Real>::PGMImage(std::string filename) {
|
|
Packit |
ea1746 |
if (!ReadFromFile(filename)) {
|
|
Packit |
ea1746 |
height_ = 0;
|
|
Packit |
ea1746 |
width_ = 0;
|
|
Packit |
ea1746 |
}
|
|
Packit |
ea1746 |
}
|
|
Packit |
ea1746 |
|
|
Packit |
ea1746 |
template<typename Real>
|
|
Packit |
ea1746 |
void PGMImage<Real>::Set(double constant) {
|
|
Packit |
ea1746 |
for (int i = 0; i < data_.size(); ++i) {
|
|
Packit |
ea1746 |
data_[i] = constant;
|
|
Packit |
ea1746 |
}
|
|
Packit |
ea1746 |
}
|
|
Packit |
ea1746 |
|
|
Packit |
ea1746 |
template<typename Real>
|
|
Packit |
ea1746 |
int PGMImage<Real>::width() const {
|
|
Packit |
ea1746 |
return width_;
|
|
Packit |
ea1746 |
}
|
|
Packit |
ea1746 |
|
|
Packit |
ea1746 |
template<typename Real>
|
|
Packit |
ea1746 |
int PGMImage<Real>::height() const {
|
|
Packit |
ea1746 |
return height_;
|
|
Packit |
ea1746 |
}
|
|
Packit |
ea1746 |
|
|
Packit |
ea1746 |
template<typename Real>
|
|
Packit |
ea1746 |
int PGMImage<Real>::NumPixels() const {
|
|
Packit |
ea1746 |
return width_ * height_;
|
|
Packit |
ea1746 |
}
|
|
Packit |
ea1746 |
|
|
Packit |
ea1746 |
template<typename Real>
|
|
Packit |
ea1746 |
Real* PGMImage<Real>::MutablePixel(int x, int y) {
|
|
Packit |
ea1746 |
return MutablePixelFromLinearIndex(LinearIndex(x, y));
|
|
Packit |
ea1746 |
}
|
|
Packit |
ea1746 |
|
|
Packit |
ea1746 |
template<typename Real>
|
|
Packit |
ea1746 |
Real PGMImage<Real>::Pixel(int x, int y) const {
|
|
Packit |
ea1746 |
return PixelFromLinearIndex(LinearIndex(x, y));
|
|
Packit |
ea1746 |
}
|
|
Packit |
ea1746 |
|
|
Packit |
ea1746 |
template<typename Real>
|
|
Packit |
ea1746 |
Real* PGMImage<Real>::MutablePixelFromLinearIndex(int index) {
|
|
Packit |
ea1746 |
CHECK(index >= 0);
|
|
Packit |
ea1746 |
CHECK(index < width_ * height_);
|
|
Packit |
ea1746 |
CHECK(index < data_.size());
|
|
Packit |
ea1746 |
return &data_[index];
|
|
Packit |
ea1746 |
}
|
|
Packit |
ea1746 |
|
|
Packit |
ea1746 |
template<typename Real>
|
|
Packit |
ea1746 |
Real PGMImage<Real>::PixelFromLinearIndex(int index) const {
|
|
Packit |
ea1746 |
CHECK(index >= 0);
|
|
Packit |
ea1746 |
CHECK(index < width_ * height_);
|
|
Packit |
ea1746 |
CHECK(index < data_.size());
|
|
Packit |
ea1746 |
return data_[index];
|
|
Packit |
ea1746 |
}
|
|
Packit |
ea1746 |
|
|
Packit |
ea1746 |
template<typename Real>
|
|
Packit |
ea1746 |
int PGMImage<Real>::LinearIndex(int x, int y) const {
|
|
Packit |
ea1746 |
return x + width_*y;
|
|
Packit |
ea1746 |
}
|
|
Packit |
ea1746 |
|
|
Packit |
ea1746 |
// Adds an image to another
|
|
Packit |
ea1746 |
template<typename Real>
|
|
Packit |
ea1746 |
void PGMImage<Real>::operator+= (const PGMImage<Real>& image) {
|
|
Packit |
ea1746 |
CHECK(data_.size() == image.data_.size());
|
|
Packit |
ea1746 |
for (int i = 0; i < data_.size(); ++i) {
|
|
Packit |
ea1746 |
data_[i] += image.data_[i];
|
|
Packit |
ea1746 |
}
|
|
Packit |
ea1746 |
}
|
|
Packit |
ea1746 |
|
|
Packit |
ea1746 |
// Adds a constant to an image
|
|
Packit |
ea1746 |
template<typename Real>
|
|
Packit |
ea1746 |
void PGMImage<Real>::operator+= (Real a) {
|
|
Packit |
ea1746 |
for (int i = 0; i < data_.size(); ++i) {
|
|
Packit |
ea1746 |
data_[i] += a;
|
|
Packit |
ea1746 |
}
|
|
Packit |
ea1746 |
}
|
|
Packit |
ea1746 |
|
|
Packit |
ea1746 |
// Multiplies an image by a constant
|
|
Packit |
ea1746 |
template<typename Real>
|
|
Packit |
ea1746 |
void PGMImage<Real>::operator*= (Real a) {
|
|
Packit |
ea1746 |
for (int i = 0; i < data_.size(); ++i) {
|
|
Packit |
ea1746 |
data_[i] *= a;
|
|
Packit |
ea1746 |
}
|
|
Packit |
ea1746 |
}
|
|
Packit |
ea1746 |
|
|
Packit |
ea1746 |
template<typename Real>
|
|
Packit |
ea1746 |
bool PGMImage<Real>::WriteToFile(std::string filename) const {
|
|
Packit |
ea1746 |
std::ofstream outputfile(filename.c_str());
|
|
Packit |
ea1746 |
outputfile << "P2" << std::endl;
|
|
Packit |
ea1746 |
outputfile << "# PGM format" << std::endl;
|
|
Packit |
ea1746 |
outputfile << " # <width> <height> <levels> " << std::endl;
|
|
Packit |
ea1746 |
outputfile << " # <data> ... " << std::endl;
|
|
Packit |
ea1746 |
outputfile << width_ << ' ' << height_ << " 255 " << std::endl;
|
|
Packit |
ea1746 |
|
|
Packit |
ea1746 |
// Write data
|
|
Packit |
ea1746 |
int num_pixels = width_*height_;
|
|
Packit |
ea1746 |
for (int i = 0; i < num_pixels; ++i) {
|
|
Packit |
ea1746 |
// Convert to integer by rounding when writing file
|
|
Packit |
ea1746 |
outputfile << static_cast<int>(data_[i] + 0.5) << ' ';
|
|
Packit |
ea1746 |
}
|
|
Packit |
ea1746 |
|
|
Packit |
ea1746 |
return bool(outputfile); // Returns true/false
|
|
Packit |
ea1746 |
}
|
|
Packit |
ea1746 |
|
|
Packit |
ea1746 |
namespace {
|
|
Packit |
ea1746 |
|
|
Packit |
ea1746 |
// Helper function to read data from a text file, ignoring "#" comments.
|
|
Packit |
ea1746 |
template<typename T>
|
|
Packit |
ea1746 |
bool GetIgnoreComment(std::istream* in, T& t) {
|
|
Packit |
ea1746 |
std::string word;
|
|
Packit |
ea1746 |
bool ok;
|
|
Packit |
ea1746 |
do {
|
|
Packit |
ea1746 |
ok = true;
|
|
Packit |
ea1746 |
(*in) >> word;
|
|
Packit |
ea1746 |
if (word.length() > 0 && word[0] == '#') {
|
|
Packit |
ea1746 |
// Comment; read the whole line
|
|
Packit |
ea1746 |
ok = false;
|
|
Packit |
ea1746 |
std::getline(*in, word);
|
|
Packit |
ea1746 |
}
|
|
Packit |
ea1746 |
} while (!ok);
|
|
Packit |
ea1746 |
|
|
Packit |
ea1746 |
// Convert the string
|
|
Packit |
ea1746 |
std::stringstream sin(word);
|
|
Packit |
ea1746 |
sin >> t;
|
|
Packit |
ea1746 |
|
|
Packit |
ea1746 |
// Check for success
|
|
Packit |
ea1746 |
if (!in || !sin) {
|
|
Packit |
ea1746 |
return false;
|
|
Packit |
ea1746 |
}
|
|
Packit |
ea1746 |
return true;
|
|
Packit |
ea1746 |
}
|
|
Packit |
ea1746 |
} // namespace
|
|
Packit |
ea1746 |
|
|
Packit |
ea1746 |
template<typename Real>
|
|
Packit |
ea1746 |
bool PGMImage<Real>::ReadFromFile(std::string filename) {
|
|
Packit |
ea1746 |
std::ifstream inputfile(filename.c_str());
|
|
Packit |
ea1746 |
|
|
Packit |
ea1746 |
// File must start with "P2"
|
|
Packit |
ea1746 |
char ch1, ch2;
|
|
Packit |
ea1746 |
inputfile >> ch1 >> ch2;
|
|
Packit |
ea1746 |
if (!inputfile || ch1 != 'P' || (ch2 != '2' && ch2 != '5')) {
|
|
Packit |
ea1746 |
return false;
|
|
Packit |
ea1746 |
}
|
|
Packit |
ea1746 |
|
|
Packit |
ea1746 |
// Read the image header
|
|
Packit |
ea1746 |
int two_fifty_five;
|
|
Packit |
ea1746 |
if (!GetIgnoreComment(&inputfile, width_) ||
|
|
Packit |
ea1746 |
!GetIgnoreComment(&inputfile, height_) ||
|
|
Packit |
ea1746 |
!GetIgnoreComment(&inputfile, two_fifty_five) ) {
|
|
Packit |
ea1746 |
return false;
|
|
Packit |
ea1746 |
}
|
|
Packit |
ea1746 |
// Assert that the number of grey levels is 255.
|
|
Packit |
ea1746 |
if (two_fifty_five != 255) {
|
|
Packit |
ea1746 |
return false;
|
|
Packit |
ea1746 |
}
|
|
Packit |
ea1746 |
|
|
Packit |
ea1746 |
// Now read the data
|
|
Packit |
ea1746 |
int num_pixels = width_*height_;
|
|
Packit |
ea1746 |
data_.resize(num_pixels);
|
|
Packit |
ea1746 |
if (ch2 == '2') {
|
|
Packit |
ea1746 |
// Ascii file
|
|
Packit |
ea1746 |
for (int i = 0; i < num_pixels; ++i) {
|
|
Packit |
ea1746 |
int pixel_data;
|
|
Packit |
ea1746 |
bool res = GetIgnoreComment(&inputfile, pixel_data);
|
|
Packit |
ea1746 |
if (!res) {
|
|
Packit |
ea1746 |
return false;
|
|
Packit |
ea1746 |
}
|
|
Packit |
ea1746 |
data_[i] = pixel_data;
|
|
Packit |
ea1746 |
}
|
|
Packit |
ea1746 |
// There cannot be anything else in the file (except comments). Try reading
|
|
Packit |
ea1746 |
// another number and return failure if that succeeded.
|
|
Packit |
ea1746 |
int temp;
|
|
Packit |
ea1746 |
bool res = GetIgnoreComment(&inputfile, temp);
|
|
Packit |
ea1746 |
if (res) {
|
|
Packit |
ea1746 |
return false;
|
|
Packit |
ea1746 |
}
|
|
Packit |
ea1746 |
} else {
|
|
Packit |
ea1746 |
// Read the line feed character
|
|
Packit |
ea1746 |
if (inputfile.get() != '\n') {
|
|
Packit |
ea1746 |
return false;
|
|
Packit |
ea1746 |
}
|
|
Packit |
ea1746 |
// Binary file
|
|
Packit |
ea1746 |
// TODO(strandmark): Will not work on Windows (linebreak conversion).
|
|
Packit |
ea1746 |
for (int i = 0; i < num_pixels; ++i) {
|
|
Packit |
ea1746 |
unsigned char pixel_data = inputfile.get();
|
|
Packit |
ea1746 |
if (!inputfile) {
|
|
Packit |
ea1746 |
return false;
|
|
Packit |
ea1746 |
}
|
|
Packit |
ea1746 |
data_[i] = pixel_data;
|
|
Packit |
ea1746 |
}
|
|
Packit |
ea1746 |
// There cannot be anything else in the file. Try reading another byte
|
|
Packit |
ea1746 |
// and return failure if that succeeded.
|
|
Packit |
ea1746 |
inputfile.get();
|
|
Packit |
ea1746 |
if (inputfile) {
|
|
Packit |
ea1746 |
return false;
|
|
Packit |
ea1746 |
}
|
|
Packit |
ea1746 |
}
|
|
Packit |
ea1746 |
|
|
Packit |
ea1746 |
return true;
|
|
Packit |
ea1746 |
}
|
|
Packit |
ea1746 |
|
|
Packit |
ea1746 |
template<typename Real>
|
|
Packit |
ea1746 |
bool PGMImage<Real>::SetData(const std::vector<Real>& new_data) {
|
|
Packit |
ea1746 |
// This function cannot change the dimensions
|
|
Packit |
ea1746 |
if (new_data.size() != data_.size()) {
|
|
Packit |
ea1746 |
return false;
|
|
Packit |
ea1746 |
}
|
|
Packit |
ea1746 |
std::copy(new_data.begin(), new_data.end(), data_.begin());
|
|
Packit |
ea1746 |
return true;
|
|
Packit |
ea1746 |
}
|
|
Packit |
ea1746 |
|
|
Packit |
ea1746 |
template<typename Real>
|
|
Packit |
ea1746 |
const std::vector<Real>& PGMImage<Real>::data() const {
|
|
Packit |
ea1746 |
return data_;
|
|
Packit |
ea1746 |
}
|
|
Packit |
ea1746 |
|
|
Packit |
ea1746 |
} // namespace examples
|
|
Packit |
ea1746 |
} // namespace ceres
|
|
Packit |
ea1746 |
|
|
Packit |
ea1746 |
|
|
Packit |
ea1746 |
#endif // CERES_EXAMPLES_PGM_IMAGE_H_
|