changeset 6:c02d12eb34d4

added LoadNifti()
author Sebastien Jodogne <s.jodogne@gmail.com>
date Tue, 18 Jul 2023 14:45:20 +0200
parents 1cc024bb662a
children e3e59de705f6
files CMakeLists.txt Resources/CMake/LibNiftiConfiguration.cmake Sources/Plugin.cpp
diffstat 3 files changed, 162 insertions(+), 1 deletions(-) [+]
line wrap: on
line diff
--- a/CMakeLists.txt	Tue Jul 18 12:39:50 2023 +0200
+++ b/CMakeLists.txt	Tue Jul 18 14:45:20 2023 +0200
@@ -62,6 +62,7 @@
 SET(USE_SYSTEM_ORTHANC_SDK ON CACHE BOOL "Use the system version of the Orthanc plugin SDK")
 
 # Parameters to fine-tune linking against system libraries
+set(USE_SYSTEM_NIFTILIB ON CACHE BOOL "Use the system version of niftilib")
 set(USE_SYSTEM_VTK ON CACHE BOOL "Use the system version of VTK")
 
 
@@ -120,6 +121,7 @@
 #####################################################################
 
 include(${CMAKE_SOURCE_DIR}/Resources/CMake/LibVtkConfiguration.cmake)
+include(${CMAKE_SOURCE_DIR}/Resources/CMake/LibNiftiConfiguration.cmake)
 
 add_definitions(
   -DHAS_ORTHANC_EXCEPTION=1
@@ -203,6 +205,7 @@
   ${ORTHANC_DICOM_SOURCES_DEPENDENCIES}
   ${ORTHANC_DICOM_SOURCES_INTERNAL}
   ${ORTHANC_STL_SOURCES}
+  ${NIFTILIB_SOURCES}
   )
 
 DefineSourceBasenameForTarget(OrthancSTL)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Resources/CMake/LibNiftiConfiguration.cmake	Tue Jul 18 14:45:20 2023 +0200
@@ -0,0 +1,60 @@
+# SPDX-FileCopyrightText: 2023 Sebastien Jodogne, UCLouvain, Belgium
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+# STL plugin for Orthanc
+# Copyright (C) 2023 Sebastien Jodogne, UCLouvain, Belgium
+#
+# This program is free software: you can redistribute it and/or
+# modify it under the terms of the GNU General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+
+if (STATIC_BUILD OR NOT USE_SYSTEM_NIFTILIB)
+  set(NIFTILIB_SOURCES_DIR ${CMAKE_BINARY_DIR}/nifti_clib-3.0.0)
+  DownloadPackage(
+    "ee40068103775a181522166e435ee82d"
+    "https://third-party.orthanc-labs.com/nifti_clib-3.0.0.tar.gz"
+    "${NIFTILIB_SOURCES_DIR}")
+
+  include_directories(
+    ${NIFTILIB_SOURCES_DIR}/niftilib
+    ${NIFTILIB_SOURCES_DIR}/znzlib
+    )
+
+  add_definitions(
+    -DHAVE_ZLIB=1
+    )
+
+  set(NIFTILIB_SOURCES
+    ${NIFTILIB_SOURCES_DIR}/niftilib/nifti1_io.c
+    ${NIFTILIB_SOURCES_DIR}/znzlib/znzlib.c
+    )
+
+else()
+  find_path(NIFTILIB_INCLUDE_DIR
+    NAMES nifti1.h
+    PATHS
+    /usr/include
+    /usr/include/nifti
+    /usr/local/include
+    /usr/local/include/nifti
+    )
+
+  check_include_file(${NIFTILIB_INCLUDE_DIR}/nifti1.h HAVE_NIFTILIB_H)
+  if (NOT HAVE_NIFTILIB_H)
+    message(FATAL_ERROR "Please install the libnifti-dev package")
+  endif()
+
+  include_directories(${NIFTILIB_INCLUDE_DIR})
+
+  link_libraries(niftiio znz)
+endif()
--- a/Sources/Plugin.cpp	Tue Jul 18 12:39:50 2023 +0200
+++ b/Sources/Plugin.cpp	Tue Jul 18 14:45:20 2023 +0200
@@ -26,6 +26,7 @@
 
 #include <EmbeddedResources.h>
 
