changeset 128:e8cfda4c8a2f

Sync + support of JPEG2000 lossless images with YBR_RCT photometric interpretation
author Sebastien Jodogne <s.jodogne@gmail.com>
date Fri, 15 Apr 2016 21:44:03 +0200
parents 5754d39b011d
children 2c73a785c08e f6c88aa6efe0
files NEWS Orthanc/Core/ChunkedBuffer.cpp Orthanc/Core/Enumerations.cpp Orthanc/Core/Enumerations.h Orthanc/Core/Images/ImageAccessor.cpp Orthanc/Core/Images/ImageAccessor.h Orthanc/Core/SQLite/Connection.cpp Orthanc/Core/SQLite/StatementReference.cpp Orthanc/Core/Toolbox.cpp Orthanc/Core/Toolbox.h Orthanc/Plugins/Samples/GdcmDecoder/GdcmImageDecoder.cpp Orthanc/Resources/CMake/BoostConfiguration.cmake Orthanc/Resources/CMake/Compiler.cmake Orthanc/Resources/CMake/JsonCppConfiguration.cmake
diffstat 14 files changed, 210 insertions(+), 40 deletions(-) [+]
line wrap: on
line diff
--- a/NEWS	Fri Mar 25 17:38:34 2016 +0100
+++ b/NEWS	Fri Apr 15 21:44:03 2016 +0200
@@ -3,6 +3,7 @@
 
 * Option "EnableGdcm" to replace the built-in decoder of Orthanc with GDCM
 * Fixed rendering of 16bpp images if values are < 0 or >= 32768
+* Decoding of JPEG2000 lossless images with YBR_RCT photometric interpretation
 
 
 Version 2.0 (2015-12-10)
--- a/Orthanc/Core/ChunkedBuffer.cpp	Fri Mar 25 17:38:34 2016 +0100
+++ b/Orthanc/Core/ChunkedBuffer.cpp	Fri Apr 15 21:44:03 2016 +0200
@@ -95,5 +95,6 @@
     }
 
     chunks_.clear();
+    numBytes_ = 0;
   }
 }
--- a/Orthanc/Core/Enumerations.cpp	Fri Mar 25 17:38:34 2016 +0100
+++ b/Orthanc/Core/Enumerations.cpp	Fri Apr 15 21:44:03 2016 +0200
@@ -212,10 +212,10 @@
         return "The specified path does not point to a directory";
 
       case ErrorCode_HttpPortInUse:
-        return "The TCP port of the HTTP server is already in use";
+        return "The TCP port of the HTTP server is privileged or already in use";
 
       case ErrorCode_DicomPortInUse:
-        return "The TCP port of the DICOM server is already in use";
+        return "The TCP port of the DICOM server is privileged or already in use";
 
       case ErrorCode_BadHttpStatusInRest:
         return "This HTTP status is not allowed in a REST API";
@@ -718,6 +718,31 @@
   }
 
 
