// -*- lsst-c++ -*- /* * LSST Data Management System * Copyright 2008, 2009, 2010 LSST Corporation. * * This product includes software developed by the * LSST Project (http://www.lsst.org/). * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the LSST License Statement and * the GNU General Public License along with this program. If not, * see . */ /** * \file * \brief Support for 2-D images * * This file contains the basic 2-d image support for LSST */ #ifndef LSST_AFW_IMAGE_IMAGE_H #define LSST_AFW_IMAGE_IMAGE_H #include #include #include #include #include #include "boost/mpl/bool.hpp" #include #include "lsst/afw/geom/Extent.h" #include "lsst/afw/geom/Box.h" #include "lsst/afw/geom/Point.h" #include "lsst/afw/image/lsstGil.h" #include "lsst/afw/image/ImageUtils.h" #include "lsst/afw/math/Function.h" #include "lsst/daf/base.h" #include "lsst/daf/base/Citizen.h" #include "lsst/pex/exceptions.h" #include "ndarray.h" namespace lsst { namespace afw { namespace formatters { template class ImageFormatter; template class DecoratedImageFormatter; } namespace fits { class Fits; class MemFileManager; } namespace image { namespace detail { // // Traits for image types // /// Base %image tag struct basic_tag { }; /// tag for an Image struct Image_tag : public basic_tag { }; /// traits class for image categories template struct image_traits { typedef typename ImageT::image_category image_category; }; // std::string const wcsNameForXY0 = "A"; // the name of the WCS to use to save (X0, Y0) to FITS files; e.g. "A" } /*********************************************************************************************************/ /// A class used to request that array accesses be checked class CheckIndices { public: explicit CheckIndices(bool check=true) : _check(check) {} operator bool() const { return _check; } private: bool _check; }; /*********************************************************************************************************/ /// \brief metafunction to extract reference type from PixelT template struct Reference { typedef typename boost::gil::channel_traits::reference type; ///< reference type }; /// \brief metafunction to extract const reference type from PixelT template struct ConstReference { typedef typename boost::gil::channel_traits::const_reference type; ///< const reference type }; enum ImageOrigin {PARENT, LOCAL}; /// \brief The base class for all %image classed (Image, Mask, MaskedImage, ...) // // You are not expected to use this class directly in your own code; use one of the // specialised subclasses // template class ImageBase : public lsst::daf::base::Persistable, public lsst::daf::base::Citizen { private: typedef typename lsst::afw::image::detail::types_traits::view_t _view_t; typedef typename lsst::afw::image::detail::types_traits::const_view_t _const_view_t; typedef ndarray::Manager Manager; public: typedef std::shared_ptr > Ptr; ///< A shared_ptr to an ImageBase typedef std::shared_ptr > ConstPtr; ///< A shared_ptr to a const ImageBase typedef detail::basic_tag image_category; ///< trait class to identify type of %image /// A single Pixel of the same type as those in the ImageBase typedef PixelT SinglePixel; /// A pixel in this ImageBase typedef PixelT Pixel; /// A Reference to a PixelT typedef typename Reference::type PixelReference; /// A ConstReference to a PixelT typedef typename ConstReference::type PixelConstReference; /// An xy_locator typedef typename _view_t::xy_locator xy_locator; /// A const_xy_locator typedef typename _view_t::xy_locator::const_t const_xy_locator; /// An STL compliant iterator typedef typename _view_t::iterator iterator; /// An STL compliant const iterator typedef typename _const_view_t::iterator const_iterator; /// An STL compliant reverse iterator typedef typename _view_t::reverse_iterator reverse_iterator; /// An STL compliant const reverse iterator typedef typename _const_view_t::reverse_iterator const_reverse_iterator; /// An iterator for traversing the pixels in a row typedef typename _view_t::x_iterator x_iterator; /// A fast STL compliant iterator for contiguous images /// N.b. The order of pixel access is undefined typedef x_iterator fast_iterator; /// An iterator for traversing the pixels in a row, created from an xy_locator typedef typename _view_t::x_iterator xy_x_iterator; /// A const iterator for traversing the pixels in a row typedef typename _const_view_t::x_iterator const_x_iterator; /// An iterator for traversing the pixels in a column typedef typename _view_t::y_iterator y_iterator; /// An iterator for traversing the pixels in a row, created from an xy_locator typedef typename _view_t::y_iterator xy_y_iterator; /// A const iterator for traversing the pixels in a column typedef typename _const_view_t::y_iterator const_y_iterator; /// A mutable ndarray representation of the image typedef typename ndarray::Array Array; /// An immutable ndarray representation of the image typedef typename ndarray::Array ConstArray; template friend class ImageBase; // needed by generalised copy constructors // /// \brief Convert a type to our SinglePixel type // template static SinglePixel PixelCast(SinglePixelT rhs) { return SinglePixel(rhs); } // // DecoratedImage needs enough access to ImageBase to read data from disk; we might be able to design around this // template friend class DecoratedImage; template friend class MaskedImage; explicit ImageBase(const geom::Extent2I & dimensions=geom::Extent2I()); explicit ImageBase(const geom::Box2I &bbox); ImageBase(const ImageBase& src, const bool deep=false); explicit ImageBase(const ImageBase& src, const geom::Box2I& bbox, const ImageOrigin origin=PARENT, const bool deep=false); /// generalised copy constructor /// /// defined here in the header so that the compiler can instantiate N(N-1) conversions between N /// ImageBase types. template ImageBase(const ImageBase& rhs, const bool deep) : lsst::daf::base::Citizen(typeid(this)) { if (!deep) { throw LSST_EXCEPT(lsst::pex::exceptions::InvalidParameterError, "Only deep copies are permitted for ImageBases with different pixel types"); } ImageBase tmp(rhs.getBBox()); copy_and_convert_pixels(rhs._gilView, tmp._gilView); // from boost::gil using std::swap; // See Meyers, Effective C++, Item 25 ImageBase::swap(tmp); // See Meyers, Effective C++, Items 11 and 43 } explicit ImageBase( Array const & array, bool deep = false, geom::Point2I const & xy0 = geom::Point2I() ); virtual ~ImageBase() { } ImageBase& operator=(const ImageBase& rhs); ImageBase& operator=(const PixelT rhs); ImageBase& operator<<=(const ImageBase& rhs); void assign(ImageBase const &rsh, geom::Box2I const &bbox = geom::Box2I(), ImageOrigin origin=PARENT); // // Operators etc. // PixelReference operator()(int x, int y); PixelReference operator()(int x, int y, CheckIndices const&); PixelConstReference operator()(int x, int y) const; PixelConstReference operator()(int x, int y, CheckIndices const&) const; PixelConstReference get0(int x, int y) const { return operator()(x-getX0(), y-getY0()); } PixelConstReference get0(int x, int y, CheckIndices const& check) const { return operator()(x-getX0(), y-getY0(), check); } void set0(int x, int y, const PixelT v) { operator()(x-getX0(), y-getY0()) = v; } void set0(int x, int y, const PixelT v, CheckIndices const& check) { operator()(x-getX0(), y-getY0(), check) = v; } /// Return the number of columns in the %image int getWidth() const { return _gilView.width(); } /// Return the number of rows in the %image int getHeight() const { return _gilView.height(); } /** * Return the %image's column-origin * * This will usually be 0 except for images created using the * ImageBase(fileName, hdu, BBox, mode) ctor or ImageBase(ImageBase, BBox) cctor * The origin can be reset with \c setXY0 */ int getX0() const { return _origin.getX(); } /** * Return the %image's row-origin * * This will usually be 0 except for images created using the * ImageBase(fileName, hdu, BBox, mode) ctor or ImageBase(ImageBase, BBox) cctor * The origin can be reset with \c setXY0 */ int getY0() const { return _origin.getY(); } /** * Return the %image's origin * * This will usually be (0, 0) except for images created using the * ImageBase(fileName, hdu, BBox, mode) ctor or ImageBase(ImageBase, BBox) cctor * The origin can be reset with \c setXY0 */ geom::Point2I getXY0() const { return _origin; } /** * @brief Convert image position to index (nearest integer and fractional parts) * * @return std::pair(nearest integer index, fractional part) */ std::pair positionToIndex( double const pos, ///< image position lsst::afw::image::xOrY const xy ///< Is this a column or row coordinate? ) const { double const fullIndex = pos - PixelZeroPos - (xy == X ? getX0() : getY0()); int const roundedIndex = static_cast(fullIndex + 0.5); double const residual = fullIndex - roundedIndex; return std::pair(roundedIndex, residual); } /** * @brief Convert image index to image position * * The LSST indexing convention is: * * the index of the bottom left pixel is 0,0 * * the position of the center of the bottom left pixel is PixelZeroPos, PixelZeroPos * * @return image position */ inline double indexToPosition( double ind, ///< image index lsst::afw::image::xOrY const xy ///< Is this a column or row coordinate? ) const { return ind + PixelZeroPos + (xy == X ? getX0() : getY0()); } /// Return the %image's size; useful for passing to constructors geom::Extent2I getDimensions() const { return geom::Extent2I(getWidth(), getHeight()); } void swap(ImageBase &rhs); Array getArray(); ConstArray getArray() const; // // Iterators and Locators // iterator begin() const; iterator end() const; reverse_iterator rbegin() const; reverse_iterator rend() const; iterator at(int x, int y) const; fast_iterator begin(bool) const; fast_iterator end(bool) const; /// Return an \c x_iterator to the start of the \c y'th row /// /// Incrementing an \c x_iterator moves it across the row x_iterator row_begin(int y) const { return _gilView.row_begin(y); } /// Return an \c x_iterator to the end of the \c y'th row x_iterator row_end(int y) const { return _gilView.row_end(y); } /// Return an \c x_iterator to the point (x, y) in the %image x_iterator x_at(int x, int y) const { return _gilView.x_at(x, y); } /// Return an \c y_iterator to the start of the \c y'th row /// /// Incrementing an \c y_iterator moves it up the column y_iterator col_begin(int x) const { return _gilView.col_begin(x); } /// Return an \c y_iterator to the end of the \c y'th row y_iterator col_end(int x) const { return _gilView.col_end(x); } /// Return an \c y_iterator to the point (x, y) in the %image y_iterator y_at(int x, int y) const { return _gilView.y_at(x, y); } /// Return an \c xy_locator at the point (x, y) in the %image /// /// Locators may be used to access a patch in an image xy_locator xy_at(int x, int y) const { return xy_locator(_gilView.xy_at(x, y)); } /** * Set the ImageBase's origin * * The origin is usually set by the constructor, so you shouldn't need this function * * \note There are use cases (e.g. memory overlays) that may want to set these values, but * don't do so unless you are an Expert. */ void setXY0(geom::Point2I const origin) { _origin=origin; } /** * Set the ImageBase's origin * * The origin is usually set by the constructor, so you shouldn't need this function * * \note There are use cases (e.g. memory overlays) that may want to set these values, but * don't do so unless you are an Expert. */ void setXY0(int const x0, int const y0) { setXY0(geom::Point2I(x0,y0)); } geom::Box2I getBBox(ImageOrigin origin=PARENT) const { if (origin == PARENT) { return geom::Box2I(_origin, getDimensions()); } else return geom::Box2I(geom::Point2I(0,0), getDimensions()); } private: geom::Point2I _origin; Manager::Ptr _manager; _view_t _gilView; //oring of ImageBase in some larger image as returned to and manipulated //by the user protected: #if !defined(SWIG) static _view_t _allocateView(geom::Extent2I const & dimensions, Manager::Ptr & manager); static _view_t _makeSubView( geom::Extent2I const & dimensions, geom::Extent2I const & offset, const _view_t & view ); _view_t _getRawView() const { return _gilView; } #endif inline bool isContiguous() const { return begin()+getWidth()*getHeight() == end(); } }; template void swap(ImageBase& a, ImageBase& b); /************************************************************************************************************/ /// A class to represent a 2-dimensional array of pixels template class Image : public ImageBase { public: template friend class MaskedImage; typedef std::shared_ptr > Ptr; typedef std::shared_ptr > ConstPtr; typedef detail::Image_tag image_category; #if !defined(SWIG) /// A templated class to return this classes' type (present in Image/Mask/MaskedImage) template struct ImageTypeFactory { /// Return the desired type typedef Image type; }; #endif template friend class Image; // needed by generalised copy constructors explicit Image(unsigned int width, unsigned int height, PixelT initialValue=0); explicit Image(geom::Extent2I const & dimensions=geom::Extent2I(), PixelT initialValue=0); explicit Image(geom::Box2I const & bbox, PixelT initialValue=0); explicit Image(Image const & rhs, geom::Box2I const & bbox, ImageOrigin const origin=PARENT, const bool deep=false); Image(const Image& rhs, const bool deep=false); /** * @brief Construct an Image by reading a regular FITS file. * * @param[in] fileName File to read. * @param[in] hdu HDU to read, 1-indexed (i.e. 1=Primary HDU). The special value * of 0 reads the Primary HDU unless it is empty, in which case it * reads the first extension HDU. * @param[in,out] metadata Metadata read from the header (may be null). * @param[in] bbox If non-empty, read only the pixels within the bounding box. * @param[in] origin Coordinate system of the bounding box; if PARENT, the bounding box * should take into account the xy0 saved with the image. */ explicit Image( std::string const & fileName, int hdu=0, PTR(lsst::daf::base::PropertySet) metadata=PTR(lsst::daf::base::PropertySet)(), geom::Box2I const & bbox=geom::Box2I(), ImageOrigin origin=PARENT ); /** * @brief Construct an Image by reading a FITS image in memory. * * @param[in] manager An object that manages the memory buffer to read. * @param[in] hdu HDU to read, 1-indexed (i.e. 1=Primary HDU). The special value * of 0 reads the Primary HDU unless it is empty, in which case it * reads the first extension HDU. * @param[in,out] metadata Metadata read from the header (may be null). * @param[in] bbox If non-empty, read only the pixels within the bounding box. * @param[in] origin Coordinate system of the bounding box; if PARENT, the bounding box * should take into account the xy0 saved with the image. */ explicit Image( fits::MemFileManager & manager, int hdu=0, PTR(lsst::daf::base::PropertySet) metadata=PTR(lsst::daf::base::PropertySet)(), geom::Box2I const & bbox=geom::Box2I(), ImageOrigin origin=PARENT ); /** * @brief Construct an Image from an already-open FITS object. * * @param[in] fitsfile A FITS object to read from, already at the desired HDU. * @param[in,out] metadata Metadata read from the header (may be null). * @param[in] bbox If non-empty, read only the pixels within the bounding box. * @param[in] origin Coordinate system of the bounding box; if PARENT, the bounding box * should take into account the xy0 saved with the image. */ explicit Image( fits::Fits & fitsfile, PTR(lsst::daf::base::PropertySet) metadata=PTR(lsst::daf::base::PropertySet)(), geom::Box2I const & bbox=geom::Box2I(), ImageOrigin origin=PARENT ); // generalised copy constructor template Image(Image const& rhs, const bool deep) : image::ImageBase(rhs, deep) {} explicit Image(ndarray::Array const & array, bool deep = false, geom::Point2I const & xy0 = geom::Point2I()) : image::ImageBase(array, deep, xy0) {} virtual ~Image() { } // // Assignment operators are not inherited // Image& operator=(const PixelT rhs); Image& operator=(const Image& rhs); /** * @brief Write an image to a regular FITS file. * * @param[in] fileName Name of the file to write. * @param[in] metadata Additional values to write to the header (may be null). * @param[in] mode "w"=Create a new file; "a"=Append a new HDU. */ void writeFits( std::string const& fileName, CONST_PTR(lsst::daf::base::PropertySet) metadata = CONST_PTR(lsst::daf::base::PropertySet)(), std::string const& mode="w" ) const; /** * @brief Write an image to a FITS RAM file. * * @param[in] manager Manager object for the memory block to write to. * @param[in] metadata Additional values to write to the header (may be null). * @param[in] mode "w"=Create a new file; "a"=Append a new HDU. */ void writeFits( fits::MemFileManager & manager, CONST_PTR(lsst::daf::base::PropertySet) metadata = CONST_PTR(lsst::daf::base::PropertySet)(), std::string const& mode="w" ) const; /** * @brief Write an image to an open FITS file object. * * @param[in] fitsfile A FITS file already open to the desired HDU. * @param[in] metadata Additional values to write to the header (may be null). */ void writeFits( fits::Fits & fitsfile, CONST_PTR(lsst::daf::base::PropertySet) metadata = CONST_PTR(lsst::daf::base::PropertySet)() ) const; /** * @brief Read an Image from a regular FITS file. * * @param[in] filename Name of the file to read. * @param[in] hdu Number of the "header-data unit" to read (where 1 is the Primary HDU). * The default value of 0 is interpreted as "the first HDU with NAXIS != 0". */ static Image readFits(std::string const & filename, int hdu=0) { return Image(filename, hdu); } /** * @brief Read an Image from a FITS RAM file. * * @param[in] manager Object that manages the memory to be read. * @param[in] hdu Number of the "header-data unit" to read (where 1 is the Primary HDU). * The default value of 0 is interpreted as "the first HDU with NAXIS != 0". */ static Image readFits(fits::MemFileManager & manager, int hdu=0) { return Image(manager, hdu); } void swap(Image &rhs); // // Operators etc. // Image& operator+=(PixelT const rhs); virtual Image& operator+=(Imageconst & rhs); Image& operator+=(lsst::afw::math::Function2 const& function); void scaledPlus(double const c, Imageconst & rhs); Image& operator-=(PixelT const rhs); Image& operator-=(Image const& rhs); Image& operator-=(lsst::afw::math::Function2 const& function); void scaledMinus(double const c, Imageconst & rhs); Image& operator*=(PixelT const rhs); Image& operator*=(Image const& rhs); void scaledMultiplies(double const c, Imageconst & rhs); Image& operator/=(PixelT const rhs); Image& operator/=(Image const& rhs); void scaledDivides(double const c, Imageconst & rhs); // In-place per-pixel sqrt(). Useful when handling variance planes. void sqrt(); protected: using ImageBase::_getRawView; private: LSST_PERSIST_FORMATTER(lsst::afw::formatters::ImageFormatter) }; template Image& operator+=(Image &lhs, Image const& rhs); template Image& operator-=(Image &lhs, Image const& rhs); template Image& operator*=(Image &lhs, Image const& rhs); template Image& operator/=(Image &lhs, Image const& rhs); template void swap(Image& a, Image& b); /************************************************************************************************************/ /** * \brief A container for an Image and its associated metadata */ template class DecoratedImage : public lsst::daf::base::Persistable, public lsst::daf::base::Citizen { public: /// shared_ptr to a DecoratedImage typedef std::shared_ptr Ptr; /// shared_ptr to a const DecoratedImage typedef std::shared_ptr ConstPtr; /// shared_ptr to the Image typedef PTR(Image) ImagePtr; /// shared_ptr to the Image as const typedef CONST_PTR(Image) ImageConstPtr; explicit DecoratedImage(const geom::Extent2I & dimensions=geom::Extent2I()); explicit DecoratedImage(const geom::Box2I & bbox); explicit DecoratedImage(PTR(Image) rhs); DecoratedImage(DecoratedImage const& rhs, const bool deep=false); explicit DecoratedImage( std::string const& fileName, const int hdu=0, geom::Box2I const& bbox=geom::Box2I(), ImageOrigin const origin = PARENT ); DecoratedImage& operator=(const DecoratedImage& image); PTR(lsst::daf::base::PropertySet) getMetadata() const { return _metadata; } void setMetadata(PTR(lsst::daf::base::PropertySet) metadata) { _metadata = metadata; } /// Return the number of columns in the %image int getWidth() const { return _image->getWidth(); } /// Return the number of rows in the %image int getHeight() const { return _image->getHeight(); } /// Return the %image's column-origin int getX0() const { return _image->getX0(); } /// Return the %image's row-origin int getY0() const { return _image->getY0(); } /// Return the %image's size; useful for passing to constructors const geom::Extent2I getDimensions() const { return _image->getDimensions(); } void swap(DecoratedImage &rhs); void writeFits( std::string const& fileName, CONST_PTR(lsst::daf::base::PropertySet) metadata = CONST_PTR(lsst::daf::base::PropertySet)(), std::string const& mode="w" ) const; /// Return a shared_ptr to the DecoratedImage's Image ImagePtr getImage() { return _image; } /// Return a shared_ptr to the DecoratedImage's Image as const ImageConstPtr getImage() const { return _image; } /** * Return the DecoratedImage's gain * \note This is mostly just a place holder for other properties that we might * want to associate with a DecoratedImage */ double getGain() const { return _gain; } /// Set the DecoratedImage's gain void setGain(double gain) { _gain = gain; } private: LSST_PERSIST_FORMATTER(lsst::afw::formatters::DecoratedImageFormatter) PTR(Image) _image; PTR(lsst::daf::base::PropertySet) _metadata; double _gain; void init(); }; template void swap(DecoratedImage& a, DecoratedImage& b); }}} // lsst::afw::image #endif