ISIS Core Library 0.7.2 (api 3.0.0)

/scr/tee1/isis/lib/Core/DataStorage/image.hpp

Go to the documentation of this file.
00001 //
00002 // C++ Interface: image
00003 //
00004 // Description:
00005 //
00006 //
00007 // Author: Enrico Reimer<reimer@cbs.mpg.de>, (C) 2009
00008 //
00009 // Copyright: See COPYING file that comes with this distribution
00010 //
00011 //
00012 
00013 #ifndef IMAGE_H
00014 #define IMAGE_H
00015 
00016 #include "chunk.hpp"
00017 
00018 #include <set>
00019 #include <boost/shared_ptr.hpp>
00020 #include <vector>
00021 #include <boost/foreach.hpp>
00022 #include <boost/numeric/ublas/matrix.hpp>
00023 #include <boost/numeric/ublas/io.hpp>
00024 #include <boost/type_traits/remove_const.hpp>
00025 #include <stack>
00026 #include "sortedchunklist.hpp"
00027 #include "common.hpp"
00028 
00029 namespace isis
00030 {
00031 namespace data
00032 {
00033 namespace _internal
00034 {
00041 template<typename CHUNK_TYPE> class ImageIteratorTemplate: public std::iterator <
00042     std::random_access_iterator_tag,
00043     typename boost::mpl::if_<boost::is_const<CHUNK_TYPE>, typename CHUNK_TYPE::const_iterator, typename CHUNK_TYPE::iterator>::type::value_type,
00044     typename boost::mpl::if_<boost::is_const<CHUNK_TYPE>, typename CHUNK_TYPE::const_iterator, typename CHUNK_TYPE::iterator>::type::difference_type,
00045     typename boost::mpl::if_<boost::is_const<CHUNK_TYPE>, typename CHUNK_TYPE::const_iterator, typename CHUNK_TYPE::iterator>::type::pointer,
00046     typename boost::mpl::if_<boost::is_const<CHUNK_TYPE>, typename CHUNK_TYPE::const_iterator, typename CHUNK_TYPE::iterator>::type::reference
00047     >
00048 {
00049 protected:
00050     typedef typename boost::mpl::if_<boost::is_const<CHUNK_TYPE>, typename CHUNK_TYPE::const_iterator, typename CHUNK_TYPE::iterator>::type inner_iterator;
00051     typedef CHUNK_TYPE chunk_type;
00052     typedef ImageIteratorTemplate<CHUNK_TYPE> ThisType;
00053 
00054     std::vector<chunk_type *> chunks;
00055     size_t ch_idx;
00056     inner_iterator current_it;
00057     typename inner_iterator::difference_type ch_len;
00058 
00059     typename inner_iterator::difference_type currentDist() const {
00060         if ( ch_idx >= chunks.size() )
00061             return 0; // if we're behind the last chunk assume we are at the "start" of the "end"-chunk
00062         else {
00063             const inner_iterator chit_begin = chunks[ch_idx]->begin(); // cast in a const or cast out a non existing one
00064             return std::distance ( chit_begin, current_it ); // so we use same iterators here
00065         }
00066     }
00067     friend class ImageIteratorTemplate<const CHUNK_TYPE>; //yes, I'm my own friend, sometimes :-) (enables the constructor below)
00068 public:
00069 
00070     //will become additional constructor from non const if this is const, otherwise overrride the default copy contructor
00071     ImageIteratorTemplate ( const ImageIteratorTemplate<typename boost::remove_const<CHUNK_TYPE>::type > &src ) :
00072         chunks ( src.chunks.begin(), src.chunks.end() ), ch_idx ( src.ch_idx ),
00073         current_it ( src.current_it ),
00074         ch_len ( src.ch_len )
00075     {}
00076 
00077     // empty constructor
00078     ImageIteratorTemplate() : ch_idx ( 0 ), ch_len ( 0 ) {}
00079 
00080 
00081     // normal conytructor
00082     explicit ImageIteratorTemplate ( const std::vector<chunk_type *>& _chunks ) :
00083         chunks ( _chunks ), ch_idx ( 0 ),
00084         current_it ( chunks[0]->begin() ),
00085         ch_len ( std::distance ( current_it, chunks[0]->end() ) )
00086     {}
00087 
00088     ThisType &operator++() {
00089         return operator+= ( 1 );
00090     }
00091     ThisType &operator--() {
00092         return operator-= ( 1 );
00093     }
00094 
00095     ThisType operator++ ( int ) {
00096         ThisType tmp = *this;
00097         operator++();
00098         return tmp;
00099     }
00100     ThisType operator-- ( int ) {
00101         ThisType tmp = *this;
00102         operator--();
00103         return tmp;
00104     }
00105 
00106     typename inner_iterator::reference operator*() const {
00107         return current_it.operator * ();
00108     }
00109     typename inner_iterator::pointer  operator->() const {
00110         return current_it.operator->();
00111     }
00112 
00113     bool operator== ( const ThisType &cmp ) const {
00114         return ch_idx == cmp.ch_idx && current_it == cmp.current_it;
00115     }
00116     bool operator!= ( const ThisType &cmp ) const {
00117         return !operator== ( cmp );
00118     }
00119 
00120     bool operator> ( const ThisType &cmp ) const {
00121         return ch_idx > cmp.ch_idx || ( ch_idx == cmp.ch_idx && current_it > cmp.current_it );
00122     }
00123     bool operator< ( const ThisType &cmp ) const {
00124         return ch_idx < cmp.ch_idx || ( ch_idx == cmp.ch_idx && current_it < cmp.current_it );
00125     }
00126 
00127 
00128     bool operator>= ( const ThisType &cmp ) const {
00129         return operator> ( cmp ) || operator== ( cmp );
00130     }
00131     bool operator<= ( const ThisType &cmp ) const {
00132         return operator< ( cmp ) || operator== ( cmp );
00133     }
00134 
00135     typename inner_iterator::difference_type operator- ( const ThisType &cmp ) const {
00136         typename inner_iterator::difference_type dist = ( ch_idx - cmp.ch_idx ) * ch_len; // get the (virtual) distance from my current block to cmp's current block
00137 
00138         if ( ch_idx >= cmp.ch_idx ) { //if I'm beyond cmp add my current pos to the distance, and substract his
00139             dist += currentDist() - cmp.currentDist();
00140         } else {
00141             dist += cmp.currentDist() - currentDist();
00142         }
00143 
00144         return dist;
00145     }
00146 
00147     ThisType operator+ ( typename ThisType::difference_type n ) const {
00148         return ThisType ( *this ) += n;
00149     }
00150     ThisType operator- ( typename ThisType::difference_type n ) const {
00151         return ThisType ( *this ) -= n;
00152     }
00153 
00154     ThisType &operator+= ( typename inner_iterator::difference_type n ) {
00155         n += currentDist(); //start from current begin (add current_it-(begin of the current chunk) to n)
00156         assert ( ( n / ch_len + static_cast<typename ThisType::difference_type> ( ch_idx ) ) >= 0 );
00157         ch_idx += n / ch_len; //if neccesary jump to next chunk
00158 
00159         if ( ch_idx < chunks.size() )
00160             current_it = chunks[ch_idx]->begin() + n % ch_len; //set new current iterator in new chunk plus the "rest"
00161         else
00162             current_it = ( * ( chunks.end() - 1 ) )->end() ; //set current_it to the last chunks end iterator if we are behind it
00163 
00164         return *this;
00165     }
00166     ThisType &operator-= ( typename inner_iterator::difference_type n ) {
00167         return operator+= ( -n );
00168     }
00169 
00170     typename ThisType::reference operator[] ( typename inner_iterator::difference_type n ) const {
00171         return * ( ThisType ( chunks ) += n );
00172     }
00173 
00174 };
00175 }
00176 
00178 class ChunkOp : std::unary_function<Chunk &, bool>
00179 {
00180 public:
00181     virtual bool operator() ( Chunk &, util::vector4<size_t> posInImage ) = 0;
00182     virtual ~ChunkOp();
00183 };
00184 
00186 class Image:
00187     public _internal::NDimensional<4>,
00188     public util::PropertyMap
00189 {
00190     dimensions minIndexingDim;
00191 public:
00201     void setIndexingDim ( dimensions d = rowDim );
00202     enum orientation {axial, reversed_axial, sagittal, reversed_sagittal, coronal, reversed_coronal};
00203 
00204     typedef _internal::ImageIteratorTemplate<Chunk> iterator;
00205     typedef _internal::ImageIteratorTemplate<const Chunk> const_iterator;
00206     typedef iterator::reference reference;
00207     typedef const_iterator::reference const_reference;
00208     static const char *neededProperties;
00209 protected:
00210     _internal::SortedChunkList set;
00211     std::vector<boost::shared_ptr<Chunk> > lookup;
00212 private:
00213     size_t chunkVolume;
00214 
00215     void deduplicateProperties();
00216 
00222     const boost::shared_ptr<Chunk> &chunkPtrAt ( size_t at ) const;
00223 
00235     inline std::pair<size_t, size_t> commonGet ( size_t first, size_t second, size_t third, size_t fourth ) const {
00236         const size_t idx[] = {first, second, third, fourth};
00237         LOG_IF ( ! clean, Debug, error )
00238                 << "Getting data from a non indexed image will result in undefined behavior. Run reIndex first.";
00239         LOG_IF ( set.isEmpty(), Debug, error )
00240                 << "Getting data from a empty image will result in undefined behavior.";
00241         LOG_IF ( !isInRange ( idx ), Debug, isis::error )
00242                 << "Index " << util::vector4<size_t> ( idx ) << " is out of range (" << getSizeAsString() << ")";
00243         const size_t index = getLinearIndex ( idx );
00244         return std::make_pair ( index / chunkVolume, index % chunkVolume );
00245     }
00246 
00247 
00248 protected:
00249     bool clean;
00250     static const char *defaultChunkEqualitySet;
00251 
00270     size_t getChunkStride ( size_t base_stride = 1 );
00276     Chunk &chunkAt ( size_t at );
00278     Image();
00279 
00280     util::fvector3 m_RowVec;
00281     util::fvector3 m_RowVecInv;
00282     util::fvector3 m_ColumnVec;
00283     util::fvector3 m_ColumnVecInv;
00284     util::fvector3 m_SliceVec;
00285     util::fvector3 m_SliceVecInv;
00286     util::fvector3 m_Offset;
00287 
00288 public:
00293     Image ( const Image &ref );
00294 
00299     template<typename T> Image ( std::list<T> &chunks, dimensions min_dim = rowDim ) :
00300         _internal::NDimensional<4>(), util::PropertyMap(), minIndexingDim ( min_dim ),
00301         set ( defaultChunkEqualitySet ),
00302         clean ( false ) {
00303         util::Singletons::get<NeededsList<Image>, 0>().applyTo( *this );
00304         set.addSecondarySort ( "acquisitionNumber" );
00305         insertChunksFromList ( chunks );
00306     }
00311     template<typename T> Image ( std::vector<T> &chunks, dimensions min_dim = rowDim ) :
00312         _internal::NDimensional<4>(), util::PropertyMap(), minIndexingDim ( min_dim ),
00313         set ( defaultChunkEqualitySet ),
00314         clean ( false ) {
00315         util::Singletons::get<NeededsList<Image>, 0>().applyTo( *this );
00316         set.addSecondarySort ( "acquisitionNumber" );
00317         std::list<T> tmp( chunks.begin(), chunks.end() );
00318         insertChunksFromList ( tmp );
00319         chunks.assign( tmp.begin(), tmp.end() );
00320     }
00321 
00327     template<typename T> size_t insertChunksFromList ( std::list<T> &chunks ) {
00328         BOOST_MPL_ASSERT ( ( boost::is_base_of<Chunk, T> ) );
00329         size_t cnt = 0;
00330 
00331         for ( typename std::list<T>::iterator i = chunks.begin(); i != chunks.end(); ) { // for all remaining chunks
00332             if ( insertChunk ( *i ) ) {
00333                 chunks.erase ( i++ );
00334                 cnt++;
00335             } else {
00336                 i++;
00337             }
00338         }
00339 
00340         if ( ! isEmpty() ) {
00341             LOG ( Debug, info ) << "Reindexing image with " << cnt << " chunks.";
00342 
00343             if ( !reIndex() ) {
00344                 LOG ( Runtime, error ) << "Failed to create image from " << cnt << " chunks.";
00345             } else {
00346                 LOG_IF ( !getMissing().empty(), Debug, warning )
00347                         << "The created image is missing some properties: " << getMissing() << ". It will be invalid.";
00348             }
00349         } else {
00350             LOG ( Debug, warning ) << "Image is empty after inserting chunks.";
00351         }
00352 
00353         return cnt;
00354     }
00355 
00356 
00360     Image ( const Chunk &chunk, dimensions min_dim = rowDim );
00361 
00366     Image &operator= ( const Image &ref );
00367 
00368     bool checkMakeClean();
00369     bool isClean() const;
00387     template <typename T> T &voxel ( size_t first, size_t second = 0, size_t third = 0, size_t fourth = 0 ) {
00388         checkMakeClean();
00389         const std::pair<size_t, size_t> index = commonGet ( first, second, third, fourth );
00390         ValueArray<T> &data = chunkAt ( index.first ).asValueArray<T>();
00391         return data[index.second];
00392     }
00393 
00406     template <typename T> const T &voxel ( size_t first, size_t second = 0, size_t third = 0, size_t fourth = 0 ) const {
00407         const std::pair<size_t, size_t> index = commonGet ( first, second, third, fourth );
00408         const ValueArray<T> &data = chunkPtrAt ( index.first )->getValueArray<T>();
00409         return data[index.second];
00410     }
00411 
00412     const util::ValueReference getVoxelValue ( size_t nrOfColumns, size_t nrOfRows = 0, size_t nrOfSlices = 0, size_t nrOfTimesteps = 0 ) const;
00413     void setVoxelValue ( const util::ValueReference &val, size_t nrOfColumns, size_t nrOfRows = 0, size_t nrOfSlices = 0, size_t nrOfTimesteps = 0 );
00414 
00425     unsigned short getMajorTypeID() const;
00427     std::string getMajorTypeName() const;
00428 
00429     iterator begin();
00430     iterator end();
00431     const_iterator begin() const;
00432     const_iterator end() const;
00433 
00439     Chunk getChunkAt ( size_t at, bool copy_metadata = true ) const;
00440 
00454     const Chunk getChunk ( size_t first, size_t second = 0, size_t third = 0, size_t fourth = 0, bool copy_metadata = true ) const;
00455 
00468     Chunk getChunk ( size_t first, size_t second = 0, size_t third = 0, size_t fourth = 0, bool copy_metadata = true );
00469 
00483     template<typename TYPE> Chunk getChunkAs ( size_t first, size_t second = 0, size_t third = 0, size_t fourth = 0, bool copy_metadata = true ) const {
00484         return getChunkAs<TYPE> ( getScalingTo ( ValueArray<TYPE>::staticID ), first, second, third, fourth, copy_metadata );
00485     }
00498     template<typename TYPE> Chunk getChunkAs ( const scaling_pair &scaling, size_t first, size_t second = 0, size_t third = 0, size_t fourth = 0, bool copy_metadata = true ) const {
00499         Chunk ret = getChunk ( first, second, third, fourth, copy_metadata ); // get a cheap copy
00500         ret.convertToType ( ValueArray<TYPE>::staticID, scaling ); // make it of type T
00501         return ret; //return that
00502     }
00503 
00505     scaling_pair getScalingTo ( unsigned short typeID, autoscaleOption scaleopt = autoscale ) const;
00506 
00507 
00516     bool insertChunk ( const Chunk &chunk );
00522     bool reIndex();
00523 
00525     bool isEmpty() const;
00526 
00532     std::list<util::PropertyValue> getChunksProperties ( const util::PropertyMap::KeyType &key, bool unique = false ) const;
00533 
00546     size_t getMaxBytesPerVoxel() const;
00547 
00553     template<typename T> std::pair<T, T> getMinMaxAs() const {
00554         util::checkType<T>();// works only for T from _internal::types
00555         std::pair<util::ValueReference, util::ValueReference> minmax = getMinMax();
00556         return std::make_pair ( minmax.first->as<T>(), minmax.second->as<T>() );
00557     }
00558 
00560     std::pair<util::ValueReference, util::ValueReference> getMinMax() const;
00561 
00566     size_t compare ( const Image &comp ) const;
00567 
00568     orientation getMainOrientation() const;
00569 
00588     bool transformCoords ( boost::numeric::ublas::matrix<float> transform_matrix, bool transformCenterIsImageCenter = false );
00589 
00603     dimensions mapScannerAxisToImageDimension ( scannerAxis scannerAxes );
00604 
00611     util::fvector3 getPhysicalCoordsFromIndex ( const util::ivector4 &index ) const;
00612 
00613 
00620     util::ivector4 getIndexFromPhysicalCoords ( const util::fvector3 &physicalCoords ) const;
00621 
00629     template<typename T> void copyToMem ( T *dst, size_t len,  scaling_pair scaling = scaling_pair() ) const {
00630         if ( clean ) {
00631             if ( scaling.first.isEmpty() || scaling.second.isEmpty() ) {
00632                 scaling = getScalingTo ( ValueArray<T>::staticID );
00633             }
00634 
00635             // we could do this using convertToType - but this solution does not need any additional temporary memory
00636             BOOST_FOREACH ( const boost::shared_ptr<Chunk> &ref, lookup ) {
00637                 const size_t cSize = ref->getSizeAsVector().product();
00638 
00639                 if ( !ref->copyToMem<T> ( dst, len, scaling ) ) {
00640                     LOG ( Runtime, error ) << "Failed to copy raw data of type " << ref->getTypeName() << " from image into memory of type " << ValueArray<T>::staticName();
00641                 } else {
00642                     if ( len < cSize ) {
00643                         LOG ( Runtime, error ) << "Abborting copy, because there is no space left in the target";
00644                         break;
00645                     }
00646 
00647                     len -= cSize;
00648                 }
00649 
00650                 dst += ref->getVolume(); // increment the cursor
00651             }
00652         } else {
00653             LOG ( Runtime, error ) << "Cannot copy from non clean images. Run reIndex first";
00654         }
00655     }
00656 
00667     template<typename T> MemChunk<T> copyAsMemChunk() const {
00668         const util::vector4<size_t> size = getSizeAsVector();
00669         data::MemChunk<T> ret ( size[0], size[1], size[2], size[3] );
00670         copyToMem<T> ( &ret.voxel<T>(0,0), ret.getVolume() );
00671         static_cast<util::PropertyMap &>( ret ) = static_cast<const util::PropertyMap &>( getChunkAt( 0 ) );
00672         return ret;
00673     }
00674 
00683     template<typename T> ValueArray<T> copyAsValueArray() const {
00684         data::ValueArray<T> ret ( getVolume() );
00685         copyToMem<T> ( ret.begin().operator->(), ret.getLength() );
00686         return ret;
00687     }
00694     void copyToValueArray ( data::ValueArrayBase &dst,  scaling_pair scaling = scaling_pair() ) const;
00695 
00703     Image copyByID( unsigned short ID = 0, scaling_pair scaling = scaling_pair() )const;
00704 
00705 
00714     std::vector<isis::data::Chunk> copyChunksToVector ( bool copy_metadata = true ) const;
00715 
00722     bool convertToType ( short unsigned int ID, isis::data::autoscaleOption scaleopt = autoscale );
00723 
00728     size_t spliceDownTo ( dimensions dim );
00729 
00736     size_t foreachChunk ( ChunkOp &op, bool copyMetaData = false );
00737 
00738 
00746     template <typename TYPE> size_t foreachVoxel ( VoxelOp<TYPE> &op ) {
00747         class _proxy: public ChunkOp
00748         {
00749             VoxelOp<TYPE> &op;
00750         public:
00751             _proxy ( VoxelOp<TYPE> &_op ) : op ( _op ) {}
00752             bool operator() ( Chunk &ch, util::vector4<size_t> posInImage ) {
00753                 return ch.foreachVoxel<TYPE> ( op, posInImage ) == 0;
00754             }
00755         };
00756         _proxy prx ( op );
00757         return convertToType ( data::ValueArray<TYPE>::staticID ) && foreachChunk ( prx, false );
00758     }
00759 
00761     size_t getNrOfRows() const;
00763     size_t getNrOfColumns() const;
00765     size_t getNrOfSlices() const;
00767     size_t getNrOfTimesteps() const;
00768 
00769     util::fvector3 getFoV() const;
00770     bool updateOrientationMatrices();
00771 
00781     std::string identify( bool withpath = true )const;
00782 };
00783 
00788 template<typename T> class TypedImage: public Image
00789 {
00790 protected:
00791     TypedImage() {} // to be used only by inheriting classes
00792 public:
00793     typedef _internal::ImageIteratorTemplate<data::ValueArray<T> > iterator;
00794     typedef _internal::ImageIteratorTemplate<const data::ValueArray<T> > const_iterator;
00795     typedef typename iterator::reference reference;
00796     typedef typename const_iterator::reference const_reference;
00798     TypedImage ( const Image &src ) : Image ( src ) { // ok we just copied the whole image
00799         //but we want it to be of type T
00800         convertToType ( ValueArray<T>::staticID );
00801     }
00803     TypedImage &operator= ( const TypedImage &ref ) { //its already of the given type - so just copy it
00804         Image::operator= ( ref );
00805         return *this;
00806     }
00808     TypedImage &operator= ( const Image &ref ) { // copy the image, and make sure its of the given type
00809         Image::operator= ( ref );
00810         convertToType ( ValueArray<T>::staticID );
00811         return *this;
00812     }
00813     void copyToMem ( void *dst ) {
00814         Image::copyToMem<T> ( ( T * ) dst );
00815     }
00816     void copyToMem ( void *dst ) const {
00817         Image::copyToMem<T> ( ( T * ) dst );
00818     }
00819     iterator begin() {
00820         if ( checkMakeClean() ) {
00821             std::vector<data::ValueArray<T>*> vec ( lookup.size() );
00822 
00823             for ( size_t i = 0; i < lookup.size(); i++ )
00824                 vec[i] = &lookup[i]->template asValueArray<T>();
00825 
00826             return iterator ( vec );
00827         } else {
00828             LOG ( Debug, error )  << "Image is not clean. Returning empty iterator ...";
00829             return iterator();
00830         }
00831     }
00832     iterator end() {
00833         return begin() + getVolume();
00834     };
00835     const_iterator begin() const {
00836         if ( isClean() ) {
00837             std::vector<const data::ValueArray<T>*> vec ( lookup.size() );
00838 
00839             for ( size_t i = 0; i < lookup.size(); i++ )
00840                 vec[i] = &lookup[i]->template asValueArray<T>();
00841 
00842             return const_iterator ( vec );
00843         } else {
00844             LOG ( Debug, error )  << "Image is not clean. Returning empty iterator ...";
00845             return const_iterator();
00846         }
00847     }
00848     const_iterator end() const {
00849         return begin() + getVolume();
00850     };
00851 };
00852 
00857 template<typename T> class MemImage: public TypedImage<T>
00858 {
00859 public:
00865     MemImage ( const Image &src ) {
00866         operator= ( src );
00867     }
00868 
00874     MemImage &operator= ( const Image &ref ) { // copy the image, and make sure its of the given type
00875 
00876         Image::operator= ( ref ); // ok we just copied the whole image
00877 
00878         //we want deep copies of the chunks, and we want them to be of type T
00879         struct : _internal::SortedChunkList::chunkPtrOperator {
00880             std::pair<util::ValueReference, util::ValueReference> scale;
00881             boost::shared_ptr<Chunk> operator() ( const boost::shared_ptr< Chunk >& ptr ) {
00882                 return boost::shared_ptr<Chunk> ( new MemChunk<T> ( *ptr, scale ) );
00883             }
00884         } conv_op;
00885         conv_op.scale = ref.getScalingTo ( ValueArray<T>::staticID );
00886         LOG ( Debug, info ) << "Computed scaling for conversion from source image: [" << conv_op.scale << "]";
00887 
00888         this->set.transform ( conv_op );
00889 
00890         if ( ref.isClean() ) {
00891             this->lookup = this->set.getLookup(); // the lookup table still points to the old chunks
00892         } else {
00893             LOG ( Debug, info ) << "Copied unclean image. Running reIndex on the copy.";
00894             this->reIndex();
00895         }
00896 
00897         return *this;
00898     }
00899 };
00900 
00901 }
00902 }
00903 
00904 #endif // IMAGE_H