From fb81ec608192a143e3c7aa9af2bbd5f8571e0052 Mon Sep 17 00:00:00 2001
From: Mickael Savinaud <mickael.savinaud@c-s.fr>
Date: Tue, 22 Nov 2011 18:41:31 +0100
Subject: [PATCH] ENH: give the possibility to set the cache size of
 JPEG2000ImageIO::Read function and tests

---
 Code/IO/otbImageFileReader.txx                |  2 +
 Code/IO/otbJPEG2000ImageIO.cxx                | 95 +++++++++++++++++--
 Code/IO/otbJPEG2000ImageIO.h                  |  7 ++
 Testing/Code/IO/CMakeLists.txt                | 42 +++++++-
 .../IO/otbGenerateClassicalQLWithJPEG2000.cxx | 65 +++++++++++++
 Testing/Code/IO/otbIOTests13.cxx              |  1 +
 6 files changed, 205 insertions(+), 7 deletions(-)
 create mode 100644 Testing/Code/IO/otbGenerateClassicalQLWithJPEG2000.cxx

diff --git a/Code/IO/otbImageFileReader.txx b/Code/IO/otbImageFileReader.txx
index 397cb80774..9062633ae1 100644
--- a/Code/IO/otbImageFileReader.txx
+++ b/Code/IO/otbImageFileReader.txx
@@ -309,6 +309,8 @@ ImageFileReader<TOutputImage>
 
       // Pass the Resolution Factor
       imageIO->SetResolutionFactor(m_AdditionalNumber);
+      if (!imageIO->GetCacheSizeInByte())
+        imageIO->SetCacheSizeInByte(135000000); // Useful for Pleiades Images 135Mb => 4tiles in cache
       }
 
 
diff --git a/Code/IO/otbJPEG2000ImageIO.cxx b/Code/IO/otbJPEG2000ImageIO.cxx
index c01e00c7f7..186d391fb7 100644
--- a/Code/IO/otbJPEG2000ImageIO.cxx
+++ b/Code/IO/otbJPEG2000ImageIO.cxx
@@ -395,7 +395,7 @@ public:
   typedef std::pair<unsigned int, opj_image_t *> CachedTileType;
   typedef std::deque<CachedTileType> TileCacheType;
 
-  /** Get a tile in cache, return null if cache does not cotain the
+  /** Get a tile in cache, return null if cache does not contain the
   tile */
   opj_image_t * GetTile(unsigned int tileIndex);
 
@@ -405,13 +405,59 @@ public:
   /** 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;
+      m_CacheSizeInTiles = m_CacheSizeInByte / m_TileCacheSizeInByte;
+      }
+  };
+
 private:
   TileCacheType m_Cache;
   unsigned int m_CacheSizeInTiles;
+  unsigned int m_CacheSizeInByte;
+  unsigned int m_TileCacheSizeInByte;
+  bool m_IsReady;
+
+  /** 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)
+JPEG2000TileCache::JPEG2000TileCache() : m_Cache(), m_CacheSizeInTiles(4), m_CacheSizeInByte(0), m_IsReady(false)
 {}
 
 JPEG2000TileCache::~JPEG2000TileCache()
@@ -419,6 +465,20 @@ 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
+                              * precision
+                              / vcl_pow(2, 2*static_cast<double>(resolution) ) ;
+
+  otbMsgDevMacro( << "m_TileCacheSizeInByte = " << m_TileCacheSizeInByte );
+}
+
 void JPEG2000TileCache::Clear()
 {
   for(TileCacheType::iterator it = m_Cache.begin();
@@ -434,6 +494,11 @@ void JPEG2000TileCache::Clear()
     erasedTile.second = NULL;
     }
   m_Cache.clear();
+
+  m_CacheSizeInTiles = 4;
+  m_CacheSizeInByte = 0;
+
+  m_IsReady = false;
 }
 
 
@@ -461,6 +526,7 @@ void JPEG2000TileCache::AddTile(unsigned int tileIndex, opj_image_t * tileData)
       return;
       }
     }
+
   if(m_Cache.size() >= m_CacheSizeInTiles)
     {
     CachedTileType erasedTile = *m_Cache.begin();
@@ -474,6 +540,7 @@ void JPEG2000TileCache::AddTile(unsigned int tileIndex, opj_image_t * tileData)
 
     m_Cache.pop_front();
     }
+
   m_Cache.push_back(CachedTileType(tileIndex, tileData));
 }
 
@@ -503,6 +570,8 @@ JPEG2000ImageIO::JPEG2000ImageIO()
 
   m_BytePerPixel = 1;
   m_ResolutionFactor = 0; // Full resolution by default
+
+  m_CacheSizeInByte = 0; // By default no cache
 }
 
 JPEG2000ImageIO::~JPEG2000ImageIO()
@@ -708,11 +777,15 @@ void JPEG2000ImageIO::Read(void* buffer)
     }
   
 
-  // Now, do cache book-keeping
-  for (std::vector<JPEG2000TileCache::CachedTileType>::iterator itTile = toReadTiles.begin(); itTile < toReadTiles.end(); ++itTile)
+  // Now, do cache book-keeping if necessary
+  if (m_TileCache->GetCacheSizeInTiles() != 0)
     {
-    m_TileCache->AddTile(itTile->first, itTile->second);
+    for (std::vector<JPEG2000TileCache::CachedTileType>::iterator itTile = toReadTiles.begin(); itTile < toReadTiles.end(); ++itTile)
+      {
+      m_TileCache->AddTile(itTile->first, itTile->second);
+      }
     }
+
   chrono.Stop();
   otbMsgDevMacro( << "JPEG2000ImageIO::Read took " << chrono.GetTotal() << " sec");
 
@@ -953,7 +1026,6 @@ void JPEG2000ImageIO::ReadImageInformation()
       }
     }
 
-
   m_Spacing[0] = 1.0 / vcl_pow(2.0, static_cast<double>(m_ResolutionFactor));
   m_Spacing[1] = 1.0 / vcl_pow(2.0, static_cast<double>(m_ResolutionFactor));
 
@@ -1033,6 +1105,17 @@ void JPEG2000ImageIO::ReadImageInformation()
     this->SetPixelType(VECTOR);
     }
 
+  // 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);
+
   otbMsgDebugMacro(<< "==========================");
   otbMsgDebugMacro(<< "ReadImageInformation: ");
   otbMsgDebugMacro(<< "Tile size (WxH): " << m_InternalReaders.front()->m_TileWidth << " x "
diff --git a/Code/IO/otbJPEG2000ImageIO.h b/Code/IO/otbJPEG2000ImageIO.h
index 42cd747c4b..1115ddfd4e 100644
--- a/Code/IO/otbJPEG2000ImageIO.h
+++ b/Code/IO/otbJPEG2000ImageIO.h
@@ -105,6 +105,9 @@ public:
   itkSetMacro(ResolutionFactor, unsigned int);
   itkGetMacro(ResolutionFactor, unsigned int);
 
+  itkSetMacro(CacheSizeInByte, unsigned int);
+  itkGetMacro(CacheSizeInByte, unsigned int);
+
 protected:
   /** Constructor.*/
   JPEG2000ImageIO();