+#include <Compression/GzipCompressor.h>
 #include <ChunkedBuffer.h>
 #include <DicomParsing/FromDcmtkBridge.h>
 #include <DicomParsing/ParsedDicomFile.h>
@@ -47,6 +48,9 @@
 
 #include <boost/thread/shared_mutex.hpp>
 
+#include <nifti1_io.h>
+
+
 // Forward declaration
 void ReadStaticAsset(std::string& target,
                      const std::string& path);
@@ -1111,10 +1115,15 @@
   padding->SetInputData(resize->GetOutput());
   padding->Update();
 
+  double range[2];
+  padding->GetOutput()->GetScalarRange(range);
+
+  const double isoValue = (range[0] + range[1]) / 2.0;
+
   vtkNew<vtkMarchingCubes> surface;
   surface->SetInputData(padding->GetOutput());
   surface->ComputeNormalsOn();
-  surface->SetValue(0, 128 /*isoValue*/);
+  surface->SetValue(0, isoValue);
   surface->Update();
 
   if (smooth)
@@ -1397,6 +1406,95 @@
 }
 
 
+
+class NiftiHeader : public boost::noncopyable
+{
+private:
+  nifti_image* image_;
+
+public:
+  NiftiHeader(const std::string& nifti)
+  {
+    nifti_1_header header;
+    if (nifti.size() < sizeof(header))
+    {
+      throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat);
+    }
+
+    memcpy(&header, nifti.c_str(), sizeof(header));
+    if (!nifti_hdr_looks_good(&header))
+    {
+      throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat);
+    }
+
+    image_ = nifti_convert_nhdr2nim(header, "dummy_filename");
+    if (image_ == NULL)
+    {
+      throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat);
+    }
+  }
+
+  ~NiftiHeader()
+  {
+    nifti_image_free(image_);
+  }
+
+  const nifti_image& GetInfo() const
+  {
+    assert(image_ != NULL);
+    return *image_;
+  }
+};
+
+
+static void LoadNifti(vtkImageData* volume,
+                      std::string& nifti)
+{
+  if (volume == NULL)
+  {
+    throw Orthanc::OrthancException(Orthanc::ErrorCode_NullPointer);
+  }
+
+  const uint8_t* p = reinterpret_cast<const uint8_t*>(nifti.c_str());
+
+  if (nifti.size() >= 2 &&
+      p[0] == 0x1f &&
+      p[1] == 0x8b)
+  {
+    Orthanc::GzipCompressor compressor;
+    std::string uncompressed;
+    Orthanc::IBufferCompressor::Uncompress(uncompressed, compressor, nifti);
+    nifti.swap(uncompressed);
+  }
+
+  NiftiHeader header(nifti);
+
+  if (header.GetInfo().ndim != 3)
+  {
+    throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat,
+                                    "Only 3D NIfTI volumes are allowed");
+  }
+
+  if (header.GetInfo().datatype != DT_UNSIGNED_CHAR)
+  {
+    throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented);
+  }
+
+  assert(static_cast<int>(header.GetInfo().nvox) == header.GetInfo().nx * header.GetInfo().ny * header.GetInfo().nz);
+
+  const size_t pixelDataOffset = sizeof(nifti_1_header) + 4 /* extension */;
+
+  if (nifti.size() != pixelDataOffset + header.GetInfo().nvox)
+  {
+    throw Orthanc::OrthancException(Orthanc::ErrorCode_CorruptedFile);
+  }
+
+  volume->SetDimensions(header.GetInfo().nx, header.GetInfo().ny, header.GetInfo().nz);
+  volume->AllocateScalars(VTK_UNSIGNED_CHAR, 1);
+  volume->SetSpacing(header.GetInfo().dx, header.GetInfo().dy, header.GetInfo().dz);
+  memcpy(volume->GetScalarPointer(), &nifti[pixelDataOffset], header.GetInfo().nvox * sizeof(unsigned char));
+}
+
 extern "C"
 {
   ORTHANC_PLUGINS_API int32_t OrthancPluginInitialize(OrthancPluginContext* context)