+  const char* EnumerationToString(PixelFormat format)
+  {
+    switch (format)
+    {
+      case PixelFormat_RGB24:
+        return "RGB24";
+
+      case PixelFormat_RGBA32:
+        return "RGBA32";
+
+      case PixelFormat_Grayscale8:
+        return "Grayscale (unsigned 8bpp)";
+
+      case PixelFormat_Grayscale16:
+        return "Grayscale (unsigned 16bpp)";
+
+      case PixelFormat_SignedGrayscale16:
+        return "Grayscale (signed 16bpp)";
+
+      default:
+        throw OrthancException(ErrorCode_ParameterOutOfRange);
+    }
+  }
+
+
   Encoding StringToEncoding(const char* encoding)
   {
     std::string s(encoding);
--- a/Orthanc/Core/Enumerations.h	Fri Mar 25 17:38:34 2016 +0100
+++ b/Orthanc/Core/Enumerations.h	Fri Apr 15 21:44:03 2016 +0200
@@ -100,8 +100,8 @@
     ErrorCode_DirectoryOverFile = 2000    /*!< The directory to be created is already occupied by a regular file */,
     ErrorCode_FileStorageCannotWrite = 2001    /*!< Unable to create a subdirectory or a file in the file storage */,
     ErrorCode_DirectoryExpected = 2002    /*!< The specified path does not point to a directory */,
-    ErrorCode_HttpPortInUse = 2003    /*!< The TCP port of the HTTP server is already in use */,
-    ErrorCode_DicomPortInUse = 2004    /*!< The TCP port of the DICOM server is already in use */,
+    ErrorCode_HttpPortInUse = 2003    /*!< The TCP port of the HTTP server is privileged or already in use */,
+    ErrorCode_DicomPortInUse = 2004    /*!< The TCP port of the DICOM server is privileged or already in use */,
     ErrorCode_BadHttpStatusInRest = 2005    /*!< This HTTP status is not allowed in a REST API */,
     ErrorCode_RegularFileExpected = 2006    /*!< The specified path does not point to a regular file */,
     ErrorCode_PathToExecutable = 2007    /*!< Unable to get the path to the executable */,
@@ -444,6 +444,8 @@
 
   const char* EnumerationToString(RequestOrigin origin);
 
+  const char* EnumerationToString(PixelFormat format);
+
   Encoding StringToEncoding(const char* encoding);
 
   ResourceType StringToResourceType(const char* type);
--- a/Orthanc/Core/Images/ImageAccessor.cpp	Fri Mar 25 17:38:34 2016 +0100
+++ b/Orthanc/Core/Images/ImageAccessor.cpp	Fri Apr 15 21:44:03 2016 +0200
@@ -121,7 +121,7 @@
   {
     if (buffer_ != NULL)
     {
-      return reinterpret_cast<const uint8_t*>(buffer_) + y * pitch_;
+      return buffer_ + y * pitch_;
     }
     else
     {
@@ -143,7 +143,7 @@
 
     if (buffer_ != NULL)
     {
-      return reinterpret_cast<uint8_t*>(buffer_) + y * pitch_;
+      return buffer_ + y * pitch_;
     }
     else
     {
@@ -174,9 +174,12 @@
     width_ = width;
     height_ = height;
     pitch_ = pitch;
-    buffer_ = const_cast<void*>(buffer);
+    buffer_ = reinterpret_cast<uint8_t*>(const_cast<void*>(buffer));
 
-    assert(GetBytesPerPixel() * width_ <= pitch_);
+    if (GetBytesPerPixel() * width_ > pitch_)
+    {
+      throw OrthancException(ErrorCode_ParameterOutOfRange);
+    }
   }
 
 
@@ -191,9 +194,12 @@
     width_ = width;
     height_ = height;
     pitch_ = pitch;
-    buffer_ = buffer;
+    buffer_ = reinterpret_cast<uint8_t*>(buffer);
 
-    assert(GetBytesPerPixel() * width_ <= pitch_);
+    if (GetBytesPerPixel() * width_ > pitch_)
+    {
+      throw OrthancException(ErrorCode_ParameterOutOfRange);
+    }
   }
 
 
@@ -226,4 +232,43 @@
     buffer.Flatten(target);
   }
 
+
+
+  ImageAccessor ImageAccessor::GetRegion(unsigned int x,
+                                         unsigned int y,
+                                         unsigned int width,
+                                         unsigned int height) const
+  {
+    if (x + width > width_ ||
+        y + height > height_)
+    {
+      throw OrthancException(ErrorCode_ParameterOutOfRange);
+    }
+    
+    ImageAccessor result;
+
+    if (width == 0 ||
+        height == 0)
+    {
+      result.AssignWritable(format_, 0, 0, 0, NULL);
+    }
+    else
+    {
+      uint8_t* p = (buffer_ + 
+                    y * pitch_ + 
+                    x * GetBytesPerPixel());
+
+      if (readOnly_)
+      {
+        result.AssignReadOnly(format_, width, height, pitch_, p);
+      }
+      else
+      {
+        result.AssignWritable(format_, width, height, pitch_, p);
+      }
+    }
+
+    return result;
+  }
+
 }
--- a/Orthanc/Core/Images/ImageAccessor.h	Fri Mar 25 17:38:34 2016 +0100
+++ b/Orthanc/Core/Images/ImageAccessor.h	Fri Apr 15 21:44:03 2016 +0200
@@ -35,6 +35,7 @@
 #include "../Enumerations.h"
 
 #include <string>
+#include <stdint.h>
 
 namespace Orthanc
 {
@@ -46,7 +47,7 @@
     unsigned int width_;
     unsigned int height_;
     unsigned int pitch_;
-    void *buffer_;
+    uint8_t *buffer_;
 
   public:
     ImageAccessor()
@@ -119,5 +120,10 @@
                         void *buffer);
 
     void ToMatlabString(std::string& target) const; 
+
+    ImageAccessor GetRegion(unsigned int x,
+                            unsigned int y,
+                            unsigned int width,
+                            unsigned int height) const;
   };
 }
