-
Guillaume Pasero authoredGuillaume Pasero authored
otbJPEG2000ImageIO.cxx 52.23 KiB
/*=========================================================================
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