@@ -128,8 +131,12 @@ private:
   /** pixel nb of octets */
   unsigned int m_BytePerPixel;
 
+  /** Resolution factor*/
   unsigned int m_ResolutionFactor;
 
+  /** Size of the cache used to reduce number of decoding operations*/
+  unsigned int m_CacheSizeInByte;
+
   /** Load data from a tile into the buffer. 2nd argument is a
 * pointer to opj_image_t, hidden in void * to avoid forward declaration. */
   void LoadTileData(void * buffer, void * tile);
diff --git a/Testing/Code/IO/CMakeLists.txt b/Testing/Code/IO/CMakeLists.txt
index 3230f5e155..03e6125b4b 100644
--- a/Testing/Code/IO/CMakeLists.txt
+++ b/Testing/Code/IO/CMakeLists.txt
@@ -2135,6 +2135,46 @@ ADD_TEST(ioTvMultiChannelROI_JPEG2000_2_TIF_PLEIADES_res5_FullQuicklook ${COMMON
          )
 ENDIF(OTB_DATA_USE_LARGEINPUT)
 
+# Test the possibility to modify the size of cache used to reduce the decoding operations
+# O tile in the cache
+ADD_TEST(ioTvJPEG2000ImageIO_CacheSize_500 ${IO_TESTS13}
+   --compare-image ${EPSILON_9}   
+         ${BASELINE}/ioClassicalQLJPEG2K_bretagne.tif
+         ${TEMP}/ioClassicalQLJPEG2K_bretagne_500.tif
+         otbGenerateClassicalQLWithJPEG2000
+         ${INPUTDATA}/bretagne.j2k
+         ${TEMP}/ioClassicalQLJPEG2K_bretagne_500.tif
+         500)
+
+# 1 tile in the cache
+ADD_TEST(ioTvJPEG2000ImageIO_CacheSize_1000 ${IO_TESTS13}
+   --compare-image ${EPSILON_9}   
+         ${BASELINE}/ioClassicalQLJPEG2K_bretagne.tif
+         ${TEMP}/ioClassicalQLJPEG2K_bretagne_1000.tif
+         otbGenerateClassicalQLWithJPEG2000
+         ${INPUTDATA}/bretagne.j2k
+         ${TEMP}/ioClassicalQLJPEG2K_bretagne_1000.tif
+         1000)
+
+# 5 tiles in the cache         
+ADD_TEST(ioTvJPEG2000ImageIO_CacheSize_5000 ${IO_TESTS13}
+   --compare-image ${EPSILON_9}   
+         ${BASELINE}/ioClassicalQLJPEG2K_bretagne.tif
+         ${TEMP}/ioClassicalQLJPEG2K_bretagne_5000.tif
+         otbGenerateClassicalQLWithJPEG2000
+         ${INPUTDATA}/bretagne.j2k
+         ${TEMP}/ioClassicalQLJPEG2K_bretagne_5000.tif
+         5000)
+
+# 135Mb in the cache          
+ADD_TEST(ioTvJPEG2000ImageIO_CacheSize_25000 ${IO_TESTS13}
+   --compare-image ${EPSILON_9}   
+         ${BASELINE}/ioClassicalQLJPEG2K_bretagne.tif
+         ${TEMP}/ioClassicalQLJPEG2K_bretagne_25000.tif
+         otbGenerateClassicalQLWithJPEG2000
+         ${INPUTDATA}/bretagne.j2k
+         ${TEMP}/ioClassicalQLJPEG2K_bretagne_25000.tif)
+
 ENDIF(OTB_COMPILE_JPEG2000)
 
 # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -3521,6 +3561,7 @@ otbJPEG2000ImageIOTestCanRead.cxx
 otbJPEG2000ImageIOTestCanWrite.cxx
 otbVectorImageTest.cxx
 otbMultiReadingInfo.cxx
+otbGenerateClassicalQLWithJPEG2000.cxx
 )
 ENDIF(OTB_COMPILE_JPEG2000)
 
