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