--- a/Orthanc/Core/SQLite/Connection.cpp	Fri Mar 25 17:38:34 2016 +0100
+++ b/Orthanc/Core/SQLite/Connection.cpp	Fri Apr 15 21:44:03 2016 +0200
@@ -163,7 +163,8 @@
       if (error == SQLITE_ERROR)
       {
 #if ORTHANC_SQLITE_STANDALONE != 1
-        LOG(ERROR) << "SQLite execute error: " << sqlite3_errmsg(db_);
+        LOG(ERROR) << "SQLite execute error: " << sqlite3_errmsg(db_)
+                   << " (" << sqlite3_extended_errcode(db_) << ")";
 #endif
 
         throw OrthancSQLiteException(ErrorCode_SQLiteExecute);
--- a/Orthanc/Core/SQLite/StatementReference.cpp	Fri Mar 25 17:38:34 2016 +0100
+++ b/Orthanc/Core/SQLite/StatementReference.cpp	Fri Apr 15 21:44:03 2016 +0200
@@ -82,7 +82,8 @@
       if (error != SQLITE_OK)
       {
 #if ORTHANC_SQLITE_STANDALONE != 1
-        LOG(ERROR) << "SQLite: " << sqlite3_errmsg(database);
+        LOG(ERROR) << "SQLite: " << sqlite3_errmsg(database)
+                   << " (" << sqlite3_extended_errcode(database) << ")";
 #endif
 
         throw OrthancSQLiteException(ErrorCode_SQLitePrepareStatement);
--- a/Orthanc/Core/Toolbox.cpp	Fri Mar 25 17:38:34 2016 +0100
+++ b/Orthanc/Core/Toolbox.cpp	Fri Apr 15 21:44:03 2016 +0200
@@ -67,7 +67,7 @@
 #include <limits.h>      /* PATH_MAX */
 #endif
 
-#if defined(__linux) || defined(__FreeBSD_kernel__) || defined(__FreeBSD__)
+#if defined(__linux__) || defined(__FreeBSD_kernel__) || defined(__FreeBSD__)
 #include <limits.h>      /* PATH_MAX */
 #include <signal.h>
 #include <unistd.h>
@@ -132,7 +132,7 @@
   {
 #if defined(_WIN32)
     ::Sleep(static_cast<DWORD>(microSeconds / static_cast<uint64_t>(1000)));
-#elif defined(__linux) || defined(__APPLE__) || defined(__FreeBSD_kernel__) || defined(__FreeBSD__)
+#elif defined(__linux__) || defined(__APPLE__) || defined(__FreeBSD_kernel__) || defined(__FreeBSD__)
     usleep(microSeconds);
 #else
 #error Support your platform here
@@ -206,6 +206,17 @@
   }
 
 
+  static std::streamsize GetStreamSize(std::istream& f)
+  {
+    // http://www.cplusplus.com/reference/iostream/istream/tellg/
+    f.seekg(0, std::ios::end);
+    std::streamsize size = f.tellg();
+    f.seekg(0, std::ios::beg);
+
+    return size;
+  }
+
+
   void Toolbox::ReadFile(std::string& content,
                          const std::string& path) 
   {
@@ -222,11 +233,7 @@
       throw OrthancException(ErrorCode_InexistentFile);
     }
 
-    // http://www.cplusplus.com/reference/iostream/istream/tellg/
-    f.seekg(0, std::ios::end);
-    std::streamsize size = f.tellg();
-    f.seekg(0, std::ios::beg);
-
+    std::streamsize size = GetStreamSize(f);
     content.resize(size);
     if (size != 0)
     {
@@ -237,6 +244,51 @@
   }
 
 
+  bool Toolbox::ReadHeader(std::string& header,
+                           const std::string& path,
+                           size_t headerSize)
+  {
+    if (!IsRegularFile(path))
+    {
+      LOG(ERROR) << std::string("The path does not point to a regular file: ") << path;
+      throw OrthancException(ErrorCode_RegularFileExpected);
+    }
+
+    boost::filesystem::ifstream f;
+    f.open(path, std::ifstream::in | std::ifstream::binary);
+    if (!f.good())
+    {
+      throw OrthancException(ErrorCode_InexistentFile);
+    }
+
+    bool full = true;
+
+    {
+      std::streamsize size = GetStreamSize(f);
+      if (size <= 0)
+      {
+        headerSize = 0;
+        full = false;
+      }
+      else if (static_cast<size_t>(size) < headerSize)
+      {
+        headerSize = size;  // Truncate to the size of the file
+        full = false;
+      }
+    }
+
+    header.resize(headerSize);
+    if (headerSize != 0)
+    {
+      f.read(reinterpret_cast<char*>(&header[0]), headerSize);
+    }
+
+    f.close();
+
+    return full;
+  }
+
+
   void Toolbox::WriteFile(const void* content,
                           size_t size,
                           const std::string& path)
@@ -577,7 +629,7 @@
     return std::string(&buffer[0]);
   }
 
-#elif defined(__linux) || defined(__FreeBSD_kernel__) || defined(__FreeBSD__)
+#elif defined(__linux__) || defined(__FreeBSD_kernel__) || defined(__FreeBSD__)
   static std::string GetPathToExecutableInternal()
   {
     std::vector<char> buffer(PATH_MAX + 1);
--- a/Orthanc/Core/Toolbox.h	Fri Mar 25 17:38:34 2016 +0100
+++ b/Orthanc/Core/Toolbox.h	Fri Apr 15 21:44:03 2016 +0200
@@ -66,6 +66,10 @@
     void ReadFile(std::string& content,
                   const std::string& path);
 
+    bool ReadHeader(std::string& header,
+                    const std::string& path,
+                    size_t headerSize);
+
     void WriteFile(const std::string& content,
                    const std::string& path);
 
--- a/Orthanc/Plugins/Samples/GdcmDecoder/GdcmImageDecoder.cpp	Fri Mar 25 17:38:34 2016 +0100
+++ b/Orthanc/Plugins/Samples/GdcmDecoder/GdcmImageDecoder.cpp	Fri Apr 15 21:44:03 2016 +0200
@@ -112,7 +112,9 @@
         else 
         {
           if (image.GetPixelFormat().GetSamplesPerPixel() == 3 &&
-              image.GetPhotometricInterpretation() != gdcm::PhotometricInterpretation::RGB)
+              image.GetPhotometricInterpretation() != gdcm::PhotometricInterpretation::RGB &&
+              (image.GetTransferSyntax() != gdcm::TransferSyntax::JPEG2000Lossless ||
+               image.GetPhotometricInterpretation() != gdcm::PhotometricInterpretation::YBR_RCT))
           {
             photometric_.reset(new gdcm::ImageChangePhotometricInterpretation());
             photometric_->SetInput(image);
@@ -188,7 +190,8 @@
       }
     }
     else if (image.GetPixelFormat().GetSamplesPerPixel() == 3 &&
-             image.GetPhotometricInterpretation() == gdcm::PhotometricInterpretation::RGB)
+             (image.GetPhotometricInterpretation() == gdcm::PhotometricInterpretation::RGB ||
+              image.GetPhotometricInterpretation() == gdcm::PhotometricInterpretation::YBR_RCT))
     {
       switch (image.GetPixelFormat())
       {
--- a/Orthanc/Resources/CMake/BoostConfiguration.cmake	Fri Mar 25 17:38:34 2016 +0100
+++ b/Orthanc/Resources/CMake/BoostConfiguration.cmake	Fri Apr 15 21:44:03 2016 +0200
@@ -8,7 +8,7 @@
   #set(Boost_USE_STATIC_LIBS ON)
 
   find_package(Boost
-    COMPONENTS filesystem thread system date_time regex locale)
+    COMPONENTS filesystem thread system date_time regex locale ${ORTHANC_BOOST_COMPONENTS})
 
   if (NOT Boost_FOUND)
     message(FATAL_ERROR "Unable to locate Boost on this system")
@@ -39,10 +39,10 @@
 
 
 if (BOOST_STATIC)
-  # Parameters for Boost 1.59.0
-  set(BOOST_NAME boost_1_59_0)
-  set(BOOST_BCP_SUFFIX bcpdigest-0.9.5)
-  set(BOOST_MD5 "08abb7cdbea0b380f9ab0d5cce476f12")
+  # Parameters for Boost 1.60.0
+  set(BOOST_NAME boost_1_60_0)
+  set(BOOST_BCP_SUFFIX bcpdigest-1.0.1)
+  set(BOOST_MD5 "0646971514a1e012fbe382c5662a8605")
   set(BOOST_URL "http://www.montefiore.ulg.ac.be/~jodogne/Orthanc/ThirdPartyDownloads/${BOOST_NAME}_${BOOST_BCP_SUFFIX}.tar.gz")
   set(BOOST_FILESYSTEM_SOURCES_DIR "${BOOST_NAME}/libs/filesystem/src") 
   set(BOOST_SOURCES_DIR ${CMAKE_BINARY_DIR}/${BOOST_NAME})
--- a/Orthanc/Resources/CMake/Compiler.cmake	Fri Mar 25 17:38:34 2016 +0100
+++ b/Orthanc/Resources/CMake/Compiler.cmake	Fri Apr 15 21:44:03 2016 +0200
@@ -10,7 +10,7 @@
   set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Wno-long-long -Wno-implicit-function-declaration")  
   # --std=c99 makes libcurl not to compile
   # -pedantic gives a lot of warnings on OpenSSL 
-  set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -pedantic -Wno-long-long -Wno-variadic-macros")
+  set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wno-long-long -Wno-variadic-macros")
 
   if (CMAKE_CROSSCOMPILING)
     # http://stackoverflow.com/a/3543845/881731
@@ -73,6 +73,11 @@
     link_libraries(dl)
   endif()
 
+  CHECK_INCLUDE_FILES(uuid/uuid.h HAVE_UUID_H)
+  if (NOT HAVE_UUID_H)
+    message(FATAL_ERROR "Please install the uuid-dev package")
+  endif()
+
 elseif(${CMAKE_SYSTEM_NAME} STREQUAL "Windows")
   if (MSVC)
     message("MSVC compiler version = " ${MSVC_VERSION} "\n")
@@ -97,6 +102,11 @@
   link_libraries(rpcrt4 ws2_32)
 
   if (CMAKE_COMPILER_IS_GNUCXX)
+    # Some additional C/C++ compiler flags for MinGW
+    SET(MINGW_NO_WARNINGS "-Wno-unused-function -Wno-unused-variable")
+    SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${MINGW_NO_WARNINGS} -Wno-pointer-to-int-cast -Wno-int-to-pointer-cast")
+    SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${MINGW_NO_WARNINGS}")
+
     # This is a patch for MinGW64
     SET(CMAKE_EXE_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,--allow-multiple-definition -static-libgcc -static-libstdc++")
     SET(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,--allow-multiple-definition -static-libgcc -static-libstdc++")
@@ -120,6 +130,11 @@
     )
   link_libraries(iconv)
 
+  CHECK_INCLUDE_FILES(uuid/uuid.h HAVE_UUID_H)
+  if (NOT HAVE_UUID_H)
+    message(FATAL_ERROR "Please install the uuid-dev package")
+  endif()
+
 endif()
 
 
@@ -139,17 +154,6 @@
 endif()
 
 
-if (${CMAKE_SYSTEM_NAME} STREQUAL "Windows")
-  CHECK_INCLUDE_FILES(rpc.h HAVE_UUID_H)
-else()
-  CHECK_INCLUDE_FILES(uuid/uuid.h HAVE_UUID_H)
-endif()
-
-if (NOT HAVE_UUID_H)
-  message(FATAL_ERROR "Please install the uuid-dev package")
-endif()
-
-
 if (STATIC_BUILD)
   add_definitions(-DORTHANC_STATIC=1)
 else()
--- a/Orthanc/Resources/CMake/JsonCppConfiguration.cmake	Fri Mar 25 17:38:34 2016 +0100
+++ b/Orthanc/Resources/CMake/JsonCppConfiguration.cmake	Fri Apr 15 21:44:03 2016 +0200
@@ -32,4 +32,29 @@
     message(FATAL_ERROR "Please install the libjsoncpp-dev package")
   endif()
 
+  # Switch to the C++11 standard if the version of JsonCpp is 1.y.z
+  if (EXISTS ${JSONCPP_INCLUDE_DIR}/json/version.h)
+    file(STRINGS
+      "${JSONCPP_INCLUDE_DIR}/json/version.h" 
+      JSONCPP_VERSION_MAJOR1 REGEX
+      ".*define JSONCPP_VERSION_MAJOR.*")
+
+    if (NOT JSONCPP_VERSION_MAJOR1)
+      message(FATAL_ERROR "Unable to extract the major version of JsonCpp")
+    endif()
+    
+    string(REGEX REPLACE
+      ".*JSONCPP_VERSION_MAJOR.*([0-9]+)$" "\\1" 
+      JSONCPP_VERSION_MAJOR ${JSONCPP_VERSION_MAJOR1})
+    message("JsonCpp major version: ${JSONCPP_VERSION_MAJOR}")
+
+    if (CMAKE_COMPILER_IS_GNUCXX AND 
+        JSONCPP_VERSION_MAJOR GREATER 0)
+      message("Switching to C++11 standard, as version of JsonCpp is >= 1.0.0")
+      set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -Wno-deprecated-declarations")
+    endif()
+  else()
+    message("Unable to detect the major version of JsonCpp, assuming < 1.0.0")
+  endif()
+
 endif()