diff --git a/CMake/Findlz4.cmake b/CMake/Findlz4.cmake new file mode 100644 index 0000000000..a79f05ade4 --- /dev/null +++ b/CMake/Findlz4.cmake @@ -0,0 +1,35 @@ +find_path(lz4_INCLUDE_DIR lz4.h + PATHS ${MITK_EXTERNAL_PROJECT_PREFIX} + PATH_SUFFIXES include + NO_DEFAULT_PATH +) +mark_as_advanced(lz4_INCLUDE_DIR) + + +find_library(lz4_LIBRARY_RELEASE lz4 + PATHS ${MITK_EXTERNAL_PROJECT_PREFIX} + PATH_SUFFIXES lib + NO_DEFAULT_PATH) +mark_as_advanced(lz4_LIBRARY_RELEASE) + + +find_library(lz4_LIBRARY_DEBUG lz4d + PATHS ${MITK_EXTERNAL_PROJECT_PREFIX} + PATH_SUFFIXES lib + NO_DEFAULT_PATH) +mark_as_advanced(lz4_LIBRARY_DEBUG) + +set(lz4_LIBRARIES) + +if(lz4_LIBRARY_RELEASE) + list(APPEND lz4_LIBRARIES optimized ${lz4_LIBRARY_RELEASE}) +endif() + +if(lz4_LIBRARY_DEBUG) + list(APPEND lz4_LIBRARIES debug ${lz4_LIBRARY_DEBUG}) +endif() + +find_package_handle_standard_args(lz4 + FOUND_VAR lz4_FOUND + REQUIRED_VARS lz4_INCLUDE_DIR lz4_LIBRARIES +) diff --git a/CMake/PackageDepends/MITK_lz4_Config.cmake b/CMake/PackageDepends/MITK_lz4_Config.cmake new file mode 100644 index 0000000000..b9b2b8ff86 --- /dev/null +++ b/CMake/PackageDepends/MITK_lz4_Config.cmake @@ -0,0 +1,2 @@ +list(APPEND ALL_INCLUDE_DIRECTORIES ${lz4_INCLUDE_DIR}) +list(APPEND ALL_LIBRARIES ${lz4_LIBRARIES}) diff --git a/CMakeExternals/ExternalProjectList.cmake b/CMakeExternals/ExternalProjectList.cmake index 67c827c45b..e275f8b0f4 100644 --- a/CMakeExternals/ExternalProjectList.cmake +++ b/CMakeExternals/ExternalProjectList.cmake @@ -1,31 +1,32 @@ mitkFunctionAddExternalProject(NAME Poco ON COMPONENTS Foundation Net Util XML Zip) mitkFunctionAddExternalProject(NAME DCMTK ON DOC "EXPERIMENTAL, superbuild only: Use DCMTK in MITK") mitkFunctionAddExternalProject(NAME OpenIGTLink OFF) mitkFunctionAddExternalProject(NAME tinyxml2 ON ADVANCED) mitkFunctionAddExternalProject(NAME GDCM ON ADVANCED) mitkFunctionAddExternalProject(NAME Eigen ON ADVANCED DOC "Use the Eigen library") mitkFunctionAddExternalProject(NAME ANN ON ADVANCED DOC "Use Approximate Nearest Neighbor Library") mitkFunctionAddExternalProject(NAME CppUnit ON ADVANCED DOC "Use CppUnit for unit tests") mitkFunctionAddExternalProject(NAME HDF5 ON ADVANCED) mitkFunctionAddExternalProject(NAME OpenCV OFF) mitkFunctionAddExternalProject(NAME Vigra OFF DEPENDS HDF5) mitkFunctionAddExternalProject(NAME ITK ON NO_CACHE DEPENDS HDF5) mitkFunctionAddExternalProject(NAME VTK ON NO_CACHE) mitkFunctionAddExternalProject(NAME Boost ON NO_CACHE) mitkFunctionAddExternalProject(NAME ZLIB OFF ADVANCED) +mitkFunctionAddExternalProject(NAME lz4 ON ADVANCED) mitkFunctionAddExternalProject(NAME cpprestsdk OFF DEPENDS Boost ZLIB ADVANCED) mitkFunctionAddExternalProject(NAME OpenMesh OFF) mitkFunctionAddExternalProject(NAME CTK ON DEPENDS Qt5 DCMTK DOC "Use CTK in MITK") mitkFunctionAddExternalProject(NAME DCMQI ON DEPENDS DCMTK ITK DOC "Use dcmqi in MITK") mitkFunctionAddExternalProject(NAME MatchPoint OFF ADVANCED DEPENDS ITK DOC "Use the MatchPoint translation image registration library") if(MITK_USE_Qt5) mitkFunctionAddExternalProject(NAME Qwt ON ADVANCED DEPENDS Qt5) endif() if(UNIX AND NOT APPLE) mitkFunctionAddExternalProject(NAME PCRE OFF ADVANCED NO_PACKAGE) mitkFunctionAddExternalProject(NAME SWIG OFF ADVANCED NO_PACKAGE DEPENDS PCRE) elseif(WIN32) mitkFunctionAddExternalProject(NAME SWIG OFF ADVANCED NO_PACKAGE) endif() diff --git a/CMakeExternals/lz4.cmake b/CMakeExternals/lz4.cmake new file mode 100644 index 0000000000..84d36c855a --- /dev/null +++ b/CMakeExternals/lz4.cmake @@ -0,0 +1,50 @@ +#----------------------------------------------------------------------------- +# lz4 +#----------------------------------------------------------------------------- + +if(MITK_USE_lz4) + # Sanity checks + if(DEFINED lz4_DIR AND NOT EXISTS "${lz4_DIR}") + message(FATAL_ERROR "lz4_DIR variable is defined but corresponds to non-existing directory") + endif() + + set(proj lz4) + set(proj_DEPENDENCIES ) + set(lz4_DEPENDS ${proj}) + + if(NOT DEFINED lz4_DIR) + + set(additional_args ) + + if(CTEST_USE_LAUNCHERS) + list(APPEND additional_args + "-DCMAKE_PROJECT_${proj}_INCLUDE:FILEPATH=${CMAKE_ROOT}/Modules/CTestUseLaunchers.cmake" + ) + endif() + + ExternalProject_Add(${proj} + LIST_SEPARATOR ${sep} + GIT_REPOSITORY https://github.com/lz4/lz4.git + GIT_TAG v1.9.3 + SOURCE_SUBDIR build/cmake + CMAKE_GENERATOR ${gen} + CMAKE_GENERATOR_PLATFORM ${gen_platform} + CMAKE_ARGS + ${ep_common_args} + ${additional_args} + CMAKE_CACHE_ARGS + ${ep_common_cache_args} + -DLZ4_BUILD_CLI:BOOL=OFF + -DLZ4_BUILD_LEGACY_LZ4C:BOOL=OFF + CMAKE_CACHE_DEFAULT_ARGS + ${ep_common_cache_default_args} + DEPENDS ${proj_DEPENDENCIES} + ) + + set(lz4_DIR "${ep_prefix}") + mitkFunctionInstallExternalCMakeProject(${proj}) + + else() + mitkMacroEmptyExternalProject(${proj} "${proj_DEPENDENCIES}") + endif() +endif() diff --git a/Documentation/Doxygen/3-DeveloperManual/Starting/SettingUpMITK/ThirdPartyLibs.dox b/Documentation/Doxygen/3-DeveloperManual/Starting/SettingUpMITK/ThirdPartyLibs.dox index 2b93804d69..5d760fedaa 100644 --- a/Documentation/Doxygen/3-DeveloperManual/Starting/SettingUpMITK/ThirdPartyLibs.dox +++ b/Documentation/Doxygen/3-DeveloperManual/Starting/SettingUpMITK/ThirdPartyLibs.dox @@ -1,107 +1,111 @@ /** \page thirdpartylibs Third-party libraries The following third-party libraries can be used with MITK by default and can, in part, be automatically downloaded during superbuild. \par ANN https://www.cs.umd.edu/~mount/ANN/ \par Boost https://www.boost.org/ \par C++ REST SDK https://github.com/Microsoft/cpprestsdk/ \par CppUnit https://sourceforge.net/projects/cppunit/ \par CTK https://commontk.org/ \par DCMTK https://dicom.offis.de/dcmtk \par Eigen http://eigen.tuxfamily.org/index.php?title=Main_Page \par GDCM https://gdcm.sourceforge.net/ \par HDF5 https://support.hdfgroup.org/HDF5/ \par ITK https://itk.org/ +\par lz4 + +https://github.com/lz4/lz4 + \par MatchPoint https://www.dkfz.de/en/sidt/projects/MatchPoint/info.html \par OpenCL https://www.khronos.org/opencl/ \par OpenCV https://opencv.org/ \par OpenIGTLink http://openigtlink.org/ \par OpenMesh https://www.openmesh.org/ \par PCRE https://www.pcre.org/ \par POCO https://pocoproject.org/ \par Python https://www.python.org/ \par Qt https://www.qt.io/ \par Qwt http://qwt.sourceforge.net/ \par SWIG http://www.swig.org/ \par TinyXML-2 http://www.grinninglizard.com/tinyxml2/ \par VIGRA https://ukoethe.github.io/vigra/ \par VTK https://vtk.org/ \par zlib https://zlib.net/ For copyright information on any of the above toolkits see the corresponding home page or the corresponding source folder. */ diff --git a/Modules/DataTypesExt/CMakeLists.txt b/Modules/DataTypesExt/CMakeLists.txt index 841888032d..fd7c68b54a 100644 --- a/Modules/DataTypesExt/CMakeLists.txt +++ b/Modules/DataTypesExt/CMakeLists.txt @@ -1,6 +1,6 @@ mitk_create_module( DEPENDS MitkCore - PACKAGE_DEPENDS PRIVATE ITK|ZLIB + PACKAGE_DEPENDS lz4 ) add_subdirectory(test) diff --git a/Modules/DataTypesExt/include/mitkApplyDiffImageOperation.h b/Modules/DataTypesExt/include/mitkApplyDiffImageOperation.h index ee25cbea9b..49e3a2f539 100644 --- a/Modules/DataTypesExt/include/mitkApplyDiffImageOperation.h +++ b/Modules/DataTypesExt/include/mitkApplyDiffImageOperation.h @@ -1,88 +1,88 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #ifndef mitkApplyDiffImageIIncluded #define mitkApplyDiffImageIIncluded #include "MitkDataTypesExtExports.h" #include "mitkCompressedImageContainer.h" #include "mitkOperation.h" namespace mitk { /** @brief Operation, that holds information about some image difference This class stores undo information for DiffImageApplier. Instances of this class are created e.g. by OverwriteSliceImageFilter. This works only for images with 1 channel (gray scale images, no color images). ApplyDiffImageOperation of course refers to an image (a segmentation usually). The refered image is observed for itk::DeleteEvent, because there is no SmartPointer used to keep the image alive -- the purpose of this class is undo and the undo stack should not keep things alive forever. - To save memory, zlib compression is used via CompressedImageContainer. + To save memory, compression is used via CompressedImageContainer. @ingroup Undo @ingroup ToolManagerEtAl */ class MITKDATATYPESEXT_EXPORT ApplyDiffImageOperation : public Operation { protected: void OnImageDeleted(); Image *m_Image; unsigned int m_SliceIndex; unsigned int m_SliceDimension; unsigned int m_TimeStep; double m_Factor; bool m_ImageStillValid; unsigned long m_DeleteTag; - CompressedImageContainer::Pointer zlibContainer; + CompressedImageContainer m_CompressedImageContainer; public: /** Pass only 2D images here. \param operationType \param image \param diffImage \param timeStep \param sliceIndex brief Which slice to extract (first one has index 0). \param sliceDimension Number of the dimension which is constant for all pixels of the desired slice (e.g. 0 for axial) */ ApplyDiffImageOperation(OperationType operationType, Image *image, Image *diffImage, unsigned int timeStep = 0, unsigned int sliceDimension = 2, unsigned int sliceIndex = 0); ~ApplyDiffImageOperation() override; // Unfortunately cannot use itkGet/SetMacros here, since Operation does not inherit itk::Object unsigned int GetSliceIndex() { return m_SliceIndex; } unsigned int GetSliceDimension() { return m_SliceDimension; } unsigned int GetTimeStep() { return m_TimeStep; } void SetFactor(double factor) { m_Factor = factor; } double GetFactor() { return m_Factor; } Image *GetImage() { return m_Image; } Image::Pointer GetDiffImage(); bool IsImageStillValid() { return m_ImageStillValid; } }; } // namespace mitk #endif diff --git a/Modules/DataTypesExt/include/mitkCompressedImageContainer.h b/Modules/DataTypesExt/include/mitkCompressedImageContainer.h index 32ead859c3..7c19a73086 100644 --- a/Modules/DataTypesExt/include/mitkCompressedImageContainer.h +++ b/Modules/DataTypesExt/include/mitkCompressedImageContainer.h @@ -1,80 +1,51 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ -#ifndef mitkCompressedImageContainer_h_Included -#define mitkCompressedImageContainer_h_Included +#ifndef mitkCompressedImageContainer_h +#define mitkCompressedImageContainer_h -#include "MitkDataTypesExtExports.h" -#include "mitkCommon.h" -#include "mitkGeometry3D.h" -#include "mitkImage.h" -#include "mitkImageDataItem.h" - -#include - -#include +#include +#include +#include +#include +#include namespace mitk { - /** - \brief Holds one (compressed) mitk::Image - - Uses zlib to compress the data of an mitk::Image. - - $Author$ - */ - class MITKDATATYPESEXT_EXPORT CompressedImageContainer : public itk::Object + class MITKDATATYPESEXT_EXPORT CompressedImageContainer { public: - mitkClassMacroItkParent(CompressedImageContainer, itk::Object); - itkFactorylessNewMacro(Self); - itkCloneMacro(Self); + CompressedImageContainer(); + ~CompressedImageContainer(); - /** - * \brief Creates a compressed version of the image. - * - * Will not hold any further SmartPointers to the image. - * - */ - void SetImage(const Image *); + CompressedImageContainer(const CompressedImageContainer&) = delete; + CompressedImageContainer& operator=(const CompressedImageContainer&) = delete; - /** - * \brief Creates a full mitk::Image from its compressed version. - * - * This Method hold no buffer, so the uncompression algorithm will be - * executed every time you call this method. Don't overdo it. - * - */ - Image::Pointer GetImage() const; + void CompressImage(const Image* image); + Image::Pointer DecompressImage() const; - protected: - CompressedImageContainer(); // purposely hidden - ~CompressedImageContainer() override; + private: + using CompressedSliceData = std::pair; + using CompressedTimeStepData = std::vector; + using CompressedImageData = std::vector; - PixelType *m_PixelType; + void ClearCompressedImageData(); - unsigned int m_ImageDimension; - std::vector m_ImageDimensions; + CompressedImageData m_CompressedImageData; - unsigned long m_OneTimeStepImageSizeInBytes; - - unsigned int m_NumberOfTimeSteps; - - /// one for each timestep. first = pointer to compressed data; second = size of buffer in bytes - std::vector> m_ByteBuffers; - - BaseGeometry::Pointer m_ImageGeometry; + std::unique_ptr m_PixelType; + TimeGeometry::Pointer m_TimeGeometry; + std::array m_SliceDimensions; }; - -} // namespace +} #endif diff --git a/Modules/DataTypesExt/src/mitkApplyDiffImageOperation.cpp b/Modules/DataTypesExt/src/mitkApplyDiffImageOperation.cpp index 30c99556ff..e17c5bff75 100644 --- a/Modules/DataTypesExt/src/mitkApplyDiffImageOperation.cpp +++ b/Modules/DataTypesExt/src/mitkApplyDiffImageOperation.cpp @@ -1,67 +1,64 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include "mitkApplyDiffImageOperation.h" #include mitk::ApplyDiffImageOperation::ApplyDiffImageOperation(OperationType operationType, Image *image, Image *diffImage, unsigned int timeStep, unsigned int sliceDimension, unsigned int sliceIndex) : Operation(operationType), m_Image(image), m_SliceIndex(sliceIndex), m_SliceDimension(sliceDimension), m_TimeStep(timeStep), m_Factor(1.0), m_ImageStillValid(false), m_DeleteTag(0) { if (image && diffImage) { // observe 3D image for DeleteEvent m_ImageStillValid = true; itk::SimpleMemberCommand::Pointer command = itk::SimpleMemberCommand::New(); command->SetCallbackFunction(this, &ApplyDiffImageOperation::OnImageDeleted); m_DeleteTag = image->AddObserver(itk::DeleteEvent(), command); // keep a compressed version of the image - zlibContainer = CompressedImageContainer::New(); - zlibContainer->SetImage(diffImage); + m_CompressedImageContainer.CompressImage(diffImage); } } mitk::ApplyDiffImageOperation::~ApplyDiffImageOperation() { if (m_ImageStillValid) { m_Image->RemoveObserver(m_DeleteTag); } } void mitk::ApplyDiffImageOperation::OnImageDeleted() { m_ImageStillValid = false; } mitk::Image::Pointer mitk::ApplyDiffImageOperation::GetDiffImage() { // uncompress image to create a valid mitk::Image - Image::Pointer image = zlibContainer->GetImage().GetPointer(); - - return image; + return m_CompressedImageContainer.DecompressImage(); } diff --git a/Modules/DataTypesExt/src/mitkCompressedImageContainer.cpp b/Modules/DataTypesExt/src/mitkCompressedImageContainer.cpp index 7d11a9e9a9..a2c1ee8e38 100644 --- a/Modules/DataTypesExt/src/mitkCompressedImageContainer.cpp +++ b/Modules/DataTypesExt/src/mitkCompressedImageContainer.cpp @@ -1,178 +1,133 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ -#include "mitkCompressedImageContainer.h" -#include "mitkImageReadAccessor.h" +#include -#include "itk_zlib.h" +#include +#include -#include +#include -mitk::CompressedImageContainer::CompressedImageContainer() : m_PixelType(nullptr), m_ImageGeometry(nullptr) +#include + +mitk::CompressedImageContainer::CompressedImageContainer() { } mitk::CompressedImageContainer::~CompressedImageContainer() { - for (auto iter = m_ByteBuffers.begin(); iter != m_ByteBuffers.end(); ++iter) - { - free(iter->first); - } - - delete m_PixelType; + this->ClearCompressedImageData(); } -void mitk::CompressedImageContainer::SetImage(const Image *image) +void mitk::CompressedImageContainer::ClearCompressedImageData() { - for (auto iter = m_ByteBuffers.begin(); iter != m_ByteBuffers.end(); ++iter) + for (const auto& image : m_CompressedImageData) { - free(iter->first); + for (auto slice : image) + delete[] slice.second; } - m_ByteBuffers.clear(); + m_CompressedImageData.clear(); - // Compress diff image using zlib (will be restored on demand) - // determine memory size occupied by voxel data - m_ImageDimension = image->GetDimension(); - m_ImageDimensions.clear(); + m_PixelType = nullptr; + m_TimeGeometry = nullptr; + m_SliceDimensions[0] = 0; + m_SliceDimensions[1] = 0; +} - m_PixelType = new mitk::PixelType(image->GetPixelType()); +void mitk::CompressedImageContainer::CompressImage(const Image* image) +{ + this->ClearCompressedImageData(); - m_OneTimeStepImageSizeInBytes = m_PixelType->GetSize(); // bits per element divided by 8 - for (unsigned int i = 0; i < m_ImageDimension; ++i) - { - unsigned int currentImageDimension = image->GetDimension(i); - m_ImageDimensions.push_back(currentImageDimension); - if (i < 3) - { - m_OneTimeStepImageSizeInBytes *= currentImageDimension; // only the 3D memory size - } - } + if (nullptr == image) + return; - m_ImageGeometry = image->GetGeometry(); + m_PixelType = std::make_unique(image->GetPixelType()); + m_TimeGeometry = image->GetTimeGeometry()->Clone(); + m_SliceDimensions[0] = image->GetDimension(0); + m_SliceDimensions[1] = image->GetDimension(1); - m_NumberOfTimeSteps = 1; - if (m_ImageDimension > 3) - { - m_NumberOfTimeSteps = image->GetDimension(3); - } + const auto numTimeSteps = m_TimeGeometry->CountTimeSteps(); + const auto numSlices = image->GetDimension(2); + const auto numSliceBytes = image->GetPixelType().GetSize() * image->GetDimension(0) * image->GetDimension(1); + + m_CompressedImageData.reserve(numTimeSteps); - for (unsigned int timestep = 0; timestep < m_NumberOfTimeSteps; ++timestep) + for (std::remove_const_t t = 0; t < numTimeSteps; ++t) { - // allocate a buffer as specified by zlib - unsigned long bufferSize = - m_OneTimeStepImageSizeInBytes + static_cast(m_OneTimeStepImageSizeInBytes * 0.2) + 12; - auto *byteBuffer = (unsigned char *)malloc(bufferSize); + CompressedTimeStepData slices; + slices.reserve(numSlices); - if (itk::Object::GetDebug()) - { - // compress image here into a buffer - MITK_INFO << "Using ZLib version: '" << zlibVersion() << "'" << std::endl - << "Attempting to compress " << m_OneTimeStepImageSizeInBytes << " image bytes into a buffer of size " - << bufferSize << std::endl; - } + ImageReadAccessor accessor(image, image->GetVolumeData(t)); - ImageReadAccessor imgAcc(image, image->GetVolumeData(timestep)); - ::Bytef *dest(byteBuffer); - ::uLongf destLen(bufferSize); - auto *source((unsigned char *)imgAcc.GetData()); - ::uLongf sourceLen(m_OneTimeStepImageSizeInBytes); - int zlibRetVal = ::compress(dest, &destLen, source, sourceLen); - if (itk::Object::GetDebug()) + for (std::remove_const_t s = 0; s < numSlices; ++s) { - if (zlibRetVal == Z_OK) + const auto* src = reinterpret_cast(accessor.GetData()) + numSliceBytes * s; + char* dest = new char[numSliceBytes]; + const auto destSize = LZ4_compress_default(src, dest, static_cast(numSliceBytes), static_cast(numSliceBytes)); + + if (0 == destSize) { - MITK_INFO << "Success, using " << destLen << " bytes of the buffer (ratio " - << ((double)destLen / (double)sourceLen) << ")" << std::endl; + MITK_ERROR << "LZ4 compression failed!"; + delete[] dest; + slices.emplace_back(0, nullptr); } else { - switch (zlibRetVal) - { - case Z_MEM_ERROR: - MITK_ERROR << "not enough memory" << std::endl; - break; - case Z_BUF_ERROR: - MITK_ERROR << "output buffer too small" << std::endl; - break; - default: - MITK_ERROR << "other, unspecified error" << std::endl; - break; - } + char* shrinkedDest = new char[destSize]; + std::copy(dest, dest + destSize, shrinkedDest); + delete[] dest; + slices.emplace_back(destSize, shrinkedDest); } } - // only use the neccessary amount of memory, realloc the buffer! - byteBuffer = (unsigned char *)realloc(byteBuffer, destLen); - bufferSize = destLen; - // MITK_INFO << "Using " << bufferSize << " bytes to store compressed image (" << destLen << " needed)" << - // std::endl; - - m_ByteBuffers.push_back(std::pair(byteBuffer, bufferSize)); + m_CompressedImageData.push_back(slices); } } -mitk::Image::Pointer mitk::CompressedImageContainer::GetImage() const +mitk::Image::Pointer mitk::CompressedImageContainer::DecompressImage() const { - if (m_ByteBuffers.empty()) + if (m_CompressedImageData.empty()) return nullptr; - // uncompress image data, create an Image - Image::Pointer image = Image::New(); - unsigned int dims[20]; // more than 20 dimensions and bang - for (unsigned int dim = 0; dim < m_ImageDimension; ++dim) - dims[dim] = m_ImageDimensions[dim]; + const auto numSlices = static_cast(m_CompressedImageData[0].size()); + const auto numTimeSteps = static_cast(m_CompressedImageData.size()); + const auto numSliceBytes = m_PixelType->GetSize() * m_SliceDimensions[0] * m_SliceDimensions[1]; + + std::array dimensions; + dimensions[0] = m_SliceDimensions[0]; + dimensions[1] = m_SliceDimensions[1]; + dimensions[2] = numSlices; + dimensions[3] = numTimeSteps; - image->Initialize(*m_PixelType, m_ImageDimension, dims); // this IS needed, right ?? But it does allocate memory -> - // does create one big lump of memory (also in windows) + auto image = Image::New(); + image->Initialize(*m_PixelType, numTimeSteps > 1 ? 4 : 3, dimensions.data()); - unsigned int timeStep(0); - for (auto iter = m_ByteBuffers.begin(); iter != m_ByteBuffers.end(); ++iter, ++timeStep) + for (std::remove_const_t t = 0; t < numTimeSteps; ++t) { - ImageReadAccessor imgAcc(image, image->GetVolumeData(timeStep)); - auto *dest((unsigned char *)imgAcc.GetData()); - ::uLongf destLen(m_OneTimeStepImageSizeInBytes); - ::Bytef *source(iter->first); - ::uLongf sourceLen(iter->second); - int zlibRetVal = ::uncompress(dest, &destLen, source, sourceLen); - if (itk::Object::GetDebug()) + ImageWriteAccessor accessor(image, image->GetVolumeData(static_cast(t))); + + for (std::remove_const_t s = 0; s < numSlices; ++s) { - if (zlibRetVal == Z_OK) - { - MITK_INFO << "Success, destLen now " << destLen << " bytes" << std::endl; - } - else - { - switch (zlibRetVal) - { - case Z_DATA_ERROR: - MITK_ERROR << "compressed data corrupted" << std::endl; - break; - case Z_MEM_ERROR: - MITK_ERROR << "not enough memory" << std::endl; - break; - case Z_BUF_ERROR: - MITK_ERROR << "output buffer too small" << std::endl; - break; - default: - MITK_ERROR << "other, unspecified error" << std::endl; - break; - } - } + auto* dest = reinterpret_cast(accessor.GetData()) + numSliceBytes * s; + const auto& slice = m_CompressedImageData[t][s]; + const auto destSize = LZ4_decompress_safe(slice.second, dest, slice.first, static_cast(numSliceBytes)); + + if (0 > destSize) + MITK_ERROR << "LZ4 decompression failed!"; } } - image->SetGeometry(m_ImageGeometry); - image->Modified(); + image->SetTimeGeometry(m_TimeGeometry->Clone()); return image; } diff --git a/Modules/DataTypesExt/test/mitkCompressedImageContainerTest.cpp b/Modules/DataTypesExt/test/mitkCompressedImageContainerTest.cpp index 4747eee7e4..ec6cf8ef05 100644 --- a/Modules/DataTypesExt/test/mitkCompressedImageContainerTest.cpp +++ b/Modules/DataTypesExt/test/mitkCompressedImageContainerTest.cpp @@ -1,182 +1,169 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include "mitkCompressedImageContainer.h" #include "mitkCoreObjectFactory.h" #include "mitkIOUtil.h" #include "mitkImageDataItem.h" #include "mitkImageReadAccessor.h" class mitkCompressedImageContainerTestClass { public: static void Test(mitk::CompressedImageContainer *container, mitk::Image *image, unsigned int &numberFailed) { - container->SetImage(image); // compress - mitk::Image::Pointer uncompressedImage = container->GetImage(); // uncompress + container->CompressImage(image); + mitk::Image::Pointer uncompressedImage = container->DecompressImage(); // check dimensions if (image->GetDimension() != uncompressedImage->GetDimension()) { ++numberFailed; std::cerr << " (EE) Number of image dimensions wrong after uncompression (was: " << image->GetDimension() << ", now: " << uncompressedImage->GetDimension() << ")" << std::endl; } for (unsigned int dim = 0; dim < image->GetDimension(); ++dim) { if (image->GetDimension(dim) != uncompressedImage->GetDimension(dim)) { ++numberFailed; std::cerr << " (EE) Image dimension " << dim << " differs after uncompression (was: " << image->GetDimension(dim) << ", now: " << uncompressedImage->GetDimension(dim) << ")" << std::endl; } } // check pixel type if (image->GetPixelType() != uncompressedImage->GetPixelType()) { ++numberFailed; std::cerr << " (EE) Pixel type wrong after uncompression:" << std::endl; mitk::PixelType m_PixelType = image->GetPixelType(); std::cout << "Original pixel type:" << std::endl; std::cout << " PixelType: " << m_PixelType.GetTypeAsString() << std::endl; std::cout << " BitsPerElement: " << m_PixelType.GetBpe() << std::endl; std::cout << " NumberOfComponents: " << m_PixelType.GetNumberOfComponents() << std::endl; std::cout << " BitsPerComponent: " << m_PixelType.GetBitsPerComponent() << std::endl; // m_PixelType = uncompressedImage->GetPixelType(); std::cout << "Uncompressed pixel type:" << std::endl; std::cout << " PixelType: " << uncompressedImage->GetPixelType().GetTypeAsString() << std::endl; std::cout << " BitsPerElement: " << uncompressedImage->GetPixelType().GetBpe() << std::endl; std::cout << " NumberOfComponents: " << uncompressedImage->GetPixelType().GetNumberOfComponents() << std::endl; std::cout << " BitsPerComponent: " << uncompressedImage->GetPixelType().GetBitsPerComponent() << std::endl; } // check data mitk::PixelType m_PixelType = image->GetPixelType(); unsigned long oneTimeStepSizeInBytes = m_PixelType.GetBpe() >> 3; // bits per element divided by 8 for (unsigned int dim = 0; dim < image->GetDimension(); ++dim) { if (dim < 3) { oneTimeStepSizeInBytes *= image->GetDimension(dim); } } unsigned int numberOfTimeSteps(1); if (image->GetDimension() > 3) { numberOfTimeSteps = image->GetDimension(3); } for (unsigned int timeStep = 0; timeStep < numberOfTimeSteps; ++timeStep) { mitk::ImageReadAccessor origImgAcc(image, image->GetVolumeData(timeStep)); mitk::ImageReadAccessor unCompImgAcc(uncompressedImage, uncompressedImage->GetVolumeData(timeStep)); auto *originalData((unsigned char *)origImgAcc.GetData()); auto *uncompressedData((unsigned char *)unCompImgAcc.GetData()); unsigned long difference(0); for (unsigned long byte = 0; byte < oneTimeStepSizeInBytes; ++byte) { if (originalData[byte] != uncompressedData[byte]) { ++difference; } } if (difference > 0) { ++numberFailed; std::cerr << " (EE) Pixel data in timestep " << timeStep << " not identical after uncompression. " << difference << " pixels different." << std::endl; break; // break "for timeStep" } } } }; /// ctest entry point int mitkCompressedImageContainerTest(int argc, char *argv[]) { // one big variable to tell if anything went wrong unsigned int numberFailed(0); // need one parameter (image filename) if (argc == 0) { std::cerr << "No file specified [FAILED]" << std::endl; return EXIT_FAILURE; } // load the image mitk::Image::Pointer image = nullptr; try { std::cout << "Testing with parameter '" << argv[1] << "'" << std::endl; image = mitk::IOUtil::Load(argv[1]); } catch (const mitk::Exception &) { std::cout << "File not an image - test will not be applied [PASSED]" << std::endl; std::cout << "[TEST DONE]" << std::endl; return EXIT_SUCCESS; } catch (itk::ExceptionObject &ex) { ++numberFailed; std::cerr << "Exception: " << ex << "[FAILED]" << std::endl; return EXIT_FAILURE; } std::cout << " (II) Could load image." << std::endl; - std::cout << "Testing instantiation" << std::endl; - // instantiation - mitk::CompressedImageContainer::Pointer container = mitk::CompressedImageContainer::New(); - if (container.IsNotNull()) { - std::cout << " (II) Instantiation works." << std::endl; - } - else - { - ++numberFailed; - std::cout << "Test failed, and it's the ugliest one!" << std::endl; - return EXIT_FAILURE; - } + mitk::CompressedImageContainer container; - // some real work - mitkCompressedImageContainerTestClass::Test(container, image, numberFailed); + // some real work + mitkCompressedImageContainerTestClass::Test(&container, image, numberFailed); - std::cout << "Testing destruction" << std::endl; - - // freeing - container = nullptr; + std::cout << "Testing destruction" << std::endl; + } std::cout << " (II) Freeing works." << std::endl; if (numberFailed > 0) { std::cerr << numberFailed << " tests failed." << std::endl; return EXIT_FAILURE; } else { std::cout << "PASSED all tests." << std::endl; return EXIT_SUCCESS; } } diff --git a/Modules/Segmentation/Algorithms/mitkDiffSliceOperation.cpp b/Modules/Segmentation/Algorithms/mitkDiffSliceOperation.cpp index bab4084f3e..61962f21f9 100644 --- a/Modules/Segmentation/Algorithms/mitkDiffSliceOperation.cpp +++ b/Modules/Segmentation/Algorithms/mitkDiffSliceOperation.cpp @@ -1,100 +1,96 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include "mitkDiffSliceOperation.h" #include #include mitk::DiffSliceOperation::DiffSliceOperation() : Operation(1) { m_TimeStep = 0; - m_zlibSliceContainer = nullptr; m_Image = nullptr; m_WorldGeometry = nullptr; m_SliceGeometry = nullptr; m_ImageIsValid = false; m_DeleteObserverTag = 0; } mitk::DiffSliceOperation::DiffSliceOperation(Image *imageVolume, const Image *slice, const SlicedGeometry3D *sliceGeometry, TimeStepType timestep, const BaseGeometry *currentWorldGeometry) : Operation(1) { m_WorldGeometry = currentWorldGeometry->Clone(); /* Quick fix for bug 12338. Guard object - fix this when clone method of PlaneGeometry is cloning the reference geometry (see bug 13392)*/ // xxxx m_GuardReferenceGeometry = mitk::BaseGeometry::New(); m_GuardReferenceGeometry = dynamic_cast(m_WorldGeometry.GetPointer())->GetReferenceGeometry(); /*---------------------------------------------------------------------------------------------------*/ m_SliceGeometry = sliceGeometry->Clone(); m_TimeStep = timestep; - m_zlibSliceContainer = CompressedImageContainer::New(); - m_zlibSliceContainer->SetImage(slice); + m_CompressedImageContainer.CompressImage(slice); m_Image = imageVolume; m_DeleteObserverTag = 0; if (m_Image) { /*add an observer to listen to the delete event of the image, this is necessary because the operation is then * invalid*/ itk::SimpleMemberCommand::Pointer command = itk::SimpleMemberCommand::New(); command->SetCallbackFunction(this, &DiffSliceOperation::OnImageDeleted); // get the id of the observer, used to remove it later on m_DeleteObserverTag = imageVolume->AddObserver(itk::DeleteEvent(), command); m_ImageIsValid = true; } else m_ImageIsValid = false; } mitk::DiffSliceOperation::~DiffSliceOperation() { m_WorldGeometry = nullptr; - m_zlibSliceContainer = nullptr; if (m_ImageIsValid) { // if the image is still there, we have to remove the observer from it m_Image->RemoveObserver(m_DeleteObserverTag); } m_Image = nullptr; } mitk::Image::Pointer mitk::DiffSliceOperation::GetSlice() { - Image::Pointer image = m_zlibSliceContainer->GetImage(); - return image; + return m_CompressedImageContainer.DecompressImage(); } bool mitk::DiffSliceOperation::IsValid() { - return m_ImageIsValid && m_zlibSliceContainer.IsNotNull() && (m_WorldGeometry.IsNotNull()); // TODO improve + return m_ImageIsValid && m_WorldGeometry.IsNotNull(); // TODO improve } void mitk::DiffSliceOperation::OnImageDeleted() { // if our imageVolume is removed e.g. from the datastorage the operation is no lnger valid m_ImageIsValid = false; } diff --git a/Modules/Segmentation/Algorithms/mitkDiffSliceOperation.h b/Modules/Segmentation/Algorithms/mitkDiffSliceOperation.h index 9647976b40..2909af129e 100644 --- a/Modules/Segmentation/Algorithms/mitkDiffSliceOperation.h +++ b/Modules/Segmentation/Algorithms/mitkDiffSliceOperation.h @@ -1,101 +1,101 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #ifndef mitkDiffSliceOperation_h_Included #define mitkDiffSliceOperation_h_Included #include "mitkCompressedImageContainer.h" #include #include #include namespace mitk { class Image; /** \brief An Operation for applying an edited slice to the volume. \sa DiffSliceOperationApplier The information for the operation is specified by properties: imageVolume the volume where the slice was extracted from. slice the slice to be applied. timestep the timestep in an 4D image. currentWorldGeometry specifies the axis where the slice has to be applied in the volume. This Operation can be used to realize undo-redo functionality for e.g. segmentation purposes. */ class MITKSEGMENTATION_EXPORT DiffSliceOperation : public Operation { public: mitkClassMacro(DiffSliceOperation, OperationActor); // itkFactorylessNewMacro(Self) // itkCloneMacro(Self) // mitkNewMacro4Param(DiffSliceOperation,mitk::Image,mitk::Image,unsigned int, mitk::PlaneGeometry); /** \brief Creates an empty instance. Note that it is not valid yet. The properties of the object have to be set. */ DiffSliceOperation(); /** \brief */ DiffSliceOperation(mitk::Image *imageVolume, const mitk::Image *slice, const SlicedGeometry3D *sliceGeometry, const TimeStepType timestep, const BaseGeometry *currentWorldGeometry); /** \brief Check if it is a valid operation.*/ bool IsValid(); /** \brief Get th image volume.*/ mitk::Image *GetImage() { return this->m_Image; } const mitk::Image* GetImage() const { return this->m_Image; } /** \brief Get the slice that is applied in the operation.*/ Image::Pointer GetSlice(); /** \brief Set timeStep*/ TimeStepType GetTimeStep() const { return this->m_TimeStep; } /** \brief Get the axis where the slice has to be applied in the volume.*/ const SlicedGeometry3D *GetSliceGeometry() const { return this->m_SliceGeometry; } /** \brief Get the axis where the slice has to be applied in the volume.*/ const BaseGeometry *GetWorldGeometry() const { return this->m_WorldGeometry; } protected: ~DiffSliceOperation() override; /** \brief Callback for image observer.*/ void OnImageDeleted(); - CompressedImageContainer::Pointer m_zlibSliceContainer; + CompressedImageContainer m_CompressedImageContainer; mitk::Image *m_Image; vtkSmartPointer m_Slice; SlicedGeometry3D::ConstPointer m_SliceGeometry; TimeStepType m_TimeStep; BaseGeometry::ConstPointer m_WorldGeometry; bool m_ImageIsValid; unsigned long m_DeleteObserverTag; mitk::BaseGeometry::ConstPointer m_GuardReferenceGeometry; }; } #endif