/*========================================================================= Program: ORFEO Toolbox Language: C++ Date: $Date$ Version: $Revision$ Copyright (c) Centre National d'Etudes Spatiales. All rights reserved. See OTBCopyright.txt for details. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the above copyright notices for more information. =========================================================================*/ #include "otbJPEG2000ImageIO.h" #include <iomanip> #include <iostream> #include <fstream> #include "otbMacro.h" #include "itksys/SystemTools.hxx" #include "itkTimeProbe.h" #include "itkMacro.h" #include "itkMetaDataObject.h" #include "otbMetaDataKey.h" #include <deque> extern "C" { #include "openjpeg.h" } #include "gdal.h" #include "gdaljp2metadata.h" #include "cpl_string.h" #include "ogr_spatialref.h" #include "ogr_srs_api.h" #include "itksys/SystemTools.hxx" #include "otbTinyXML.h" #include "itkMutexLock.h" #include "itkMutexLockHolder.h" void OpjImageDestroy(opj_image_t * img) { if(img) { otbopenjpeg_opj_image_destroy(img); } } void OpjCodestreamDestroy(opj_codestream_info_v2 * cstr) { otbopenjpeg_opj_destroy_cstr_info_v2(&cstr); } void FileDestroy(FILE * file) { if(file) { fclose(file); } } /** Divide an integer by a power of 2 and round upwards @return Returns a divided by 2^b */ inline int int_ceildivpow2(int a, int b) { return (a + (1 << b) - 1) >> b; } inline unsigned int uint_ceildivpow2(unsigned int a, unsigned int b) { return (a + (1 << b) - 1) >> b; } /** sample error debug callback expecting no client object */ void error_callback(const char *msg, void *client_data) { (void) msg; (void) client_data; otbMsgDevMacro(<< "OpenJPEG error: " << msg); } /** sample warning debug callback expecting no client object */ void warning_callback(const char *msg, void *client_data) { (void) msg; (void) client_data; otbMsgDevMacro(<< "OpenJPEG warning: " << msg); } /** sample debug callback expecting no client object */ void info_callback(const char *msg, void *client_data) { (void) msg; (void) client_data; otbMsgDevMacro(<< "OpenJPEG info: " << msg); } namespace otb { /** Compute offsets needed to read the data from the tile decoded and * offsets needed to write into the output buffer. * * Please note that this function uses a bare pointer instead of a * shared one because it is called within LoadTileData, which hides * the opj_image_t pointer between a void * for proper hiding of * openjpeg API. */ void ComputeOffsets(opj_image_t * tile, const itk::ImageIORegion & ioRegion, unsigned int &l_width_src, unsigned int &l_height_dest, unsigned int &l_width_dest, unsigned int &l_start_offset_dest, unsigned int &l_start_offset_src); /************************************************************************/ /* JPEG2000 metadata reader based on GDAL functionalities */ /************************************************************************/ class JPEG2000MetadataReader { public: JPEG2000MetadataReader(const char *filename ) { m_MetadataIsRead = false; if( m_JP2Metadata.ReadAndParse(filename) ) { m_MetadataIsRead = true; } }; ~JPEG2000MetadataReader(){}; /** Get the geoTransform from file*/ std::vector<double> GetGeoTransform(){ std::vector<double> geoTransform; for (unsigned int i = 0; i< 6; i++ ) geoTransform.push_back(m_JP2Metadata.adfGeoTransform[i]); return geoTransform; }; /** Check if image in the file have a geoTransform*/ bool HaveGeoTransform(){ return static_cast<bool>(m_JP2Metadata.bHaveGeoTransform); }; /** Get the nb of GCP from file*/ int GetGCPCount(){ return m_JP2Metadata.nGCPCount; }; /** Get the GCPs from file*/ std::vector<GDAL_GCP> GetGCPs(){ std::vector<GDAL_GCP> gcps; int nbGCP = m_JP2Metadata.nGCPCount; for (int i = 0; i< nbGCP; i++ ) gcps.push_back(m_JP2Metadata.pasGCPList[i]); return gcps; }; /** Get the projectionRef from file*/ const char* GetProjectionRef() { if (m_JP2Metadata.pszProjection) return m_JP2Metadata.pszProjection; else return NULL; }; /** Get the GML box from file*/ char** GetGMLMetadata() { if (m_JP2Metadata.papszGMLMetadata) return m_JP2Metadata.papszGMLMetadata; else return NULL; }; // Get the origin from GML box (use tinyxml to parse the GML box) bool GetOriginFromGMLBox (std::vector<double> &origin); /** Check if the file has been correctly read*/ bool m_MetadataIsRead; private: /** GDAL structure where store metadata read from JP2 file*/ GDALJP2Metadata m_JP2Metadata; }; bool JPEG2000MetadataReader::GetOriginFromGMLBox (std::vector<double> &origin) { if (!m_MetadataIsRead) return false; std::string gmlString = static_cast<std::string> (m_JP2Metadata.papszGMLMetadata[0]); gmlString.erase(0,18); // We need to remove first part to create a true xml stream otbMsgDevMacro( << "XML extract from GML box: " << gmlString ); TiXmlDocument doc; doc.Parse(gmlString.c_str()); // Create xml doc from a string TiXmlHandle docHandle( &doc ); TiXmlElement* originTag = docHandle.FirstChild( "gml:FeatureCollection" ) .FirstChild( "gml:featureMember" ) .FirstChild( "gml:FeatureCollection" ) .FirstChild( "gml:featureMember" ) .FirstChild( "gml:GridCoverage" ) .FirstChild( "gml:gridDomain") .FirstChild( "gml:Grid" ) .FirstChild( "gml:limits" ) .FirstChild( "gml:GridEnvelope" ) .FirstChild( "gml:low").ToElement(); if(originTag) { otbMsgDevMacro( << "\t Origin (" << originTag->Value() <<" tag)= "<< originTag->GetText()); } else { otbMsgDevMacro( << "Didn't find the GML element which indicate the origin!" ); return false; } std::vector<itksys::String> originValues; originValues = itksys::SystemTools::SplitString(originTag->GetText(),' ', false); std::istringstream ss0 (originValues[0]); std::istringstream ss1 (originValues[1]); ss0 >> origin[1]; ss1 >> origin[0]; origin[0] += -1.0 + 0.5; origin[1] += -1.0 + 0.5; otbMsgDevMacro( << "\t Origin from GML box: " << origin[0] << ", " << origin[1] ); return true; } /************************************************************************/ /* JPEG2000 internal reader based on openjpeg */ /************************************************************************/ class JPEG2000InternalReader { public: JPEG2000InternalReader(); ~JPEG2000InternalReader() { this->Clean(); } boost::shared_ptr<opj_image_t> DecodeTile(unsigned int tileIndex); const std::vector<unsigned int> & GetAvailableResolutions(){return this->m_AvailableResolutions; }; void Clean(); int CanRead(); int Open(const char *filename, unsigned int resolution); bool m_IsOpen; OPJ_CODEC_FORMAT m_CodecFormat; unsigned int m_Width; unsigned int m_Height; unsigned int m_NbOfComponent; std::vector<unsigned int> m_XResolution; std::vector<unsigned int> m_YResolution; std::vector<unsigned int> m_Precision; std::vector<int> m_Signed; unsigned int m_TileWidth; unsigned int m_TileHeight; unsigned int m_XNbOfTile; unsigned int m_YNbOfTile; std::vector<unsigned int> m_AvailableResolutions; unsigned int m_ResolutionFactor; private: boost::shared_ptr<FILE> m_File; int Initialize(); }; int JPEG2000InternalReader::Open(const char *filename, unsigned int resolution) { this->Clean(); // Open the file this->m_File = boost::shared_ptr<FILE>(fopen(filename, "rb"),FileDestroy); if (!this->m_File) { this->Clean(); return 0; } // Find the codec file format std::string lFileName(filename); if (itksys::SystemTools::LowerCase(itksys::SystemTools::GetFilenameLastExtension(lFileName)) == ".j2k") { this->m_CodecFormat = CODEC_J2K; } else if (itksys::SystemTools::LowerCase(itksys::SystemTools::GetFilenameLastExtension(lFileName)) == ".jp2" || itksys::SystemTools::LowerCase(itksys::SystemTools::GetFilenameLastExtension(lFileName)) == ".jpx") { this->m_CodecFormat = CODEC_JP2; } else { this->Clean(); return 0; } this->m_ResolutionFactor = resolution; // Initialize the codec and the stream if (!this->Initialize()) { this->Clean(); return 0; } this->m_IsOpen = true; return 1; } void JPEG2000InternalReader::Clean() { m_File = boost::shared_ptr<FILE>(); this->m_XResolution.clear(); this->m_YResolution.clear(); this->m_Precision.clear(); this->m_Signed.clear(); m_AvailableResolutions.clear(); this->m_Width = 0; this->m_Height = 0; this->m_NbOfComponent = 0; this->m_TileWidth = 0; this->m_TileHeight = 0; this->m_XNbOfTile = 0; this->m_YNbOfTile = 0; this->m_IsOpen = false; this->m_CodecFormat = CODEC_UNKNOWN; } boost::shared_ptr<opj_image_t> JPEG2000InternalReader::DecodeTile(unsigned int tileIndex) { if (!m_File) { this->Clean(); return boost::shared_ptr<opj_image_t>(); } // Creating the file stream boost::shared_ptr<opj_stream_t> stream = boost::shared_ptr<opj_stream_t>(otbopenjpeg_opj_stream_create_default_file_stream(m_File.get(), true),otbopenjpeg_opj_stream_destroy); if (!stream) { this->Clean(); return boost::shared_ptr<opj_image_t>(); } // Creating the codec boost::shared_ptr<opj_codec_t> codec = boost::shared_ptr<opj_codec_t>(otbopenjpeg_opj_create_decompress_v2(this->m_CodecFormat),otbopenjpeg_opj_destroy_codec); if (!codec) { this->Clean(); return boost::shared_ptr<opj_image_t>(); } // Setting default parameters opj_dparameters_t parameters; otbopenjpeg_opj_set_default_decoder_parameters(¶meters); parameters.cp_reduce = static_cast<int>(this->m_ResolutionFactor); // Set default event mgr opj_event_mgr_t eventManager; eventManager.info_handler = info_callback; eventManager.warning_handler = warning_callback; eventManager.error_handler = error_callback; // Setup the decoder decoding parameters using user parameters if (!otbopenjpeg_opj_setup_decoder_v2(codec.get(), ¶meters, &eventManager)) { this->Clean(); return boost::shared_ptr<opj_image_t>(); } // Read the main header of the codestream and if necessary the JP2 // boxes opj_image_t * unsafeOpjImgPtr = NULL; if (!otbopenjpeg_opj_read_header(stream.get(), codec.get(),&unsafeOpjImgPtr)) { boost::shared_ptr<opj_image_t> image = boost::shared_ptr<opj_image_t>(unsafeOpjImgPtr,OpjImageDestroy); this->Clean(); return boost::shared_ptr<opj_image_t>(); } boost::shared_ptr<opj_image_t> image = boost::shared_ptr<opj_image_t>(unsafeOpjImgPtr,OpjImageDestroy); if( otbopenjpeg_opj_get_decoded_tile(codec.get(), stream.get(), image.get(), tileIndex)) { otbMsgDevMacro(<<"Tile "<<tileIndex<<" read from file"); return image; } else { this->Clean(); return boost::shared_ptr<opj_image_t>(); } } JPEG2000InternalReader::JPEG2000InternalReader() { this->Clean(); } int JPEG2000InternalReader::Initialize() { if (m_File) { // Creating the file stream boost::shared_ptr<opj_stream_t> stream = boost::shared_ptr<opj_stream_t>(otbopenjpeg_opj_stream_create_default_file_stream(m_File.get(), true),otbopenjpeg_opj_stream_destroy); if (!stream) { this->Clean(); return 0; } // Creating the codec boost::shared_ptr<opj_codec_t> codec = boost::shared_ptr<opj_codec_t>(otbopenjpeg_opj_create_decompress_v2(this->m_CodecFormat),otbopenjpeg_opj_destroy_codec); if (!codec) { this->Clean(); return 0; } // Setting default parameters opj_dparameters_t parameters; otbopenjpeg_opj_set_default_decoder_parameters(¶meters); parameters.cp_reduce = static_cast<int>(this->m_ResolutionFactor); // Set default event mgr opj_event_mgr_t eventManager; eventManager.info_handler = info_callback; eventManager.warning_handler = warning_callback; eventManager.error_handler = error_callback; // Setup the decoder decoding parameters using user parameters if (!otbopenjpeg_opj_setup_decoder_v2(codec.get(), ¶meters, &eventManager)) { this->Clean(); return 0; } // Read the main header of the codestream and if necessary the JP2 // boxes opj_image_t * unsafeOpjImgPtr = NULL; if (!otbopenjpeg_opj_read_header(stream.get(), codec.get(),&unsafeOpjImgPtr)) { boost::shared_ptr<opj_image_t> image = boost::shared_ptr<opj_image_t>(unsafeOpjImgPtr,OpjImageDestroy); this->Clean(); return 0; } boost::shared_ptr<opj_image_t> image = boost::shared_ptr<opj_image_t>(unsafeOpjImgPtr,OpjImageDestroy); // Get the codestream information boost::shared_ptr<opj_codestream_info_v2> cstrInfo = boost::shared_ptr<opj_codestream_info_v2>(otbopenjpeg_opj_get_cstr_info(codec.get()),OpjCodestreamDestroy); if (!cstrInfo) { std::cerr << "ERROR while get codestream info" << std::endl; this->Clean(); return 0; } // We can now retrieve the main information of the image and the codestream // (based on the first component and with no subsampling) this->m_Width = image->comps->w; this->m_Height = image->comps->h; otbMsgDevMacro(<< "JPEG2000InternalReader dimension (after reading header) = " << image->comps->w << " x " << image->comps->w << " x " << image->comps->h ); this->m_TileHeight = cstrInfo->tdy; this->m_TileWidth = cstrInfo->tdx; this->m_XNbOfTile = cstrInfo->tw; this->m_YNbOfTile = cstrInfo->th; this->m_NbOfComponent = image->numcomps; for (unsigned int itComp = 0; itComp < this->m_NbOfComponent; itComp++) { this->m_Precision.push_back( image->comps[itComp].prec); this->m_Signed.push_back( image->comps[itComp].sgnd); this->m_XResolution.push_back( image->comps[itComp].dx); this->m_YResolution.push_back( image->comps[itComp].dy); } // Warning: This value is based on the first component of the default tile parameters. unsigned int numResAvailable = cstrInfo->m_default_tile_info.tccp_info[0].numresolutions; m_AvailableResolutions.clear(); for (unsigned int itRes = 0; itRes < numResAvailable; itRes++) { m_AvailableResolutions.push_back(itRes); } } return 1; } int JPEG2000InternalReader::CanRead() { if (Initialize() && ( this->m_Width > 0 ) && ( this->m_Height > 0 ) && ( this->m_TileWidth > 0 ) && ( this->m_TileHeight > 0 ) && ( this->m_XNbOfTile > 0 ) && ( this->m_YNbOfTile > 0 ) && ( this->m_NbOfComponent > 0 ) ) { // We manage only JPEG2000 file with characteristics which are equal between components for(unsigned int itComp = 0; itComp < this->m_NbOfComponent - 1; itComp++) { if ( (this->m_Precision[itComp] != this->m_Precision[itComp+1]) && (this->m_Signed[itComp] != this->m_Signed[itComp+1]) && (this->m_XResolution[itComp] != this->m_XResolution[itComp+1]) && (!this->m_XResolution[itComp]) && (this->m_YResolution[itComp] != this->m_YResolution[itComp+1]) && (!this->m_YResolution[itComp]) ) { return 0; } } return 1; } else return 0; } /************************************************************************/ /* Class to manage JPEG2000 tile cache system */ /************************************************************************/ class JPEG2000TileCache { public: JPEG2000TileCache(); ~JPEG2000TileCache(); typedef std::pair<unsigned int, boost::shared_ptr<opj_image_t> > CachedTileType; typedef std::deque<CachedTileType> TileCacheType; /** Get a tile in cache, return null if cache does not contain the tile */ boost::shared_ptr<opj_image_t> GetTile(unsigned int tileIndex); /** Register a new tile in cache */ void AddTile(unsigned int tileIndex, boost::shared_ptr<opj_image_t> tileData); /** Remove the front tile */ void RemoveOneTile(); /** Clear the cache */ void Clear(); /** Initialize some parameters linked to the cache size in memory*/ void Initialize(unsigned int originalWidthTile, unsigned int originalHeightTile, unsigned int nbComponent, unsigned int precision, unsigned int resolution) { this->EstimateTileCacheSize(originalWidthTile, originalHeightTile, nbComponent, precision, resolution); m_CacheSizeInByte = m_CacheSizeInTiles * m_TileCacheSizeInByte; m_IsReady = true; }; /** Set the size of the cache with in terms of number of tiles */ void SetCacheSizeInTiles(unsigned int nbOfTiles) { if (nbOfTiles > 0 && m_IsReady) { m_CacheSizeInTiles = nbOfTiles; m_CacheSizeInByte = m_CacheSizeInTiles * m_TileCacheSizeInByte; } }; /** Get the size of the cache in terms of number of tiles */ unsigned int GetCacheSizeInTiles() {return m_CacheSizeInTiles; }; /** Set the size of the cache with in terms of Bytes */ void SetCacheSizeInByte(unsigned int sizeInByte) { if (sizeInByte > 0 && m_IsReady) { m_CacheSizeInByte = sizeInByte; if (m_TileCacheSizeInByte) m_CacheSizeInTiles = m_CacheSizeInByte / m_TileCacheSizeInByte; else m_CacheSizeInTiles = 0; } }; unsigned int GetCurrentNumberOfTileInCache() { return static_cast<unsigned int>(m_Cache.size()); }; private: TileCacheType m_Cache; unsigned int m_CacheSizeInTiles; unsigned int m_CacheSizeInByte; unsigned int m_TileCacheSizeInByte; bool m_IsReady; itk::SimpleMutexLock m_Mutex; /** Estimate the size of a tile in memory*/ void EstimateTileCacheSize(unsigned int originalWidthTile, unsigned int originalHeightTile, unsigned int nbComponent, unsigned int precision, unsigned int resolution); }; JPEG2000TileCache::JPEG2000TileCache() : m_Cache(), m_CacheSizeInTiles(4), m_CacheSizeInByte(0), m_IsReady(false) {} JPEG2000TileCache::~JPEG2000TileCache() { this->Clear(); } void JPEG2000TileCache::EstimateTileCacheSize(unsigned int originalWidthTile, unsigned int originalHeightTile, unsigned int nbComponent, unsigned int precision, unsigned int resolution) { this->m_TileCacheSizeInByte = originalWidthTile * originalHeightTile * nbComponent * sizeof(OPJ_INT32) / vcl_pow(2, 2*static_cast<double>(resolution) ); if (!this->m_TileCacheSizeInByte) { otbMsgDevMacro( << "TileCacheSizeInByte is estimated at " << m_TileCacheSizeInByte << " bytes so we don't used the cache"); } otbMsgDevMacro( << "m_TileCacheSizeInByte = " << m_TileCacheSizeInByte ); } void JPEG2000TileCache::Clear() { m_Cache.clear(); } boost::shared_ptr<opj_image_t> JPEG2000TileCache::GetTile(unsigned int tileIndex) { for(TileCacheType::iterator it = m_Cache.begin(); it != m_Cache.end(); ++it) { if(it->first == tileIndex) { otbMsgDevMacro(<<"Tile "<<it->first<<" loaded from cache"); return it->second; } } return boost::shared_ptr<opj_image_t>(); } void JPEG2000TileCache::RemoveOneTile() { if(!m_Cache.empty()) { m_Cache.pop_front(); } } void JPEG2000TileCache::AddTile(unsigned int tileIndex, boost::shared_ptr<opj_image_t> tileData) { // This helper class makes sure the Mutex is unlocked // in the event an exception is thrown. itk::MutexLockHolder<itk::SimpleMutexLock> mutexHolder(m_Mutex); if(!m_IsReady) { std::cerr<<(this)<<" Cache is not configured !"<<std::endl; } for(TileCacheType::const_iterator it = m_Cache.begin(); it != m_Cache.end(); ++it) { if(it->first == tileIndex) { return; } } if(m_Cache.size() >= m_CacheSizeInTiles) { this->RemoveOneTile(); } m_Cache.push_back(CachedTileType(tileIndex, tileData)); } /************************************************************************/ /* JPEG2000ImageIO */ /************************************************************************/ JPEG2000ImageIO::JPEG2000ImageIO() { // Initialize multi-threader m_Threader = itk::MultiThreader::New(); m_NumberOfThreads = m_Threader->GetNumberOfThreads(); for(int i = 0; i<m_NumberOfThreads; ++i) { m_InternalReaders.push_back(boost::shared_ptr<JPEG2000InternalReader>(new JPEG2000InternalReader)); } m_TileCache = boost::shared_ptr<JPEG2000TileCache>(new JPEG2000TileCache); // By default set number of dimensions to two. this->SetNumberOfDimensions(2); m_PixelType = SCALAR; m_ComponentType = UCHAR; // Set default spacing to one m_Spacing[0] = 1.0; m_Spacing[1] = 1.0; // Set default origin to zero m_Origin[0] = 0.5; m_Origin[1] = 0.5; m_BytePerPixel = 1; m_ResolutionFactor = 0; // Full resolution by default m_CacheSizeInByte = 0; // By default no cache } JPEG2000ImageIO::~JPEG2000ImageIO() {} bool JPEG2000ImageIO::CanReadFile(const char* filename) { if (filename == NULL) { itkDebugMacro(<< "No filename specified."); return false; } bool success = true; for(ReaderVectorType::iterator it = m_InternalReaders.begin(); it != m_InternalReaders.end(); ++it) { if ( !(*it)->Open(filename, m_ResolutionFactor) ) { success = false; } } // If one of the readers fails, clean everything if(!success) { for(ReaderVectorType::iterator it = m_InternalReaders.begin(); it != m_InternalReaders.end(); ++it) { (*it)->Clean(); } } return success; } // Used to print information about this object void JPEG2000ImageIO::PrintSelf(std::ostream& os, itk::Indent indent) const { Superclass::PrintSelf(os, indent); os << indent << "Resolution Factor: " << m_ResolutionFactor << "\n"; } // Read a 3D image not implemented yet void JPEG2000ImageIO::ReadVolume(void*) { } /** Internal structure used for passing image data into the threading library */ struct ThreadStruct { std::vector<boost::shared_ptr<JPEG2000InternalReader> > Readers; std::vector<JPEG2000TileCache::CachedTileType> * Tiles; JPEG2000ImageIO::Pointer IO; boost::shared_ptr<JPEG2000TileCache> Cache; void * Buffer; }; /** Get Info about all resolution in jpeg2000 file */ bool JPEG2000ImageIO::GetResolutionInfo(std::vector<unsigned int>& res, std::vector<std::string>& desc) { res = this->m_InternalReaders[0]->GetAvailableResolutions(); if (res.empty()) return false; int originalWidth = m_InternalReaders[0]->m_Width; int originalHeight = m_InternalReaders[0]->m_Height; for (std::vector<unsigned int>::iterator itRes = res.begin(); itRes < res.end(); itRes++) { // For each resolution we will compute the tile dim and image dim std::ostringstream oss; int w = int_ceildivpow2( originalWidth, *itRes); int h = int_ceildivpow2( originalHeight, *itRes); int tw = int_ceildivpow2(m_InternalReaders[0]->m_TileWidth, *itRes); int th = int_ceildivpow2(m_InternalReaders[0]->m_TileHeight, *itRes); oss << "Resolution: " << *itRes << " (Image [w x h]: " << w << "x" << h << ", Tile [w x h]: " << tw << "x" << th << ")"; desc.push_back(oss.str()); } return true; } bool JPEG2000ImageIO::GetAvailableResolutions(std::vector<unsigned int>& res) { res = this->m_InternalReaders[0]->GetAvailableResolutions(); if (res.empty()) return false; return true; } unsigned int JPEG2000ImageIO::GetNumberOfOverviews() { std::vector<unsigned int> tempResList = this->m_InternalReaders[0]->GetAvailableResolutions(); if (tempResList.empty()) { itkExceptionMacro(<< "Available resolutions in JPEG2000 is empty"); } return tempResList.size() - 1; } // Read image void JPEG2000ImageIO::Read(void* buffer) { itk::TimeProbe chrono; chrono.Start(); // Check if conversion succeed if (buffer == NULL) { itkExceptionMacro(<< "JPEG2000ImageIO : Bad alloc"); return; } // Re-open the file if it was closed bool open = true; for(ReaderVectorType::iterator it = m_InternalReaders.begin(); it != m_InternalReaders.end(); ++it) { open = (*it)->m_IsOpen && open; } if ( !open ) { if ( !this->CanReadFile( m_FileName.c_str() ) ) { itkExceptionMacro(<< "Cannot open file " << this->m_FileName << "!"); return; } } if (m_ResolutionFactor >= this->m_InternalReaders[0]->m_AvailableResolutions.size()) { itkExceptionMacro(<< "Resolution not available in the file!"); return; } std::vector<unsigned int> tileList = this->ComputeTileList(); if (tileList.empty()) { for(ReaderVectorType::iterator it = m_InternalReaders.begin(); it != m_InternalReaders.end(); ++it) { (*it)->Clean(); } itkExceptionMacro(<< " IORegion does not correspond to any tile!"); } // Here we sort between tiles from cache and tiles to read std::vector<JPEG2000TileCache::CachedTileType> cachedTiles; std::vector<JPEG2000TileCache::CachedTileType> toReadTiles; for (std::vector<unsigned int>::iterator itTile = tileList.begin(); itTile < tileList.end(); ++itTile) { boost::shared_ptr<opj_image_t> currentImage(m_TileCache->GetTile(*itTile)); JPEG2000TileCache::CachedTileType currentTile = std::make_pair((*itTile), currentImage); if(!currentImage) { toReadTiles.push_back(currentTile); } else { cachedTiles.push_back(currentTile); } } // First, load tiles from cache for (std::vector<JPEG2000TileCache::CachedTileType>::iterator itTile = cachedTiles.begin(); itTile < cachedTiles.end(); ++itTile) { this->LoadTileData(buffer, itTile->second.get()); } // If we will read more tiles than the cache size, clear it if(toReadTiles.size() > m_TileCache->GetCacheSizeInTiles()) { m_TileCache->Clear(); } // Else if there is not enough rooms from new tiles else if(toReadTiles.size() > (m_TileCache->GetCacheSizeInTiles() - m_TileCache->GetCurrentNumberOfTileInCache())) { int nbTileToRemove = toReadTiles.size() - (m_TileCache->GetCacheSizeInTiles() - m_TileCache->GetCurrentNumberOfTileInCache()); // Remove from cache as many tiles that will be read in this step for (int itTileR = 0; itTileR < nbTileToRemove; ++itTileR) { m_TileCache->RemoveOneTile(); } } // Decode all tiles not in cache in parallel if(!toReadTiles.empty()) { unsigned int nbThreads = itk::MultiThreader::GetGlobalDefaultNumberOfThreads(); if (nbThreads > toReadTiles.size()) { nbThreads = toReadTiles.size(); } this->GetMultiThreader()->SetNumberOfThreads(nbThreads); // Set up the multithreaded processing ThreadStruct str; str.Readers = m_InternalReaders; str.Tiles = &toReadTiles; str.IO = this; str.Cache = m_TileCache; str.Buffer = buffer; // Set-up multi-threader this->GetMultiThreader()->SetSingleMethod(this->ThreaderCallback, &str); // multithread the execution this->GetMultiThreader()->SingleMethodExecute(); } chrono.Stop(); otbMsgDevMacro( << "JPEG2000ImageIO::Read took " << chrono.GetTotal() << " sec"); for(ReaderVectorType::iterator it = m_InternalReaders.begin(); it != m_InternalReaders.end(); ++it) { (*it)->Clean(); } } // The void * interface prevent us to propagate shared_ptr here void JPEG2000ImageIO::LoadTileData(void * buffer, void * currentTile) { opj_image_t * tile = static_cast<opj_image_t *>(currentTile); if(!tile) { itkExceptionMacro(<<"Tile needed but not loaded."); } // Get nb. of lines and columns of the region to read /* -Wunused-variable int lNbLines = this->GetIORegion().GetSize()[1]; */ int lNbColumns = this->GetIORegion().GetSize()[0]; /* -Wunused-variable int lFirstLine = this->GetIORegion().GetIndex()[1]; // [1... ] int lFirstColumn = this->GetIORegion().GetIndex()[0]; // [1... ] */ unsigned int lWidthSrc; // Width of the input pixel in nb of pixel unsigned int lHeightDest; // Height of the area where write in nb of pixel unsigned int lWidthDest; // Width of the area where write in nb of pixel unsigned int lStartOffsetPxlDest; // Offset where begin to write the area in the otb buffer in nb of pixel unsigned int lStartOffsetPxlSrc; // Offset where begin to write the area in the otb buffer in nb of pixel ComputeOffsets(tile, this->GetIORegion(), lWidthSrc, lHeightDest, lWidthDest, lStartOffsetPxlDest, lStartOffsetPxlSrc); switch (this->GetComponentType()) { case CHAR: { char *p = static_cast<char *> (buffer); for (unsigned int j = 0; j < lHeightDest; ++j) { char* current_dst_line = p + (lStartOffsetPxlDest + j * lNbColumns) * this->m_NumberOfComponents; for (unsigned int k = 0; k < lWidthDest; ++k) { for (unsigned int itComp = 0; itComp < this->m_NumberOfComponents; itComp++) { OPJ_INT32* data = tile->comps[itComp].data; *(current_dst_line++) = static_cast<char> (data[lStartOffsetPxlSrc + k + j * lWidthSrc]); } } } } break; case UCHAR: { unsigned char *p = static_cast<unsigned char *> (buffer); for (unsigned int j = 0; j < lHeightDest; ++j) { unsigned char* current_dst_line = p + (lStartOffsetPxlDest + j * lNbColumns) * this->m_NumberOfComponents; for (unsigned int k = 0; k < lWidthDest; ++k) { for (unsigned int itComp = 0; itComp < this->m_NumberOfComponents; itComp++) { OPJ_INT32* data = tile->comps[itComp].data; unsigned char component_val = data[lStartOffsetPxlSrc + k + j * lWidthSrc] & 0xff; *(current_dst_line++) = static_cast<unsigned char> (component_val); } } } } break; case SHORT: { short *p = static_cast<short *> (buffer); for (unsigned int j = 0; j < lHeightDest; ++j) { short* current_dst_line = p + (lStartOffsetPxlDest + j * lNbColumns) * this->m_NumberOfComponents; for (unsigned int k = 0; k < lWidthDest; ++k) { for (unsigned int itComp = 0; itComp < this->m_NumberOfComponents; itComp++) { OPJ_INT32* data = tile->comps[itComp].data; *(current_dst_line++) = static_cast<short> (data[lStartOffsetPxlSrc + k + j * lWidthSrc]); } } } } break; case USHORT: { unsigned short *p = static_cast<unsigned short *> (buffer); for (unsigned int j = 0; j < lHeightDest; ++j) { unsigned short* current_dst_line = p + (lStartOffsetPxlDest + j * lNbColumns) * this->m_NumberOfComponents; for (unsigned int k = 0; k < lWidthDest; ++k) { for (unsigned int itComp = 0; itComp < this->m_NumberOfComponents; itComp++) { OPJ_INT32* data = tile->comps[itComp].data; *(current_dst_line++) = static_cast<unsigned short> (data[lStartOffsetPxlSrc + k + j * lWidthSrc] & 0xffff); } } } } break; case INT: case UINT: default: itkGenericExceptionMacro(<< "This data type is not handled"); break; } } ITK_THREAD_RETURN_TYPE JPEG2000ImageIO::ThreaderCallback( void *arg ) { ThreadStruct *str; unsigned int total, threadCount; itk::ThreadIdType threadId; threadId = ((itk::MultiThreader::ThreadInfoStruct *)(arg))->ThreadID; threadCount = ((itk::MultiThreader::ThreadInfoStruct *)(arg))->NumberOfThreads; str = (ThreadStruct *)(((itk::MultiThreader::ThreadInfoStruct *)(arg))->UserData); // Retrieve data std::vector<boost::shared_ptr<JPEG2000InternalReader> > readers = str->Readers; std::vector<JPEG2000TileCache::CachedTileType> * tiles = str->Tiles; JPEG2000ImageIO::Pointer io = str->IO; boost::shared_ptr<JPEG2000TileCache> cache = str->Cache; void * buffer = str->Buffer; total = std::min((unsigned int)tiles->size(), threadCount); if(total == 0) { return ITK_THREAD_RETURN_VALUE; } unsigned int tilesPerThread = tiles->size()/total; if(tilesPerThread == 0) { tilesPerThread = 1; } for(unsigned int i = threadId * tilesPerThread; i < tilesPerThread * (threadId+1); ++i) { boost::shared_ptr<opj_image_t> currentTile = readers.at(threadId)->DecodeTile(tiles->at(i).first); // Check if tile is valid if(!currentTile) { readers.at(threadId)->Clean(); itkGenericExceptionMacro(" otbopenjpeg failed to decode the desired tile "<<tiles->at(i).first << "!"); } otbMsgDevMacro(<< " Tile " << tiles->at(i).first << " decoded by thread "<<threadId); io->LoadTileData(buffer, currentTile.get()); if (cache->GetCacheSizeInTiles() != 0) { cache->AddTile(tiles->at(i).first, currentTile); } } unsigned int lastTile = threadCount*tilesPerThread + threadId; // TODO: check this last part if(lastTile < tiles->size()) { boost::shared_ptr<opj_image_t> currentTile = readers.at(threadId)->DecodeTile(tiles->at(lastTile).first); // Check if tile is valid if(!currentTile) { readers.at(threadId)->Clean(); itkGenericExceptionMacro(" otbopenjpeg failed to decode the desired tile "<<tiles->at(lastTile).first << "!"); } otbMsgDevMacro(<< " Tile " <<tiles->at(lastTile).first << " decoded by thread "<<threadId); io->LoadTileData(buffer, currentTile.get()); if (cache->GetCacheSizeInTiles() != 0) { cache->AddTile(tiles->at(lastTile).first, currentTile); } } return ITK_THREAD_RETURN_VALUE; } void JPEG2000ImageIO::ConfigureCache() { itk::ExposeMetaData<unsigned int>(this->GetMetaDataDictionary(), MetaDataKey::CacheSizeInBytes, m_CacheSizeInByte); // Initialize some parameters of the tile cache this->m_TileCache->Initialize(m_InternalReaders.front()->m_TileWidth, m_InternalReaders.front()->m_TileHeight, m_InternalReaders.front()->m_NbOfComponent, m_BytePerPixel, m_ResolutionFactor); // If available set the size of the cache if (this->m_CacheSizeInByte) this->m_TileCache->SetCacheSizeInByte(this->m_CacheSizeInByte); } void JPEG2000ImageIO::ReadImageInformation() { // Extract metadata // In case the metadata are not set, the function silently returns, doing nothing itk::ExposeMetaData<unsigned int>(this->GetMetaDataDictionary(), MetaDataKey::ResolutionFactor, m_ResolutionFactor); // Now initialize the itk dictionary itk::MetaDataDictionary& dict = this->GetMetaDataDictionary(); JPEG2000MetadataReader lJP2MetadataReader(m_FileName.c_str()); if (lJP2MetadataReader.m_MetadataIsRead) { otbMsgDevMacro(<<"JPEG2000 file has metadata available!"); /* GEOTRANSFORM */ if (lJP2MetadataReader.HaveGeoTransform()) { otbMsgDevMacro(<< "JPEG2000 file has a geotransform!"); std::vector<double> geoTransform = lJP2MetadataReader.GetGeoTransform(); itk::EncapsulateMetaData<MetaDataKey::VectorType>(dict, MetaDataKey::GeoTransformKey, geoTransform); /*std::cout << "from gml box, geotransform: "; for (int i = 0; i < 6; i++) { std::cout << geoTransform[i] << ", "; } std::cout << std::endl; */ // Retrieve origin and spacing from the geo transform m_Spacing[0] = geoTransform[1]; m_Spacing[1] = geoTransform[5]; if ( m_Spacing[0]== 0 || m_Spacing[1] == 0) { // Manage case where axis are not standard if (geoTransform[2] != 0 && geoTransform[4] != 0 ) { m_Spacing[0] = geoTransform[2]; m_Spacing[1] = geoTransform[4]; } else { otbWarningMacro(<< "JPEG2000 file has an incorrect geotransform (spacing = 0)!"); m_Spacing[0] = 1; m_Spacing[1] = 1; } } // Geotransforms with a non-null rotation are not supported // Beware : GDAL origin is at the corner of the top-left pixel // whereas OTB/ITK origin is at the centre of the top-left pixel m_Origin[0] = geoTransform[0] + 0.5*m_Spacing[0]; m_Origin[1] = geoTransform[3] + 0.5*m_Spacing[1]; } /* GCPs */ if (lJP2MetadataReader.GetGCPCount() > 0) { // No GCPprojRef return by GDALJP2metadata std::string gcpProjectionKey = "UNKNOWN"; itk::EncapsulateMetaData<std::string>(dict, MetaDataKey::GCPProjectionKey, gcpProjectionKey); int nbGCPs = lJP2MetadataReader.GetGCPCount(); otbMsgDevMacro(<< "JPEG2000 file has "<< nbGCPs << " GCPs!"); itk::EncapsulateMetaData<int>(dict, MetaDataKey::GCPCountKey, nbGCPs); std::vector<GDAL_GCP> gcps = lJP2MetadataReader.GetGCPs(); std::string key; for (int cpt = 0; cpt < nbGCPs; ++cpt) { GDAL_GCP currentGCP = gcps[cpt]; OTB_GCP pOtbGCP; pOtbGCP.m_Id = std::string(currentGCP.pszId); pOtbGCP.m_Info = std::string(currentGCP.pszInfo); pOtbGCP.m_GCPRow = currentGCP.dfGCPLine; pOtbGCP.m_GCPCol = currentGCP.dfGCPPixel; pOtbGCP.m_GCPX = currentGCP.dfGCPX; pOtbGCP.m_GCPY = currentGCP.dfGCPY; pOtbGCP.m_GCPZ = currentGCP.dfGCPZ; // Complete the key with the GCP number : GCP_i std::ostringstream lStream; lStream << MetaDataKey::GCPParametersKey << cpt; key = lStream.str(); itk::EncapsulateMetaData<OTB_GCP>(dict, key, pOtbGCP); } } /* GMLMetadata*/ char** papszGMLMetadata; papszGMLMetadata = lJP2MetadataReader.GetGMLMetadata(); if (CSLCount(papszGMLMetadata) > 0) { otbMsgDevMacro(<< "JPEG2000 file has GMLMetadata!"); std::string key; for (int cpt = 0; papszGMLMetadata[cpt] != NULL; ++cpt) { std::ostringstream lStream; lStream << MetaDataKey::MetadataKey << cpt; key = lStream.str(); itk::EncapsulateMetaData<std::string>(dict, key, static_cast<std::string> (papszGMLMetadata[cpt])); otbMsgDevMacro( << static_cast<std::string>(papszGMLMetadata[cpt])); } } /* ProjectionRef*/ if (lJP2MetadataReader.GetProjectionRef() && !std::string(lJP2MetadataReader.GetProjectionRef()).empty() ) { OGRSpatialReferenceH pSR = OSRNewSpatialReference(NULL); const char * pszProjection = NULL; pszProjection = lJP2MetadataReader.GetProjectionRef(); if (OSRImportFromWkt(pSR, (char **) (&pszProjection)) == OGRERR_NONE) { char * pszPrettyWkt = NULL; OSRExportToPrettyWkt(pSR, &pszPrettyWkt, FALSE); itk::EncapsulateMetaData<std::string> (dict, MetaDataKey::ProjectionRefKey, static_cast<std::string>(pszPrettyWkt)); CPLFree(pszPrettyWkt); } else { itk::EncapsulateMetaData<std::string>(dict, MetaDataKey::ProjectionRefKey, static_cast<std::string>(lJP2MetadataReader.GetProjectionRef())); } if (pSR != NULL) { OSRRelease(pSR); pSR = NULL; } } else { otbMsgDevMacro( << "NO PROJECTION IN GML BOX => SENSOR MODEL " ); m_Origin[0] = 0.5; m_Origin[1] = 0.5; m_Spacing[0] = 1; m_Spacing[1] = 1; lJP2MetadataReader.GetOriginFromGMLBox(m_Origin); } otbMsgDevMacro(<< "FROM GML box: " << "Origin: " << m_Origin[0] << ", " << m_Origin[1] << " | Spacing: " << m_Spacing[0] << ", " << m_Spacing[1] ); } else { otbMsgDevMacro( << "JPEG2000 file has NO metadata available!"); m_Origin[0] = 0.5; m_Origin[1] = 0.5; m_Spacing[0] = 1; m_Spacing[1] = 1; } // If the internal image was not open we open it. // This is usually done when the user sets the ImageIO manually if ( !m_InternalReaders.front()->m_IsOpen ) { if ( !this->CanReadFile( m_FileName.c_str() ) ) { itkExceptionMacro(<< "Cannot open file " << this->m_FileName << "!"); return; } } // Check some internal parameters of the JPEG2000 file if ( !this->m_InternalReaders.front()->CanRead()) { itkExceptionMacro(<< "Cannot read this file because some JPEG2000 parameters are not supported!"); this->m_InternalReaders.front()->Clean(); return; } // If one of the readers fails, clean everything if(this->m_ResolutionFactor != 0) { for(ReaderVectorType::iterator it = m_InternalReaders.begin(); it != m_InternalReaders.end(); ++it) { (*it)->Clean(); } bool success = true; for(ReaderVectorType::iterator it = m_InternalReaders.begin(); it != m_InternalReaders.end(); ++it) { if ( !(*it)->Open( m_FileName.c_str(), this->m_ResolutionFactor ) ) { success = false; } (*it)->m_IsOpen = true; } // If one of the readers fails, clean everything if(!success) { for(ReaderVectorType::iterator it = m_InternalReaders.begin(); it != m_InternalReaders.end(); ++it) { (*it)->Clean(); } itkExceptionMacro(<< "Cannot open this file with this resolution!"); return; } } // Encapsulate tile hints for streaming unsigned int tileHintX = m_InternalReaders.front()->m_TileWidth / static_cast<unsigned int>(vcl_pow(2.0, static_cast<int>(m_ResolutionFactor) )); unsigned int tileHintY = m_InternalReaders.front()->m_TileHeight / static_cast<unsigned int>(vcl_pow(2.0, static_cast<int>(m_ResolutionFactor) )); itk::EncapsulateMetaData<unsigned int>(dict, MetaDataKey::TileHintX, tileHintX); itk::EncapsulateMetaData<unsigned int>(dict, MetaDataKey::TileHintY, tileHintY); m_Spacing[0] *= vcl_pow(2.0, static_cast<double>(m_ResolutionFactor)); m_Spacing[1] *= vcl_pow(2.0, static_cast<double>(m_ResolutionFactor)); // If we have some spacing information we use it // could be needed for other j2k image but not for pleiades // if ( (m_InternalReaders.front()->m_XResolution.front() > 0) && (m_InternalReaders.front()->m_YResolution.front() > 0) ) // { // // We check previously that the X and Y resolution is equal between the components // m_Spacing[0] *= m_InternalReaders.front()->m_XResolution[0]; // m_Spacing[1] *= m_InternalReaders.front()->m_YResolution[0]; // } m_Dimensions[0] = m_InternalReaders.front()->m_Width; m_Dimensions[1] = m_InternalReaders.front()->m_Height; this->SetNumberOfDimensions(2); if (m_Dimensions[0] == 0 || m_Dimensions[1] == 0) { itkExceptionMacro(<< "Image size is null."); } this->SetNumberOfComponents(m_InternalReaders.front()->m_NbOfComponent); // Automatically set the Type to Binary for JPEG2000 data this->SetFileTypeToBinary(); // We check previously that these values are equal between all components unsigned int precision = m_InternalReaders.front()->m_Precision[0]; int isSigned = m_InternalReaders.front()->m_Signed[0]; if (precision <= 8) { m_BytePerPixel = 1; if (isSigned) { SetComponentType(CHAR); } else { SetComponentType(UCHAR); } } else if (precision <= 16) { m_BytePerPixel = 2; if (isSigned) { SetComponentType(SHORT); } else { SetComponentType(USHORT); } } else { m_BytePerPixel = 4; if (isSigned) { SetComponentType(INT); } else { SetComponentType(UINT); } } if (this->GetNumberOfComponents() == 1) { this->SetPixelType(SCALAR); } else { this->SetPixelType(VECTOR); } this->ConfigureCache(); otbMsgDebugMacro(<< "=========================="); otbMsgDebugMacro(<< "ReadImageInformation: "); otbMsgDebugMacro(<< "Tile size (WxH): " << m_InternalReaders.front()->m_TileWidth << " x " << m_InternalReaders.front()->m_TileHeight); otbMsgDebugMacro(<< "Number of tiles (Xdim x Ydim) : " << m_InternalReaders.front()->m_XNbOfTile << " x " << m_InternalReaders.front()->m_YNbOfTile); otbMsgDebugMacro(<< "Precision: " << precision); otbMsgDebugMacro(<< "Signed: " << isSigned); otbMsgDebugMacro(<< "Number of octet per value: " << m_BytePerPixel); otbMsgDebugMacro(<< "=========================="); otbMsgDebugMacro(<< "Driver to read: JPEG2000"); otbMsgDebugMacro(<< " Read file : " << m_FileName); otbMsgDebugMacro(<< " Size : " << m_Dimensions[0] << "," << m_Dimensions[1]); otbMsgDebugMacro(<< " ComponentType : " << this->GetComponentType()); otbMsgDebugMacro(<< " NumberOfComponents : " << this->GetNumberOfComponents()); otbMsgDebugMacro(<< " ComponentSize : " << this->GetComponentSize()); otbMsgDebugMacro(<< " GetPixelSize : " << this->GetPixelSize()); } // Compute the tile index list from the GetRegion std::vector<unsigned int> JPEG2000ImageIO::ComputeTileList() { std::vector<unsigned int> tileVector; // Get nb. of lines and columns of the region to decode unsigned int startX = this->GetIORegion().GetIndex()[0]; unsigned int endX = this->GetIORegion().GetIndex()[0] + this->GetIORegion().GetSize()[0]; unsigned int startY = this->GetIORegion().GetIndex()[1]; unsigned int endY = this->GetIORegion().GetIndex()[1] + this->GetIORegion().GetSize()[1]; // Compute index of tile recover by the decoded area unsigned int tile_size_x = m_InternalReaders.front()->m_TileWidth; unsigned int tile_size_y = m_InternalReaders.front()->m_TileHeight; unsigned int width = m_Dimensions[0]; unsigned int height = m_Dimensions[1]; unsigned int nbOfTileX = m_InternalReaders.front()->m_XNbOfTile; unsigned int nbOfTileY = m_InternalReaders.front()->m_YNbOfTile; unsigned int tilePosX0, tilePosX1; unsigned int tilePosY0, tilePosY1; for (unsigned int itTileY = 0; itTileY < nbOfTileY; itTileY++) { tilePosY0 = uint_ceildivpow2( itTileY*tile_size_y, m_ResolutionFactor ); tilePosY1 = std::min( uint_ceildivpow2( (itTileY+1)*tile_size_y, m_ResolutionFactor ), height ); for (unsigned int itTileX = 0; itTileX < nbOfTileX; itTileX++) { tilePosX0 = uint_ceildivpow2( itTileX*tile_size_x, m_ResolutionFactor ); tilePosX1 = std::min( uint_ceildivpow2( (itTileX+1)*tile_size_x, m_ResolutionFactor ), width); if ( (tilePosX1 - tilePosX0) && (tilePosY1 - tilePosY0) && (tilePosX1 > startX) && (tilePosX0 < endX ) && (tilePosY1 > startY) && (tilePosY0 < endY ) ) tileVector.push_back(itTileX + itTileY * nbOfTileX); } } return tileVector; } /** Compute the offsets in Pixel to move the input buffer from openjpeg * to the corresponding area of the otb output buffer * */ void ComputeOffsets( opj_image_t * currentTile, const itk::ImageIORegion & ioRegion, unsigned int &l_width_src, // Width of the input pixel in nb of pixel unsigned int &l_height_dest, // Height of the area where write in nb of pixel unsigned int &l_width_dest, // Width of the area where write in nb of pixel unsigned int &l_start_offset_dest, // Offset where begin to write the area in the otb buffer in nb of pixel unsigned int &l_start_offset_src // Offset where begin to read the data in the openjpeg decoded data in nb of pixel ) { // Characteristics of the input buffer from openpjeg unsigned int l_x0_src = int_ceildivpow2(currentTile->x0, currentTile->comps->factor); unsigned int l_y0_src = int_ceildivpow2(currentTile->y0, currentTile->comps->factor); unsigned int l_x1_src = int_ceildivpow2(currentTile->x1, currentTile->comps->factor); unsigned int l_y1_src = int_ceildivpow2(currentTile->y1, currentTile->comps->factor); // Size of input buffer from openjpeg l_width_src = l_x1_src - l_x0_src; unsigned int l_height_src = l_y1_src - l_y0_src; // Characteristics of the otb region unsigned int l_x0_dest = ioRegion.GetIndex()[0]; unsigned int l_x1_dest = ioRegion.GetIndex()[0] + ioRegion.GetSize()[0]; unsigned int l_y0_dest = ioRegion.GetIndex()[1]; unsigned int l_y1_dest = ioRegion.GetIndex()[1] + ioRegion.GetSize()[1]; unsigned int l_start_x_dest, l_start_y_dest; unsigned int l_offset_x0_src, l_offset_y0_src; /*-----*/ /* Compute the origin (l_offset_x0_src, l_offset_y0_src ) * of the input buffer (decoded tile) which will be move * in the output buffer. * Compute the area of the output buffer (l_start_x_dest, * l_start_y_dest, l_width_dest, l_height_dest) which will be modified * by this input area. */ if (l_x0_dest < l_x0_src) { l_start_x_dest = l_x0_src - l_x0_dest; l_offset_x0_src = 0; if (l_x1_dest >= l_x1_src) { l_width_dest = l_width_src; } else { l_width_dest = l_x1_dest - l_x0_src; } } else { l_start_x_dest = 0; l_offset_x0_src = l_x0_dest - l_x0_src; if (l_x1_dest >= l_x1_src) { l_width_dest = l_width_src - l_offset_x0_src; } else { l_width_dest = l_x1_dest - l_x0_dest; } } if (l_y0_dest < l_y0_src) { l_start_y_dest = l_y0_src - l_y0_dest; l_offset_y0_src = 0; if (l_y1_dest >= l_y1_src) { l_height_dest = l_height_src; } else { l_height_dest = l_y1_dest - l_y0_src; } } else { l_start_y_dest = 0; l_offset_y0_src = l_y0_dest - l_y0_src; if (l_y1_dest >= l_y1_src) { l_height_dest = l_height_src - l_offset_y0_src; } else { l_height_dest = l_y1_dest - l_y0_dest; } } /*-----*/ /* Compute the input buffer offset */ l_start_offset_src = l_offset_x0_src + l_offset_y0_src * l_width_src; /* Compute the output buffer offset */ l_start_offset_dest = l_start_x_dest + l_start_y_dest * (l_x1_dest - l_x0_dest); /* std::cout << "SRC coordinates: l_start_x_src= " << l_x0_src << ", l_start_y_src= " << l_y0_src << ", l_width_src= " << l_width_src << ", l_height_src= " << l_height_src << std::endl; std::cout << "SRC tile offset: " << l_offset_x0_src << ", " << l_offset_y0_src << std::endl; std::cout << "SRC buffer offset: " << l_start_offset_src << std::endl; std::cout << "DEST coordinates: l_start_x_dest= " << l_start_x_dest << ", l_start_y_dest= " << l_start_y_dest << ", l_width_dest= " << l_width_dest << ", l_height_dest= " << l_height_dest << std::endl; std::cout << "DEST start offset: " << l_start_offset_dest << std::endl; */ } // Not yet implemented bool JPEG2000ImageIO::CanWriteFile(const char* /*filename*/) { return false; } // Not yet implemented void JPEG2000ImageIO::Write(const void* /*buffer*/) { } void JPEG2000ImageIO::WriteImageInformation() { if (m_FileName == "") { itkExceptionMacro(<< "A FileName must be specified."); } if (CanWriteFile(m_FileName.c_str()) == false) { itkExceptionMacro(<< "The file " << m_FileName.c_str() << " is not defined as a JPEG2000 file"); } otbMsgDebugMacro(<< "Driver to write: JPEG2000"); otbMsgDebugMacro(<< " Write file : " << m_FileName); otbMsgDebugMacro(<< " Size : " << m_Dimensions[0] << "," << m_Dimensions[1]); otbMsgDebugMacro(<< " ComponentType : " << this->GetComponentType()); otbMsgDebugMacro(<< " NumberOfComponents : " << this->GetNumberOfComponents()); otbMsgDebugMacro(<< " ComponentSize : " << this->GetComponentSize()); otbMsgDebugMacro(<< " GetPixelSize : " << this->GetPixelSize()); } } // end namespace otb