@@ -3665,7 +3706,6 @@ OTB_ADD_EXECUTABLE(otbIOTests10 "${BasicIO_SRCS10}" "OTBIO;OTBTesting")
 OTB_ADD_EXECUTABLE(otbIOTests11 "${BasicIO_SRCS11}" "OTBIO;OTBTesting")
 OTB_ADD_EXECUTABLE(otbIOTests12 "${BasicIO_SRCS12}" "OTBIO;OTBTesting")
 
-# Case for releae 2.2.1
 IF(OTB_COMPILE_JPEG2000)
    IF(NOT BUILD_SHARED_LIBS)
       ADD_DEFINITIONS(-DOPJ_STATIC)
diff --git a/Testing/Code/IO/otbGenerateClassicalQLWithJPEG2000.cxx b/Testing/Code/IO/otbGenerateClassicalQLWithJPEG2000.cxx
new file mode 100644
index 0000000000..9b7e184faa
--- /dev/null
+++ b/Testing/Code/IO/otbGenerateClassicalQLWithJPEG2000.cxx
@@ -0,0 +1,65 @@
+/*=========================================================================
+
+  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 "otbImageFileReader.h"
+#include "otbImageFileWriter.h"
+#include "otbVectorImage.h"
+#include "otbStreamingShrinkImageFilter.h"
+#include "otbJPEG2000ImageIO.h"
+
+int otbGenerateClassicalQLWithJPEG2000(int argc, char * argv[])
+{
+  char *             inputFilename = argv[1];
+  char *             outputFilename = argv[2];
+  unsigned int       cacheSize = 0;
+
+  if (argc == 4)
+    cacheSize = atoi(argv[3]) * 1000;
+
+  const unsigned int Dimension = 2;
+
+  typedef unsigned int                                          PixelType;
+  typedef otb::VectorImage<PixelType, Dimension>                ImageType;
+  typedef otb::ImageFileReader<ImageType>                       ReaderType;
+  typedef otb::ImageFileWriter<ImageType>                       WriterType;
+  typedef otb::StreamingShrinkImageFilter<ImageType, ImageType> ShrinkType;
+
+  ReaderType::Pointer reader = ReaderType::New();
+  ShrinkType::Pointer shrink = ShrinkType::New();
+  WriterType::Pointer writer = WriterType::New();
+
+  reader->SetFileName(inputFilename);
+
+  otb::JPEG2000ImageIO::Pointer imageIO = otb::JPEG2000ImageIO::New();
+  reader->SetImageIO(imageIO);
+
+  imageIO->SetCacheSizeInByte(cacheSize);
+
+  reader->GenerateOutputInformation();
+
+  writer->SetFileName(outputFilename);
+  shrink->SetShrinkFactor(10);
+
+  shrink->SetInput(reader->GetOutput());
+  shrink->Update();
+
+  writer->SetInput(shrink->GetOutput());
+
+  writer->Update();
+
+  return EXIT_SUCCESS;
+}
diff --git a/Testing/Code/IO/otbIOTests13.cxx b/Testing/Code/IO/otbIOTests13.cxx
index f5eb4e8d81..eb2d57286b 100644
--- a/Testing/Code/IO/otbIOTests13.cxx
+++ b/Testing/Code/IO/otbIOTests13.cxx
@@ -28,4 +28,5 @@ void RegisterTests()
   REGISTER_TEST(otbJPEG2000ImageIOTestCanWrite);
   REGISTER_TEST(otbMultiResolutionReadingInfo);
   REGISTER_TEST(otbVectorImageTest);
+  REGISTER_TEST(otbGenerateClassicalQLWithJPEG2000);
 }
-- 
GitLab