ISIS Core Library 0.7.2 (api 3.0.0)
|
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