ISIS Core Library 0.7.2 (api 3.0.0)
|
00001 // kate: show-indent on; indent-mode tab; indent-width 4; tab-width 4; replace-tabs off; auto-insert-doxygen on 00002 00003 // 00004 // C++ Implementation: image 00005 // 00006 // Description: 00007 // 00008 // 00009 // Author: Enrico Reimer<reimer@cbs.mpg.de>, (C) 2009 00010 // 00011 // Copyright: See COPYING file that comes with this distribution 00012 // 00013 // 00014 00015 #ifdef _MSC_VER 00016 #pragma warning(disable:4996 4244) 00017 #endif 00018 00019 #include "image.hpp" 00020 #include "../CoreUtils/vector.hpp" 00021 #include <boost/foreach.hpp> 00022 #include "../CoreUtils/property.hpp" 00023 #include <boost/token_iterator.hpp> 00024 00025 #define _USE_MATH_DEFINES 1 00026 #include <math.h> 00027 #include <cmath> 00028 00029 namespace isis 00030 { 00031 namespace data 00032 { 00033 00034 ChunkOp::~ChunkOp() {} 00035 00036 Image::Image ( ) : set( defaultChunkEqualitySet ), clean( false ) 00037 { 00038 util::Singletons::get<NeededsList<Image>, 0>().applyTo( *this ); 00039 set.addSecondarySort( "acquisitionNumber" ); 00040 } 00041 00042 Image::Image ( const Chunk &chunk, dimensions min_dim ) : 00043 _internal::NDimensional<4>(), util::PropertyMap(), minIndexingDim( min_dim ), set( defaultChunkEqualitySet ), clean( false ) 00044 { 00045 util::Singletons::get<NeededsList<Image>, 0>().applyTo( *this ); 00046 set.addSecondarySort( "acquisitionNumber" ); 00047 00048 if ( ! ( insertChunk( chunk ) && reIndex() && isClean() ) ) { 00049 LOG( Runtime, error ) << "Failed to create image from single chunk."; 00050 } else if( !isValid() ) { 00051 LOG_IF( !getMissing().empty(), Debug, warning ) 00052 << "The created image is missing some properties: " << getMissing() << ". It will be invalid."; 00053 } 00054 } 00055 00056 Image::Image( const data::Image &ref ): _internal::NDimensional<4>(), util::PropertyMap(), 00057 set( "" )/*SortedChunkList has no default constructor - lets just make an empty (and invalid) set*/ 00058 { 00059 ( *this ) = ref; // set will be replaced here anyway 00060 } 00061 00062 Image &Image::operator=( const data::Image &ref ) 00063 { 00064 //deep copy bases 00065 static_cast<util::PropertyMap &>( *this ) = static_cast<const util::PropertyMap &>( ref ); 00066 static_cast<_internal::NDimensional< 4 >&>( *this ) = static_cast<const _internal::NDimensional< 4 >&>( ref ); 00067 //deep copy members 00068 chunkVolume = ref.chunkVolume; 00069 clean = ref.clean; 00070 set = ref.set; 00071 minIndexingDim = ref.minIndexingDim; 00072 //replace all chunks (in set) by cheap copies of them 00073 struct : public _internal::SortedChunkList::chunkPtrOperator { 00074 boost::shared_ptr< Chunk > operator()( const boost::shared_ptr< Chunk >& ptr ) { 00075 return boost::shared_ptr< Chunk > ( new Chunk( *ptr ) ); 00076 } 00077 } replace; 00078 set.transform( replace ); 00079 lookup = set.getLookup(); 00080 return *this; 00081 } 00082 00083 00084 bool Image::checkMakeClean() 00085 { 00086 if ( ! clean ) { 00087 LOG( Debug, info ) << "Image is not clean. Running reIndex ..."; 00088 00089 if( !reIndex() ) { 00090 LOG( Runtime, error ) << "Reindexing failed -- undefined behavior ahead ..."; 00091 } 00092 } 00093 00094 return clean; 00095 } 00096 bool Image::isClean()const 00097 { 00098 return clean; 00099 } 00100 00101 void Image::deduplicateProperties() 00102 { 00103 LOG_IF( lookup.empty(), Debug, error ) << "The lookup table is empty. Won't do anything."; 00104 const boost::shared_ptr<Chunk> &first = lookup[0]; 00105 //@todo might fail if the image contains a prop that differs to that in the Chunks (which is equal in the chunks) 00106 util::PropertyMap common; 00107 util::PropertyMap::KeyList uniques; 00108 first->toCommonUnique( common, uniques, true ); 00109 00110 for ( size_t i = 1; i < lookup.size(); i++ ) { 00111 lookup[i]->toCommonUnique( common, uniques, false ); 00112 } 00113 00114 LOG( Debug, info ) << uniques.size() << " Chunk-unique properties found in the Image"; 00115 LOG_IF( uniques.size(), Debug, verbose_info ) << util::listToString( uniques.begin(), uniques.end(), ", " ); 00116 00117 // list should not be unified - they belong into their chunk even if they are common 00118 common.remove( common.findLists() ); 00119 00120 join( common ); 00121 LOG_IF( ! common.isEmpty(), Debug, verbose_info ) << "common properties saved into the image " << common; 00122 00123 //remove common props from the chunks 00124 for ( size_t i = 0; i != lookup.size(); i++ ) 00125 lookup[i]->remove( common, false ); //this _won't keep needed properties - so from here on the chunks of the image are invalid 00126 00127 LOG_IF( ! common.isEmpty(), Debug, verbose_info ) << "common properties removed from " << lookup.size() << " chunks: " << common; 00128 00129 } 00130 00131 bool Image::insertChunk ( const Chunk &chunk ) 00132 { 00133 if ( chunk.getVolume() == 0 ) { 00134 LOG( Runtime, error ) 00135 << "Cannot insert empty Chunk (Size is " << chunk.getSizeAsString() << ")."; 00136 return false; 00137 } 00138 00139 if ( ! chunk.isValid() ) { 00140 LOG( Runtime, error ) 00141 << "Cannot insert invalid chunk. Missing properties: " << chunk.getMissing(); 00142 return false; 00143 } 00144 00145 if( clean ) { 00146 LOG( Runtime, warning ) << "Inserting into already indexed images is inefficient. You should not do that."; 00147 00148 // re-gather all properties of the chunks from the image 00149 BOOST_FOREACH( boost::shared_ptr<Chunk> &ref, lookup ) { 00150 ref->join( *this ); 00151 } 00152 } 00153 00154 00155 if( set.insert( chunk ) ) { // if the insertion was successful the image has to be reindexed anyway 00156 clean = false; 00157 lookup.clear(); 00158 return true; 00159 } else { 00160 // if the insertion failed but the image was clean - de-duplicate properties again 00161 // the image is still clean - no need reindex 00162 if( clean ) 00163 deduplicateProperties(); 00164 00165 return false; 00166 } 00167 } 00168 00169 void Image::setIndexingDim( dimensions d ) 00170 { 00171 minIndexingDim = d; 00172 00173 if( clean ) { 00174 LOG( Debug, warning ) << "Image was allready indexed. reIndexing ..."; 00175 reIndex(); 00176 } 00177 } 00178 00179 util::fvector3 Image::getPhysicalCoordsFromIndex( const isis::util::ivector4 &voxelCoords ) const 00180 { 00181 return util::fvector3( voxelCoords[0] * m_RowVec[0] + voxelCoords[1] * m_ColumnVec[0] + voxelCoords[2] * m_SliceVec[0], 00182 voxelCoords[0] * m_RowVec[1] + voxelCoords[1] * m_ColumnVec[1] + voxelCoords[2] * m_SliceVec[1], 00183 voxelCoords[0] * m_RowVec[2] + voxelCoords[1] * m_ColumnVec[2] + voxelCoords[2] * m_SliceVec[2] ) 00184 + m_Offset ; 00185 } 00186 00187 00188 00189 00190 util::ivector4 Image::getIndexFromPhysicalCoords( const isis::util::fvector3 &physicalCoords ) const 00191 { 00192 const util::fvector3 vec1 = physicalCoords - m_Offset; 00193 util::fvector4 _ret = util::fvector4( vec1[0] * m_RowVecInv[0] + vec1[1] * m_ColumnVecInv[0] + vec1[2] * m_SliceVecInv[0], 00194 vec1[0] * m_RowVecInv[1] + vec1[1] * m_ColumnVecInv[1] + vec1[2] * m_SliceVecInv[1], 00195 vec1[0] * m_RowVecInv[2] + vec1[1] * m_ColumnVecInv[2] + vec1[2] * m_SliceVecInv[2] ); 00196 00197 for( uint8_t i = 0; i < 3; i++ ) { 00198 if( _ret[i] < 0 ) _ret[i] -= 0.5; 00199 else _ret[i] += 0.5; 00200 } 00201 00202 return _ret; 00203 } 00204 00205 00206 bool Image::updateOrientationMatrices() 00207 { 00208 const util::fvector3 rowVec = getPropertyAs<util::fvector3>( "rowVec" ); 00209 const util::fvector3 columnVec = getPropertyAs<util::fvector3>( "columnVec" ); 00210 const util::fvector3 sliceVec = getPropertyAs<util::fvector3>( "sliceVec" ); 00211 m_Offset = getPropertyAs<util::fvector3>( "indexOrigin" ); 00212 util::fvector3 spacing = getPropertyAs<util::fvector3>( "voxelSize" ) + ( hasProperty( "voxelGap" ) ? getPropertyAs<util::fvector3>( "voxelGap" ) : util::fvector3( 0, 0, 0 ) ); 00213 00214 for( unsigned short i = 0; i < 3; i++ ) { 00215 if( spacing[i] == 0 ) spacing[i] = 1; 00216 } 00217 00218 m_RowVec = util::fvector3( rowVec[0] * spacing[0], rowVec[1] * spacing[0], rowVec[2] * spacing[0] ); 00219 m_ColumnVec = util::fvector3( columnVec[0] * spacing[1], columnVec[1] * spacing[1], columnVec[2] * spacing[1] ); 00220 m_SliceVec = util::fvector3( sliceVec[0] * spacing[2], sliceVec[1] * spacing[2], sliceVec[2] * spacing[2] ); 00221 LOG( Debug, verbose_info ) << "Created orientation matrix: "; 00222 LOG( Debug, verbose_info ) << "[ " << m_RowVec[0] << " " << m_ColumnVec[0] << " " << m_SliceVec[0] << " ] + " << m_Offset[0]; 00223 LOG( Debug, verbose_info ) << "[ " << m_RowVec[1] << " " << m_ColumnVec[1] << " " << m_SliceVec[1] << " ] + " << m_Offset[1]; 00224 LOG( Debug, verbose_info ) << "[ " << m_RowVec[2] << " " << m_ColumnVec[2] << " " << m_SliceVec[2] << " ] + " << m_Offset[2]; 00225 00226 //since we do not want to calculate the inverse matrix with every getVoxelCoords call again we do it here once 00227 for ( size_t i = 0; i < 3; i++ ) { 00228 spacing[i] = spacing[i] ? 1.0 / spacing[i] : 0; 00229 } 00230 00231 //for inversion of the orientation we use boost::ublas 00232 using namespace boost::numeric::ublas; 00233 matrix<float> orientation = matrix<float>( 3, 3 ); 00234 matrix<float> inverse = matrix<float>( 3, 3 ); 00235 00236 for( size_t i = 0; i < 3; i++ ) { 00237 orientation( i, 0 ) = m_RowVec[i]; 00238 orientation( i, 1 ) = m_ColumnVec[i]; 00239 orientation( i, 2 ) = m_SliceVec[i]; 00240 } 00241 00242 if( !_internal::inverseMatrix<float>( orientation, inverse ) ) { 00243 LOG( Runtime, error ) << "Could not create the inverse of the orientation matrix!"; 00244 return false; 00245 }; 00246 00247 for( size_t i = 0; i < 3; i++ ) { 00248 m_RowVecInv[i] = inverse( i, 0 ); 00249 m_ColumnVecInv[i] = inverse( i, 1 ); 00250 m_SliceVecInv[i] = inverse( i, 2 ); 00251 } 00252 00253 LOG( Debug, verbose_info ) << "Created transposed orientation matrix: "; 00254 LOG( Debug, verbose_info ) << "[ " << m_RowVecInv[0] << " " << m_ColumnVecInv[0] << " " << m_SliceVecInv[0] << " ] + " << m_Offset[0]; 00255 LOG( Debug, verbose_info ) << "[ " << m_RowVecInv[1] << " " << m_ColumnVecInv[1] << " " << m_SliceVecInv[1] << " ] + " << m_Offset[1]; 00256 LOG( Debug, verbose_info ) << "[ " << m_RowVecInv[2] << " " << m_ColumnVecInv[2] << " " << m_SliceVecInv[2] << " ] + " << m_Offset[2]; 00257 return true; 00258 } 00259 00260 bool Image::transformCoords( boost::numeric::ublas::matrix< float > transform_matrix, bool transformCenterIsImageCenter ) 00261 { 00262 //for transforming we have to ensure to have the below properties in our chunks and image 00263 static const char *neededProps[] = {"indexOrigin", "rowVec", "columnVec", "sliceVec", "voxelSize"}; 00264 //propagate needed properties to chunks 00265 std::set<PropPath> propPathList; 00266 BOOST_FOREACH ( const char * prop, neededProps ) { 00267 const util::PropertyMap::PropPath pPath( prop ); 00268 00269 if( hasProperty ( pPath ) ) { 00270 const util::fvector3 p = getPropertyAs<util::fvector3> ( pPath ); 00271 BOOST_FOREACH ( std::vector<boost::shared_ptr< data::Chunk> >::reference chRef, lookup ) { 00272 if ( !chRef->hasProperty ( pPath ) ) { 00273 chRef->setPropertyAs<util::fvector3> ( pPath, p ); 00274 propPathList.insert( pPath ); 00275 } 00276 } 00277 } else { 00278 LOG( Runtime, error ) << "Cannot do transformCoords on image without " << prop; 00279 return false; 00280 } 00281 } 00282 00283 BOOST_FOREACH ( std::vector<boost::shared_ptr< data::Chunk> >::reference chRef, lookup ) { 00284 if ( !chRef->transformCoords ( transform_matrix, transformCenterIsImageCenter ) ) { 00285 return false; 00286 } 00287 00288 BOOST_FOREACH( std::list<PropPath>::const_reference pPathNotNeeded, propPathList ) { 00289 chRef->remove( pPathNotNeeded ); 00290 } 00291 } 00292 // establish initial state 00293 00294 if ( !isis::data::_internal::transformCoords ( *this, getSizeAsVector(), transform_matrix, transformCenterIsImageCenter ) ) { 00295 LOG ( Runtime, error ) << "Error during transforming the coords of the image."; 00296 return false; 00297 } 00298 00299 if ( !updateOrientationMatrices() ) { 00300 LOG ( Runtime, error ) << "Could not update the orientation matrices of the image!"; 00301 return false; 00302 } 00303 00304 return true; 00305 } 00306 00307 dimensions Image::mapScannerAxisToImageDimension( scannerAxis scannerAxes ) 00308 { 00309 updateOrientationMatrices(); 00310 boost::numeric::ublas::matrix<float> latchedOrientation = boost::numeric::ublas::zero_matrix<float>( 4, 4 ); 00311 boost::numeric::ublas::vector<float>mapping( 4 ); 00312 latchedOrientation( m_RowVec.getBiggestVecElemAbs(), 0 ) = 1; 00313 latchedOrientation( m_ColumnVec.getBiggestVecElemAbs(), 1 ) = 1; 00314 latchedOrientation( m_SliceVec.getBiggestVecElemAbs(), 2 ) = 1; 00315 latchedOrientation( 3, 3 ) = 1; 00316 00317 for( size_t i = 0; i < 4; i++ ) { 00318 mapping( i ) = i; 00319 } 00320 00321 return static_cast<dimensions>( boost::numeric::ublas::prod( latchedOrientation, mapping )( scannerAxes ) ); 00322 00323 } 00324 00325 00326 bool Image::reIndex() 00327 { 00328 if ( set.isEmpty() ) { 00329 LOG( Debug, warning ) << "Reindexing an empty image is useless."; 00330 return false; 00331 } 00332 00333 if( !set.isRectangular() ) { 00334 LOG( Runtime, error ) << "The image is incomplete. Aborting reindex. (geometric size is " << set.getHorizontalSize() << ")"; 00335 return false; 00336 } 00337 00338 //redo lookup table 00339 lookup = set.getLookup(); 00340 util::FixedVector<size_t, dims> structure_size; //storage for the size of the chunk structure 00341 structure_size.fill( 1 ); 00342 //get primary attributes from geometrically first chunk - will be usefull 00343 const Chunk &first = chunkAt( 0 ); 00344 //start indexing at eigther the chunk-size or the givem minIndexingDim (whichever is bigger) 00345 const unsigned short chunk_dims = std::max<unsigned short>( first.getRelevantDims(), minIndexingDim ); 00346 chunkVolume = first.getVolume(); 00348 // Determine structure of the image by searching for dimensional breaks in the chunklist 00350 //get indexOrigin from the geometrically first chunk 00351 propertyValue( "indexOrigin" ) = first.propertyValue( "indexOrigin" ); 00352 //if there are many chunks, they must leave at least on dimension to the image to "sort" them in 00353 const size_t timesteps = set.getHorizontalSize(); 00354 const unsigned short sortDims = dims - ( timesteps > 1 ? 1 : 0 ); // dont use the uppermost dim, if the timesteps are already there 00355 00356 if ( chunk_dims >= Image::dims ) { 00357 if ( lookup.size() > 1 ) { 00358 LOG( Runtime, error ) 00359 << "Cannot handle multiple Chunks, if they have more than " 00360 << Image::dims - 1 << " dimensions"; 00361 return false; 00362 } 00363 00364 //if there is only one chunk, its ok - the image will consist only of this one, 00365 //and commonGet will allways return <0,set.begin()->getLinearIndex()> 00366 //thus in this case voxel() equals set.begin()->voxel() 00367 } else {// OK there is at least one dimension to sort in the chunks 00368 LOG( Debug, info ) << "Computing strides for dimensions " << util::MSubject( chunk_dims + 1 ) << " to " << util::MSubject( sortDims ); 00369 00370 // check the chunks for at least one dimensional break - use that for the size of that dimension 00371 for ( unsigned short i = chunk_dims; i < sortDims; i++ ) { //if there are dimensions left figure out their size 00372 structure_size[i] = getChunkStride( structure_size.product() ) / structure_size.product(); 00373 assert( structure_size[i] != 0 ); 00374 } 00375 } 00376 00377 if ( sortDims < dims ) { //if there is a timedim (not all dims was used for geometric sort) 00378 assert( structure_size[sortDims] == 1 ); 00379 structure_size[sortDims] = timesteps; // fill the dim above the top geometric dim with the timesteps 00380 } 00381 00382 assert( structure_size.product() == lookup.size() ); 00383 //Clean up the properties 00384 deduplicateProperties(); 00385 00386 // add the chunk-size to the image-size 00387 for ( unsigned short i = 0; i < chunk_dims; i++ ) 00388 structure_size[i] = first.getDimSize( i ); 00389 00390 init( structure_size ); // set size of the image 00392 //reconstruct some redundant information, if its missing 00394 const util::PropertyMap::KeyType vectors[] = {"rowVec", "columnVec", "sliceVec"}; 00395 int oneCnt = 0; 00396 BOOST_FOREACH( const util::PropertyMap::KeyType & ref, vectors ) { 00397 if ( hasProperty( ref ) ) { 00398 util::PropertyValue &prop = propertyValue( ref ); 00399 LOG_IF( !prop.is<util::fvector3>(), Debug, error ) << "Using " << prop.getTypeName() << " as " << util::Value<util::fvector3>::staticName(); 00400 util::fvector3 &vec = prop.castTo<util::fvector3>(); 00401 00402 if( vec.sqlen() == 0 ) { 00403 util::fvector3 v_one; 00404 v_one[oneCnt] = 1; 00405 LOG( Runtime, error ) 00406 << "The existing " << ref << " " << vec << ( hasProperty( "source" ) ? " from " + getPropertyAs<std::string>( "source" ) : "" ) << " has the length zero. Falling back to " << v_one << "."; 00407 vec = v_one; 00408 } 00409 00410 vec.norm(); 00411 } 00412 00413 oneCnt++; 00414 } 00415 00416 util::fvector3 &voxeSize = propertyValue( "voxelSize" ).castTo<util::fvector3>(); 00417 00418 for( int i = 0; i < 3; i++ ) { 00419 if( voxeSize[i] == 0 || std::isinf( voxeSize[i] ) ) { 00420 LOG( Runtime, warning ) << "voxelSize[" << i << "]==" << voxeSize[i] << " is invalid, using 1"; 00421 voxeSize[i] = 1; 00422 } 00423 } 00424 00425 00426 //if we have at least two slides (and have slides (with different positions) at all) 00427 if ( chunk_dims == 2 && structure_size[2] > 1 && first.hasProperty( "indexOrigin" ) ) { 00428 const util::fvector3 thisV = first.getPropertyAs<util::fvector3>( "indexOrigin" ); 00429 const Chunk &last = chunkAt( structure_size[2] - 1 ); 00430 00431 if ( last.hasProperty( "indexOrigin" ) ) { 00432 const util::fvector3 lastV = last.getPropertyAs<util::fvector3>( "indexOrigin" ); 00433 //check the slice vector 00434 util::fvector3 distVecNorm = lastV - thisV; 00435 LOG_IF( distVecNorm.len() == 0, Runtime, error ) 00436 << "The distance between the the first and the last chunk is zero. Thats bad, because I'm going to normalize it."; 00437 distVecNorm.norm(); 00438 00439 if ( hasProperty( "sliceVec" ) ) { 00440 const util::fvector3 sliceVec = getPropertyAs<util::fvector3>( "sliceVec" ); 00441 LOG_IF( ! distVecNorm.fuzzyEqual( sliceVec ), Runtime, info ) 00442 << "The existing sliceVec " << sliceVec 00443 << " differs from the distance vector between chunk 0 and " << structure_size[2] - 1 00444 << " " << distVecNorm; 00445 } else { 00446 LOG( Debug, info ) 00447 << "used the distance between chunk 0 and " << structure_size[2] - 1 00448 << " to synthesize the missing sliceVec as " << distVecNorm; 00449 propertyValue( "sliceVec" ) = distVecNorm; 00450 } 00451 } 00452 00453 const Chunk &next = chunkAt( 1 ); 00454 00455 if ( next.hasProperty( "indexOrigin" ) ) { 00456 const util::fvector3 nextV = next.getPropertyAs<util::fvector3>( "indexOrigin" ); 00457 const float sliceDist = ( nextV - thisV ).len() - voxeSize[2]; 00458 00459 if ( sliceDist > 0 ) { 00460 const float inf = std::numeric_limits<float>::infinity(); 00461 00462 if ( ! hasProperty( "voxelGap" ) ) { // @todo check this 00463 setPropertyAs( "voxelGap", util::fvector3( 0, 0, inf ) ); 00464 } 00465 00466 util::fvector3 &voxelGap = propertyValue( "voxelGap" ).castTo<util::fvector3>(); //if there is no voxelGap yet, we create it 00467 00468 if ( voxelGap[2] != inf ) { 00469 if ( ! util::fuzzyEqual( voxelGap[2], sliceDist ) ) { 00470 LOG_IF( ! util::fuzzyEqual( voxelGap[2], sliceDist ), Runtime, warning ) 00471 << "The existing slice distance (voxelGap[2]) " << util::MSubject( voxelGap[2] ) 00472 << " differs from the distance between chunk 0 and 1, which is " << sliceDist; 00473 } 00474 } else { 00475 voxelGap[2] = sliceDist; 00476 LOG( Debug, info ) 00477 << "used the distance between chunk 0 and 1 to synthesize the missing slice distance (voxelGap[2]) as " 00478 << sliceDist; 00479 } 00480 } 00481 } 00482 } 00483 00484 //if we have row- and column- vector 00485 if ( hasProperty( "rowVec" ) && hasProperty( "columnVec" ) ) { 00486 util::fvector3 &row = propertyValue( "rowVec" ).castTo<util::fvector3>(); 00487 util::fvector3 &column = propertyValue( "columnVec" ).castTo<util::fvector3>(); 00488 LOG_IF( row.dot( column ) > 0.01, Runtime, warning ) << "The cosine between the columns and the rows of the image is bigger than 0.01"; 00489 const util::fvector3 crossVec = util::fvector3( //we could use their cross-product as sliceVector 00490 row[1] * column[2] - row[2] * column[1], 00491 row[2] * column[0] - row[0] * column[2], 00492 row[0] * column[1] - row[1] * column[0] 00493 ); 00494 00495 if ( hasProperty( "sliceVec" ) ) { 00496 util::fvector3 &sliceVec = propertyValue( "sliceVec" ).castTo<util::fvector3>(); //get the slice vector 00497 LOG_IF( std::acos( crossVec.dot( sliceVec ) ) > 180 / M_PI, Runtime, warning ) //angle more than one degree 00498 << "The existing sliceVec " << sliceVec 00499 << " differs from the cross product of the row- and column vector " << crossVec; 00500 } else { 00501 // We dont know anything about the slice-direction 00502 // we just guess its along the positive cross-product between row- and column direction 00503 // so at least warn the user if we do that long shot 00504 LOG( Runtime, info ) 00505 << "used the cross product between rowVec and columnVec as sliceVec:" 00506 << crossVec << ". That might be wrong!"; 00507 setPropertyAs( "sliceVec", crossVec ); 00508 } 00509 } 00510 00511 if ( hasProperty( "fov" ) ) { 00512 util::fvector3 &propFoV = propertyValue( "fov" ).castTo<util::fvector3>(); 00513 00514 const util::fvector3 &calcFoV = getFoV(); 00515 00516 bool ok = true; 00517 00518 for ( size_t i = 0; i < 3; i++ ) { 00519 if ( propFoV[i] != -std::numeric_limits<float>::infinity() ) { 00520 ok &= util::fuzzyEqual( propFoV[i], calcFoV[i] ); 00521 } else 00522 propFoV[i] = calcFoV[i]; 00523 } 00524 00525 LOG_IF( ! ok, Runtime, info ) 00526 << "The calculated field of view differs from the stored " << propFoV << "/" << calcFoV; 00527 } 00528 00529 LOG_IF( ! isValid(), Runtime, warning ) << "The image is not valid after reindexing. Missing properties: " << getMissing(); 00530 00531 // check if there is a list in any chunk 00532 bool found = false; 00533 00534 for ( size_t i = 0; i < lookup.size() && found == false; i++ ) { 00535 const KeyList lists_list = lookup[i]->findLists(); 00536 LOG_IF( !lists_list.empty(), Debug, info ) << "Found property-lists " << util::MSubject( lists_list ) << " in chunk number " << i << " going to splice the image"; 00537 found = !lists_list.empty(); 00538 } 00539 00540 if( found ) { // splice down the image one step if there are some 00541 const size_t relDims = lookup[0]->getRelevantDims(); 00542 assert( relDims > 1 ); 00543 spliceDownTo( static_cast<data::dimensions>( relDims - 1 ) ); 00544 } 00545 00546 00547 updateOrientationMatrices(); 00548 return clean = isValid(); 00549 } 00550 00551 bool Image::isEmpty()const { 00552 return set.isEmpty(); 00553 } 00554 00555 const boost::shared_ptr< Chunk >& Image::chunkPtrAt( size_t pos )const { 00556 LOG_IF( lookup.empty(), Debug, error ) << "The lookup table is empty. Run reIndex first."; 00557 LOG_IF( pos >= lookup.size(), Debug, error ) << "Index is out of the range of the lookup table (" << pos << ">=" << lookup.size() << ")."; 00558 const boost::shared_ptr<Chunk> &ptr = lookup[pos]; 00559 LOG_IF( !ptr, Debug, error ) << "There is no chunk at " << pos << ". This usually happens in incomplete images."; 00560 return ptr; 00561 } 00562 00563 Chunk Image::getChunkAt( size_t pos, bool copy_metadata )const { 00564 Chunk ret( *chunkPtrAt( pos ) ); 00565 00566 if( copy_metadata )ret.join( *this ); // copy all metadata from the image in here 00567 00568 return ret; 00569 } 00570 Chunk &Image::chunkAt( size_t pos ) { 00571 return *chunkPtrAt( pos ); 00572 } 00573 00574 Chunk Image::getChunk ( size_t first, size_t second, size_t third, size_t fourth, bool copy_metadata ) { 00575 checkMakeClean(); 00576 return const_cast<const Image &>( *this ).getChunk( first, second, third, fourth, copy_metadata ); // use the const version 00577 } 00578 00579 const Chunk Image::getChunk ( size_t first, size_t second, size_t third, size_t fourth, bool copy_metadata ) const { 00580 const size_t index = commonGet( first, second, third, fourth ).first; 00581 return getChunkAt( index, copy_metadata ); 00582 } 00583 00584 void Image::copyToValueArray( ValueArrayBase & dst, scaling_pair scaling ) const { 00585 if( getVolume() > dst.getLength() ) { 00586 LOG( Runtime, error ) << "Image wont fit into the ValueArray, wont copy.."; 00587 return; 00588 } 00589 00590 if ( clean ) { 00591 if ( scaling.first.isEmpty() || scaling.second.isEmpty() ) { 00592 scaling = getScalingTo ( dst.getTypeID() ); 00593 } 00594 00595 std::vector< ValueArrayReference > targets; 00596 00597 if( lookup.size() > 1 ) { //if there are more than 1 chunks 00598 //splice target to have the same parts as the image 00599 targets = dst.splice( lookup.front()->getVolume() ); 00600 } else { 00601 //just put that ValueArray into the list 00602 targets.push_back( dst ); 00603 } 00604 00605 std::vector< ValueArrayReference >::iterator target = targets.begin(); 00606 BOOST_FOREACH ( const boost::shared_ptr<Chunk> &ref, lookup ) { // copy chunks into the parts 00607 if ( !ref->getValueArrayBase().copyTo ( **target, scaling ) ) { 00608 LOG ( Runtime, error ) 00609 << "Failed to copy raw data of type " << ref->getTypeName() << " from " << getSizeAsString() << "-image into ValueArray of type " 00610 << dst.getTypeName() << " and length " << dst.getLength(); 00611 } 00612 00613 target++; 00614 } 00615 } else { 00616 LOG ( Runtime, error ) << "Cannot copy from non clean images. Run reIndex first"; 00617 } 00618 } 00619 00620 Image Image::copyByID( short unsigned int ID, scaling_pair scaling ) const { 00621 Image ret( *this ); // ok we just cheap-copied the whole image 00622 00623 //we want deep copies of the chunks, and we want them to be of type ID 00624 struct : _internal::SortedChunkList::chunkPtrOperator { 00625 std::pair<util::ValueReference, util::ValueReference> scale; 00626 unsigned short ID; 00627 boost::shared_ptr<Chunk> operator() ( const boost::shared_ptr< Chunk >& ptr ) { 00628 return boost::shared_ptr<Chunk> ( new Chunk ( ptr->copyByID( ID, scale ) ) ); 00629 } 00630 } conv_op; 00631 00632 if( ID && ( scaling.first.isEmpty() || scaling.second.isEmpty() ) ) // if we have an ID but no scaling, compute it 00633 conv_op.scale = getScalingTo( ID ); 00634 00635 conv_op.ID = ID; 00636 00637 ret.set.transform ( conv_op ); 00638 00639 if ( ret.isClean() ) { 00640 ret.lookup = ret.set.getLookup(); // the lookup table still points to the old chunks 00641 } else { 00642 LOG ( Debug, info ) << "Copied unclean image. Running reIndex on the copy."; 00643 ret.reIndex(); 00644 } 00645 00646 return *this; 00647 00648 } 00649 00650 std::vector< Chunk > Image::copyChunksToVector( bool copy_metadata )const { 00651 std::vector<isis::data::Chunk> ret; 00652 ret.reserve( lookup.size() ); 00653 std::vector<boost::shared_ptr<Chunk> >::const_iterator at = lookup.begin(); 00654 const std::vector<boost::shared_ptr<Chunk> >::const_iterator end = lookup.end(); 00655 00656 while ( at != end ) { 00657 ret.push_back( **( at++ ) ); 00658 00659 if( copy_metadata ) 00660 ret.back().join( *this ); 00661 } 00662 00663 return ret; 00664 } 00665 00666 size_t Image::getChunkStride ( size_t base_stride ) { 00667 LOG_IF( set.isEmpty(), Runtime, error ) << "Trying to get chunk stride in an empty image"; 00668 LOG_IF( lookup.empty(), Debug, error ) << "Lookup table for chunks is empty. Do reIndex() first!"; 00669 00670 if ( lookup.size() >= 4 * base_stride ) { 00671 /* there can't be any stride with less than 3*base_stride chunks (which would actually be an invalid image) 00672 * _____ 00673 * |c c| has no stride/dimensional break 00674 * _____ 00675 * |c c| 00676 * |c | has a dimensional break, but is invalid 00677 * _____ 00678 * |c c| 00679 * |c c| is the first reasonable case 00680 */ 00681 // get the distance between first and second chunk for comparision 00682 const util::fvector3 firstV = chunkAt( 0 ).getPropertyAs<util::fvector3>( "indexOrigin" ); 00683 const util::fvector3 secondV = chunkAt( base_stride ).getPropertyAs<util::fvector3>( "indexOrigin" ); 00684 const util::fvector3 dist1 = secondV - firstV; 00685 00686 if( dist1.sqlen() == 0 ) { //if there is no geometric structure anymore - so asume its flat from here on 00687 LOG( Debug, info ) << "Distance between 0 and " << util::MSubject( base_stride ) 00688 << " is zero. Assuming there are no dimensional breaks anymore. Returning " << util::MSubject( base_stride ); 00689 return base_stride; 00690 } else for ( size_t i = base_stride; i < lookup.size() - base_stride; i += base_stride ) { // compare every follwing distance to that 00691 const util::fvector3 thisV = chunkAt( i ).getPropertyAs<util::fvector3>( "indexOrigin" ); 00692 const util::fvector3 nextV = chunkAt( i + base_stride ).getPropertyAs<util::fvector3>( "indexOrigin" ); 00693 const util::fvector3 distFirst = nextV - firstV; 00694 const util::fvector3 distThis = nextV - thisV; 00695 LOG( Debug, verbose_info ) 00696 << "Distance between chunk " << util::MSubject( i ) << " and " << util::MSubject( i + base_stride ) 00697 << " is " << distThis.len() << ". Distance between 0 and " << util::MSubject( i + base_stride ) << " is " << distFirst.len(); 00698 00699 if ( distFirst.sqlen() <= distThis.sqlen() ) { // the next chunk is nearer to the begin than to this => dimensional break => leave 00700 LOG( Debug, info ) 00701 << "Distance between chunk " << util::MSubject( i + base_stride ) 00702 << " and 0 is not bigger than the distance between " << util::MSubject( i + base_stride ) 00703 << " and " << util::MSubject( i ) << ", assuming dimensional break at " << i + base_stride; 00704 return i + base_stride; 00705 } 00706 } 00707 } else if ( lookup.size() % base_stride ) { 00708 LOG( Runtime, error ) 00709 << "The amount of chunks (" << lookup.size() 00710 << ") is not divisible by the block size of the dimension below (" << base_stride 00711 << "). Maybe the image is incomplete."; 00712 LOG( Runtime, warning ) 00713 << "Ignoring " << lookup.size() % base_stride << " chunks."; 00714 return lookup.size() - ( lookup.size() % base_stride ); 00715 } 00716 00717 //we didn't find any break, so we assume its a linear image |c c ... c| 00718 LOG( Debug, info ) 00719 << "No dimensional break found, assuming it to be at the end (" << lookup.size() << "/" << set.getHorizontalSize() << ")"; 00720 return lookup.size() / set.getHorizontalSize(); 00721 } 00722 00723 std::list<util::PropertyValue> Image::getChunksProperties( const util::PropertyMap::KeyType & key, bool unique )const { 00724 std::list<util::PropertyValue > ret; 00725 00726 if( clean ) { 00727 BOOST_FOREACH( const boost::shared_ptr<Chunk> &ref, lookup ) { 00728 const util::PropertyValue &prop = ref->propertyValue( key ); 00729 00730 if ( unique && prop.isEmpty() ) //if unique is requested and the property is empty 00731 continue; //skip it 00732 else if ( unique && !ret.empty() && prop == ret.back() ) 00733 //if unique is requested and the property is equal to the one added before 00734 continue;//skip it 00735 else 00736 ret.push_back( prop ); 00737 } 00738 } else { 00739 LOG( Runtime, error ) << "Cannot get chunk-properties from non clean images. Run reIndex first"; 00740 } 00741 00742 return ret; 00743 } 00744 00745 size_t Image::getMaxBytesPerVoxel() const { 00746 size_t bytes = chunkPtrAt( 0 )->getBytesPerVoxel(); 00747 BOOST_FOREACH( const boost::shared_ptr<Chunk> &ref, lookup ) { 00748 LOG_IF( bytes != ref->getBytesPerVoxel(), Debug, warning ) 00749 << "Not all voxels have the same byte size (" << bytes << "!=" << ref->getBytesPerVoxel() << "). Using the biggest."; 00750 00751 if( bytes < ref->getBytesPerVoxel() ) { 00752 bytes = ref->getBytesPerVoxel(); 00753 } 00754 } 00755 return bytes; 00756 } 00757 00758 std::pair<util::ValueReference, util::ValueReference> Image::getMinMax () const { 00759 std::pair<util::ValueReference, util::ValueReference> ret; 00760 00761 if( !lookup.empty() ) { 00762 std::vector<boost::shared_ptr<Chunk> >::const_iterator i = lookup.begin(); 00763 ret = ( *i )->getMinMax(); 00764 00765 for( ++i; i != lookup.end(); ++i ) { 00766 std::pair<util::ValueReference, util::ValueReference> current = ( *i )->getMinMax(); 00767 00768 if( ret.first->gt( *current.first ) ) 00769 ret.first = current.first; 00770 00771 if( ret.second->lt( *current.second ) ) 00772 ret.second = current.second; 00773 } 00774 } 00775 00776 return ret; 00777 } 00778 00779 // @todo this wont work with images of more 2 two different data types 00780 std::pair< util::ValueReference, util::ValueReference > Image::getScalingTo( short unsigned int targetID, autoscaleOption scaleopt ) const { 00781 LOG_IF( !clean, Debug, error ) << "You should run reIndex before running this"; 00782 std::pair<util::ValueReference, util::ValueReference> minmax = getMinMax(); 00783 00784 BOOST_FOREACH( const boost::shared_ptr<const Chunk> &ref, lookup ) { //find a chunk which would be converted 00785 if( targetID != ref->getTypeID() ) { 00786 const scaling_pair scale = ref->getScalingTo( targetID, minmax, scaleopt ); 00787 LOG_IF( scale.first.isEmpty() || scale.second.isEmpty(), Debug, error ) << "Returning an invalid scaling. This is bad!"; 00788 return scale; // and ask that for the scaling 00789 } 00790 } 00791 return std::make_pair( //ok seems like no conversion is needed - return 1/0 00792 util::ValueReference( util::Value<uint8_t>( 1 ) ), 00793 util::ValueReference( util::Value<uint8_t>( 0 ) ) 00794 ); 00795 } 00796 00797 size_t Image::compare( const isis::data::Image & comp ) const { 00798 size_t ret = 0; 00799 LOG_IF( ! ( clean && comp.clean ), Debug, error ) 00800 << "Comparing unindexed images will cause you trouble, run reIndex()!"; 00801 00802 if ( getSizeAsVector() != comp.getSizeAsVector() ) { 00803 LOG( Runtime, warning ) << "Size of images differs (" << getSizeAsVector() << "/" 00804 << comp.getSizeAsVector() << "). Adding difference to the result."; 00805 ret += ( getSizeAsVector() - comp.getSizeAsVector() ).product(); 00806 } 00807 00808 util::ivector4 compVect( util::minVector( chunkPtrAt( 0 )->getSizeAsVector(), comp.chunkPtrAt( 0 )->getSizeAsVector() ) ); 00809 util::ivector4 start; 00810 const size_t increment = compVect.product(); 00811 00812 for ( size_t i = 0; i < getVolume(); i += increment ) { 00813 const size_t nexti = i + increment - 1; 00814 const std::pair<size_t, size_t> c1pair1( i / chunkVolume, i % chunkVolume ); 00815 const std::pair<size_t, size_t> c1pair2( nexti / chunkVolume, nexti % chunkVolume ); 00816 const std::pair<size_t, size_t> c2pair1( i / comp.chunkVolume, i % comp.chunkVolume ); 00817 assert( c1pair1.first == c1pair2.first ); 00818 LOG( Debug, verbose_info ) << "Comparing chunks at " << c1pair1.first << " and " << c2pair1.first; 00819 const Chunk &c1 = *chunkPtrAt( c1pair1.first ); 00820 const Chunk &c2 = *( comp.chunkPtrAt( c2pair1.first ) ); 00821 LOG( Debug, verbose_info ) 00822 << "Start positions are " << c1pair1.second << " and " << c2pair1.second 00823 << " and the length is " << c1pair2.second - c1pair1.second; 00824 ret += c1.getValueArrayBase().compare( c1pair1.second, c1pair2.second, c2.getValueArrayBase(), c2pair1.second ); 00825 } 00826 00827 return ret; 00828 } 00829 00830 Image::orientation Image::getMainOrientation()const { 00831 LOG_IF( ! isValid() || ! clean, Debug, warning ) << "You should not run this on non clean image. Run reIndex first."; 00832 util::fvector3 row = getPropertyAs<util::fvector3>( "rowVec" ); 00833 util::fvector3 column = getPropertyAs<util::fvector3>( "columnVec" ); 00834 row.norm(); 00835 column.norm(); 00836 LOG_IF( row.dot( column ) > 0.01, Runtime, warning ) << "The cosine between the columns and the rows of the image is bigger than 0.01"; 00837 const util::fvector3 crossVec = util::fvector3( 00838 row[1] * column[2] - row[2] * column[1], 00839 row[2] * column[0] - row[0] * column[2], 00840 row[0] * column[1] - row[1] * column[0] 00841 ); 00842 const util::fvector3 x( 1, 0 ), y( 0, 1 ), z( 0, 0, 1 ); 00843 double a_axial = std::acos( crossVec.dot( z ) ) / M_PI; 00844 double a_sagittal = std::acos( crossVec.dot( x ) ) / M_PI; 00845 double a_coronal = std::acos( crossVec.dot( y ) ) / M_PI; 00846 bool a_inverse = false, s_inverse = false, c_inverse = false; 00847 LOG( Debug, info ) << "Angles to vectors are " << ( a_sagittal * 180 ) << " to x, " << ( a_coronal * 180 ) << " to y and " << ( a_axial * 180 ) << " to z"; 00848 00849 if( a_axial > .5 ) { 00850 a_axial = std::abs( a_axial - 1 ); 00851 a_inverse = true; 00852 } 00853 00854 if( a_sagittal > .5 ) { 00855 a_sagittal = std::abs( a_sagittal - 1 ); 00856 s_inverse = true; 00857 } 00858 00859 if( a_coronal > .5 ) { 00860 a_coronal = std::abs( a_coronal - 1 ); 00861 c_inverse = true; 00862 } 00863 00864 if( a_axial <= .25 ) 00865 return a_inverse ? reversed_axial : axial; 00866 else if( a_sagittal <= .25 ) 00867 return s_inverse ? reversed_sagittal : sagittal; 00868 else if( a_coronal <= .25 ) 00869 return c_inverse ? reversed_coronal : coronal; 00870 else 00871 assert( false ); 00872 00873 return axial; //will never be reached 00874 } 00875 00876 unsigned short Image::getMajorTypeID() const { 00877 switch( getChunk( 0 ).getTypeID() ) { // dont do smart typeID detection for types who cant do minmax 00878 case data::ValueArray<util::color24>::staticID: 00879 case data::ValueArray<util::color48>::staticID: 00880 case data::ValueArray<std::complex< float > >::staticID: 00881 case data::ValueArray<std::complex< double > >::staticID: 00882 LOG( Debug, info ) << "Using flat typeID for " << getChunk( 0 ).getTypeName() << " because I cannot compute min/max"; 00883 return getChunk( 0 ).getTypeID(); 00884 break; 00885 default: 00886 std::pair<util::ValueReference, util::ValueReference> minmax = getMinMax(); 00887 LOG( Debug, info ) << "Determining datatype of image with the value range " << minmax; 00888 00889 if( minmax.first->getTypeID() == minmax.second->getTypeID() ) { // ok min and max are the same type - trivial case 00890 return minmax.first->getTypeID() << 8; // btw: we do the shift, because min and max are Value - but we want the ID's ValueArray 00891 } else if( minmax.first->fitsInto( minmax.second->getTypeID() ) ) { // if min fits into the type of max, use that 00892 return minmax.second->getTypeID() << 8; //@todo maybe use a global static function here instead of a obscure shit operation 00893 } else if( minmax.second->fitsInto( minmax.first->getTypeID() ) ) { // if max fits into the type of min, use that 00894 return minmax.first->getTypeID() << 8; 00895 } else { 00896 LOG( Runtime, error ) << "Sorry I dont know which datatype I should use. (" << minmax.first->getTypeName() << " or " << minmax.second->getTypeName() << ")"; 00897 std::stringstream o; 00898 o << "Type selection failed. Range was: " << minmax; 00899 throw( std::logic_error( o.str() ) ); 00900 } 00901 00902 break; 00903 } 00904 00905 return 0; // id 0 is invalid 00906 } 00907 std::string Image::getMajorTypeName() const { 00908 return util::getTypeMap()[getMajorTypeID()]; 00909 } 00910 00911 bool Image::convertToType( short unsigned int ID, autoscaleOption scaleopt ) { 00912 bool retVal = true; 00913 BOOST_FOREACH( boost::shared_ptr<Chunk> &ref, lookup ) { 00914 retVal &= ( ref->getTypeID() == ID ); 00915 } 00916 00917 if( retVal ) // if all chunks allready have the requested type we can skip the rest 00918 return true; 00919 00920 // get value range of the image for the conversion 00921 scaling_pair scale = getScalingTo( ID, scaleopt ); 00922 00923 LOG( Debug, info ) << "Computed scaling of the original image data: [" << scale << "]"; 00924 retVal = true; 00925 //we want all chunks to be of type ID - so tell them 00926 BOOST_FOREACH( boost::shared_ptr<Chunk> &ref, lookup ) { 00927 retVal &= ref->convertToType( ID, scale ); 00928 } 00929 return retVal; 00930 } 00931 00932 size_t Image::spliceDownTo( dimensions dim ) { //rowDim = 0, columnDim, sliceDim, timeDim 00933 if( lookup[0]->getRelevantDims() < ( size_t ) dim ) { 00934 LOG( Debug, error ) << "The dimensionality of the chunks of this image is already below " << dim << " cannot splice it."; 00935 return 0; 00936 } else if( lookup[0]->getRelevantDims() == ( size_t ) dim ) { 00937 LOG( Debug, info ) << "Skipping useless splicing, relevantDims is already " << lookup[0]->getRelevantDims(); 00938 return lookup.size(); 00939 } 00940 00941 util::vector4<size_t> image_size = getSizeAsVector(); 00942 00943 for( int i = 0; i < dim; i++ ) 00944 image_size[i] = 1; 00945 00946 // get a list of needed properties (everything which is missing in a newly created chunk plus everything which is needed for autosplice) 00947 const std::list<util::PropertyMap::KeyType> splice_needed = util::stringToList<util::PropertyMap::KeyType>( util::PropertyMap::KeyType( "voxelSize,voxelGap,rowVec,columnVec,sliceVec,indexOrigin,acquisitionNumber" ), ',' ); 00948 util::PropertyMap::KeyList needed = MemChunk<short>( 1 ).getMissing(); 00949 needed.insert( splice_needed.begin(), splice_needed.end() ); 00950 struct splicer { 00951 dimensions m_dim; 00952 Image &m_image; 00953 size_t m_amount; 00954 splicer( dimensions dimemsion, size_t amount, Image &image ): m_dim( dimemsion ), m_image( image ), m_amount( amount ) {} 00955 void operator()( const Chunk &ch ) { 00956 const size_t topDim = ch.getRelevantDims() - 1; 00957 00958 if( topDim >= ( size_t ) m_dim ) { // ok we still have to splice that 00959 const size_t subSize = m_image.getSizeAsVector()[topDim]; 00960 assert( !( m_amount % subSize ) ); // there must not be any "remaining" 00961 splicer sub( m_dim, m_amount / subSize, m_image ); 00962 BOOST_FOREACH( const Chunk & ref, ch.autoSplice( uint32_t( m_amount / subSize ) ) ) { 00963 sub( ref ); 00964 } 00965 } else { // seems like we're done - insert it into the image 00966 assert( ch.getRelevantDims() == ( size_t ) m_dim ); // index of the higest dim>1 (ch.getRelevantDims()-1) shall be equal to the dim below the requested splicing (m_dim-1) 00967 LOG( Debug, verbose_info ) << "Inserting splice result of size " << ch.getSizeAsVector() << " at " << ch.propertyValue( "indexOrigin" ); 00968 m_image.insertChunk( ch ); 00969 } 00970 } 00971 }; 00972 std::vector<boost::shared_ptr<Chunk> > buffer = lookup; // store the old lookup table 00973 lookup.clear(); 00974 set.clear(); // clear the image, so we can insert the splices 00975 clean = false; // mark the image for reIndexing 00976 //static_cast<util::PropertyMap::base_type*>(this)->clear(); we can keep the common properties - they will be merged with thier own copies from the chunks on the next reIndex 00977 splicer splice( dim, image_size.product(), *this ); 00978 BOOST_FOREACH( boost::shared_ptr<Chunk> &ref, buffer ) { 00979 BOOST_FOREACH( const util::PropertyMap::KeyType & need, needed ) { //get back properties needed for the 00980 if( !ref->hasProperty( need ) && this->hasProperty( need ) ) { 00981 LOG( Debug, info ) << "Copying " << need << "=" << this->propertyValue( need ) << " from the image to the chunk for splicing"; 00982 ref->propertyValue( need ) = this->propertyValue( need ); 00983 } 00984 } 00985 splice( *ref ); 00986 } 00987 reIndex(); 00988 return lookup.size(); 00989 } 00990 00991 size_t Image::foreachChunk( ChunkOp & op, bool copyMetaData ) { 00992 size_t err = 0; 00993 if(checkMakeClean()){ 00994 util::vector4<size_t> imgSize = getSizeAsVector(); 00995 util::vector4<size_t> chunkSize = getChunk( 0, 0, 0, 0 ).getSizeAsVector(); 00996 util::vector4<size_t> pos; 00997 00998 for( pos[timeDim] = 0; pos[timeDim] < imgSize[timeDim]; pos[timeDim] += chunkSize[timeDim] ) { 00999 for( pos[sliceDim] = 0; pos[sliceDim] < imgSize[sliceDim]; pos[sliceDim] += chunkSize[sliceDim] ) { 01000 for( pos[columnDim] = 0; pos[columnDim] < imgSize[columnDim]; pos[columnDim] += chunkSize[columnDim] ) { 01001 for( pos[rowDim] = 0; pos[rowDim] < imgSize[rowDim]; pos[rowDim] += chunkSize[rowDim] ) { 01002 Chunk ch = getChunk( pos[rowDim], pos[columnDim], pos[sliceDim], pos[timeDim], copyMetaData ); 01003 01004 if( op( ch, pos ) == false ) 01005 err++; 01006 } 01007 } 01008 } 01009 } 01010 } 01011 return err; 01012 } 01013 01014 size_t Image::getNrOfColumns() const { 01015 return getDimSize( data::rowDim ); 01016 } 01017 01018 size_t Image::getNrOfRows() const { 01019 return getDimSize( data::columnDim ); 01020 } 01021 size_t Image::getNrOfSlices() const { 01022 return getDimSize( data::sliceDim ); 01023 } 01024 size_t Image::getNrOfTimesteps() const { 01025 return getDimSize( data::timeDim ); 01026 } 01027 01028 util::fvector3 Image::getFoV() const { 01029 util::fvector4 voxelGap; 01030 01031 if ( hasProperty( "voxelGap" ) ) { 01032 voxelGap = getPropertyAs<util::fvector4>( "voxelGap" ); 01033 01034 for ( size_t i = 0; i < 3; i++ ) 01035 if ( voxelGap[i] == -std::numeric_limits<float>::infinity() ) { 01036 LOG( Runtime, info ) << "Ignoring unknown voxel gap in direction " << i; 01037 voxelGap[i] = 0; 01038 } 01039 } 01040 01041 const util::fvector4 ret = _internal::NDimensional<4>::getFoV( getPropertyAs<util::fvector4>( "voxelSize" ), voxelGap ); 01042 01043 LOG_IF( ret[timeDim], Runtime, warning ) << "Ignoring fourth dim extend of " << ret[timeDim] << " in Image"; 01044 01045 return util::fvector3( ret[0], ret[1], ret[2] ); 01046 } 01047 01048 Image::iterator Image::begin() { 01049 if( checkMakeClean() ) { 01050 std::vector<Chunk *> vec( lookup.size() ); 01051 01052 for( size_t i = 0; i < lookup.size(); i++ ) 01053 vec[i] = lookup[i].get(); 01054 01055 return iterator( vec ); 01056 } else { 01057 LOG( Debug, error ) << "Image is not clean. Returning empty iterator ..."; 01058 return iterator(); 01059 } 01060 } 01061 Image::iterator Image::end() {return begin() + getVolume();} 01062 Image::const_iterator Image::begin()const { 01063 if( isClean() ) { 01064 std::vector<const Chunk *> vec( lookup.size() ); 01065 01066 for( size_t i = 0; i < lookup.size(); i++ ) 01067 vec[i] = lookup[i].get(); 01068 01069 return const_iterator( vec ); 01070 } else { 01071 LOG( Debug, error ) << "Image is not clean. Returning empty iterator ..."; 01072 return const_iterator(); 01073 } 01074 } 01075 Image::const_iterator Image::end()const {return begin() + getVolume();} 01076 01077 const util::ValueReference Image::getVoxelValue ( size_t nrOfColumns, size_t nrOfRows, size_t nrOfSlices, size_t nrOfTimesteps ) const { 01078 const size_t idx[] = {nrOfColumns, nrOfRows, nrOfSlices, nrOfTimesteps}; 01079 LOG_IF( !isInRange( idx ), Debug, isis::error ) 01080 << "Index " << util::vector4<size_t>( idx ) << " is out of range (" << getSizeAsString() << ")"; 01081 return begin()[getLinearIndex( idx )]; 01082 } 01083 void Image::setVoxelValue ( const util::ValueReference & val, size_t nrOfColumns, size_t nrOfRows, size_t nrOfSlices, size_t nrOfTimesteps ) { 01084 const size_t idx[] = {nrOfColumns, nrOfRows, nrOfSlices, nrOfTimesteps}; 01085 LOG_IF( !isInRange( idx ), Debug, isis::error ) 01086 << "Index " << util::vector4<size_t>( idx ) << " is out of range (" << getSizeAsString() << ")"; 01087 begin()[getLinearIndex( idx )] = val; 01088 } 01089 01090 std::string Image::identify ( bool withpath )const { 01091 return 01092 "\"S" 01093 + getPropertyAs<std::string>( "sequenceNumber" ) 01094 + ( hasProperty( "sequenceDescription" ) ? 01095 ( "_" + getPropertyAs<std::string>( "sequenceDescription" ) ) : 01096 "" 01097 ) + "\"" 01098 + ( withpath ? 01099 ( std::string( " from " ) + getCommonSource( *this ).file_string() ) : 01100 "" ) 01101 + ( hasProperty( "sequenceStart" ) ? 01102 ( " taken at " + getPropertyAs<std::string>( "sequenceStart" ) ) : 01103 "" 01104 ); 01105 } 01106 01107 01108 } // END namespace data 01109 } // END namespace isis