diff --git a/Modules/OpenCL/mitkOclDataSetToDataSetFilter.cpp b/Modules/OpenCL/mitkOclDataSetToDataSetFilter.cpp index 2cf7acab5d..be0075557e 100644 --- a/Modules/OpenCL/mitkOclDataSetToDataSetFilter.cpp +++ b/Modules/OpenCL/mitkOclDataSetToDataSetFilter.cpp @@ -1,128 +1,127 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkOclDataSetToDataSetFilter.h" #include "mitkOclDataSet.h" #include "mitkException.h" mitk::OclDataSetToDataSetFilter::OclDataSetToDataSetFilter() { m_Output = mitk::OclDataSet::New(); } - mitk::OclDataSetToDataSetFilter::~OclDataSetToDataSetFilter() { } mitk::OclDataSet::Pointer mitk::OclDataSetToDataSetFilter::GetGPUOutput() { return this->m_Output; } void* mitk::OclDataSetToDataSetFilter::GetOutput() { void* pData = m_Output->TransferDataToCPU(m_CommandQue); return pData; } int mitk::OclDataSetToDataSetFilter::GetBytesPerElem() { return this->m_CurrentSizeOutput; } bool mitk::OclDataSetToDataSetFilter::InitExec(cl_kernel ckKernel, unsigned int* dimensions, size_t outputDataSize, unsigned int outputBpE) { cl_int clErr = 0; if (m_Input.IsNull()) mitkThrow() << "Input DataSet is null."; // get DataSet size once const unsigned int uiDataSetWidth = dimensions[0]; const unsigned int uiDataSetHeight = dimensions[1]; const unsigned int uiDataSetDepth = dimensions[2]; // compute work sizes this->SetWorkingSize(8, uiDataSetWidth, 8, uiDataSetHeight, 8, uiDataSetDepth); cl_mem clBuffIn = m_Input->GetGPUBuffer(); cl_mem clBuffOut = m_Output->GetGPUBuffer(); if (!clBuffIn) { if (m_Input->TransferDataToGPU(m_CommandQue) != CL_SUCCESS) { mitkThrow() << "Could not create / initialize gpu DataSet."; } clBuffIn = m_Input->GetGPUBuffer(); } // output DataSet not initialized or output buffer size changed if (!clBuffOut || (size_t)m_Output->GetBufferSize() != outputDataSize) { MITK_DEBUG << "Create GPU DataSet call " << uiDataSetWidth << "x" << uiDataSetHeight << "x" << uiDataSetDepth; m_Output->SetBpE(outputBpE); m_Output->SetBufferSize(outputDataSize); clBuffOut = m_Output->CreateGPUBuffer(); m_CurrentSizeOutput = outputBpE; } clErr = 0; clErr = clSetKernelArg(ckKernel, 0, sizeof(cl_mem), &clBuffIn); clErr |= clSetKernelArg(ckKernel, 1, sizeof(cl_mem), &clBuffOut); CHECK_OCL_ERR(clErr); if (clErr != CL_SUCCESS) mitkThrow() << "OpenCL Part initialization failed with " << GetOclErrorAsString(clErr); return(clErr == CL_SUCCESS); } bool mitk::OclDataSetToDataSetFilter::InitExecNoInput(cl_kernel ckKernel, unsigned int* dimensions, size_t outputDataSize, unsigned int outputBpE) { cl_int clErr = 0; // get DataSet size once const unsigned int uiDataSetWidth = dimensions[0]; const unsigned int uiDataSetHeight = dimensions[1]; const unsigned int uiDataSetDepth = dimensions[2]; // compute work sizes this->SetWorkingSize(8, uiDataSetWidth, 8, uiDataSetHeight, 8, uiDataSetDepth); cl_mem clBuffOut = m_Output->GetGPUBuffer(); // output DataSet not initialized or output buffer size changed if (!clBuffOut || (size_t)m_Output->GetBufferSize() != outputDataSize) { MITK_DEBUG << "Create GPU DataSet call " << uiDataSetWidth << "x" << uiDataSetHeight << "x" << uiDataSetDepth; m_Output->SetBpE(outputBpE); m_Output->SetBufferSize(outputDataSize); clBuffOut = m_Output->CreateGPUBuffer(); m_CurrentSizeOutput = outputBpE; } clErr = clSetKernelArg(ckKernel, 0, sizeof(cl_mem), &clBuffOut); CHECK_OCL_ERR(clErr); if (clErr != CL_SUCCESS) mitkThrow() << "OpenCL Part initialization failed with " << GetOclErrorAsString(clErr); return(clErr == CL_SUCCESS); -} \ No newline at end of file +} diff --git a/Modules/PhotoacousticsAlgorithms/CMakeLists.txt b/Modules/PhotoacousticsAlgorithms/CMakeLists.txt index 7b8587334b..7eb8df47cd 100644 --- a/Modules/PhotoacousticsAlgorithms/CMakeLists.txt +++ b/Modules/PhotoacousticsAlgorithms/CMakeLists.txt @@ -1,16 +1,19 @@ set(dependencies_list MitkCore MitkAlgorithmsExt) IF(MITK_USE_OpenCL) add_definitions(-DPHOTOACOUSTICS_USE_GPU) set(dependencies_list ${dependencies_list} MitkOpenCL) message("Using OpenCL in PhotoacousticAlgorithms") ENDIF(MITK_USE_OpenCL) MITK_CREATE_MODULE( SUBPROJECTS DEPENDS ${dependencies_list} #AUTOLOAD_WITH MitkCore - INCLUDE_DIRS PUBLIC Algorithms/ITKUltrasound Algorithms Algorithms/OCL + INCLUDE_DIRS PUBLIC include INTERNAL_INCLUDE_DIRS ${INCLUDE_DIRS_INTERNAL} PACKAGE_DEPENDS ITK|ITKFFT+ITKImageCompose+ITKImageIntensity -) \ No newline at end of file +) + +add_subdirectory(test) +add_subdirectory(MitkPABeamformingTool) diff --git a/Modules/PhotoacousticsAlgorithms/MitkPABeamformingTool/CMakeLists.txt b/Modules/PhotoacousticsAlgorithms/MitkPABeamformingTool/CMakeLists.txt new file mode 100644 index 0000000000..73ded2d3cf --- /dev/null +++ b/Modules/PhotoacousticsAlgorithms/MitkPABeamformingTool/CMakeLists.txt @@ -0,0 +1,11 @@ +OPTION(BUILD_PhotoacousticBeamformingTool "Build MiniApp for beamforming of a PA image" ON) + +IF(BUILD_PhotoacousticBeamformingTool) + PROJECT( MitkPABeamformingTool ) + mitk_create_executable(PABeamformingTool + DEPENDS MitkCommandLine MitkCore MitkPhotoacousticsAlgorithms + PACKAGE_DEPENDS + CPP_FILES PABeamformingTool.cpp) + + install(TARGETS ${EXECUTABLE_TARGET} RUNTIME DESTINATION bin) + ENDIF() diff --git a/Modules/PhotoacousticsAlgorithms/MitkPABeamformingTool/PABeamformingTool.cpp b/Modules/PhotoacousticsAlgorithms/MitkPABeamformingTool/PABeamformingTool.cpp new file mode 100644 index 0000000000..1a9ce11188 --- /dev/null +++ b/Modules/PhotoacousticsAlgorithms/MitkPABeamformingTool/PABeamformingTool.cpp @@ -0,0 +1,230 @@ +/*=================================================================== + +The Medical Imaging Interaction Toolkit (MITK) + +Copyright (c) German Cancer Research Center, +Division of Medical and Biological Informatics. +All rights reserved. + +This software is distributed WITHOUT ANY WARRANTY; without +even the implied warranty of MERCHANTABILITY or FITNESS FOR +A PARTICULAR PURPOSE. + +See LICENSE.txt or http://www.mitk.org for details. + +===================================================================*/ + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +struct InputParameters +{ + mitk::Image::Pointer inputImage; + std::string outputFilename; + bool verbose; + float speedOfSound; + unsigned int cutoff; + float angle; + unsigned int samples; + mitk::BeamformingSettings::BeamformingAlgorithm algorithm; +}; + +InputParameters parseInput(int argc, char* argv[]) +{ + mitkCommandLineParser parser; + parser.setCategory("MITK-Photoacoustics"); + parser.setTitle("Mitk Photoacoustics Beamforming Tool"); + parser.setDescription("Reads a nrrd file as an input and applies a beamforming method as set with the parameters."); + parser.setContributor("Computer Assisted Medical Interventions, DKFZ"); + + parser.setArgumentPrefix("--", "-"); + + parser.beginGroup("Required parameters"); + parser.addArgument( + "inputImage", "i", mitkCommandLineParser::InputImage, + "Input image (mitk::Image)", "input image (.nrrd file)", + us::Any(), false); + parser.addArgument( + "output", "o", mitkCommandLineParser::OutputFile, + "Output filename", "output image (.nrrd file)", + us::Any(), false); + parser.endGroup(); + + parser.beginGroup("Optional parameters"); + parser.addArgument( + "verbose", "v", mitkCommandLineParser::Bool, + "Verbose Output", "Whether to produce verbose, or rather debug output. (default: false)"); + parser.addArgument( + "speed-of-sound", "sos", mitkCommandLineParser::Float, + "Speed of Sound [m/s]", "The average speed of sound as assumed for the reconstruction in [m/s]. (default: 1500)"); + parser.addArgument( + "cutoff", "co", mitkCommandLineParser::Int, + "cutoff margin on the top of the image [pixels]", "The number of pixels to be ignored for this filter in [pixels] (default: 0)."); + parser.addArgument( + "angle", "a", mitkCommandLineParser::Float, + "field of view of the transducer elements [degrees]", "The field of view of each individual transducer element [degrees] (default: 27)."); + parser.addArgument( + "samples", "s", mitkCommandLineParser::Int, + "samples per reconstruction line [pixels]", "The pixels along the y axis in the beamformed image [pixels] (default: 2048)."); + parser.addArgument( + "algorithm", "alg", mitkCommandLineParser::String, + "one of [\"DAS\", \"DMAS\", \"sDMAS\"]", "The beamforming algorithm to be used for reconstruction (default: DAS)."); + parser.endGroup(); + + InputParameters input; + + std::map parsedArgs = parser.parseArguments(argc, argv); + if (parsedArgs.size() == 0) + exit(-1); + + if (parsedArgs.count("verbose")) + { + input.verbose = true; + } + else + { + input.verbose = false; + } + MITK_INFO(input.verbose) << "### VERBOSE OUTPUT ENABLED ###"; + + if (parsedArgs.count("inputImage")) + { + MITK_INFO(input.verbose) << "Reading input image..."; + input.inputImage = mitk::IOUtil::Load(us::any_cast(parsedArgs["inputImage"])); + MITK_INFO(input.verbose) << "Reading input image...[Done]"; + } + else + { + mitkThrow() << "No input image given."; + } + + if (parsedArgs.count("output")) + { + input.outputFilename = us::any_cast(parsedArgs["output"]); + } + else + { + mitkThrow() << "No output image path given.."; + } + + if (parsedArgs.count("speed-of-sound")) + { + input.speedOfSound = us::any_cast(parsedArgs["speed-of-sound"]); + } + else + { + input.speedOfSound = 1500; + } + + if (parsedArgs.count("cutoff")) + { + input.cutoff = us::any_cast(parsedArgs["cutoff"]); + } + else + { + input.cutoff = 0; + } + + if (parsedArgs.count("angle")) + { + input.angle = us::any_cast(parsedArgs["angle"]); + } + else + { + input.angle = 27; + } + + if (parsedArgs.count("samples")) + { + input.samples = us::any_cast(parsedArgs["samples"]); + } + else + { + input.samples = 2048; + } + + if (parsedArgs.count("algorithm")) + { + std::string algorithm = us::any_cast(parsedArgs["algorithm"]); + MITK_INFO(input.verbose) << "Parsing algorithm: " << algorithm; + if (algorithm == "DAS") + input.algorithm = mitk::BeamformingSettings::BeamformingAlgorithm::DAS; + else if (algorithm == "DMAS") + input.algorithm = mitk::BeamformingSettings::BeamformingAlgorithm::DMAS; + else if (algorithm == "sDMAS") + input.algorithm = mitk::BeamformingSettings::BeamformingAlgorithm::sDMAS; + else + { + MITK_INFO(input.verbose) << "Not a valid beamforming algorithm: " << algorithm << " Reverting to DAS"; + input.algorithm = mitk::BeamformingSettings::BeamformingAlgorithm::DAS; + } + + MITK_INFO(input.verbose) << "Sucessfully set algorithm: " << algorithm; + } + else + { + input.algorithm = mitk::BeamformingSettings::BeamformingAlgorithm::DAS; + MITK_INFO(input.verbose) << "No matching algorithm found. Using DAS."; + } + + return input; +} + +mitk::BeamformingSettings::Pointer ParseSettings(InputParameters &input) +{ + mitk::BeamformingSettings::Pointer outputSettings = mitk::BeamformingSettings::New( + (float)(input.inputImage->GetGeometry()->GetSpacing()[0] / 1000), + (float)(input.speedOfSound), + (float)(input.inputImage->GetGeometry()->GetSpacing()[1] / 1000000), + input.angle, + true, + input.inputImage->GetDimension(1), + input.inputImage->GetDimension(0), + input.inputImage->GetDimensions(), + 0.04, + false, + 16, + mitk::BeamformingSettings::DelayCalc::Spherical, + mitk::BeamformingSettings::Apodization::Box, + input.inputImage->GetDimension(0), + input.algorithm + ); + + return outputSettings; +} + +int main(int argc, char * argv[]) +{ + auto input = parseInput(argc, argv); + + MITK_INFO(input.verbose) << "Beamforming input image..."; + + mitk::PhotoacousticFilterService::Pointer m_BeamformingService = mitk::PhotoacousticFilterService::New(); + + mitk::BeamformingSettings::Pointer settings = ParseSettings(input); + + mitk::CastToFloatImageFilter::Pointer castFilter = mitk::CastToFloatImageFilter::New(); + castFilter->SetInput(input.inputImage); + castFilter->Update(); + auto floatImage = castFilter->GetOutput(); + + auto output = m_BeamformingService->ApplyBeamforming(floatImage, settings); + MITK_INFO(input.verbose) << "Applying BModeFilter to image..."; + auto output2 = m_BeamformingService->ApplyBmodeFilter(output, mitk::PhotoacousticFilterService::EnvelopeDetection, false); + MITK_INFO(input.verbose) << "Applying BModeFilter to image...[Done]"; + + MITK_INFO(input.verbose) << "Saving image..."; + mitk::IOUtil::Save(output2, input.outputFilename); + MITK_INFO(input.verbose) << "Saving image...[Done]"; + + MITK_INFO(input.verbose) << "Beamforming input image...[Done]"; +} diff --git a/Modules/PhotoacousticsAlgorithms/MitkPABeamformingTool/files.cmake b/Modules/PhotoacousticsAlgorithms/MitkPABeamformingTool/files.cmake new file mode 100644 index 0000000000..1bed148e42 --- /dev/null +++ b/Modules/PhotoacousticsAlgorithms/MitkPABeamformingTool/files.cmake @@ -0,0 +1,3 @@ +set(CPP_FILES + PABeamformingTool.cpp +) diff --git a/Modules/PhotoacousticsAlgorithms/Resources/BModeAbs.cl b/Modules/PhotoacousticsAlgorithms/Resources/BModeAbs.cl deleted file mode 100644 index 5dfd66d02d..0000000000 --- a/Modules/PhotoacousticsAlgorithms/Resources/BModeAbs.cl +++ /dev/null @@ -1,38 +0,0 @@ -/*=================================================================== - -The Medical Imaging Interaction Toolkit (MITK) - -Copyright (c) German Cancer Research Center, -Division of Medical and Biological Informatics. -All rights reserved. - -This software is distributed WITHOUT ANY WARRANTY; without -even the implied warranty of MERCHANTABILITY or FITNESS FOR -A PARTICULAR PURPOSE. - -See LICENSE.txt or http://www.mitk.org for details. - -===================================================================*/ - -__kernel void ckBmodeAbs( - __global float* dSource, // input image - __global float* dDest, // output buffer - unsigned int size -) -{ - // get thread identifier - unsigned int globalPosX = get_global_id(0); - unsigned int globalPosY = get_global_id(1); - unsigned int globalPosZ = get_global_id(2); - - // get image width and height - unsigned short inputS = get_global_size(1); - unsigned short inputL = get_global_size(0); - unsigned short slices = get_global_size(2); - - // terminate non-valid threads - if ( globalPosX + inputL * globalPosY + inputL * inputS * globalPosZ < size ) - { - dDest[ globalPosZ * inputL * inputS + globalPosY * inputL + globalPosX ] = fabs(dSource[ globalPosZ * inputL * inputS + globalPosY * inputL + globalPosX ]); - } -} \ No newline at end of file diff --git a/Modules/PhotoacousticsAlgorithms/Resources/BModeAbsLog.cl b/Modules/PhotoacousticsAlgorithms/Resources/BModeAbsLog.cl deleted file mode 100644 index 842e5a5b52..0000000000 --- a/Modules/PhotoacousticsAlgorithms/Resources/BModeAbsLog.cl +++ /dev/null @@ -1,38 +0,0 @@ -/*=================================================================== - -The Medical Imaging Interaction Toolkit (MITK) - -Copyright (c) German Cancer Research Center, -Division of Medical and Biological Informatics. -All rights reserved. - -This software is distributed WITHOUT ANY WARRANTY; without -even the implied warranty of MERCHANTABILITY or FITNESS FOR -A PARTICULAR PURPOSE. - -See LICENSE.txt or http://www.mitk.org for details. - -===================================================================*/ - -__kernel void ckBmodeAbsLog( - __global float* dSource, // input image - __global float* dDest, // output buffer - unsigned int size -) -{ - // get thread identifier - unsigned int globalPosX = get_global_id(0); - unsigned int globalPosY = get_global_id(1); - unsigned int globalPosZ = get_global_id(2); - - // get image width and height - unsigned short inputS = get_global_size(1); - unsigned short inputL = get_global_size(0); - unsigned short slices = get_global_size(2); - - // terminate non-valid threads - if ( globalPosX + inputL * globalPosY + inputL * inputS * globalPosZ < size ) - { - dDest[ globalPosZ * inputL * inputS + globalPosY * inputL + globalPosX ] = log(fabs(dSource[ globalPosZ * inputL * inputS + globalPosY * inputL + globalPosX ])); - } -} \ No newline at end of file diff --git a/Modules/PhotoacousticsAlgorithms/Resources/DelayCalculation.cl b/Modules/PhotoacousticsAlgorithms/Resources/DelayCalculation.cl index 0be471b2b4..c0ee5224e4 100644 --- a/Modules/PhotoacousticsAlgorithms/Resources/DelayCalculation.cl +++ b/Modules/PhotoacousticsAlgorithms/Resources/DelayCalculation.cl @@ -1,69 +1,70 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ __kernel void ckDelayCalculationQuad( __global unsigned short *gDest, __global unsigned short *usedLines, unsigned int inputL, unsigned int inputS, unsigned int outputL, unsigned int outputS, char isPAImage, - float delayMultiplicatorRaw // parameters + float delayMultiplicatorRaw, + float percentOfImageReconstructed // parameters ) { uint globalPosX = get_global_id(0); uint globalPosY = get_global_id(1); if (globalPosX * 2 < outputL && globalPosY < outputS) { float l_i = 0; // we calculate the delays relative to line zero - float s_i = (float)globalPosY / (float)outputS * (float)inputS / 2; + float s_i = (float)globalPosY / (float)outputS * (float)inputS / (2-isPAImage) * percentOfImageReconstructed; float l_s = (float)globalPosX / (float)outputL * (float)inputL; // the currently calculated line float delayMultiplicator = delayMultiplicatorRaw / s_i; gDest[globalPosY * (outputL / 2) + globalPosX] = delayMultiplicator * pow((l_s - l_i), 2) + s_i + (1-isPAImage)*s_i; } } __kernel void ckDelayCalculationSphe( __global unsigned short *gDest, __global unsigned short *usedLines, unsigned int inputL, unsigned int inputS, unsigned int outputL, unsigned int outputS, char isPAImage, - float delayMultiplicatorRaw // parameters + float delayMultiplicatorRaw, + float percentOfImageReconstructed // parameters ) { uint globalPosX = get_global_id(0); uint globalPosY = get_global_id(1); if (globalPosX * 2 < outputL && globalPosY < outputS) { float l_i = 0; // we calculate the delays relative to line zero - float s_i = (float)globalPosY / (float)outputS * (float)inputS / 2; - + float s_i = (float)globalPosY / (float)outputS * (float)inputS / (2-isPAImage) * percentOfImageReconstructed; float l_s = (float)globalPosX / (float)outputL * (float)inputL; // the currently calculated line gDest[globalPosY * (outputL / 2) + globalPosX] = sqrt( pow(s_i, 2) + pow((delayMultiplicatorRaw * ((l_s - l_i)) / inputL), 2) ) + (1-isPAImage)*s_i; } } \ No newline at end of file diff --git a/Modules/PhotoacousticsAlgorithms/Resources/UsedLinesCalculation.cl b/Modules/PhotoacousticsAlgorithms/Resources/UsedLinesCalculation.cl index 638de2b279..aa63d1562e 100644 --- a/Modules/PhotoacousticsAlgorithms/Resources/UsedLinesCalculation.cl +++ b/Modules/PhotoacousticsAlgorithms/Resources/UsedLinesCalculation.cl @@ -1,50 +1,52 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ __kernel void ckUsedLines( __global unsigned short* dDest, // output buffer float partMult, unsigned int inputL, unsigned int inputS, unsigned int outputL, - unsigned int outputS + unsigned int outputS, + char isPAImage, + float percentOfImageReconstructed // parameters ) { // get thread identifier unsigned int globalPosX = get_global_id(0); unsigned int globalPosY = get_global_id(1); //unsigned short outputS = get_global_size(1); //unsigned short outputL = get_global_size(0); // terminate non-valid threads if ( globalPosX < outputL && globalPosY < outputS) { float l_i = (float)globalPosX / outputL * inputL; - float s_i = (float)globalPosY / outputS * inputS / 2; + float s_i = (float)globalPosY / (float)outputS * (float)inputS / (2-isPAImage) * percentOfImageReconstructed; float part = partMult * s_i; if (part < 1) part = 1; unsigned short maxLine = min((l_i + part) + 1, (float)inputL); unsigned short minLine = max((l_i - part), 0.0f); dDest[globalPosY * 3 * outputL + 3 * globalPosX] = (maxLine - minLine); //usedLines dDest[globalPosY * 3 * outputL + 3 * globalPosX + 1] = minLine; //minLine dDest[globalPosY * 3 * outputL + 3 * globalPosX + 2] = maxLine; //maxLine } } \ No newline at end of file diff --git a/Modules/PhotoacousticsAlgorithms/files.cmake b/Modules/PhotoacousticsAlgorithms/files.cmake index b3fdaff026..7767967748 100644 --- a/Modules/PhotoacousticsAlgorithms/files.cmake +++ b/Modules/PhotoacousticsAlgorithms/files.cmake @@ -1,25 +1,29 @@ file(GLOB_RECURSE H_FILES RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}" "${CMAKE_CURRENT_SOURCE_DIR}/include/*") set(CPP_FILES - source/mitkPhotoacousticImage.cpp - source/mitkPhotoacousticBeamformingFilter.cpp + source/filters/mitkBeamformingFilter.cpp + source/filters/mitkBeamformingSettings.cpp + source/filters/mitkImageSliceSelectionFilter.cpp + source/filters/mitkCastToFloatImageFilter.cpp + source/filters/mitkCropImageFilter.cpp + source/filters/mitkBandpassFilter.cpp source/OpenCLFilter/mitkPhotoacousticBModeFilter.cpp + source/utils/mitkPhotoacousticFilterService.cpp + source/utils/mitkBeamformingUtils.cpp ) IF(MITK_USE_OpenCL) list(APPEND CPP_FILES source/OpenCLFilter/mitkPhotoacousticOCLBeamformingFilter.cpp source/OpenCLFilter/mitkPhotoacousticOCLUsedLinesCalculation.cpp source/OpenCLFilter/mitkPhotoacousticOCLDelayCalculation.cpp ) ENDIF(MITK_USE_OpenCL) set(RESOURCE_FILES - BModeAbs.cl - BModeAbsLog.cl UsedLinesCalculation.cl DelayCalculation.cl DMAS.cl DAS.cl sDMAS.cl ) diff --git a/Modules/PhotoacousticsAlgorithms/include/OpenCLFilter/mitkPhotoacousticBModeFilter.h b/Modules/PhotoacousticsAlgorithms/include/OpenCLFilter/mitkPhotoacousticBModeFilter.h index aed8d626fe..2f692e24fd 100644 --- a/Modules/PhotoacousticsAlgorithms/include/OpenCLFilter/mitkPhotoacousticBModeFilter.h +++ b/Modules/PhotoacousticsAlgorithms/include/OpenCLFilter/mitkPhotoacousticBModeFilter.h @@ -1,140 +1,67 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef _MITKPHOTOACOUSTICSBMODEFILTER_H_ #define _MITKPHOTOACOUSTICSBMODEFILTER_H_ -#if defined(PHOTOACOUSTICS_USE_GPU) || DOXYGEN -#include "mitkOclDataSetToDataSetFilter.h" -#endif - #include #include "mitkImageToImageFilter.h" namespace mitk { - #if defined(PHOTOACOUSTICS_USE_GPU) || DOXYGEN - - /*! - * \brief Class implementing a mitk::OclDataSetToDataSetFilter for BMode filtering on GPU - * - * The only parameter that needs to be provided is whether it should use a logfilter. - * Currently this class only performs an absolute BMode filter. - */ - class PhotoacousticOCLBModeFilter : public OclDataSetToDataSetFilter, public itk::Object - { - - public: - mitkClassMacroItkParent(PhotoacousticOCLBModeFilter, itk::Object); - itkNewMacro(Self); - - /** \brief Set the input image to be processed - */ - void SetInput(Image::Pointer image); - - void Update(); - - /** \brief Set parameters for the filter - * - * @param useLogFilter If true, the filter will apply a logfilter on the processed image - */ - void SetParameters(bool useLogFilter) - { - m_UseLogFilter = useLogFilter; - } - - /** - * @brief GetOutput Returns an mitk::Image constructed from the processed data - */ - mitk::Image::Pointer GetOutput(); - - protected: - - PhotoacousticOCLBModeFilter(); - - virtual ~PhotoacousticOCLBModeFilter(); - - bool Initialize(); - - void Execute(); - - mitk::PixelType GetOutputType() - { - return mitk::MakeScalarPixelType(); - } - - int GetBytesPerElem() - { - return sizeof(float); - } - - virtual us::Module* GetModule(); - - private: - /** The OpenCL kernel for the filter */ - cl_kernel m_PixelCalculation; - bool m_UseLogFilter; - - mitk::Image::Pointer m_InputImage; - unsigned int m_InputDim[3]; - unsigned int m_Size; - - }; - #endif - /*! * \brief Class implementing a mitk::ImageToImageFilter for BMode filtering on CPU * * The only parameter that needs to be provided is whether it should use a logfilter. * Currently this class only performs an absolute BMode filter. */ class PhotoacousticBModeFilter : public ImageToImageFilter { public: mitkClassMacro(PhotoacousticBModeFilter, ImageToImageFilter); - itkFactorylessNewMacro(Self) - itkCloneMacro(Self) + itkFactorylessNewMacro(Self); + itkCloneMacro(Self); /** \brief Set parameters for the filter * * @param useLogFilter If true, the filter will apply a logfilter on the processed image */ - void SetParameters(bool useLogFilter) + void UseLogFilter(bool useLogFilter) { m_UseLogFilter = useLogFilter; } protected: PhotoacousticBModeFilter(); ~PhotoacousticBModeFilter() override; void GenerateInputRequestedRegion() override; void GenerateOutputInformation() override; void GenerateData() override; //##Description //## @brief Time when Header was last initialized itk::TimeStamp m_TimeOfHeaderInitialization; bool m_UseLogFilter; }; } -#endif +#endif /* _MITKPHOTOACOUSTICSBMODEFILTER_H_ */ diff --git a/Modules/PhotoacousticsAlgorithms/include/OpenCLFilter/mitkPhotoacousticOCLBeamformingFilter.h b/Modules/PhotoacousticsAlgorithms/include/OpenCLFilter/mitkPhotoacousticOCLBeamformingFilter.h index 535b95d21d..7490c71f57 100644 --- a/Modules/PhotoacousticsAlgorithms/include/OpenCLFilter/mitkPhotoacousticOCLBeamformingFilter.h +++ b/Modules/PhotoacousticsAlgorithms/include/OpenCLFilter/mitkPhotoacousticOCLBeamformingFilter.h @@ -1,154 +1,145 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef _MITKPHOTOACOUSTICSOCLBEAMFORMER_H_ #define _MITKPHOTOACOUSTICSOCLBEAMFORMER_H_ #include #if defined(PHOTOACOUSTICS_USE_GPU) || DOXYGEN #include "mitkOclDataSetToDataSetFilter.h" - +#include "mitkBeamformingSettings.h" #include "mitkPhotoacousticOCLDelayCalculation.h" #include "mitkPhotoacousticOCLUsedLinesCalculation.h" -#include "mitkPhotoacousticBeamformingSettings.h" #include namespace mitk { /*! * \brief Class implementing a mitk::OclDataSetToDataSetFilter for beamforming on GPU * * The class must be given a configuration class instance of mitk::BeamformingSettings for beamforming parameters through mitk::PhotoacousticOCLBeamformingFilter::SetConfig(BeamformingSettings settings) * Additional configuration of the apodisation function is needed. */ -class PhotoacousticOCLBeamformingFilter : public OclDataSetToDataSetFilter, public itk::Object -{ - -public: - mitkClassMacroItkParent(PhotoacousticOCLBeamformingFilter, itk::Object); - itkNewMacro(Self); - - /** - * @brief SetInput Set the input data through an image. Arbitrary images are supported - */ - void SetInput(Image::Pointer image); - /** - * brief SetInput Manually set the input data while providing 3 dimensions and memory size of the input data (Bytes per element). - */ - void SetInput(void* data, unsigned int* dimensions, unsigned int BpE); - /** - * @brief GetOutput Get a pointer to the processed data. The standard datatype is float. - */ - void* GetOutput(); - - /** - * @brief GetOutputAsImage Returns an mitk::Image constructed from the processed data - */ - mitk::Image::Pointer GetOutputAsImage(); - - /** \brief Update the filter */ - void Update(); - - /** \brief Set the Apodisation function to apply when beamforming */ - void SetApodisation(float* apodisation, unsigned short apodArraySize) + class PhotoacousticOCLBeamformingFilter : public OclDataSetToDataSetFilter, public itk::Object { - m_ApodArraySize = apodArraySize; - m_Apodisation = apodisation; - } - - /** \brief Set beamforming settings to use when beamforming */ - void SetConfig(BeamformingSettings settings) - { - m_ConfOld = m_Conf; - m_Conf = settings; - } + public: + mitkClassMacroItkParent(PhotoacousticOCLBeamformingFilter, itk::Object); + mitkNewMacro1Param(Self, BeamformingSettings::Pointer); + + /** + * @brief SetInput Set the input data through an image. Arbitrary images are supported + */ + void SetInput(Image::Pointer image, unsigned int inputSlices); + /** + * brief SetInput Manually set the input data while providing 3 dimensions and memory size of the input data (Bytes per element). + */ + void SetInput(void* data, unsigned int* dimensions, unsigned int BpE); + /** + * @brief GetOutput Get a pointer to the processed data. The standard datatype is float. + */ + void* GetOutput(); + + /** + * @brief GetOutputAsImage Returns an mitk::Image constructed from the processed data + */ + mitk::Image::Pointer GetOutputAsImage(); + + /** \brief Update the filter */ + void Update(); + + /** \brief Set the Apodisation function to apply when beamforming */ + void SetApodisation(const float* apodisation, unsigned short apodArraySize) + { + m_ApodArraySize = apodArraySize; + m_Apodisation = apodisation; + } -protected: + protected: - PhotoacousticOCLBeamformingFilter(); - virtual ~PhotoacousticOCLBeamformingFilter(); + PhotoacousticOCLBeamformingFilter(BeamformingSettings::Pointer settings); + virtual ~PhotoacousticOCLBeamformingFilter(); - /** \brief Initialize the filter */ - bool Initialize(); + /** \brief Initialize the filter */ + bool Initialize(); - /** \brief Updated the used data for beamforming depending on whether the configuration has significantly changed */ - void UpdateDataBuffers(); + /** \brief Updated the used data for beamforming depending on whether the configuration has significantly changed */ + void UpdateDataBuffers(); - /** \brief Execute the filter */ - void Execute(); + /** \brief Execute the filter */ + void Execute(); - mitk::PixelType GetOutputType() - { - return mitk::MakeScalarPixelType(); - } + mitk::PixelType GetOutputType() + { + return mitk::MakeScalarPixelType(); + } - int GetBytesPerElem() - { - return sizeof(float); - } + int GetBytesPerElem() + { + return sizeof(float); + } - virtual us::Module* GetModule(); + virtual us::Module* GetModule(); -private: - /** The OpenCL kernel for the filter */ - cl_kernel m_PixelCalculation; + private: + /** The OpenCL kernel for the filter */ + cl_kernel m_PixelCalculation; - unsigned int m_OutputDim[3]; + unsigned int m_OutputDim[3]; - float* m_Apodisation; - unsigned short m_ApodArraySize; + const float* m_Apodisation; + unsigned short m_ApodArraySize; + unsigned int m_inputSlices; - unsigned short m_PAImage; + unsigned short m_PAImage; - BeamformingSettings m_Conf; - BeamformingSettings m_ConfOld; + BeamformingSettings::Pointer m_Conf; - mitk::Image::Pointer m_InputImage; + mitk::Image::Pointer m_InputImage; - size_t m_ChunkSize[3]; + size_t m_ChunkSize[3]; - mitk::OCLUsedLinesCalculation::Pointer m_UsedLinesCalculation; - mitk::OCLDelayCalculation::Pointer m_DelayCalculation; + mitk::OCLUsedLinesCalculation::Pointer m_UsedLinesCalculation; + mitk::OCLDelayCalculation::Pointer m_DelayCalculation; - cl_mem m_ApodizationBuffer; - cl_mem m_MemoryLocationsBuffer; - cl_mem m_DelaysBuffer; - cl_mem m_UsedLinesBuffer; -}; + cl_mem m_ApodizationBuffer; + cl_mem m_MemoryLocationsBuffer; + cl_mem m_DelaysBuffer; + cl_mem m_UsedLinesBuffer; + }; } #else namespace mitk { class PhotoacousticOCLBeamformingFilter : public itk::Object { public: mitkClassMacroItkParent(mitk::PhotoacousticOCLBeamformingFilter, itk::Object); itkNewMacro(Self); protected: /** Constructor */ PhotoacousticOCLBeamformingFilter() {} /** Destructor */ ~PhotoacousticOCLBeamformingFilter() override {} }; } #endif #endif diff --git a/Modules/PhotoacousticsAlgorithms/include/OpenCLFilter/mitkPhotoacousticOCLDelayCalculation.h b/Modules/PhotoacousticsAlgorithms/include/OpenCLFilter/mitkPhotoacousticOCLDelayCalculation.h index 061f7c7448..3e60b4ec5c 100644 --- a/Modules/PhotoacousticsAlgorithms/include/OpenCLFilter/mitkPhotoacousticOCLDelayCalculation.h +++ b/Modules/PhotoacousticsAlgorithms/include/OpenCLFilter/mitkPhotoacousticOCLDelayCalculation.h @@ -1,99 +1,89 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef _MITKPHOTOACOUSTICSDELAYCALC_H_ #define _MITKPHOTOACOUSTICSDELAYCALC_H_ #if defined(PHOTOACOUSTICS_USE_GPU) || DOXYGEN #include "mitkOclDataSetToDataSetFilter.h" #include -#include "mitkPhotoacousticBeamformingSettings.h" +#include "mitkBeamformingSettings.h" namespace mitk { /*! * \brief Class implementing a mitk::OclDataSetToDataSetFilter to calculate the delays used for beamforming. * * The class must be given a configuration class instance of mitk::BeamformingSettings for beamforming parameters through mitk::OCLDelayCalculation::SetConfig(BeamformingSettings conf) * Additionally the output of an instance of mitk::OCLUsedLinesCalculation is needed to calculate the delays. */ class OCLDelayCalculation : public OclDataSetToDataSetFilter, public itk::Object { - public: mitkClassMacroItkParent(OCLDelayCalculation, itk::Object); - itkNewMacro(Self); + mitkNewMacro1Param(Self, mitk::BeamformingSettings::Pointer); void Update(); - /** \brief Sets a new configuration to use. - * - * @param conf The configuration set to use for the calculation of the delays. - */ - void SetConfig(BeamformingSettings conf) - { - m_Conf = conf; - } - /** \brief Sets the usedLines buffer object to use for the calculation of the delays. * * @param usedLines An buffer generated as the output of an instance of mitk::OCLUsedLinesCalculation. */ void SetInputs(cl_mem usedLines) { m_UsedLines = usedLines; } protected: - OCLDelayCalculation(); + OCLDelayCalculation(mitk::BeamformingSettings::Pointer settings); virtual ~OCLDelayCalculation(); /** Initialize the filter */ bool Initialize(); void Execute(); mitk::PixelType GetOutputType() { return mitk::MakeScalarPixelType(); } int GetBytesPerElem() { return sizeof(unsigned short); } virtual us::Module* GetModule(); int m_sizeThis; private: /** The OpenCL kernel for the filter */ cl_kernel m_PixelCalculation; - BeamformingSettings m_Conf; + BeamformingSettings::Pointer m_Conf; cl_mem m_UsedLines; unsigned int m_BufferSize; float m_DelayMultiplicatorRaw; char m_IsPAImage; size_t m_ChunkSize[3]; }; } #endif -#endif \ No newline at end of file +#endif diff --git a/Modules/PhotoacousticsAlgorithms/include/OpenCLFilter/mitkPhotoacousticOCLUsedLinesCalculation.h b/Modules/PhotoacousticsAlgorithms/include/OpenCLFilter/mitkPhotoacousticOCLUsedLinesCalculation.h index e787d2009a..4394764e23 100644 --- a/Modules/PhotoacousticsAlgorithms/include/OpenCLFilter/mitkPhotoacousticOCLUsedLinesCalculation.h +++ b/Modules/PhotoacousticsAlgorithms/include/OpenCLFilter/mitkPhotoacousticOCLUsedLinesCalculation.h @@ -1,90 +1,79 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef _MITKPHOTOACOUSTICSOCLUSEDLINESCALCULATION_H_ #define _MITKPHOTOACOUSTICSOCLUSEDLINESCALCULATION_H_ #if defined(PHOTOACOUSTICS_USE_GPU) || DOXYGEN #include "mitkOclDataSetToDataSetFilter.h" #include -#include "mitkPhotoacousticBeamformingSettings.h" +#include "mitkBeamformingSettings.h" namespace mitk { /*! * \brief Class implementing a mitk::OclDataSetToDataSetFilter to calculate which lines each sample should use when beamforming. * * The class must be given a configuration class instance of mitk::BeamformingSettings for beamforming parameters through mitk::OCLDelayCalculation::SetConfig(BeamformingSettings conf) */ class OCLUsedLinesCalculation : public OclDataSetToDataSetFilter, public itk::Object { - public: mitkClassMacroItkParent(OCLUsedLinesCalculation, itk::Object); - itkNewMacro(Self); + mitkNewMacro1Param(Self, mitk::BeamformingSettings::Pointer); void Update(); - /** \brief Sets a new configuration to use. - * - * @param conf The configuration set to use for the calculation of the used lines. - */ - void SetConfig(BeamformingSettings conf) - { - m_Conf = conf; - } - protected: /** Constructor */ - OCLUsedLinesCalculation(); + OCLUsedLinesCalculation(mitk::BeamformingSettings::Pointer settings); /** Destructor */ virtual ~OCLUsedLinesCalculation(); /** Initialize the filter */ bool Initialize(); void Execute(); mitk::PixelType GetOutputType() { return mitk::MakeScalarPixelType(); } int GetBytesPerElem() { return sizeof(unsigned short); } virtual us::Module* GetModule(); int m_sizeThis; - private: /** The OpenCL kernel for the filter */ cl_kernel m_PixelCalculation; - BeamformingSettings m_Conf; + BeamformingSettings::Pointer m_Conf; float m_part; size_t m_ChunkSize[3]; }; } #endif #endif diff --git a/Modules/PhotoacousticsAlgorithms/include/mitkBandpassFilter.h b/Modules/PhotoacousticsAlgorithms/include/mitkBandpassFilter.h new file mode 100644 index 0000000000..375e34ce34 --- /dev/null +++ b/Modules/PhotoacousticsAlgorithms/include/mitkBandpassFilter.h @@ -0,0 +1,59 @@ +/*=================================================================== + +The Medical Imaging Interaction Toolkit (MITK) + +Copyright (c) German Cancer Research Center, +Division of Medical and Biological Informatics. +All rights reserved. + +This software is distributed WITHOUT ANY WARRANTY; without +even the implied warranty of MERCHANTABILITY or FITNESS FOR +A PARTICULAR PURPOSE. + +See LICENSE.txt or http://www.mitk.org for details. + +===================================================================*/ + +#ifndef MITK_BANDPASS_FILTER +#define MITK_BANDPASS_FILTER + +#include "mitkImageToImageFilter.h" +#include "MitkPhotoacousticsAlgorithmsExports.h" +#include "mitkPhotoacousticFilterService.h" + +namespace mitk { + /*! + * \brief Class implementing an mitk::ImageToImageFilter for casting any mitk image to a float image + */ + + class MITKPHOTOACOUSTICSALGORITHMS_EXPORT BandpassFilter : public ImageToImageFilter + { + public: + mitkClassMacro(BandpassFilter, ImageToImageFilter); + + itkFactorylessNewMacro(Self); + itkCloneMacro(Self); + + itkSetMacro(HighPass, float); + itkSetMacro(LowPass, float); + itkSetMacro(HighPassAlpha, float); + itkSetMacro(LowPassAlpha, float); + + protected: + BandpassFilter(); + + ~BandpassFilter() override; + + void SanityCheckPreconditions(); + + float m_HighPass; + float m_LowPass; + float m_HighPassAlpha; + float m_LowPassAlpha; + mitk::PhotoacousticFilterService::Pointer m_FilterService; + + void GenerateData() override; + }; +} // namespace mitk + +#endif //MITK_CAST_TO_FLOAT_IMAGE_FILTER diff --git a/Modules/PhotoacousticsAlgorithms/include/mitkBeamformingFilter.h b/Modules/PhotoacousticsAlgorithms/include/mitkBeamformingFilter.h new file mode 100644 index 0000000000..8724808d36 --- /dev/null +++ b/Modules/PhotoacousticsAlgorithms/include/mitkBeamformingFilter.h @@ -0,0 +1,89 @@ +/*=================================================================== + +The Medical Imaging Interaction Toolkit (MITK) + +Copyright (c) German Cancer Research Center, +Division of Medical and Biological Informatics. +All rights reserved. + +This software is distributed WITHOUT ANY WARRANTY; without +even the implied warranty of MERCHANTABILITY or FITNESS FOR +A PARTICULAR PURPOSE. + +See LICENSE.txt or http://www.mitk.org for details. + +===================================================================*/ + +#ifndef MITK_PHOTOACOUSTICS_BEAMFORMING_FILTER +#define MITK_PHOTOACOUSTICS_BEAMFORMING_FILTER + +#include "mitkImageToImageFilter.h" +#include +#include "./OpenCLFilter/mitkPhotoacousticOCLBeamformingFilter.h" +#include "mitkBeamformingSettings.h" +#include "mitkBeamformingUtils.h" +#include "MitkPhotoacousticsAlgorithmsExports.h" + +namespace mitk { + /*! + * \brief Class implementing an mitk::ImageToImageFilter for beamforming on both CPU and GPU + * + * The class must be given a configuration class instance of mitk::BeamformingSettings for beamforming parameters through mitk::BeamformingFilter::Configure(BeamformingSettings settings) + * Whether the GPU is used can be set in the configuration. + * For significant problems or important messages a string is written, which can be accessed via GetMessageString(). + */ + + class MITKPHOTOACOUSTICSALGORITHMS_EXPORT BeamformingFilter : public ImageToImageFilter + { + public: + mitkClassMacro(BeamformingFilter, ImageToImageFilter); + + mitkNewMacro1Param(Self, mitk::BeamformingSettings::Pointer); + itkCloneMacro(Self); + + /** \brief Sets a callback for progress checking + * + * An std::function can be set, through which progress of the currently updating filter is reported. + * The integer argument is a number between 0 an 100 to indicate how far completion has been achieved, the std::string argument indicates what the filter is currently doing. + */ + void SetProgressHandle(std::function progressHandle); + + protected: + BeamformingFilter(mitk::BeamformingSettings::Pointer settings); + + ~BeamformingFilter() override; + + void GenerateInputRequestedRegion() override; + + void GenerateOutputInformation() override; + + void GenerateData() override; + + //##Description + //## @brief Time when Header was last initialized + itk::TimeStamp m_TimeOfHeaderInitialization; + + /** \brief The std::function, through which progress of the currently updating filter is reported. + */ + std::function m_ProgressHandle; + + float* m_OutputData; + float* m_InputData; + float* m_InputDataPuffer; + + /** \brief Current configuration set + */ + BeamformingSettings::Pointer m_Conf; + + /** + * The size of the apodization array when it last changed. + */ + int m_LastApodizationArraySize; + + /** \brief Pointer to the GPU beamforming filter class; for performance reasons the filter is initialized within the constructor and kept for all later computations. + */ + mitk::PhotoacousticOCLBeamformingFilter::Pointer m_BeamformingOclFilter; + }; +} // namespace mitk + +#endif //MITK_PHOTOACOUSTICS_BEAMFORMING_FILTER diff --git a/Modules/PhotoacousticsAlgorithms/include/mitkBeamformingSettings.h b/Modules/PhotoacousticsAlgorithms/include/mitkBeamformingSettings.h new file mode 100644 index 0000000000..5b76798dee --- /dev/null +++ b/Modules/PhotoacousticsAlgorithms/include/mitkBeamformingSettings.h @@ -0,0 +1,223 @@ +/*=================================================================== + +The Medical Imaging Interaction Toolkit (MITK) + +Copyright (c) German Cancer Research Center, +Division of Medical and Biological Informatics. +All rights reserved. + +This software is distributed WITHOUT ANY WARRANTY; without +even the implied warranty of MERCHANTABILITY or FITNESS FOR +A PARTICULAR PURPOSE. + +See LICENSE.txt or http://www.mitk.org for details. + +===================================================================*/ + +#ifndef MITK_BEAMFORMING_SETTINGS +#define MITK_BEAMFORMING_SETTINGS + +#include +#include +#include +#include + +namespace mitk { + /*! + * \brief Class holding the configuration data for the beamforming filters mitk::BeamformingFilter and mitk::PhotoacousticOCLBeamformingFilter + * + * A detailed description can be seen below. All parameters should be set manually for successfull beamforming. + */ + + class MITKPHOTOACOUSTICSALGORITHMS_EXPORT BeamformingSettings : public itk::Object + { + public: + mitkClassMacroItkParent(BeamformingSettings, itk::Object); + mitkNewMacro1Param(BeamformingSettings, std::string); + itkCloneMacro(Self); + + /** \brief Available delay calculation methods: + * - Spherical delay for best results. + * - DEPRECATED quadratic Taylor approximation for slightly faster results with hardly any quality loss. + */ + enum DelayCalc { QuadApprox, Spherical }; + + /** \brief Available apodization functions: + * - Hamming function. + * - Von-Hann function. + * - Box function. + */ + enum Apodization { Hamm, Hann, Box }; + + /** \brief Available beamforming algorithms: + * - DAS (Delay and sum). + * - DMAS (Delay multiply and sum). + */ + enum BeamformingAlgorithm { DMAS, DAS, sDMAS }; + + itkGetConstMacro(PitchInMeters, float); + itkGetConstMacro(SpeedOfSound, float); + itkGetConstMacro(TimeSpacing, float); + itkGetConstMacro(Angle, float); + itkGetConstMacro(IsPhotoacousticImage, bool); + itkGetConstMacro(TransducerElements, unsigned int); + itkGetConstMacro(SamplesPerLine, unsigned int); + itkGetConstMacro(ReconstructionLines, unsigned int); + itkGetConstMacro(InputDim, const unsigned int*); + itkGetConstMacro(UseGPU, bool); + itkGetConstMacro(GPUBatchSize, unsigned int); + itkGetConstMacro(DelayCalculationMethod, DelayCalc); + itkGetConstMacro(ApodizationFunction, const float*); + itkGetConstMacro(Apod, Apodization); + itkGetConstMacro(ApodizationArraySize, int); + itkGetConstMacro(Algorithm, BeamformingAlgorithm); + itkGetConstMacro(ReconstructionDepth, float); + + /** \brief function for mitk::PhotoacousticOCLBeamformingFilter to check whether buffers need to be updated + * this method only checks parameters relevant for the openCL implementation + */ + static bool SettingsChangedOpenCL(const BeamformingSettings::Pointer lhs, const BeamformingSettings::Pointer rhs) + { + return !((abs(lhs->GetAngle() - rhs->GetAngle()) < 0.01f) && // 0.01 degree error margin + (lhs->GetApod() == rhs->GetApod()) && + (lhs->GetDelayCalculationMethod() == rhs->GetDelayCalculationMethod()) && + (lhs->GetIsPhotoacousticImage() == rhs->GetIsPhotoacousticImage()) && + (abs(lhs->GetPitchInMeters() - rhs->GetPitchInMeters()) < 0.000001f) && // 0.0001 mm error margin + (lhs->GetReconstructionLines() == rhs->GetReconstructionLines()) && + (lhs->GetSamplesPerLine() == rhs->GetSamplesPerLine()) && + (lhs->GetReconstructionDepth() == rhs->GetReconstructionDepth()) && + (abs(lhs->GetSpeedOfSound() - rhs->GetSpeedOfSound()) < 0.01f) && + (abs(lhs->GetTimeSpacing() - rhs->GetTimeSpacing()) < 0.00000000001f) && //0.01 ns error margin + (lhs->GetTransducerElements() == rhs->GetTransducerElements())); + } + + static Pointer New(float pitchInMeters, + float speedOfSound, + float timeSpacing, + float angle, + bool isPhotoacousticImage, + unsigned int samplesPerLine, + unsigned int reconstructionLines, + unsigned int* inputDim, + float reconstructionDepth, + bool useGPU, + unsigned int GPUBatchSize, + DelayCalc delayCalculationMethod, + Apodization apod, + unsigned int apodizationArraySize, + BeamformingAlgorithm algorithm) + { + Pointer smartPtr = new BeamformingSettings(pitchInMeters, + speedOfSound, + timeSpacing, + angle, + isPhotoacousticImage, + samplesPerLine, + reconstructionLines, + inputDim, + reconstructionDepth, + useGPU, + GPUBatchSize, + delayCalculationMethod, + apod, + apodizationArraySize, + algorithm); + smartPtr->UnRegister(); + return smartPtr; + } + + protected: + + /** + */ + BeamformingSettings(std::string xmlFile); + + /** + */ + BeamformingSettings(float pitchInMeters, + float speedOfSound, + float timeSpacing, + float angle, + bool isPhotoacousticImage, + unsigned int samplesPerLine, + unsigned int reconstructionLines, + unsigned int* inputDim, + float reconstructionDepth, + bool useGPU, + unsigned int GPUBatchSize, + DelayCalc delayCalculationMethod, + Apodization apod, + unsigned int apodizationArraySize, + BeamformingAlgorithm algorithm + ); + + ~BeamformingSettings(); + + /** \brief Pitch of the used transducer in [m]. + */ + float m_PitchInMeters; + + /** \brief Speed of sound in the used medium in [m/s]. + */ + float m_SpeedOfSound; + + /** \brief The time spacing of the input image + */ + float m_TimeSpacing; // [s] + + /** \brief The angle of the transducer elements + */ + float m_Angle; + + /** \brief Flag whether processed image is a photoacoustic image or an ultrasound image + */ + bool m_IsPhotoacousticImage; + + /** \brief How many transducer elements the used transducer had. + */ + unsigned int m_TransducerElements; + + /** \brief How many vertical samples should be used in the final image. + */ + unsigned int m_SamplesPerLine; + + /** \brief How many lines should be reconstructed in the final image. + */ + unsigned int m_ReconstructionLines; + + /** \brief Sets the dimensions of the inputImage. + */ + const unsigned int* m_InputDim; + + /** \brief The Depth up to which the filter should reconstruct the image [m] + */ + float m_ReconstructionDepth; + + /** \brief Decides whether GPU computing should be used + */ + bool m_UseGPU; + + unsigned int m_GPUBatchSize; + /** \brief Sets the amount of image slices in batches when GPU is used + */ + + /** \brief Sets how the delays for beamforming should be calculated. + */ + DelayCalc m_DelayCalculationMethod; + + const float* m_ApodizationFunction; + + /** \brief Sets the used apodization function. + */ + Apodization m_Apod; + + /** \brief Sets the resolution of the apodization array (must be greater than 0). + */ + int m_ApodizationArraySize; + + /** \brief Sets the used beamforming algorithm. + */ + BeamformingAlgorithm m_Algorithm; + }; +} +#endif //MITK_BEAMFORMING_SETTINGS diff --git a/Modules/PhotoacousticsAlgorithms/include/mitkBeamformingUtils.h b/Modules/PhotoacousticsAlgorithms/include/mitkBeamformingUtils.h new file mode 100644 index 0000000000..9060284cfd --- /dev/null +++ b/Modules/PhotoacousticsAlgorithms/include/mitkBeamformingUtils.h @@ -0,0 +1,80 @@ +/*=================================================================== + +The Medical Imaging Interaction Toolkit (MITK) + +Copyright (c) German Cancer Research Center, +Division of Medical and Biological Informatics. +All rights reserved. + +This software is distributed WITHOUT ANY WARRANTY; without +even the implied warranty of MERCHANTABILITY or FITNESS FOR +A PARTICULAR PURPOSE. + +See LICENSE.txt or http://www.mitk.org for details. + +===================================================================*/ + +#ifndef MITK_BEAMFORMING_FILTER_UTILS +#define MITK_BEAMFORMING_FILTER_UTILS + +#include "mitkImageToImageFilter.h" +#include +#include "./OpenCLFilter/mitkPhotoacousticOCLBeamformingFilter.h" +#include "mitkBeamformingSettings.h" + +namespace mitk { + /*! + * \brief Class implementing util functionality for beamforming on CPU + * + */ + class BeamformingUtils final + { + public: + + /** \brief Function to perform beamforming on CPU for a single line, using DAS and quadratic delay + */ + static void DASQuadraticLine(float* input, float* output, float inputDim[2], float outputDim[2], const short& line, const mitk::BeamformingSettings::Pointer config); + + /** \brief Function to perform beamforming on CPU for a single line, using DAS and spherical delay + */ + static void DASSphericalLine(float* input, float* output, float inputDim[2], float outputDim[2], const short& line, const mitk::BeamformingSettings::Pointer config); + + /** \brief Function to perform beamforming on CPU for a single line, using DMAS and quadratic delay + */ + static void DMASQuadraticLine(float* input, float* output, float inputDim[2], float outputDim[2], const short& line, const mitk::BeamformingSettings::Pointer config); + + /** \brief Function to perform beamforming on CPU for a single line, using DMAS and spherical delay + */ + static void DMASSphericalLine(float* input, float* output, float inputDim[2], float outputDim[2], const short& line, const mitk::BeamformingSettings::Pointer config); + + /** \brief Function to perform beamforming on CPU for a single line, using signed DMAS and quadratic delay + */ + static void sDMASQuadraticLine(float* input, float* output, float inputDim[2], float outputDim[2], const short& line, const mitk::BeamformingSettings::Pointer config); + + /** \brief Function to perform beamforming on CPU for a single line, using signed DMAS and spherical delay + */ + static void sDMASSphericalLine(float* input, float* output, float inputDim[2], float outputDim[2], const short& line, const mitk::BeamformingSettings::Pointer config); + + /** \brief Pointer holding the Von-Hann apodization window for beamforming + * @param samples the resolution at which the window is created + */ + static float* VonHannFunction(int samples); + + /** \brief Function to create a Hamming apodization window + * @param samples the resolution at which the window is created + */ + static float* HammFunction(int samples); + + /** \brief Function to create a Box apodization window + * @param samples the resolution at which the window is created + */ + static float* BoxFunction(int samples); + + protected: + BeamformingUtils(); + + ~BeamformingUtils(); + }; +} // namespace mitk + +#endif //MITK_BEAMFORMING_FILTER_UTILS diff --git a/Modules/PhotoacousticsAlgorithms/include/mitkCastToFloatImageFilter.h b/Modules/PhotoacousticsAlgorithms/include/mitkCastToFloatImageFilter.h new file mode 100644 index 0000000000..0a109c5602 --- /dev/null +++ b/Modules/PhotoacousticsAlgorithms/include/mitkCastToFloatImageFilter.h @@ -0,0 +1,45 @@ +/*=================================================================== + +The Medical Imaging Interaction Toolkit (MITK) + +Copyright (c) German Cancer Research Center, +Division of Medical and Biological Informatics. +All rights reserved. + +This software is distributed WITHOUT ANY WARRANTY; without +even the implied warranty of MERCHANTABILITY or FITNESS FOR +A PARTICULAR PURPOSE. + +See LICENSE.txt or http://www.mitk.org for details. + +===================================================================*/ + +#ifndef MITK_CAST_TO_FLOAT_IMAGE_FILTER +#define MITK_CAST_TO_FLOAT_IMAGE_FILTER + +#include "mitkImageToImageFilter.h" +#include "MitkPhotoacousticsAlgorithmsExports.h" + +namespace mitk { + /*! + * \brief Class implementing an mitk::ImageToImageFilter for casting any mitk image to a float image + */ + + class MITKPHOTOACOUSTICSALGORITHMS_EXPORT CastToFloatImageFilter : public ImageToImageFilter + { + public: + mitkClassMacro(CastToFloatImageFilter, ImageToImageFilter); + + itkFactorylessNewMacro(Self); + itkCloneMacro(Self); + + protected: + CastToFloatImageFilter(); + + ~CastToFloatImageFilter() override; + + void GenerateData() override; + }; +} // namespace mitk + +#endif //MITK_CAST_TO_FLOAT_IMAGE_FILTER diff --git a/Modules/PhotoacousticsAlgorithms/include/mitkCropImageFilter.h b/Modules/PhotoacousticsAlgorithms/include/mitkCropImageFilter.h new file mode 100644 index 0000000000..017cc8327f --- /dev/null +++ b/Modules/PhotoacousticsAlgorithms/include/mitkCropImageFilter.h @@ -0,0 +1,69 @@ +/*=================================================================== + +The Medical Imaging Interaction Toolkit (MITK) + +Copyright (c) German Cancer Research Center, +Division of Medical and Biological Informatics. +All rights reserved. + +This software is distributed WITHOUT ANY WARRANTY; without +even the implied warranty of MERCHANTABILITY or FITNESS FOR +A PARTICULAR PURPOSE. + +See LICENSE.txt or http://www.mitk.org for details. + +===================================================================*/ + +#ifndef MITK_CROP_IMAGE_FILTER +#define MITK_CROP_IMAGE_FILTER + +#include +#include "mitkImageToImageFilter.h" +#include "itkMacro.h" + +namespace mitk { + /*! + * \brief Class implementing an mitk::ImageToImageFilter for casting any mitk image to a float image + */ + + class MITKPHOTOACOUSTICSALGORITHMS_EXPORT CropImageFilter : public ImageToImageFilter + { + public: + mitkClassMacro(CropImageFilter, ImageToImageFilter); + + itkFactorylessNewMacro(Self); + itkCloneMacro(Self); + + itkGetMacro(XPixelsCropStart, unsigned int); + itkSetMacro(XPixelsCropStart, unsigned int); + itkGetMacro(YPixelsCropStart, unsigned int); + itkSetMacro(YPixelsCropStart, unsigned int); + itkGetMacro(ZPixelsCropStart, unsigned int); + itkSetMacro(ZPixelsCropStart, unsigned int); + + itkGetMacro(XPixelsCropEnd, unsigned int); + itkSetMacro(XPixelsCropEnd, unsigned int); + itkGetMacro(YPixelsCropEnd, unsigned int); + itkSetMacro(YPixelsCropEnd, unsigned int); + itkGetMacro(ZPixelsCropEnd, unsigned int); + itkSetMacro(ZPixelsCropEnd, unsigned int); + + protected: + CropImageFilter(); + + ~CropImageFilter() override; + + void GenerateData() override; + + void SanityCheckPreconditions(); + + unsigned int m_XPixelsCropStart; + unsigned int m_YPixelsCropStart; + unsigned int m_ZPixelsCropStart; + unsigned int m_XPixelsCropEnd; + unsigned int m_YPixelsCropEnd; + unsigned int m_ZPixelsCropEnd; + }; +} // namespace mitk + +#endif //MITK_CROP_IMAGE_FILTER diff --git a/Modules/PhotoacousticsAlgorithms/include/mitkImageSliceSelectionFilter.h b/Modules/PhotoacousticsAlgorithms/include/mitkImageSliceSelectionFilter.h new file mode 100644 index 0000000000..ac51c1176d --- /dev/null +++ b/Modules/PhotoacousticsAlgorithms/include/mitkImageSliceSelectionFilter.h @@ -0,0 +1,44 @@ +/*=================================================================== + +The Medical Imaging Interaction Toolkit (MITK) + +Copyright (c) German Cancer Research Center, +Division of Medical and Biological Informatics. +All rights reserved. + +This software is distributed WITHOUT ANY WARRANTY; without +even the implied warranty of MERCHANTABILITY or FITNESS FOR +A PARTICULAR PURPOSE. + +See LICENSE.txt or http://www.mitk.org for details. + +===================================================================*/ + +#ifndef MITK_IMAGE_SLICE_SELECTION_FILTER +#define MITK_IMAGE_SLICE_SELECTION_FILTER + +#include "mitkImageToImageFilter.h" + +namespace mitk { + /*! + * \brief Class implementing an mitk::ImageToImageFilter for casting any mitk image to a float image + */ + + class ImageSliceSelectionFilter : public ImageToImageFilter + { + public: + mitkClassMacro(ImageSliceSelectionFilter, ImageToImageFilter); + + itkFactorylessNewMacro(Self); + itkCloneMacro(Self); + + protected: + ImageSliceSelectionFilter(); + + ~ImageSliceSelectionFilter() override; + + void GenerateData() override; + }; +} // namespace mitk + +#endif //MITK_IMAGE_SLICE_SELECTION_FILTER diff --git a/Modules/PhotoacousticsAlgorithms/include/mitkPhotoacousticBeamformingFilter.h b/Modules/PhotoacousticsAlgorithms/include/mitkPhotoacousticBeamformingFilter.h deleted file mode 100644 index 7908fccbae..0000000000 --- a/Modules/PhotoacousticsAlgorithms/include/mitkPhotoacousticBeamformingFilter.h +++ /dev/null @@ -1,151 +0,0 @@ -/*=================================================================== - -The Medical Imaging Interaction Toolkit (MITK) - -Copyright (c) German Cancer Research Center, -Division of Medical and Biological Informatics. -All rights reserved. - -This software is distributed WITHOUT ANY WARRANTY; without -even the implied warranty of MERCHANTABILITY or FITNESS FOR -A PARTICULAR PURPOSE. - -See LICENSE.txt or http://www.mitk.org for details. - -===================================================================*/ - -#ifndef MITK_PHOTOACOUSTICS_BEAMFORMING_FILTER -#define MITK_PHOTOACOUSTICS_BEAMFORMING_FILTER - -#include "mitkImageToImageFilter.h" -#include -#include "./OpenCLFilter/mitkPhotoacousticOCLBeamformingFilter.h" -#include "mitkPhotoacousticBeamformingSettings.h" - -namespace mitk { - /*! - * \brief Class implementing an mitk::ImageToImageFilter for beamforming on both CPU and GPU - * - * The class must be given a configuration class instance of mitk::BeamformingSettings for beamforming parameters through mitk::BeamformingFilter::Configure(BeamformingSettings settings) - * Whether the GPU is used can be set in the configuration. - * For significant problems or important messages a string is written, which can be accessed via GetMessageString(). - */ - - class BeamformingFilter : public ImageToImageFilter - { - public: - mitkClassMacro(BeamformingFilter, ImageToImageFilter); - - itkFactorylessNewMacro(Self) - itkCloneMacro(Self) - - /** \brief Sets a new configuration to use - * - * @param settings The configuration set to use for beamforming - */ - void Configure(BeamformingSettings settings) - { - m_ConfOld = m_Conf; - m_Conf = settings; - } - - /** \brief Sets a new configuration to use - * - * The Filter writes important messages that can be retrieved through this method; if nothing is to be reported, it returns "noMessage". - * @return The message - */ - std::string GetMessageString() - { - return m_Message; - } - - /** \brief Sets a callback for progress checking - * - * An std::function can be set, through which progress of the currently updating filter is reported. - * The integer argument is a number between 0 an 100 to indicate how far completion has been achieved, the std::string argument indicates what the filter is currently doing. - */ - void SetProgressHandle(std::function progressHandle); - - protected: - BeamformingFilter(); - - ~BeamformingFilter() override; - - void GenerateInputRequestedRegion() override; - - void GenerateOutputInformation() override; - - void GenerateData() override; - - //##Description - //## @brief Time when Header was last initialized - itk::TimeStamp m_TimeOfHeaderInitialization; - - /** \brief The std::function, through which progress of the currently updating filter is reported. - */ - std::function m_ProgressHandle; - - /** \brief Pointer holding the Von-Hann apodization window for beamforming - * @param samples the resolution at which the window is created - */ - float* VonHannFunction(int samples); - /** \brief Function to create a Hamming apodization window - * @param samples the resolution at which the window is created - */ - float* HammFunction(int samples); - /** \brief Function to create a Box apodization window - * @param samples the resolution at which the window is created - */ - float* BoxFunction(int samples); - - /** \brief Function to perform beamforming on CPU for a single line, using DAS and quadratic delay - */ - void DASQuadraticLine(float* input, float* output, float inputDim[2], float outputDim[2], const short& line, float* apodisation, const short& apodArraySize); - /** \brief Function to perform beamforming on CPU for a single line, using DAS and spherical delay - */ - void DASSphericalLine(float* input, float* output, float inputDim[2], float outputDim[2], const short& line, float* apodisation, const short& apodArraySize); - /** \brief Function to perform beamforming on CPU for a single line, using DMAS and quadratic delay - */ - void DMASQuadraticLine(float* input, float* output, float inputDim[2], float outputDim[2], const short& line, float* apodisation, const short& apodArraySize); - /** \brief Function to perform beamforming on CPU for a single line, using DMAS and spherical delay - */ - void DMASSphericalLine(float* input, float* output, float inputDim[2], float outputDim[2], const short& line, float* apodisation, const short& apodArraySize); - /** \brief Function to perform beamforming on CPU for a single line, using signed DMAS and quadratic delay - */ - void sDMASQuadraticLine(float* input, float* output, float inputDim[2], float outputDim[2], const short& line, float* apodisation, const short& apodArraySize); - /** \brief Function to perform beamforming on CPU for a single line, using signed DMAS and spherical delay - */ - void sDMASSphericalLine(float* input, float* output, float inputDim[2], float outputDim[2], const short& line, float* apodisation, const short& apodArraySize); - - float* m_OutputData; - float* m_InputData; - float* m_InputDataPuffer; - - /** \brief Pointer holding the Von-Hann apodization window for beamforming - */ - float* m_VonHannFunction; - /** \brief Pointer holding the Hamming apodization window for beamforming - */ - float* m_HammFunction; - /** \brief Pointer holding the Box apodization window for beamforming - */ - float* m_BoxFunction; - - /** \brief Current configuration set - */ - BeamformingSettings m_Conf; - /** \brief Previous configuration set to selectively update used data for beamforming - */ - BeamformingSettings m_ConfOld; - - /** \brief Pointer to the GPU beamforming filter class; for performance reasons the filter is initialized within the constructor and kept for all later computations. - */ - mitk::PhotoacousticOCLBeamformingFilter::Pointer m_BeamformingOclFilter; - - /** \brief The message returned by mitk::BeamformingFilter::GetMessageString() - */ - std::string m_Message; - }; -} // namespace mitk - -#endif //MITK_PHOTOACOUSTICS_BEAMFORMING_FILTER diff --git a/Modules/PhotoacousticsAlgorithms/include/mitkPhotoacousticBeamformingSettings.h b/Modules/PhotoacousticsAlgorithms/include/mitkPhotoacousticBeamformingSettings.h deleted file mode 100644 index a48b5e78a3..0000000000 --- a/Modules/PhotoacousticsAlgorithms/include/mitkPhotoacousticBeamformingSettings.h +++ /dev/null @@ -1,137 +0,0 @@ -/*=================================================================== - -The Medical Imaging Interaction Toolkit (MITK) - -Copyright (c) German Cancer Research Center, -Division of Medical and Biological Informatics. -All rights reserved. - -This software is distributed WITHOUT ANY WARRANTY; without -even the implied warranty of MERCHANTABILITY or FITNESS FOR -A PARTICULAR PURPOSE. - -See LICENSE.txt or http://www.mitk.org for details. - -===================================================================*/ - -#ifndef MITK_PHOTOACOUSTICS_BEAMFORMING_SETTINGS -#define MITK_PHOTOACOUSTICS_BEAMFORMING_SETTINGS - - - -namespace mitk { - /*! - * \brief Class holding the configuration data for the beamforming filters mitk::BeamformingFilter and mitk::PhotoacousticOCLBeamformingFilter - * - * A detailed description can be seen below. All parameters should be set manually for successfull beamforming. - */ - - class BeamformingSettings - { - public: - /** \brief Pitch of the used transducer in [m]. - */ - float Pitch = 0.0003; - /** \brief Speed of sound in the used medium in [m/s]. - */ - float SpeedOfSound = 1540; - /** \brief This parameter is not neccessary to be set, as it's never used. - */ - float RecordTime = 0.00006; // [s] - /** \brief The time spacing of the input image - */ - float TimeSpacing = 0.0000000000001; // [s] - /** \brief The angle of the transducer elements - */ - float Angle = -1; - /** \brief Flag whether processed image is a photoacoustic image or an ultrasound image - */ - bool isPhotoacousticImage = true; - - /** \brief How many transducer elements the used transducer had. - */ - unsigned short TransducerElements = 128; - /** \brief How many vertical samples should be used in the final image. - */ - unsigned int SamplesPerLine = 2048; - /** \brief How many lines should be reconstructed in the final image. - */ - unsigned int ReconstructionLines = 128; - /** \brief Sets how many voxels should be cut off from the top of the image before beamforming, to potentially avoid artifacts. - */ - unsigned int upperCutoff = 0; - /** \brief Sets whether only the slices selected by mitk::BeamformingSettings::CropBounds should be beamformed. - */ - bool partial = false; - /** \brief Sets the first and last slice to be beamformed. - */ - unsigned int CropBounds[2] = { 0,0 }; - /** \brief Sets the dimensions of the inputImage. - */ - unsigned int inputDim[3] = { 1,1,1 }; - - /** \brief Decides whether GPU computing should be used - */ - bool UseGPU = true; - - /** \brief Available delay calculation methods: - * - Spherical delay for best results. - * - A quadratic Taylor approximation for slightly faster results with hardly any quality loss. - */ - enum DelayCalc { QuadApprox, Spherical }; - /** \brief Sets how the delays for beamforming should be calculated. - */ - DelayCalc DelayCalculationMethod = QuadApprox; - - /** \brief Available apodization functions: - * - Hamming function. - * - Von-Hann function. - * - Box function. - */ - enum Apodization { Hamm, Hann, Box }; - /** \brief Sets the used apodization function. - */ - Apodization Apod = Hann; - /** \brief Sets the resolution of the apodization array (must be greater than 0). - */ - int apodizationArraySize = 128; - - /** \brief Available beamforming algorithms: - * - DAS (Delay and sum). - * - DMAS (Delay multiply and sum). - */ - enum BeamformingAlgorithm { DMAS, DAS, sDMAS}; - /** \brief Sets the used beamforming algorithm. - */ - BeamformingAlgorithm Algorithm = DAS; - - /** \brief Sets whether after beamforming a bandpass should be automatically applied - */ - bool UseBP = false; - /** \brief Sets the position at which lower frequencies are completely cut off in Hz. - */ - float BPHighPass = 50; - /** \brief Sets the position at which higher frequencies are completely cut off in Hz. - */ - float BPLowPass = 50; - - /** \brief function for mitk::PhotoacousticOCLBeamformingFilter to check whether buffers need to be updated - * this method only checks parameters relevant for the openCL implementation - */ - static bool SettingsChangedOpenCL(const BeamformingSettings& lhs, const BeamformingSettings& rhs) - { - return !((abs(lhs.Angle - rhs.Angle) < 0.01f) && // 0.01 degree error margin - (lhs.Apod == rhs.Apod) && - (lhs.DelayCalculationMethod == rhs.DelayCalculationMethod) && - (lhs.isPhotoacousticImage == rhs.isPhotoacousticImage) && - (abs(lhs.Pitch - rhs.Pitch) < 0.000001f) && // 0.0001 mm error margin - (lhs.ReconstructionLines == rhs.ReconstructionLines) && - (abs(lhs.RecordTime - rhs.RecordTime) < 0.00000001f) && // 10 ns error margin - (lhs.SamplesPerLine == rhs.SamplesPerLine) && - (abs(lhs.SpeedOfSound - rhs.SpeedOfSound) < 0.01f) && - (abs(lhs.TimeSpacing - rhs.TimeSpacing) < 0.00000000001f) && //0.01 ns error margin - (lhs.TransducerElements == rhs.TransducerElements)); - } - }; -} -#endif \ No newline at end of file diff --git a/Modules/PhotoacousticsAlgorithms/include/mitkPhotoacousticFilterService.h b/Modules/PhotoacousticsAlgorithms/include/mitkPhotoacousticFilterService.h new file mode 100644 index 0000000000..cbe872a42d --- /dev/null +++ b/Modules/PhotoacousticsAlgorithms/include/mitkPhotoacousticFilterService.h @@ -0,0 +1,125 @@ +/*=================================================================== + +The Medical Imaging Interaction Toolkit (MITK) + +Copyright (c) German Cancer Research Center, +Division of Medical and Biological Informatics. +All rights reserved. + +This software is distributed WITHOUT ANY WARRANTY; without +even the implied warranty of MERCHANTABILITY or FITNESS FOR +A PARTICULAR PURPOSE. + +See LICENSE.txt or http://www.mitk.org for details. + +===================================================================*/ + +#ifndef mitkPhotoacousticFilterService_H_HEADER_INCLUDED +#define mitkPhotoacousticFilterService_H_HEADER_INCLUDED + +#include "itkObject.h" +#include "mitkCommon.h" +#include "mitkImage.h" +#include + +#include "mitkBeamformingSettings.h" +#include "mitkBeamformingFilter.h" +#include "MitkPhotoacousticsAlgorithmsExports.h" + +namespace mitk { + /*! + * \brief Class holding methods to apply all Filters within the Photoacoustics Algorithms Module + * + * Implemented are: + * - A B-Mode Filter + * - A Resampling Filter + * - Beamforming on GPU and CPU + * - A Bandpass Filter + */ + + class MITKPHOTOACOUSTICSALGORITHMS_EXPORT PhotoacousticFilterService : public itk::Object + { + public: + mitkClassMacroItkParent(mitk::PhotoacousticFilterService, itk::Object); + itkFactorylessNewMacro(Self); + + /** \brief Defines the methods for the B-Mode filter + * Currently implemented are an Envelope Detection filter and a simple Absolute filter. + */ + enum BModeMethod { EnvelopeDetection, Abs }; + + /** \brief Applies a B-Mode Filter + * + * Applies a B-Mode filter using the given parameters. + * @param inputImage The image to be processed. + * @param method The kind of B-Mode Filter to be used. + * @param UseLogFilter Setting this to true will apply a simple logarithm to the image after the B-Mode Filter has been applied. + * @param resampleSpacing If this is set to 0, nothing will be done; otherwise, the image is resampled to a spacing of resampleSpacing mm per pixel. + * @return The processed image is returned after the filter has finished. + */ + mitk::Image::Pointer ApplyBmodeFilter(mitk::Image::Pointer inputImage, + BModeMethod method = BModeMethod::Abs, + bool UseLogFilter = false); + + /** \brief Resamples the given image + * + * Resamples an image using the given parameters. + * @param inputImage The image to be processed. + * @param outputSize An array of dimensions the image should be resampled to. + * @return The processed image is returned after the filter has finished. + */ + mitk::Image::Pointer ApplyResampling(mitk::Image::Pointer inputImage, double outputSpacing[2]); + + /** \brief Beamforms the given image + * + * Resamples an image using the given parameters. + * @param inputImage The image to be processed. + * @param config The configuration set to be used for beamforming. + * @param progressHandle An std::function, through which progress of the currently updating filter is reported. + * The integer argument is a number between 0 an 100 to indicate how far completion has been achieved, the std::string argument indicates what the filter is currently doing. + * @return The processed image is returned after the filter has finished. + */ + mitk::Image::Pointer ApplyBeamforming(mitk::Image::Pointer inputImage, BeamformingSettings::Pointer config, std::function progressHandle = [](int, std::string) {}); + + /** \brief Crops the given image + * + * Crops an image in 3 dimension using the given parameters. + * @param inputImage The image to be processed. + * @param above How many voxels will be cut from the top of the image. + * @param below How many voxels will be cut from the bottom of the image. + * @param right How many voxels will be cut from the right side of the image. + * @param left How many voxels will be cut from the left side of the image. + * @param minSlice The first slice to be present in the resulting image. + * @param maxSlice The last slice to be present in the resulting image. + * @return The processed image is returned after the filter has finished. For the purposes of this module, the returned image is always of type float. + */ + mitk::Image::Pointer ApplyCropping(mitk::Image::Pointer inputImage, int above, int below, int right, int left, int minSlice, int maxSlice); + + /** \brief Applies a Bandpass filter to the given image + * + * Applies a bandpass filter to the given image using the given parameters. + * @param data The image to be processed. + * @param BPHighPass The position at which Lower frequencies are completely cut off in Hz. + * @param BPLowPass The position at which Higher frequencies are completely cut off in Hz. + * @param alphaHighPass The high pass tukey window parameter to control the shape of the bandpass filter: 0 will make it a Box function, 1 a Hann function. alpha can be set between those two bounds. + * @param alphaLowPass The low passtukey window parameter to control the shape of the bandpass filter: 0 will make it a Box function, 1 a Hann function. alpha can be set between those two bounds. + * @return The processed image is returned after the filter has finished. + */ + mitk::Image::Pointer ApplyBandpassFilter(mitk::Image::Pointer data, + float BPHighPass, float BPLowPass, + float alphaHighPass, float alphaLowPass); + + protected: + PhotoacousticFilterService(); + ~PhotoacousticFilterService() override; + + /** \brief + For performance reasons, an instance of the Beamforming filter is initialized as soon as possible and kept for all further uses. + */ + mitk::BeamformingFilter::Pointer m_BeamformingFilter; + + mitk::Image::Pointer ConvertToFloat(mitk::Image::Pointer); + }; +} // namespace mitk + +#endif /* mitkPhotoacousticFilterService_H_HEADER_INCLUDED */ diff --git a/Modules/PhotoacousticsAlgorithms/include/mitkPhotoacousticImage.h b/Modules/PhotoacousticsAlgorithms/include/mitkPhotoacousticImage.h deleted file mode 100644 index 404c814412..0000000000 --- a/Modules/PhotoacousticsAlgorithms/include/mitkPhotoacousticImage.h +++ /dev/null @@ -1,129 +0,0 @@ -/*=================================================================== - -The Medical Imaging Interaction Toolkit (MITK) - -Copyright (c) German Cancer Research Center, -Division of Medical and Biological Informatics. -All rights reserved. - -This software is distributed WITHOUT ANY WARRANTY; without -even the implied warranty of MERCHANTABILITY or FITNESS FOR -A PARTICULAR PURPOSE. - -See LICENSE.txt or http://www.mitk.org for details. - -===================================================================*/ - -#ifndef mitkPhotoacousticImage_H_HEADER_INCLUDED -#define mitkPhotoacousticImage_H_HEADER_INCLUDED - -#include "itkObject.h" -#include "mitkCommon.h" -#include "mitkImage.h" -#include - -#include "mitkPhotoacousticBeamformingSettings.h" -#include "mitkPhotoacousticBeamformingFilter.h" -#include "MitkPhotoacousticsAlgorithmsExports.h" - -namespace mitk { - /*! - * \brief Class holding methods to apply all Filters within the Photoacoustics Algorithms Module - * - * Implemented are: - * - A B-Mode Filter - * - A Resampling Filter - * - Beamforming on GPU and CPU - * - A Bandpass Filter - */ - - class MITKPHOTOACOUSTICSALGORITHMS_EXPORT PhotoacousticImage : public itk::Object - { - public: - mitkClassMacroItkParent(mitk::PhotoacousticImage, itk::Object); - itkFactorylessNewMacro(Self); - /** \brief Defines the methods for the B-Mode filter - * Currently implemented are an Envelope Detection filter and a simple Absolute filter. - */ - enum BModeMethod { EnvelopeDetection, Abs }; - - /** \brief Applies a B-Mode Filter - * - * Applies a B-Mode filter using the given parameters. - * @param inputImage The image to be processed. - * @param method The kind of B-Mode Filter to be used. - * @param UseGPU Setting this to true will allow the Filter to use the GPU. - * @param UseLogFilter Setting this to true will apply a simple logarithm to the image after the B-Mode Filter has been applied. - * @param resampleSpacing If this is set to 0, nothing will be done; otherwise, the image is resampled to a spacing of resampleSpacing mm per pixel. - * @return The processed image is returned after the filter has finished. - */ - mitk::Image::Pointer ApplyBmodeFilter(mitk::Image::Pointer inputImage, BModeMethod method = BModeMethod::Abs, bool UseGPU = false, bool UseLogFilter = false, float resampleSpacing = 0.15); - - // mitk::Image::Pointer ApplyScatteringCompensation(mitk::Image::Pointer inputImage, int scatteringCoefficient); - - /** \brief Resamples the given image - * - * Resamples an image using the given parameters. - * @param inputImage The image to be processed. - * @param outputSize An array of dimensions the image should be resampled to. - * @return The processed image is returned after the filter has finished. - */ - mitk::Image::Pointer ApplyResampling(mitk::Image::Pointer inputImage, unsigned int outputSize[2]); - - /** \brief Beamforms the given image - * - * Resamples an image using the given parameters. - * @param inputImage The image to be processed. - * @param config The configuration set to be used for beamforming. - * @param message A string into which potentially critical messages will be written. - * @param progressHandle An std::function, through which progress of the currently updating filter is reported. - * The integer argument is a number between 0 an 100 to indicate how far completion has been achieved, the std::string argument indicates what the filter is currently doing. - * @return The processed image is returned after the filter has finished. - */ - mitk::Image::Pointer ApplyBeamforming(mitk::Image::Pointer inputImage, BeamformingSettings config, std::string& message, std::function progressHandle = [](int, std::string) {}); - - /** \brief Crops the given image - * - * Crops an image in 3 dimension using the given parameters. - * @param inputImage The image to be processed. - * @param above How many voxels will be cut from the top of the image. - * @param below How many voxels will be cut from the bottom of the image. - * @param right How many voxels will be cut from the right side of the image. - * @param left How many voxels will be cut from the left side of the image. - * @param minSlice The first slice to be present in the resulting image. - * @param maxSlice The last slice to be present in the resulting image. - * @return The processed image is returned after the filter has finished. For the purposes of this module, the returned image is always of type float. - */ - mitk::Image::Pointer ApplyCropping(mitk::Image::Pointer inputImage, int above, int below, int right, int left, int minSlice, int maxSlice); - - /** \brief Applies a Bandpass filter to the given image - * - * Applies a bandpass filter to the given image using the given parameters. - * @param data The image to be processed. - * @param recordTime The depth of the image in seconds. - * @param BPHighPass The position at which Lower frequencies are completely cut off in Hz. - * @param BPLowPass The position at which Higher frequencies are completely cut off in Hz. - * @param alpha The tukey window parameter to control the shape of the bandpass filter: 0 will make it a Box function, 1 a Hann function. alpha can be set between those two bounds. - * @return The processed image is returned after the filter has finished. - */ - mitk::Image::Pointer BandpassFilter(mitk::Image::Pointer data, float recordTime, - float BPHighPass, float BPLowPass, - float alphaHighPass, float alphaLowPass); - - protected: - PhotoacousticImage(); - ~PhotoacousticImage() override; - - /** \brief For performance reasons, an instance of the Beamforming filter is initialized as soon as possible and kept for all further uses. - */ - mitk::BeamformingFilter::Pointer m_BeamformingFilter; - - /** \brief Function that creates a Tukey function for the bandpass - */ - itk::Image::Pointer BPFunction(mitk::Image::Pointer reference, - int cutoffFrequencyPixelHighPass, int cutoffFrequencyPixelLowPass, - float alphaHighPass, float alphaLowPass); - }; -} // namespace mitk - -#endif /* mitkPhotoacousticImage_H_HEADER_INCLUDED */ diff --git a/Modules/PhotoacousticsAlgorithms/mitkPhotoacousticImage.cpp b/Modules/PhotoacousticsAlgorithms/mitkPhotoacousticImage.cpp deleted file mode 100644 index 343aecf11a..0000000000 --- a/Modules/PhotoacousticsAlgorithms/mitkPhotoacousticImage.cpp +++ /dev/null @@ -1,520 +0,0 @@ -/*=================================================================== - -The Medical Imaging Interaction Toolkit (MITK) - -Copyright (c) German Cancer Research Center, -Division of Medical and Biological Informatics. -All rights reserved. - -This software is distributed WITHOUT ANY WARRANTY; without -even the implied warranty of MERCHANTABILITY or FITNESS FOR -A PARTICULAR PURPOSE. - -See LICENSE.txt or http://www.mitk.org for details. - -===================================================================*/ - -#include "mitkPhotoacousticImage.h" -#include "ITKFilter/ITKUltrasound/itkBModeImageFilter.h" -#include "ITKFilter/itkPhotoacousticBModeImageFilter.h" -#include "mitkImageCast.h" -#include "mitkITKImageImport.h" -#include "mitkPhotoacousticBeamformingFilter.h" -#include -#include -#include "./OpenCLFilter/mitkPhotoacousticBModeFilter.h" - - -// itk dependencies -#include "itkImage.h" -#include "itkResampleImageFilter.h" -#include "itkCastImageFilter.h" -#include "itkCropImageFilter.h" -#include "itkRescaleIntensityImageFilter.h" -#include "itkIntensityWindowingImageFilter.h" -#include -#include "itkMultiplyImageFilter.h" -#include "itkBSplineInterpolateImageFunction.h" -#include - -// needed itk image filters -#include "mitkITKImageImport.h" -#include "itkFFTShiftImageFilter.h" -#include "itkMultiplyImageFilter.h" -#include "itkComplexToModulusImageFilter.h" -#include -#include "ITKFilter/ITKUltrasound/itkFFT1DComplexConjugateToRealImageFilter.h" -#include "ITKFilter/ITKUltrasound/itkFFT1DRealToComplexConjugateImageFilter.h" - -mitk::PhotoacousticImage::PhotoacousticImage() -{ - MITK_INFO << "[PhotoacousticImage Debug] created that image"; -} - -mitk::PhotoacousticImage::~PhotoacousticImage() -{ - MITK_INFO << "[PhotoacousticImage Debug] destroyed that image"; -} - -mitk::Image::Pointer mitk::PhotoacousticImage::ApplyBmodeFilter(mitk::Image::Pointer inputImage, BModeMethod method, bool UseGPU, bool UseLogFilter, float resampleSpacing) -{ - // the image needs to be of floating point type for the envelope filter to work; the casting is done automatically by the CastToItkImage - typedef itk::Image< float, 3 > itkFloatImageType; - typedef itk::IdentityTransform TransformType; - - if (method == BModeMethod::Abs) - { - mitk::Image::Pointer input; - mitk::Image::Pointer out; - if (inputImage->GetPixelType().GetTypeAsString() == "scalar (float)" || inputImage->GetPixelType().GetTypeAsString() == " (float)") - input = inputImage; - else - input = ApplyCropping(inputImage, 0, 0, 0, 0, 0, inputImage->GetDimension(2) - 1); - - if (!UseGPU) - { - PhotoacousticBModeFilter::Pointer filter = PhotoacousticBModeFilter::New(); - filter->SetParameters(UseLogFilter); - filter->SetInput(input); - filter->Update(); - - out = filter->GetOutput(); - - if (resampleSpacing == 0) - return out; - } - #ifdef PHOTOACOUSTICS_USE_GPU - else - { - PhotoacousticOCLBModeFilter::Pointer filter = PhotoacousticOCLBModeFilter::New(); - filter->SetParameters(UseLogFilter); - filter->SetInput(input); - filter->Update(); - - out = filter->GetOutput(); - - if (resampleSpacing == 0) - return out; - } - #endif - - typedef itk::ResampleImageFilter < itkFloatImageType, itkFloatImageType > ResampleImageFilter; - ResampleImageFilter::Pointer resampleImageFilter = ResampleImageFilter::New(); - - itkFloatImageType::Pointer itkImage; - mitk::CastToItkImage(out, itkImage); - itkFloatImageType::SpacingType outputSpacing; - itkFloatImageType::SizeType inputSize = itkImage->GetLargestPossibleRegion().GetSize(); - itkFloatImageType::SizeType outputSize = inputSize; - - outputSpacing[0] = itkImage->GetSpacing()[0]; - outputSpacing[1] = resampleSpacing; - outputSpacing[2] = itkImage->GetSpacing()[2]; - - outputSize[1] = inputSize[1] * itkImage->GetSpacing()[1] / outputSpacing[1]; - - typedef itk::IdentityTransform TransformType; - resampleImageFilter->SetInput(itkImage); - resampleImageFilter->SetSize(outputSize); - resampleImageFilter->SetOutputSpacing(outputSpacing); - resampleImageFilter->SetTransform(TransformType::New()); - - resampleImageFilter->UpdateLargestPossibleRegion(); - return mitk::GrabItkImageMemory(resampleImageFilter->GetOutput()); - } - else if (method == BModeMethod::ShapeDetection) - { - typedef itk::BModeImageFilter < itkFloatImageType, itkFloatImageType > BModeFilterType; - BModeFilterType::Pointer bModeFilter = BModeFilterType::New(); // LogFilter - - typedef itk::PhotoacousticBModeImageFilter < itkFloatImageType, itkFloatImageType > PhotoacousticBModeImageFilter; - PhotoacousticBModeImageFilter::Pointer photoacousticBModeFilter = PhotoacousticBModeImageFilter::New(); // No LogFilter - - typedef itk::ResampleImageFilter < itkFloatImageType, itkFloatImageType > ResampleImageFilter; - ResampleImageFilter::Pointer resampleImageFilter = ResampleImageFilter::New(); - - itkFloatImageType::Pointer itkImage; - - mitk::CastToItkImage(inputImage, itkImage); - - itkFloatImageType::Pointer bmode; - - if (UseLogFilter) - { - bModeFilter->SetInput(itkImage); - bModeFilter->SetDirection(1); - bmode = bModeFilter->GetOutput(); - } - else - { - photoacousticBModeFilter->SetInput(itkImage); - photoacousticBModeFilter->SetDirection(1); - bmode = photoacousticBModeFilter->GetOutput(); - } - - // resampleSpacing == 0 means: do no resampling - if (resampleSpacing == 0) - { - return mitk::GrabItkImageMemory(bmode); - } - - itkFloatImageType::SpacingType outputSpacing; - itkFloatImageType::SizeType inputSize = itkImage->GetLargestPossibleRegion().GetSize(); - itkFloatImageType::SizeType outputSize = inputSize; - - outputSpacing[0] = itkImage->GetSpacing()[0]; - outputSpacing[1] = resampleSpacing; - outputSpacing[2] = itkImage->GetSpacing()[2]; - - outputSize[1] = inputSize[1] * itkImage->GetSpacing()[1] / outputSpacing[1]; - - resampleImageFilter->SetInput(bmode); - resampleImageFilter->SetSize(outputSize); - resampleImageFilter->SetOutputSpacing(outputSpacing); - resampleImageFilter->SetTransform(TransformType::New()); - - resampleImageFilter->UpdateLargestPossibleRegion(); - return mitk::GrabItkImageMemory(resampleImageFilter->GetOutput()); - } - return nullptr; -} - -/*mitk::Image::Pointer mitk::PhotoacousticImage::ApplyScatteringCompensation(mitk::Image::Pointer inputImage, int scattering) -{ - typedef itk::Image< float, 3 > itkFloatImageType; - typedef itk::MultiplyImageFilter MultiplyImageFilterType; - - itkFloatImageType::Pointer itkImage; - mitk::CastToItkImage(inputImage, itkImage); - - MultiplyImageFilterType::Pointer multiplyFilter = MultiplyImageFilterType::New(); - multiplyFilter->SetInput1(itkImage); - multiplyFilter->SetInput2(m_FluenceCompResizedItk.at(m_ScatteringCoefficient)); - - return mitk::GrabItkImageMemory(multiplyFilter->GetOutput()); -}*/ - -mitk::Image::Pointer mitk::PhotoacousticImage::ApplyResampling(mitk::Image::Pointer inputImage, unsigned int outputSize[2]) -{ - typedef itk::Image< float, 3 > itkFloatImageType; - - typedef itk::ResampleImageFilter < itkFloatImageType, itkFloatImageType > ResampleImageFilter; - ResampleImageFilter::Pointer resampleImageFilter = ResampleImageFilter::New(); - typedef itk::LinearInterpolateImageFunction T_Interpolator; - itkFloatImageType::Pointer itkImage; - - mitk::CastToItkImage(inputImage, itkImage); - - itkFloatImageType::SpacingType outputSpacingItk; - itkFloatImageType::SizeType inputSizeItk = itkImage->GetLargestPossibleRegion().GetSize(); - itkFloatImageType::SizeType outputSizeItk = inputSizeItk; - - outputSizeItk[0] = outputSize[0]; - outputSizeItk[1] = outputSize[1]; - outputSizeItk[2] = inputSizeItk[2]; - - outputSpacingItk[0] = itkImage->GetSpacing()[0] * (static_cast(inputSizeItk[0]) / static_cast(outputSizeItk[0])); - outputSpacingItk[1] = itkImage->GetSpacing()[1] * (static_cast(inputSizeItk[1]) / static_cast(outputSizeItk[1])); - outputSpacingItk[2] = itkImage->GetSpacing()[2]; - - typedef itk::IdentityTransform TransformType; - T_Interpolator::Pointer _pInterpolator = T_Interpolator::New(); - resampleImageFilter->SetInput(itkImage); - resampleImageFilter->SetSize(outputSizeItk); - resampleImageFilter->SetOutputSpacing(outputSpacingItk); - resampleImageFilter->SetTransform(TransformType::New()); - resampleImageFilter->SetInterpolator(_pInterpolator); - - resampleImageFilter->UpdateLargestPossibleRegion(); - return mitk::GrabItkImageMemory(resampleImageFilter->GetOutput()); -} - -mitk::Image::Pointer mitk::PhotoacousticImage::ApplyCropping(mitk::Image::Pointer inputImage, int above, int below, int right, int left, int minSlice, int maxSlice) -{ - - unsigned int inputDim[3] = { inputImage->GetDimension(0), inputImage->GetDimension(1), inputImage->GetDimension(2) }; - unsigned int outputDim[3] = { inputImage->GetDimension(0) - left - right, inputImage->GetDimension(1) - (unsigned int)above - (unsigned int)below, (unsigned int)maxSlice - (unsigned int)minSlice + 1 }; - - void* inputData; - float* outputData = new float[outputDim[0] * outputDim[1] * outputDim[2]]; - - ImageReadAccessor acc(inputImage); - inputData = const_cast(acc.GetData()); - - // convert the data to float by default - // as of now only those float, short, float are used at all... though it's easy to add other ones - if (inputImage->GetPixelType().GetTypeAsString() == "scalar (float)" || inputImage->GetPixelType().GetTypeAsString() == " (float)") - { - // copy the data into the cropped image - for (unsigned short sl = 0; sl < outputDim[2]; ++sl) - { - for (unsigned short l = 0; l < outputDim[0]; ++l) - { - for (unsigned short s = 0; s < outputDim[1]; ++s) - { - outputData[l + s*(unsigned short)outputDim[0] + sl*outputDim[0] * outputDim[1]] = (float)((float*)inputData)[(l + left) + (s + above)*(unsigned short)inputDim[0] + (sl + minSlice)*inputDim[0] * inputDim[1]]; - } - } - } - } - else if (inputImage->GetPixelType().GetTypeAsString() == "scalar (short)" || inputImage->GetPixelType().GetTypeAsString() == " (short)") - { - // copy the data unsigned shorto the cropped image - for (unsigned short sl = 0; sl < outputDim[2]; ++sl) - { - for (unsigned short l = 0; l < outputDim[0]; ++l) - { - for (unsigned short s = 0; s < outputDim[1]; ++s) - { - outputData[l + s*(unsigned short)outputDim[0] + sl*outputDim[0] * outputDim[1]] = (float)((short*)inputData)[(l + left) + (s + above)*(unsigned short)inputDim[0] + (sl + minSlice)*inputDim[0] * inputDim[1]]; - } - } - } - } - else if (inputImage->GetPixelType().GetTypeAsString() == "scalar (double)" || inputImage->GetPixelType().GetTypeAsString() == " (double)") - { - // copy the data unsigned shorto the cropped image - for (unsigned short sl = 0; sl < outputDim[2]; ++sl) - { - for (unsigned short l = 0; l < outputDim[0]; ++l) - { - for (unsigned short s = 0; s < outputDim[1]; ++s) - { - outputData[l + s*(unsigned short)outputDim[0] + sl*outputDim[0] * outputDim[1]] = (float)((double*)inputData)[(l + left) + (s + above)*(unsigned short)inputDim[0] + (sl + minSlice)*inputDim[0] * inputDim[1]]; - } - } - } - } - else - { - MITK_INFO << "Could not determine pixel type"; - } - - mitk::Image::Pointer output = mitk::Image::New(); - output->Initialize(mitk::MakeScalarPixelType(), 3, outputDim); - output->SetSpacing(inputImage->GetGeometry()->GetSpacing()); - output->SetImportVolume(outputData, 0, 0, mitk::Image::ReferenceMemory); - - return output; -} - -mitk::Image::Pointer mitk::PhotoacousticImage::ApplyBeamforming(mitk::Image::Pointer inputImage, BeamformingSettings config, int cutoff, std::function progressHandle) -{ - config.RecordTime = config.RecordTime - (cutoff) / inputImage->GetDimension(1) * config.RecordTime; // adjust the recorded time lost by cropping - progressHandle(0, "cropping image"); - if (!config.partial) - { - config.CropBounds[0] = 0; - config.CropBounds[1] = inputImage->GetDimension(2) - 1; - } - - Image::Pointer processedImage = ApplyCropping(inputImage, cutoff, 0, 0, 0, config.CropBounds[0], config.CropBounds[1]); - config.inputDim[0] = processedImage->GetDimension(0); - config.inputDim[1] = processedImage->GetDimension(1); - config.inputDim[2] = processedImage->GetDimension(2); - - // perform the beamforming - BeamformingFilter::Pointer Beamformer = BeamformingFilter::New(); - Beamformer->SetInput(processedImage); - Beamformer->Configure(config); - Beamformer->SetProgressHandle(progressHandle); - Beamformer->UpdateLargestPossibleRegion(); - - processedImage = Beamformer->GetOutput(); - - return processedImage; -} - -mitk::Image::Pointer mitk::PhotoacousticImage::BandpassFilter(mitk::Image::Pointer data, float recordTime, float BPHighPass, float BPLowPass, float alpha) -{ - bool powerOfTwo = false; - int finalPower = 0; - for (int i = 1; pow(2, i) <= data->GetDimension(1); ++i) - { - finalPower = i; - if (pow(2, i) == data->GetDimension(1)) - { - powerOfTwo = true; - } - } - if (!powerOfTwo) - { - unsigned int dim[2] = { data->GetDimension(0), (unsigned int)pow(2,finalPower+1)}; - data = ApplyResampling(data, dim); - } - - MITK_INFO << data->GetDimension(0); - - // do a fourier transform, multiply with an appropriate window for the filter, and transform back - typedef float PixelType; - typedef itk::Image< PixelType, 3 > RealImageType; - RealImageType::Pointer image; - - mitk::CastToItkImage(data, image); - - typedef itk::FFT1DRealToComplexConjugateImageFilter ForwardFFTFilterType; - typedef ForwardFFTFilterType::OutputImageType ComplexImageType; - ForwardFFTFilterType::Pointer forwardFFTFilter = ForwardFFTFilterType::New(); - forwardFFTFilter->SetInput(image); - forwardFFTFilter->SetDirection(1); - try - { - forwardFFTFilter->UpdateOutputInformation(); - } - catch (itk::ExceptionObject & error) - { - std::cerr << "Error: " << error << std::endl; - MITK_WARN << "Bandpass could not be applied"; - return data; - } - - float singleVoxel = 1 / (recordTime / data->GetDimension(1)) / 2 / 1000; - float cutoffPixelHighPass = std::min(BPHighPass / singleVoxel, (float)data->GetDimension(1) / 2); - float cutoffPixelLowPass = std::min(BPLowPass / singleVoxel, (float)data->GetDimension(1) / 2 - cutoffPixelHighPass); - - RealImageType::Pointer fftMultiplicator = BPFunction(data, cutoffPixelHighPass, cutoffPixelLowPass, alpha); - - typedef itk::MultiplyImageFilter< ComplexImageType, - RealImageType, - ComplexImageType > - MultiplyFilterType; - MultiplyFilterType::Pointer multiplyFilter = MultiplyFilterType::New(); - multiplyFilter->SetInput1(forwardFFTFilter->GetOutput()); - multiplyFilter->SetInput2(fftMultiplicator); - - /*itk::ComplexToModulusImageFilter::Pointer toReal = itk::ComplexToModulusImageFilter::New(); - toReal->SetInput(forwardFFTFilter->GetOutput()); - return GrabItkImageMemory(toReal->GetOutput()); - return GrabItkImageMemory(fftMultiplicator); *///DEBUG - - typedef itk::FFT1DComplexConjugateToRealImageFilter< ComplexImageType, RealImageType > InverseFilterType; - InverseFilterType::Pointer inverseFFTFilter = InverseFilterType::New(); - inverseFFTFilter->SetInput(multiplyFilter->GetOutput()); - inverseFFTFilter->SetDirection(1); - - return GrabItkImageMemory(inverseFFTFilter->GetOutput()); -} - -itk::Image::Pointer mitk::PhotoacousticImage::BPFunction(mitk::Image::Pointer reference, int cutoffFrequencyPixelHighPass, int cutoffFrequencyPixelLowPass, float alpha) -{ - float* imageData = new float[reference->GetDimension(0)*reference->GetDimension(1)]; - - // tukey window - float width = reference->GetDimension(1) / 2 - (float)cutoffFrequencyPixelHighPass - (float)cutoffFrequencyPixelLowPass; - float center = (float)cutoffFrequencyPixelHighPass / 2 + width / 2; - - - MITK_INFO << width << "width " << center << "center " << alpha; - - for (unsigned int n = 0; n < reference->GetDimension(1); ++n) - { - imageData[reference->GetDimension(0)*n] = 0; - } - - for (int n = 0; n < width; ++n) - { - if (n <= (alpha*(width - 1)) / 2) - { - imageData[reference->GetDimension(0)*(int)(n + center - (width / 2))] = (1 + cos(itk::Math::pi*(2 * n / (alpha*(width - 1)) - 1))) / 2; - } - else if (n >= (width - 1)*(1 - alpha / 2) && n <= (width - 1)) - { - imageData[reference->GetDimension(0)*(int)(n + center - (width / 2))] = (1 + cos(itk::Math::pi*(2 * n / (alpha*(width - 1)) + 1 - 2 / alpha))) / 2; - } - else - { - imageData[reference->GetDimension(0)*(int)(n + center - (width / 2))] = 1; - } - } - - // Butterworth-Filter - /* - // first, write the HighPass - if (cutoffFrequencyPixelHighPass != reference->GetDimension(1) / 2) - { - for (int n = 0; n < reference->GetDimension(1) / 2; ++n) - { - imageData[reference->GetDimension(0)*n] = 1 / (1 + pow( - (float)n / (float)(reference->GetDimension(1) / 2 - cutoffFrequencyPixelHighPass) - , 2 * butterworthOrder)); - } - } - else - { - for (int n = 0; n < reference->GetDimension(1) / 2; ++n) - { - imageData[reference->GetDimension(0)*n] = 1; - } - } - - // now, the LowPass - for (int n = 0; n < reference->GetDimension(1) / 2; ++n) - { - imageData[reference->GetDimension(0)*n] *= 1 / (1 + pow( - (float)(reference->GetDimension(1) / 2 - 1 - n) / (float)(reference->GetDimension(1) / 2 - cutoffFrequencyPixelLowPass) - , 2 * butterworthOrder)); - } - */ - - // mirror the first half of the image - for (unsigned int n = reference->GetDimension(1) / 2; n < reference->GetDimension(1); ++n) - { - imageData[reference->GetDimension(0)*n] = imageData[(reference->GetDimension(1) - (n + 1)) * reference->GetDimension(0)]; - } - - - // copy and paste to all lines - for (unsigned int line = 1; line < reference->GetDimension(0); ++line) - { - for (unsigned int sample = 0; sample < reference->GetDimension(1); ++sample) - { - imageData[reference->GetDimension(0)*sample + line] = imageData[reference->GetDimension(0)*sample]; - } - } - - typedef itk::Image< float, 3U > ImageType; - ImageType::RegionType region; - ImageType::IndexType start; - start.Fill(0); - - region.SetIndex(start); - - ImageType::SizeType size; - size[0] = reference->GetDimension(0); - size[1] = reference->GetDimension(1); - size[2] = reference->GetDimension(2); - - region.SetSize(size); - - ImageType::SpacingType SpacingItk; - SpacingItk[0] = reference->GetGeometry()->GetSpacing()[0]; - SpacingItk[1] = reference->GetGeometry()->GetSpacing()[1]; - SpacingItk[2] = reference->GetGeometry()->GetSpacing()[2]; - - ImageType::Pointer image = ImageType::New(); - image->SetRegions(region); - image->Allocate(); - image->FillBuffer(itk::NumericTraits::Zero); - image->SetSpacing(SpacingItk); - - ImageType::IndexType pixelIndex; - - for (ImageType::IndexValueType slice = 0; slice < reference->GetDimension(2); ++slice) - { - for (ImageType::IndexValueType line = 0; line < reference->GetDimension(0); ++line) - { - for (ImageType::IndexValueType sample = 0; sample < reference->GetDimension(1); ++sample) - { - pixelIndex[0] = line; - pixelIndex[1] = sample; - pixelIndex[2] = slice; - - image->SetPixel(pixelIndex, imageData[line + sample*reference->GetDimension(0)]); - } - } - } - - delete[] imageData; - - return image; -} \ No newline at end of file diff --git a/Modules/PhotoacousticsAlgorithms/mitkPhotoacousticImage.h b/Modules/PhotoacousticsAlgorithms/mitkPhotoacousticImage.h deleted file mode 100644 index 2200124e8d..0000000000 --- a/Modules/PhotoacousticsAlgorithms/mitkPhotoacousticImage.h +++ /dev/null @@ -1,50 +0,0 @@ -/*=================================================================== - -The Medical Imaging Interaction Toolkit (MITK) - -Copyright (c) German Cancer Research Center, -Division of Medical and Biological Informatics. -All rights reserved. - -This software is distributed WITHOUT ANY WARRANTY; without -even the implied warranty of MERCHANTABILITY or FITNESS FOR -A PARTICULAR PURPOSE. - -See LICENSE.txt or http://www.mitk.org for details. - -===================================================================*/ - -#ifndef mitkPhotoacousticImage_H_HEADER_INCLUDED -#define mitkPhotoacousticImage_H_HEADER_INCLUDED - -#include "itkObject.h" -#include "mitkCommon.h" -#include "mitkImage.h" -#include - -#include "mitkPhotoacousticBeamformingFilter.h" -#include "MitkPhotoacousticsAlgorithmsExports.h" - -namespace mitk { - - class MITKPHOTOACOUSTICSALGORITHMS_EXPORT PhotoacousticImage : public itk::Object - { - public: - mitkClassMacroItkParent(mitk::PhotoacousticImage, itk::Object); - itkFactorylessNewMacro(Self); - enum BModeMethod { ShapeDetection, Abs }; - mitk::Image::Pointer ApplyBmodeFilter(mitk::Image::Pointer inputImage, BModeMethod method = BModeMethod::Abs, bool UseGPU = false, bool UseLogFilter = false, float resampleSpacing = 0.15); -// mitk::Image::Pointer ApplyScatteringCompensation(mitk::Image::Pointer inputImage, int scatteringCoefficient); - mitk::Image::Pointer ApplyResampling(mitk::Image::Pointer inputImage, unsigned int outputSize[2]); - mitk::Image::Pointer ApplyBeamforming(mitk::Image::Pointer inputImage, BeamformingSettings config, int cutoff, std::function progressHandle = [](int, std::string) {}); - mitk::Image::Pointer ApplyCropping(mitk::Image::Pointer inputImage, int above, int below, int right, int left, int minSlice, int maxSlice); - mitk::Image::Pointer BandpassFilter(mitk::Image::Pointer data, float recordTime, float BPHighPass, float BPLowPass, float alpha); - protected: - PhotoacousticImage(); - virtual ~PhotoacousticImage(); - - itk::Image::Pointer BPFunction(mitk::Image::Pointer reference, int cutoffFrequencyPixelHighPass, int cutoffFrequencyPixelLowPass, float alpha); - }; -} // namespace mitk - -#endif /* mitkPhotoacousticImage_H_HEADER_INCLUDED */ diff --git a/Modules/PhotoacousticsAlgorithms/source/OpenCLFilter/mitkPhotoacousticBModeFilter.cpp b/Modules/PhotoacousticsAlgorithms/source/OpenCLFilter/mitkPhotoacousticBModeFilter.cpp index f9db6a7105..8408229d5a 100644 --- a/Modules/PhotoacousticsAlgorithms/source/OpenCLFilter/mitkPhotoacousticBModeFilter.cpp +++ b/Modules/PhotoacousticsAlgorithms/source/OpenCLFilter/mitkPhotoacousticBModeFilter.cpp @@ -1,221 +1,91 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "./OpenCLFilter/mitkPhotoacousticBModeFilter.h" #include "usServiceReference.h" #include -#if defined(PHOTOACOUSTICS_USE_GPU) || DOXYGEN - -mitk::PhotoacousticOCLBModeFilter::PhotoacousticOCLBModeFilter() - : m_PixelCalculation(NULL) -{ - this->AddSourceFile("BModeAbs.cl"); - this->AddSourceFile("BModeAbsLog.cl"); - - this->m_FilterID = "BModeFilter"; - - this->Initialize(); -} - -mitk::PhotoacousticOCLBModeFilter::~PhotoacousticOCLBModeFilter() -{ - if (this->m_PixelCalculation) - { - clReleaseKernel(m_PixelCalculation); - } -} - -void mitk::PhotoacousticOCLBModeFilter::Update() -{ - //Check if context & program available - if (!this->Initialize()) - { - us::ServiceReference ref = GetModuleContext()->GetServiceReference(); - OclResourceService* resources = GetModuleContext()->GetService(ref); - - // clean-up also the resources - resources->InvalidateStorage(); - mitkThrow() << "Filter is not initialized. Cannot update."; - } - else { - // Execute - this->Execute(); - } -} - -void mitk::PhotoacousticOCLBModeFilter::Execute() -{ - try - { - size_t outputSize = m_InputDim[0] * m_InputDim[1] * m_InputDim[2]; - this->InitExec(this->m_PixelCalculation, m_InputDim, outputSize, sizeof(float)); - } - catch (const mitk::Exception& e) - { - MITK_ERROR << "Catched exception while initializing filter: " << e.what(); - return; - } - - cl_int clErr; - clErr = clSetKernelArg(this->m_PixelCalculation, 2, sizeof(cl_uint), &(this->m_Size)); - - CHECK_OCL_ERR(clErr); - - // execute the filter on a 3D NDRange - this->ExecuteKernel(m_PixelCalculation, 3); - - // signalize the GPU-side data changed - m_Output->Modified(GPU_DATA); -} - -us::Module *mitk::PhotoacousticOCLBModeFilter::GetModule() -{ - return us::GetModuleContext()->GetModule(); -} - -bool mitk::PhotoacousticOCLBModeFilter::Initialize() -{ - bool buildErr = true; - cl_int clErr = 0; - - if (OclFilter::Initialize()) - { - if(m_UseLogFilter) - this->m_PixelCalculation = clCreateKernel(this->m_ClProgram, "ckBmodeAbsLog", &clErr); - else - this->m_PixelCalculation = clCreateKernel(this->m_ClProgram, "ckBmodeAbs", &clErr); - buildErr |= CHECK_OCL_ERR(clErr); - } - return (OclFilter::IsInitialized() && buildErr); -} - -void mitk::PhotoacousticOCLBModeFilter::SetInput(mitk::Image::Pointer image) -{ - OclDataSetToDataSetFilter::SetInput(image); - - m_InputImage = image; - m_InputDim[0] = m_InputImage->GetDimension(0); - m_InputDim[1] = m_InputImage->GetDimension(1); - m_InputDim[2] = m_InputImage->GetDimension(2); - m_Size = m_InputDim[0] * m_InputDim[1] * m_InputDim[2]; -} - -mitk::Image::Pointer mitk::PhotoacousticOCLBModeFilter::GetOutput() -{ - mitk::Image::Pointer outputImage = mitk::Image::New(); - - if (m_Output->IsModified(GPU_DATA)) - { - void* pData = m_Output->TransferDataToCPU(m_CommandQue); - - const unsigned int dimension = 3; - unsigned int dimensions[3] = { m_InputDim[0], m_InputDim[1], m_InputDim[2] }; - - const mitk::SlicedGeometry3D::Pointer p_slg = m_InputImage->GetSlicedGeometry(); - - MITK_DEBUG << "Creating new MITK Image."; - - outputImage->Initialize(this->GetOutputType(), dimension, dimensions); - outputImage->SetSpacing(p_slg->GetSpacing()); - outputImage->SetGeometry(m_InputImage->GetGeometry()); - outputImage->SetImportVolume(pData, 0, 0, mitk::Image::ReferenceMemory); - } - - MITK_DEBUG << "Image Initialized."; - - return outputImage; -} - -#endif - - mitk::PhotoacousticBModeFilter::PhotoacousticBModeFilter() : m_UseLogFilter(false) { this->SetNumberOfIndexedInputs(1); this->SetNumberOfRequiredInputs(1); } mitk::PhotoacousticBModeFilter::~PhotoacousticBModeFilter() { } void mitk::PhotoacousticBModeFilter::GenerateInputRequestedRegion() { Superclass::GenerateInputRequestedRegion(); - mitk::Image* output = this->GetOutput(); - mitk::Image* input = const_cast (this->GetInput()); + mitk::Image::Pointer output = this->GetOutput(); + mitk::Image::Pointer input = this->GetInput(); + if (!output->IsInitialized()) - { return; - } input->SetRequestedRegionToLargestPossibleRegion(); - - //GenerateTimeInInputRegion(output, input); } void mitk::PhotoacousticBModeFilter::GenerateOutputInformation() { mitk::Image::ConstPointer input = this->GetInput(); mitk::Image::Pointer output = this->GetOutput(); if ((output->IsInitialized()) && (this->GetMTime() <= m_TimeOfHeaderInitialization.GetMTime())) return; - itkDebugMacro(<< "GenerateOutputInformation()"); - - output->Initialize(input); + output->Initialize(input->GetPixelType(), input->GetDimension(), input->GetDimensions()); output->GetGeometry()->SetSpacing(input->GetGeometry()->GetSpacing()); output->GetGeometry()->Modified(); output->SetPropertyList(input->GetPropertyList()->Clone()); m_TimeOfHeaderInitialization.Modified(); } void mitk::PhotoacousticBModeFilter::GenerateData() { GenerateOutputInformation(); mitk::Image::Pointer input = this->GetInput(); mitk::Image::Pointer output = this->GetOutput(); if (!output->IsInitialized()) return; mitk::ImageReadAccessor reader(input); unsigned int size = output->GetDimension(0) * output->GetDimension(1) * output->GetDimension(2); - float* InputData = (float*)const_cast(reader.GetData()); + const float* InputData = (const float*)(reader.GetData()); float* OutputData = new float[size]; - if(!m_UseLogFilter) + if (!m_UseLogFilter) for (unsigned int i = 0; i < size; ++i) { OutputData[i] = abs(InputData[i]); } else { for (unsigned int i = 0; i < size; ++i) { OutputData[i] = log(abs(InputData[i])); } } - output->SetImportVolume(OutputData, 0, 0, mitk::Image::ImportMemoryManagementType::ManageMemory); - + output->SetImportVolume(OutputData, 0, 0, mitk::Image::ImportMemoryManagementType::CopyMemory); + delete[] OutputData; m_TimeOfHeaderInitialization.Modified(); -} \ No newline at end of file +} diff --git a/Modules/PhotoacousticsAlgorithms/source/OpenCLFilter/mitkPhotoacousticOCLBeamformingFilter.cpp b/Modules/PhotoacousticsAlgorithms/source/OpenCLFilter/mitkPhotoacousticOCLBeamformingFilter.cpp index 575c29b240..3c3759fe24 100644 --- a/Modules/PhotoacousticsAlgorithms/source/OpenCLFilter/mitkPhotoacousticOCLBeamformingFilter.cpp +++ b/Modules/PhotoacousticsAlgorithms/source/OpenCLFilter/mitkPhotoacousticOCLBeamformingFilter.cpp @@ -1,264 +1,259 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #if defined(PHOTOACOUSTICS_USE_GPU) || DOXYGEN #include "./OpenCLFilter/mitkPhotoacousticOCLBeamformingFilter.h" #include "usServiceReference.h" -mitk::PhotoacousticOCLBeamformingFilter::PhotoacousticOCLBeamformingFilter() -: m_PixelCalculation( NULL ), m_InputImage(mitk::Image::New()), m_ApodizationBuffer(nullptr), m_MemoryLocationsBuffer(nullptr), m_DelaysBuffer(nullptr), m_UsedLinesBuffer(nullptr) +mitk::PhotoacousticOCLBeamformingFilter::PhotoacousticOCLBeamformingFilter(BeamformingSettings::Pointer settings) : + m_PixelCalculation(NULL), + m_inputSlices(1), + m_Conf(settings), + m_InputImage(mitk::Image::New()), + m_ApodizationBuffer(nullptr), + m_MemoryLocationsBuffer(nullptr), + m_DelaysBuffer(nullptr), + m_UsedLinesBuffer(nullptr) { + MITK_INFO << "Instantiating OCL beamforming Filter..."; this->AddSourceFile("DAS.cl"); this->AddSourceFile("DMAS.cl"); this->AddSourceFile("sDMAS.cl"); this->m_FilterID = "OpenCLBeamformingFilter"; this->Initialize(); unsigned int dim[] = { 128, 2048, 2 }; - mitk::Vector3D spacing; - spacing[0] = 1; - spacing[1] = 1; - spacing[2] = 1; - m_InputImage->Initialize(mitk::MakeScalarPixelType(), 3, dim); - m_InputImage->SetSpacing(spacing); m_ChunkSize[0] = 128; m_ChunkSize[1] = 128; m_ChunkSize[2] = 8; - m_UsedLinesCalculation = mitk::OCLUsedLinesCalculation::New(); - m_DelayCalculation = mitk::OCLDelayCalculation::New(); + m_UsedLinesCalculation = mitk::OCLUsedLinesCalculation::New(m_Conf); + m_DelayCalculation = mitk::OCLDelayCalculation::New(m_Conf); + MITK_INFO << "Instantiating OCL beamforming Filter...[Done]"; } mitk::PhotoacousticOCLBeamformingFilter::~PhotoacousticOCLBeamformingFilter() { - if ( this->m_PixelCalculation ) + if (this->m_PixelCalculation) { - clReleaseKernel( m_PixelCalculation ); + clReleaseKernel(m_PixelCalculation); } if (m_ApodizationBuffer) clReleaseMemObject(m_ApodizationBuffer); } void mitk::PhotoacousticOCLBeamformingFilter::Update() { //Check if context & program available if (!this->Initialize()) { us::ServiceReference ref = GetModuleContext()->GetServiceReference(); OclResourceService* resources = GetModuleContext()->GetService(ref); // clean-up also the resources resources->InvalidateStorage(); - mitkThrow() <<"Filter is not initialized. Cannot update."; + mitkThrow() << "Filter is not initialized. Cannot update."; } - else{ + else { // Execute this->Execute(); } } void mitk::PhotoacousticOCLBeamformingFilter::UpdateDataBuffers() { /*us::ServiceReference ref = GetModuleContext()->GetServiceReference(); OclResourceService* resources = GetModuleContext()->GetService(ref); cl_ulong globalMemSize = oclGetGlobalMemSize(resources->GetCurrentDevice());*/ //Initialize the Output try { - MITK_DEBUG << "Updating Workgroup size for new dimensions"; - size_t outputSize = (size_t)m_Conf.ReconstructionLines * (size_t)m_Conf.SamplesPerLine * (size_t)m_Conf.inputDim[2]; - m_OutputDim[0] = m_Conf.ReconstructionLines; - m_OutputDim[1] = m_Conf.SamplesPerLine; - m_OutputDim[2] = m_Conf.inputDim[2]; + size_t outputSize = (size_t)m_Conf->GetReconstructionLines() * (size_t)m_Conf->GetSamplesPerLine() * + (size_t)m_inputSlices; + m_OutputDim[0] = m_Conf->GetReconstructionLines(); + m_OutputDim[1] = m_Conf->GetSamplesPerLine(); + m_OutputDim[2] = m_inputSlices; this->InitExec(this->m_PixelCalculation, m_OutputDim, outputSize, sizeof(float)); } catch (const mitk::Exception& e) { MITK_ERROR << "Caught exception while initializing filter: " << e.what(); return; } - if (BeamformingSettings::SettingsChangedOpenCL(m_Conf, m_ConfOld)) - { - cl_int clErr = 0; - MITK_DEBUG << "Updating GPU Buffers for new configuration"; + //TODO FIXME + cl_int clErr = 0; + MITK_DEBUG << "Updating GPU Buffers for new configuration"; - // create the apodisation buffer + // create the apodisation buffer - if (m_Apodisation == nullptr) - { - MITK_INFO << "No apodisation function set; Beamforming will be done without any apodisation."; - m_Apodisation = new float[1]; - m_Apodisation[0] = 1; - m_ApodArraySize = 1; - } + if (m_Apodisation == nullptr) + { + MITK_INFO << "No apodisation function set; Beamforming will be done without any apodisation."; + m_Apodisation = new float[1]{ 1 }; + m_ApodArraySize = 1; + } - us::ServiceReference ref = GetModuleContext()->GetServiceReference(); - OclResourceService* resources = GetModuleContext()->GetService(ref); - cl_context gpuContext = resources->GetContext(); + us::ServiceReference ref = GetModuleContext()->GetServiceReference(); + OclResourceService* resources = GetModuleContext()->GetService(ref); + cl_context gpuContext = resources->GetContext(); - if (m_ApodizationBuffer) clReleaseMemObject(m_ApodizationBuffer); + if (m_ApodizationBuffer) clReleaseMemObject(m_ApodizationBuffer); - this->m_ApodizationBuffer = clCreateBuffer(gpuContext, CL_MEM_READ_ONLY | CL_MEM_USE_HOST_PTR, sizeof(float) * m_ApodArraySize, m_Apodisation, &clErr); - CHECK_OCL_ERR(clErr); + this->m_ApodizationBuffer = clCreateBuffer(gpuContext, CL_MEM_READ_ONLY | CL_MEM_USE_HOST_PTR, sizeof(float) * m_ApodArraySize, const_cast(m_Apodisation), &clErr); + CHECK_OCL_ERR(clErr); - // calculate used lines + // calculate used lines - m_UsedLinesCalculation->SetConfig(m_Conf); - m_UsedLinesCalculation->Update(); - m_UsedLinesBuffer = m_UsedLinesCalculation->GetGPUOutput()->GetGPUBuffer(); + m_UsedLinesCalculation->Update(); + m_UsedLinesBuffer = m_UsedLinesCalculation->GetGPUOutput()->GetGPUBuffer(); - // calculate the Delays - m_DelayCalculation->SetConfig(m_Conf); - m_DelayCalculation->SetInputs(m_UsedLinesBuffer); - m_DelayCalculation->Update(); + // calculate the Delays + m_DelayCalculation->SetInputs(m_UsedLinesBuffer); + m_DelayCalculation->Update(); - m_DelaysBuffer = m_DelayCalculation->GetGPUOutput()->GetGPUBuffer(); + m_DelaysBuffer = m_DelayCalculation->GetGPUOutput()->GetGPUBuffer(); - m_ConfOld = m_Conf; - } + //m_ConfOld = m_Conf; } void mitk::PhotoacousticOCLBeamformingFilter::Execute() { cl_int clErr = 0; UpdateDataBuffers(); + unsigned int reconstructionLines = this->m_Conf->GetReconstructionLines(); + unsigned int samplesPerLine = this->m_Conf->GetSamplesPerLine(); + clErr = clSetKernelArg(this->m_PixelCalculation, 2, sizeof(cl_mem), &(this->m_UsedLinesBuffer)); clErr |= clSetKernelArg(this->m_PixelCalculation, 3, sizeof(cl_mem), &(this->m_DelaysBuffer)); clErr |= clSetKernelArg(this->m_PixelCalculation, 4, sizeof(cl_mem), &(this->m_ApodizationBuffer)); clErr |= clSetKernelArg(this->m_PixelCalculation, 5, sizeof(cl_ushort), &(this->m_ApodArraySize)); - clErr |= clSetKernelArg(this->m_PixelCalculation, 6, sizeof(cl_uint), &(this->m_Conf.inputDim[0])); - clErr |= clSetKernelArg(this->m_PixelCalculation, 7, sizeof(cl_uint), &(this->m_Conf.inputDim[1])); - clErr |= clSetKernelArg(this->m_PixelCalculation, 8, sizeof(cl_uint), &(this->m_Conf.inputDim[2])); - clErr |= clSetKernelArg(this->m_PixelCalculation, 9, sizeof(cl_uint), &(this->m_Conf.ReconstructionLines)); - clErr |= clSetKernelArg(this->m_PixelCalculation, 10, sizeof(cl_uint), &(this->m_Conf.SamplesPerLine)); + clErr |= clSetKernelArg(this->m_PixelCalculation, 6, sizeof(cl_uint), &(this->m_Conf->GetInputDim()[0])); + clErr |= clSetKernelArg(this->m_PixelCalculation, 7, sizeof(cl_uint), &(this->m_Conf->GetInputDim()[1])); + clErr |= clSetKernelArg(this->m_PixelCalculation, 8, sizeof(cl_uint), &(m_OutputDim[2])); + clErr |= clSetKernelArg(this->m_PixelCalculation, 9, sizeof(cl_uint), &(reconstructionLines)); + clErr |= clSetKernelArg(this->m_PixelCalculation, 10, sizeof(cl_uint), &(samplesPerLine)); - // execute the filter on a 3D NDRange + // execute the filter on a 2D/3D NDRange if (m_OutputDim[2] == 1 || m_ChunkSize[2] == 1) { - if(!this->ExecuteKernelChunksInBatches(m_PixelCalculation, 2, m_ChunkSize, 16, 50)) + if (!this->ExecuteKernelChunksInBatches(m_PixelCalculation, 2, m_ChunkSize, 16, 50)) mitkThrow() << "openCL Error when executing Kernel"; } else { - if(!this->ExecuteKernelChunksInBatches(m_PixelCalculation, 3, m_ChunkSize, 16, 50)) + if (!this->ExecuteKernelChunksInBatches(m_PixelCalculation, 3, m_ChunkSize, 16, 50)) mitkThrow() << "openCL Error when executing Kernel"; } // signalize the GPU-side data changed - m_Output->Modified( GPU_DATA ); + m_Output->Modified(GPU_DATA); } us::Module *mitk::PhotoacousticOCLBeamformingFilter::GetModule() { return us::GetModuleContext()->GetModule(); } bool mitk::PhotoacousticOCLBeamformingFilter::Initialize() { bool buildErr = true; cl_int clErr = 0; - if ( OclFilter::Initialize() ) + if (OclFilter::Initialize()) { - switch (m_Conf.Algorithm) + switch (m_Conf->GetAlgorithm()) + { + case BeamformingSettings::BeamformingAlgorithm::DAS: + { + this->m_PixelCalculation = clCreateKernel(this->m_ClProgram, "ckDAS", &clErr); + break; + } + case BeamformingSettings::BeamformingAlgorithm::DMAS: + { + this->m_PixelCalculation = clCreateKernel(this->m_ClProgram, "ckDMAS", &clErr); + break; + } + case BeamformingSettings::BeamformingAlgorithm::sDMAS: + { + this->m_PixelCalculation = clCreateKernel(this->m_ClProgram, "cksDMAS", &clErr); + break; + } + default: { - case BeamformingSettings::BeamformingAlgorithm::DAS: - { - this->m_PixelCalculation = clCreateKernel(this->m_ClProgram, "ckDAS", &clErr); - break; - } - case BeamformingSettings::BeamformingAlgorithm::DMAS: - { - this->m_PixelCalculation = clCreateKernel(this->m_ClProgram, "ckDMAS", &clErr); - break; - } - case BeamformingSettings::BeamformingAlgorithm::sDMAS: - { - this->m_PixelCalculation = clCreateKernel(this->m_ClProgram, "cksDMAS", &clErr); - break; - } - default: - { - MITK_INFO << "No beamforming algorithm specified, setting to DAS"; - this->m_PixelCalculation = clCreateKernel(this->m_ClProgram, "ckDAS", &clErr); - break; - } + MITK_INFO << "No beamforming algorithm specified, setting to DAS"; + this->m_PixelCalculation = clCreateKernel(this->m_ClProgram, "ckDAS", &clErr); + break; } - buildErr |= CHECK_OCL_ERR( clErr ); + } + buildErr |= CHECK_OCL_ERR(clErr); } CHECK_OCL_ERR(clErr); - return (OclFilter::IsInitialized() && buildErr ); + return (OclFilter::IsInitialized() && buildErr); } -void mitk::PhotoacousticOCLBeamformingFilter::SetInput(mitk::Image::Pointer image) +void mitk::PhotoacousticOCLBeamformingFilter::SetInput(mitk::Image::Pointer image, unsigned int inputSlices) { OclDataSetToDataSetFilter::SetInput(image); - m_InputImage = image; - m_Conf.inputDim[0] = m_InputImage->GetDimension(0); - m_Conf.inputDim[1] = m_InputImage->GetDimension(1); - m_Conf.inputDim[2] = m_InputImage->GetDimension(2); + m_inputSlices = inputSlices; } void mitk::PhotoacousticOCLBeamformingFilter::SetInput(void* data, unsigned int* dimensions, unsigned int BpE) { OclDataSetToDataSetFilter::SetInput(data, dimensions[0] * dimensions[1] * dimensions[2], BpE); - - m_Conf.inputDim[0] = dimensions[0]; - m_Conf.inputDim[1] = dimensions[1]; - m_Conf.inputDim[2] = dimensions[2]; } mitk::Image::Pointer mitk::PhotoacousticOCLBeamformingFilter::GetOutputAsImage() { mitk::Image::Pointer outputImage = mitk::Image::New(); if (m_Output->IsModified(GPU_DATA)) { void* pData = m_Output->TransferDataToCPU(m_CommandQue); const unsigned int dimension = 3; unsigned int dimensions[3] = { (unsigned int)m_OutputDim[0], (unsigned int)m_OutputDim[1], (unsigned int)m_OutputDim[2] }; const mitk::SlicedGeometry3D::Pointer p_slg = m_InputImage->GetSlicedGeometry(); MITK_DEBUG << "Creating new MITK Image."; outputImage->Initialize(this->GetOutputType(), dimension, dimensions); outputImage->SetSpacing(p_slg->GetSpacing()); - outputImage->SetImportVolume(pData, 0, 0, mitk::Image::ImportMemoryManagementType::ManageMemory); + outputImage->SetImportVolume(pData, 0, 0, mitk::Image::ImportMemoryManagementType::CopyMemory); + free(pData); } MITK_DEBUG << "Image Initialized."; return outputImage; } void* mitk::PhotoacousticOCLBeamformingFilter::GetOutput() { return OclDataSetToDataSetFilter::GetOutput(); } #endif diff --git a/Modules/PhotoacousticsAlgorithms/source/OpenCLFilter/mitkPhotoacousticOCLDelayCalculation.cpp b/Modules/PhotoacousticsAlgorithms/source/OpenCLFilter/mitkPhotoacousticOCLDelayCalculation.cpp index bbee18bc42..cc236090bc 100644 --- a/Modules/PhotoacousticsAlgorithms/source/OpenCLFilter/mitkPhotoacousticOCLDelayCalculation.cpp +++ b/Modules/PhotoacousticsAlgorithms/source/OpenCLFilter/mitkPhotoacousticOCLDelayCalculation.cpp @@ -1,125 +1,136 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #define _USE_MATH_DEFINES #include #include "./OpenCLFilter/mitkPhotoacousticOCLDelayCalculation.h" #include "usServiceReference.h" #include "mitkImageReadAccessor.h" -mitk::OCLDelayCalculation::OCLDelayCalculation() - : m_PixelCalculation(NULL) +mitk::OCLDelayCalculation::OCLDelayCalculation(mitk::BeamformingSettings::Pointer settings) + : m_PixelCalculation(NULL), + m_Conf(settings) { this->AddSourceFile("DelayCalculation.cl"); this->m_FilterID = "DelayCalculation"; m_ChunkSize[0] = 128; m_ChunkSize[1] = 128; m_ChunkSize[2] = 8; this->Initialize(); } mitk::OCLDelayCalculation::~OCLDelayCalculation() { if (this->m_PixelCalculation) { clReleaseKernel(m_PixelCalculation); } } void mitk::OCLDelayCalculation::Update() { //Check if context & program available if (!this->Initialize()) { us::ServiceReference ref = GetModuleContext()->GetServiceReference(); OclResourceService* resources = GetModuleContext()->GetService(ref); // clean-up also the resources resources->InvalidateStorage(); mitkThrow() << "Filter is not initialized. Cannot update."; } else { // Execute this->Execute(); } } void mitk::OCLDelayCalculation::Execute() { cl_int clErr = 0; - - unsigned int gridDim[3] = { m_Conf.ReconstructionLines / 2, m_Conf.SamplesPerLine, 1 }; + + unsigned int gridDim[3] = { m_Conf->GetReconstructionLines() / 2, m_Conf->GetSamplesPerLine(), 1 }; m_BufferSize = gridDim[0] * gridDim[1] * 1; try { this->InitExecNoInput(this->m_PixelCalculation, gridDim, m_BufferSize, sizeof(unsigned short)); } catch (const mitk::Exception& e) { MITK_ERROR << "Caught exception while initializing Delay Calculation filter: " << e.what(); return; } // This calculation is the same for all kernels, so for performance reasons simply perform it here instead of within the kernels - if (m_Conf.DelayCalculationMethod == BeamformingSettings::DelayCalc::QuadApprox) - m_DelayMultiplicatorRaw = pow(1 / (m_Conf.TimeSpacing*m_Conf.SpeedOfSound) * m_Conf.Pitch * (float)m_Conf.TransducerElements / (float)m_Conf.inputDim[0], 2) / 2; - else if (m_Conf.DelayCalculationMethod == BeamformingSettings::DelayCalc::Spherical) - m_DelayMultiplicatorRaw = 1 / (m_Conf.TimeSpacing*m_Conf.SpeedOfSound) * (m_Conf.Pitch*(float)m_Conf.TransducerElements); + if (m_Conf->GetDelayCalculationMethod() == BeamformingSettings::DelayCalc::QuadApprox) + m_DelayMultiplicatorRaw = pow(1 / (m_Conf->GetTimeSpacing()*m_Conf->GetSpeedOfSound()) * + m_Conf->GetPitchInMeters() * (float)m_Conf->GetTransducerElements() / (float)m_Conf->GetInputDim()[0], 2) / 2; + else if (m_Conf->GetDelayCalculationMethod() == BeamformingSettings::DelayCalc::Spherical) + m_DelayMultiplicatorRaw = 1 / (m_Conf->GetTimeSpacing()*m_Conf->GetSpeedOfSound()) * + (m_Conf->GetPitchInMeters()*(float)m_Conf->GetTransducerElements()); // as openCL does not support bool as a kernel argument, we need to buffer this value in a char... - m_IsPAImage = m_Conf.isPhotoacousticImage; - + m_IsPAImage = m_Conf->GetIsPhotoacousticImage(); + + unsigned int reconstructionLines = this->m_Conf->GetReconstructionLines(); + unsigned int samplesperLine = this->m_Conf->GetSamplesPerLine(); + + float percentOfImageReconstructed = (float)(m_Conf->GetReconstructionDepth()) / + (float)(m_Conf->GetInputDim()[1] * m_Conf->GetSpeedOfSound() * m_Conf->GetTimeSpacing() / (float)(2 - (int)m_Conf->GetIsPhotoacousticImage())); + percentOfImageReconstructed = percentOfImageReconstructed <= 1 ? percentOfImageReconstructed : 1; + clErr = clSetKernelArg(this->m_PixelCalculation, 1, sizeof(cl_mem), &(this->m_UsedLines)); - clErr |= clSetKernelArg(this->m_PixelCalculation, 2, sizeof(cl_uint), &(this->m_Conf.inputDim[0])); - clErr |= clSetKernelArg(this->m_PixelCalculation, 3, sizeof(cl_uint), &(this->m_Conf.inputDim[1])); - clErr |= clSetKernelArg(this->m_PixelCalculation, 4, sizeof(cl_uint), &(this->m_Conf.ReconstructionLines)); - clErr |= clSetKernelArg(this->m_PixelCalculation, 5, sizeof(cl_uint), &(this->m_Conf.SamplesPerLine)); + clErr |= clSetKernelArg(this->m_PixelCalculation, 2, sizeof(cl_uint), &(this->m_Conf->GetInputDim()[0])); + clErr |= clSetKernelArg(this->m_PixelCalculation, 3, sizeof(cl_uint), &(this->m_Conf->GetInputDim()[1])); + clErr |= clSetKernelArg(this->m_PixelCalculation, 4, sizeof(cl_uint), &(reconstructionLines)); + clErr |= clSetKernelArg(this->m_PixelCalculation, 5, sizeof(cl_uint), &(samplesperLine)); clErr |= clSetKernelArg(this->m_PixelCalculation, 6, sizeof(cl_char), &(this->m_IsPAImage)); clErr |= clSetKernelArg(this->m_PixelCalculation, 7, sizeof(cl_float), &(this->m_DelayMultiplicatorRaw)); - + clErr |= clSetKernelArg(this->m_PixelCalculation, 8, sizeof(cl_float), &(percentOfImageReconstructed)); + CHECK_OCL_ERR(clErr); // execute the filter on a 3D NDRange if (!this->ExecuteKernelChunksInBatches(m_PixelCalculation, 2, m_ChunkSize, 16, 50)) mitkThrow() << "openCL Error when executing Kernel"; // signalize the GPU-side data changed m_Output->Modified(GPU_DATA); } us::Module *mitk::OCLDelayCalculation::GetModule() { return us::GetModuleContext()->GetModule(); } bool mitk::OCLDelayCalculation::Initialize() { bool buildErr = true; cl_int clErr = 0; if (OclFilter::Initialize()) { - if(m_Conf.DelayCalculationMethod == BeamformingSettings::DelayCalc::QuadApprox) + if (m_Conf->GetDelayCalculationMethod() == BeamformingSettings::DelayCalc::QuadApprox) this->m_PixelCalculation = clCreateKernel(this->m_ClProgram, "ckDelayCalculationQuad", &clErr); - if (m_Conf.DelayCalculationMethod == BeamformingSettings::DelayCalc::Spherical) + if (m_Conf->GetDelayCalculationMethod() == BeamformingSettings::DelayCalc::Spherical) this->m_PixelCalculation = clCreateKernel(this->m_ClProgram, "ckDelayCalculationSphe", &clErr); buildErr |= CHECK_OCL_ERR(clErr); } return (OclFilter::IsInitialized() && buildErr); -} \ No newline at end of file +} diff --git a/Modules/PhotoacousticsAlgorithms/source/OpenCLFilter/mitkPhotoacousticOCLUsedLinesCalculation.cpp b/Modules/PhotoacousticsAlgorithms/source/OpenCLFilter/mitkPhotoacousticOCLUsedLinesCalculation.cpp index 08f79410c3..dda6a3eef9 100644 --- a/Modules/PhotoacousticsAlgorithms/source/OpenCLFilter/mitkPhotoacousticOCLUsedLinesCalculation.cpp +++ b/Modules/PhotoacousticsAlgorithms/source/OpenCLFilter/mitkPhotoacousticOCLUsedLinesCalculation.cpp @@ -1,114 +1,127 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #if defined(PHOTOACOUSTICS_USE_GPU) || DOXYGEN #include "./OpenCLFilter/mitkPhotoacousticOCLUsedLinesCalculation.h" #include "usServiceReference.h" #include "mitkImageReadAccessor.h" -mitk::OCLUsedLinesCalculation::OCLUsedLinesCalculation() - : m_PixelCalculation(NULL) +mitk::OCLUsedLinesCalculation::OCLUsedLinesCalculation(mitk::BeamformingSettings::Pointer settings) + : m_PixelCalculation(NULL), + m_Conf(settings) { this->AddSourceFile("UsedLinesCalculation.cl"); this->m_FilterID = "UsedLinesCalculation"; m_ChunkSize[0] = 128; m_ChunkSize[1] = 128; m_ChunkSize[2] = 8; this->Initialize(); } mitk::OCLUsedLinesCalculation::~OCLUsedLinesCalculation() { if (this->m_PixelCalculation) { clReleaseKernel(m_PixelCalculation); } } void mitk::OCLUsedLinesCalculation::Update() { //Check if context & program available if (!this->Initialize()) { us::ServiceReference ref = GetModuleContext()->GetServiceReference(); OclResourceService* resources = GetModuleContext()->GetService(ref); // clean-up also the resources resources->InvalidateStorage(); mitkThrow() << "Filter is not initialized. Cannot update."; } else { // Execute this->Execute(); } } void mitk::OCLUsedLinesCalculation::Execute() { cl_int clErr = 0; - unsigned int gridDim[3] = { m_Conf.ReconstructionLines, m_Conf.SamplesPerLine, 1 }; + unsigned int gridDim[3] = { m_Conf->GetReconstructionLines(), m_Conf->GetSamplesPerLine(), 1 }; size_t outputSize = gridDim[0] * gridDim[1] * 3; try { this->InitExecNoInput(this->m_PixelCalculation, gridDim, outputSize, sizeof(unsigned short)); } catch (const mitk::Exception& e) { MITK_ERROR << "Caught exception while initializing UsedLines filter: " << e.what(); return; } // This calculation is the same for all kernels, so for performance reasons simply perform it here instead of within the kernels - m_part = (tan(m_Conf.Angle / 360 * 2 * itk::Math::pi) * ((m_Conf.SpeedOfSound * m_Conf.TimeSpacing)) / (m_Conf.Pitch * m_Conf.TransducerElements)) * m_Conf.inputDim[0]; - + m_part = (tan(m_Conf->GetAngle() / 360 * 2 * itk::Math::pi) * + ((m_Conf->GetSpeedOfSound() * m_Conf->GetTimeSpacing())) / + (m_Conf->GetPitchInMeters() * m_Conf->GetTransducerElements())) * m_Conf->GetInputDim()[0]; + + unsigned int reconLines = this->m_Conf->GetReconstructionLines(); + unsigned int samplesPerLine = this->m_Conf->GetSamplesPerLine(); + char isPAImage = this->m_Conf->GetIsPhotoacousticImage(); + + float percentOfImageReconstructed = (float)(m_Conf->GetReconstructionDepth()) / + (float)(m_Conf->GetInputDim()[1] * m_Conf->GetSpeedOfSound() * m_Conf->GetTimeSpacing() / (float)(2 - (int)m_Conf->GetIsPhotoacousticImage())); + percentOfImageReconstructed = percentOfImageReconstructed <= 1 ? percentOfImageReconstructed : 1; + clErr = clSetKernelArg(this->m_PixelCalculation, 1, sizeof(cl_float), &(this->m_part)); - clErr |= clSetKernelArg(this->m_PixelCalculation, 2, sizeof(cl_uint), &(this->m_Conf.inputDim[0])); - clErr |= clSetKernelArg(this->m_PixelCalculation, 3, sizeof(cl_uint), &(this->m_Conf.inputDim[1])); - clErr |= clSetKernelArg(this->m_PixelCalculation, 4, sizeof(cl_uint), &(this->m_Conf.ReconstructionLines)); - clErr |= clSetKernelArg(this->m_PixelCalculation, 5, sizeof(cl_uint), &(this->m_Conf.SamplesPerLine)); + clErr |= clSetKernelArg(this->m_PixelCalculation, 2, sizeof(cl_uint), &(this->m_Conf->GetInputDim()[0])); + clErr |= clSetKernelArg(this->m_PixelCalculation, 3, sizeof(cl_uint), &(this->m_Conf->GetInputDim()[1])); + clErr |= clSetKernelArg(this->m_PixelCalculation, 4, sizeof(cl_uint), &(reconLines)); + clErr |= clSetKernelArg(this->m_PixelCalculation, 5, sizeof(cl_uint), &(samplesPerLine)); + clErr |= clSetKernelArg(this->m_PixelCalculation, 6, sizeof(cl_char), &(isPAImage)); + clErr |= clSetKernelArg(this->m_PixelCalculation, 7, sizeof(cl_float), &(percentOfImageReconstructed)); CHECK_OCL_ERR(clErr); // execute the filter on a 2D NDRange if (!this->ExecuteKernelChunksInBatches(m_PixelCalculation, 2, m_ChunkSize, 16, 50)) mitkThrow() << "openCL Error when executing Kernel"; // signalize the GPU-side data changed m_Output->Modified(GPU_DATA); } us::Module *mitk::OCLUsedLinesCalculation::GetModule() { return us::GetModuleContext()->GetModule(); } bool mitk::OCLUsedLinesCalculation::Initialize() { bool buildErr = true; cl_int clErr = 0; if (OclFilter::Initialize()) { this->m_PixelCalculation = clCreateKernel(this->m_ClProgram, "ckUsedLines", &clErr); buildErr |= CHECK_OCL_ERR(clErr); } return (OclFilter::IsInitialized() && buildErr); } #endif diff --git a/Modules/PhotoacousticsAlgorithms/source/filters/mitkBandpassFilter.cpp b/Modules/PhotoacousticsAlgorithms/source/filters/mitkBandpassFilter.cpp new file mode 100644 index 0000000000..1e36f07613 --- /dev/null +++ b/Modules/PhotoacousticsAlgorithms/source/filters/mitkBandpassFilter.cpp @@ -0,0 +1,266 @@ +/*=================================================================== +mitkCastToFloatImageFilter +The Medical Imaging Interaction Toolkit (MITK) + +Copyright (c) German Cancer Research Center, +Division of Medical and Biological Informatics. +All rights reserved. + +This software is distributed WITHOUT ANY WARRANTY; without +even the implied warranty of MERCHANTABILITY or FITNESS FOR +A PARTICULAR PURPOSE. + +See LICENSE.txt or http://www.mitk.org for details. + +===================================================================*/ + +#define _USE_MATH_DEFINES +#include + +#include "mitkBandpassFilter.h" + +#include "../ITKFilter/ITKUltrasound/itkFFT1DComplexConjugateToRealImageFilter.h" +#include "../ITKFilter/ITKUltrasound/itkFFT1DRealToComplexConjugateImageFilter.h" +#include "itkComplexToModulusImageFilter.h" +#include "itkMultiplyImageFilter.h" +#include "mitkIOUtil.h" +#include "mitkITKImageImport.h" +#include "mitkImageCast.h" + +mitk::BandpassFilter::BandpassFilter() + : m_HighPass(0), + m_LowPass(50), + m_HighPassAlpha(1), + m_LowPassAlpha(1), + m_FilterService(mitk::PhotoacousticFilterService::New()) +{ + MITK_INFO << "Instantiating BandpassFilter..."; + SetNumberOfIndexedInputs(1); + SetNumberOfIndexedOutputs(1); + MITK_INFO << "Instantiating BandpassFilter...[Done]"; +} + +mitk::BandpassFilter::~BandpassFilter() +{ + MITK_INFO << "Destructed BandpassFilter."; +} + +void mitk::BandpassFilter::SanityCheckPreconditions() +{ + auto input = GetInput(); + + std::string type = input->GetPixelType().GetTypeAsString(); + if (!(type == "scalar (float)" || type == " (float)")) + { + MITK_ERROR << "This filter can currently only handle float type images."; + mitkThrow() << "This filter can currently only handle float type images."; + } + if (m_HighPass > m_LowPass) + { + MITK_ERROR << "High Pass is higher than Low Pass; aborting."; + mitkThrow() << "High Pass is higher than Low Pass; aborting."; + } +} + +itk::Image::Pointer BPFunction(mitk::Image::Pointer reference, + float cutoffFrequencyPixelHighPass, + float cutoffFrequencyPixelLowPass, + float alphaHighPass, + float alphaLowPass) +{ + float *imageData = new float[reference->GetDimension(0) * reference->GetDimension(1)]; + + float width = cutoffFrequencyPixelLowPass - cutoffFrequencyPixelHighPass; + float center = cutoffFrequencyPixelHighPass + width / 2.f; + + for (unsigned int n = 0; n < reference->GetDimension(1); ++n) + { + imageData[reference->GetDimension(0) * n] = 0; + } + for (int n = 0; n < width; ++n) + { + imageData[reference->GetDimension(0) * (int)(n + center - (width / 2.f))] = 1; + if (n <= (alphaHighPass * (width - 1)) / 2.f) + { + if (alphaHighPass > 0.00001f) + { + imageData[reference->GetDimension(0) * (int)(n + center - (width / 2.f))] = + (1 + cos(itk::Math::pi * (2 * n / (alphaHighPass * (width - 1)) - 1))) / 2; + } + else + { + imageData[reference->GetDimension(0) * (int)(n + center - (width / 2.f))] = 1; + } + } + else if (n >= (width - 1) * (1 - alphaLowPass / 2.f)) + { + if (alphaLowPass > 0.00001f) + { + imageData[reference->GetDimension(0) * (int)(n + center - (width / 2.f))] = + (1 + cos(itk::Math::pi * (2 * n / (alphaLowPass * (width - 1)) + 1 - 2 / alphaLowPass))) / 2; + } + else + { + imageData[reference->GetDimension(0) * (int)(n + center - (width / 2.f))] = 1; + } + } + } + + for (unsigned int n = reference->GetDimension(1) / 2; n < reference->GetDimension(1); ++n) + { + imageData[reference->GetDimension(0) * n] = + imageData[(reference->GetDimension(1) - (n + 1)) * reference->GetDimension(0)]; + } + + for (unsigned int line = 1; line < reference->GetDimension(0); ++line) + { + for (unsigned int sample = 0; sample < reference->GetDimension(1); ++sample) + { + imageData[reference->GetDimension(0) * sample + line] = imageData[reference->GetDimension(0) * sample]; + } + } + + typedef itk::Image ImageType; + ImageType::RegionType region; + ImageType::IndexType start; + start.Fill(0); + + region.SetIndex(start); + + ImageType::SizeType size; + size[0] = reference->GetDimension(0); + size[1] = reference->GetDimension(1); + size[2] = reference->GetDimension(2); + + region.SetSize(size); + + ImageType::SpacingType SpacingItk; + SpacingItk[0] = reference->GetGeometry()->GetSpacing()[0]; + SpacingItk[1] = reference->GetGeometry()->GetSpacing()[1]; + SpacingItk[2] = reference->GetGeometry()->GetSpacing()[2]; + + ImageType::Pointer image = ImageType::New(); + image->SetRegions(region); + image->Allocate(); + image->FillBuffer(itk::NumericTraits::Zero); + image->SetSpacing(SpacingItk); + + ImageType::IndexType pixelIndex; + + for (unsigned int slice = 0; slice < reference->GetDimension(2); ++slice) + { + for (unsigned int line = 0; line < reference->GetDimension(0); ++line) + { + for (unsigned int sample = 0; sample < reference->GetDimension(1); ++sample) + { + pixelIndex[0] = line; + pixelIndex[1] = sample; + pixelIndex[2] = slice; + + image->SetPixel(pixelIndex, imageData[line + sample * reference->GetDimension(0)]); + } + } + } + + delete[] imageData; + + return image; +} + +void mitk::BandpassFilter::GenerateData() +{ + SanityCheckPreconditions(); + auto input = GetInput(); + auto output = GetOutput(); + mitk::Image::Pointer resampledInput = input; + + double powerOfTwo = std::log2(input->GetDimension(1)); + int finalSize = 0; + + // check if this is a power of two by checking that log2 is int + if (std::fmod(powerOfTwo, 1.0) >= std::numeric_limits::epsilon()) + { + finalSize = (int)pow(2, std::ceil(powerOfTwo)); + double spacing_x = input->GetGeometry()->GetSpacing()[0]; + double spacing_y = input->GetGeometry()->GetSpacing()[1] * ((double)input->GetDimensions()[1]) / finalSize; + double dim[2] = {spacing_x, spacing_y}; + resampledInput = m_FilterService->ApplyResampling(input, dim); + } + + // do a fourier transform, multiply with an appropriate window for the filter, and transform back + typedef itk::Image RealImageType; + RealImageType::Pointer image; + + mitk::CastToItkImage(resampledInput, image); + typedef itk::FFT1DRealToComplexConjugateImageFilter ForwardFFTFilterType; + typedef ForwardFFTFilterType::OutputImageType ComplexImageType; + ForwardFFTFilterType::Pointer forwardFFTFilter = ForwardFFTFilterType::New(); + forwardFFTFilter->SetInput(image); + forwardFFTFilter->SetDirection(1); + + try + { + forwardFFTFilter->UpdateOutputInformation(); + } + catch (itk::ExceptionObject &error) + { + std::cerr << "Error: " << error << std::endl; + MITK_ERROR << "Bandpass could not be applied"; + mitkThrow() << "bandpass error"; + } + + if (m_HighPass > m_LowPass) + mitkThrow() << "High pass frequency higher than low pass frequency, abort"; + + float singleVoxel = + 2 * M_PI / ((float)resampledInput->GetGeometry()->GetSpacing()[1] * resampledInput->GetDimension(1)); // [MHz] + float cutoffPixelHighPass = std::min((m_HighPass / singleVoxel), (float)resampledInput->GetDimension(1) / 2.0f); + float cutoffPixelLowPass = std::min((m_LowPass / singleVoxel), (float)resampledInput->GetDimension(1) / 2.0f); + + MITK_INFO << "SingleVoxel: " << singleVoxel; + MITK_INFO << "HighPass: " << m_HighPass; + MITK_INFO << "LowPass: " << m_LowPass; + + MITK_INFO << "cutoffPixelHighPass: " << cutoffPixelHighPass; + MITK_INFO << "cutoffPixelLowPass: " << cutoffPixelLowPass; + + RealImageType::Pointer fftMultiplicator = + BPFunction(resampledInput, cutoffPixelHighPass, cutoffPixelLowPass, m_HighPassAlpha, m_LowPassAlpha); + + typedef itk::MultiplyImageFilter MultiplyFilterType; + MultiplyFilterType::Pointer multiplyFilter = MultiplyFilterType::New(); + multiplyFilter->SetInput1(forwardFFTFilter->GetOutput()); + multiplyFilter->SetInput2(fftMultiplicator); + + /* + GrabItkImageMemory(fftMultiplicator, output); + mitk::IOUtil::Save(output, "D:/fftImage.nrrd"); + + typedef itk::ComplexToModulusImageFilter< ComplexImageType, RealImageType > modulusType; + modulusType::Pointer modul = modulusType::New(); + + modul->SetInput(multiplyFilter->GetOutput()); + GrabItkImageMemory(modul->GetOutput(), output); + mitk::IOUtil::Save(output, "D:/fftout.nrrd"); + + modul->SetInput(forwardFFTFilter->GetOutput()); + GrabItkImageMemory(modul->GetOutput(), output); + mitk::IOUtil::Save(output, "D:/fftin.nrrd");*/ + + typedef itk::FFT1DComplexConjugateToRealImageFilter InverseFilterType; + InverseFilterType::Pointer inverseFFTFilter = InverseFilterType::New(); + inverseFFTFilter->SetInput(multiplyFilter->GetOutput()); + inverseFFTFilter->SetDirection(1); + + GrabItkImageMemory(inverseFFTFilter->GetOutput(), output); + + double spacing_x = input->GetGeometry()->GetSpacing()[0]; + double spacing_y = input->GetGeometry()->GetSpacing()[1]; + double dim[2] = {spacing_x, spacing_y}; + auto resampledOutput = m_FilterService->ApplyResampling(output, dim); + + output->Initialize(mitk::MakeScalarPixelType(), 3, input->GetDimensions()); + output->SetSpacing(resampledOutput->GetGeometry()->GetSpacing()); + ImageReadAccessor copy(resampledOutput); + output->SetImportVolume(copy.GetData()); +} diff --git a/Modules/PhotoacousticsAlgorithms/source/filters/mitkBeamformingFilter.cpp b/Modules/PhotoacousticsAlgorithms/source/filters/mitkBeamformingFilter.cpp new file mode 100644 index 0000000000..b1f655f1bf --- /dev/null +++ b/Modules/PhotoacousticsAlgorithms/source/filters/mitkBeamformingFilter.cpp @@ -0,0 +1,297 @@ +/*=================================================================== +mitkBeamformingFilter +The Medical Imaging Interaction Toolkit (MITK) + +Copyright (c) German Cancer Research Center, +Division of Medical and Biological Informatics. +All rights reserved. + +This software is distributed WITHOUT ANY WARRANTY; without +even the implied warranty of MERCHANTABILITY or FITNESS FOR +A PARTICULAR PURPOSE. + +See LICENSE.txt or http://www.mitk.org for details. + +===================================================================*/ + +#include "mitkProperties.h" +#include "mitkImageReadAccessor.h" +#include +#include +#include +#include +#include +#include "mitkImageCast.h" +#include "mitkBeamformingFilter.h" +#include "mitkBeamformingUtils.h" + +mitk::BeamformingFilter::BeamformingFilter(mitk::BeamformingSettings::Pointer settings) : + m_OutputData(nullptr), + m_InputData(nullptr), + m_Conf(settings) +{ + MITK_INFO << "Instantiating BeamformingFilter..."; + this->SetNumberOfIndexedInputs(1); + this->SetNumberOfRequiredInputs(1); + + m_ProgressHandle = [](int, std::string) {}; +#if defined(PHOTOACOUSTICS_USE_GPU) + m_BeamformingOclFilter = mitk::PhotoacousticOCLBeamformingFilter::New(m_Conf); +#else + m_BeamformingOclFilter = mitk::PhotoacousticOCLBeamformingFilter::New(); +#endif + + MITK_INFO << "Instantiating BeamformingFilter...[Done]"; +} + +void mitk::BeamformingFilter::SetProgressHandle(std::function progressHandle) +{ + m_ProgressHandle = progressHandle; +} + +mitk::BeamformingFilter::~BeamformingFilter() +{ + MITK_INFO << "Destructed BeamformingFilter"; +} + +void mitk::BeamformingFilter::GenerateInputRequestedRegion() +{ + Superclass::GenerateInputRequestedRegion(); + + mitk::Image* output = this->GetOutput(); + mitk::Image* input = const_cast (this->GetInput()); + if (!output->IsInitialized()) + { + return; + } + + input->SetRequestedRegionToLargestPossibleRegion(); +} + +void mitk::BeamformingFilter::GenerateOutputInformation() +{ + mitk::Image::ConstPointer input = this->GetInput(); + mitk::Image::Pointer output = this->GetOutput(); + + if ((output->IsInitialized()) && (this->GetMTime() <= m_TimeOfHeaderInitialization.GetMTime())) + return; + + mitk::Vector3D spacing; + spacing[0] = m_Conf->GetPitchInMeters() * m_Conf->GetTransducerElements() * 1000 / m_Conf->GetReconstructionLines(); + float desiredYSpacing = m_Conf->GetReconstructionDepth() * 1000 / m_Conf->GetSamplesPerLine(); + float maxYSpacing = m_Conf->GetSpeedOfSound() * m_Conf->GetTimeSpacing() * input->GetDimension(1) / m_Conf->GetSamplesPerLine() * 1000; + spacing[1] = desiredYSpacing < maxYSpacing ? desiredYSpacing : maxYSpacing; + spacing[2] = 1; + + unsigned int dim[] = { m_Conf->GetReconstructionLines(), m_Conf->GetSamplesPerLine(), input->GetDimension(2)}; + output->Initialize(mitk::MakeScalarPixelType(), 3, dim); + output->GetGeometry()->SetSpacing(spacing); + output->GetGeometry()->Modified(); + output->SetPropertyList(input->GetPropertyList()->Clone()); + + m_TimeOfHeaderInitialization.Modified(); +} + +void mitk::BeamformingFilter::GenerateData() +{ + mitk::Image::Pointer input = this->GetInput(); + if (!(input->GetPixelType().GetTypeAsString() == "scalar (float)" || input->GetPixelType().GetTypeAsString() == " (float)")) + { + MITK_ERROR << "Pixel type of input needs to be float for this filter to work."; + mitkThrow() << "Pixel type of input needs to be float for this filter to work."; + } + + GenerateOutputInformation(); + mitk::Image::Pointer output = this->GetOutput(); + + if (!output->IsInitialized()) + return; + + auto begin = std::chrono::high_resolution_clock::now(); // debbuging the performance... + + if (!m_Conf->GetUseGPU()) + { + int progInterval = output->GetDimension(2) / 20 > 1 ? output->GetDimension(2) / 20 : 1; + // the interval at which we update the gui progress bar + + float inputDim[2] = { (float)input->GetDimension(0), (float)input->GetDimension(1) }; + float outputDim[2] = { (float)output->GetDimension(0), (float)output->GetDimension(1) }; + + for (unsigned int i = 0; i < output->GetDimension(2); ++i) // seperate Slices should get Beamforming seperately applied + { + mitk::ImageReadAccessor inputReadAccessor(input, input->GetSliceData(i)); + m_InputData = (float*)inputReadAccessor.GetData(); + + m_OutputData = new float[m_Conf->GetReconstructionLines()*m_Conf->GetSamplesPerLine()]; + + // fill the image with zeros + for (int l = 0; l < outputDim[0]; ++l) + { + for (int s = 0; s < outputDim[1]; ++s) + { + m_OutputData[l*(short)outputDim[1] + s] = 0; + } + } + + std::thread *threads = new std::thread[(short)outputDim[0]]; + + // every line will be beamformed in a seperate thread + if (m_Conf->GetAlgorithm() == BeamformingSettings::BeamformingAlgorithm::DAS) + { + if (m_Conf->GetDelayCalculationMethod() == BeamformingSettings::DelayCalc::QuadApprox) + { + for (short line = 0; line < outputDim[0]; ++line) + { + threads[line] = std::thread(&BeamformingUtils::DASQuadraticLine, m_InputData, + m_OutputData, inputDim, outputDim, line, m_Conf); + } + } + else if (m_Conf->GetDelayCalculationMethod() == BeamformingSettings::DelayCalc::Spherical) + { + for (short line = 0; line < outputDim[0]; ++line) + { + threads[line] = std::thread(&BeamformingUtils::DASSphericalLine, m_InputData, + m_OutputData, inputDim, outputDim, line, m_Conf); + } + } + } + else if (m_Conf->GetAlgorithm() == BeamformingSettings::BeamformingAlgorithm::DMAS) + { + if (m_Conf->GetDelayCalculationMethod() == BeamformingSettings::DelayCalc::QuadApprox) + { + for (short line = 0; line < outputDim[0]; ++line) + { + threads[line] = std::thread(&BeamformingUtils::DMASQuadraticLine, m_InputData, + m_OutputData, inputDim, outputDim, line, m_Conf); + } + } + else if (m_Conf->GetDelayCalculationMethod() == BeamformingSettings::DelayCalc::Spherical) + { + for (short line = 0; line < outputDim[0]; ++line) + { + threads[line] = std::thread(&BeamformingUtils::DMASSphericalLine, m_InputData, + m_OutputData, inputDim, outputDim, line, m_Conf); + } + } + } + else if (m_Conf->GetAlgorithm() == BeamformingSettings::BeamformingAlgorithm::sDMAS) + { + if (m_Conf->GetDelayCalculationMethod() == BeamformingSettings::DelayCalc::QuadApprox) + { + for (short line = 0; line < outputDim[0]; ++line) + { + threads[line] = std::thread(&BeamformingUtils::sDMASQuadraticLine, m_InputData, + m_OutputData, inputDim, outputDim, line, m_Conf); + } + } + else if (m_Conf->GetDelayCalculationMethod() == BeamformingSettings::DelayCalc::Spherical) + { + for (short line = 0; line < outputDim[0]; ++line) + { + threads[line] = std::thread(&BeamformingUtils::sDMASSphericalLine, m_InputData, + m_OutputData, inputDim, outputDim, line, m_Conf); + } + } + } + // wait for all lines to finish + for (short line = 0; line < outputDim[0]; ++line) + { + threads[line].join(); + } + + output->SetSlice(m_OutputData, i); + + if (i % progInterval == 0) + m_ProgressHandle((int)((i + 1) / (float)output->GetDimension(2) * 100), "performing reconstruction"); + + delete[] m_OutputData; + m_OutputData = nullptr; + m_InputData = nullptr; + } + } +#if defined(PHOTOACOUSTICS_USE_GPU) || DOXYGEN + else + { + try + { + // first, we check whether the data is float, other formats are unsupported + if (!(input->GetPixelType().GetTypeAsString() == "scalar (float)" || input->GetPixelType().GetTypeAsString() == " (float)")) + { + MITK_ERROR << "Pixel type is not float, abort"; + mitkThrow() << "Pixel type is not float, abort"; + } + + unsigned long availableMemory = m_BeamformingOclFilter->GetDeviceMemory(); + + unsigned int batchSize = m_Conf->GetGPUBatchSize(); + unsigned int batches = (unsigned int)((float)input->GetDimension(2) / batchSize) + (input->GetDimension(2) % batchSize > 0); + + unsigned int batchDim[] = { input->GetDimension(0), input->GetDimension(1), batchSize }; + unsigned int batchDimLast[] = { input->GetDimension(0), input->GetDimension(1), input->GetDimension(2) % batchSize }; + + // the following safeguard is probably only needed for absurdly small GPU memory + if((unsigned long)batchSize * + ((unsigned long)(batchDim[0] * batchDim[1]) * 4 + // single input image (float) + (unsigned long)(m_Conf->GetReconstructionLines() * m_Conf->GetSamplesPerLine()) * 4) // single output image (float) + > availableMemory - + (unsigned long)(m_Conf->GetReconstructionLines() / 2 * m_Conf->GetSamplesPerLine()) * 2 - // Delays buffer (unsigned short) + (unsigned long)(m_Conf->GetReconstructionLines() * m_Conf->GetSamplesPerLine()) * 3 * 2 - // UsedLines buffer (unsigned short) + 50 * 1024 * 1024)// 50 MB buffer for local data, system purposes etc + { + MITK_ERROR << "device memory too small for GPU beamforming; try decreasing the batch size"; + return; + } + + mitk::ImageReadAccessor copy(input); + + for (unsigned int i = 0; i < batches; ++i) + { + m_ProgressHandle(100.f * (float)i / (float)batches, "performing reconstruction"); + + mitk::Image::Pointer inputBatch = mitk::Image::New(); + unsigned int num_Slices = 1; + if (i == batches - 1 && (input->GetDimension(2) % batchSize > 0)) + { + inputBatch->Initialize(mitk::MakeScalarPixelType(), 3, batchDimLast); + num_Slices = batchDimLast[2]; + } + else + { + inputBatch->Initialize(mitk::MakeScalarPixelType(), 3, batchDim); + num_Slices = batchDim[2]; + } + + inputBatch->SetSpacing(input->GetGeometry()->GetSpacing()); + + inputBatch->SetImportVolume(&(((float*)copy.GetData())[input->GetDimension(0) * input->GetDimension(1) * batchSize * i])); + + m_BeamformingOclFilter->SetApodisation(m_Conf->GetApodizationFunction(), m_Conf->GetApodizationArraySize()); + m_BeamformingOclFilter->SetInput(inputBatch, num_Slices); + m_BeamformingOclFilter->Update(); + + void* out = m_BeamformingOclFilter->GetOutput(); + + for (unsigned int slice = 0; slice < num_Slices; ++slice) + { + output->SetImportSlice( + &(((float*)out)[m_Conf->GetReconstructionLines() * m_Conf->GetSamplesPerLine() * slice]), + batchSize * i + slice, 0, 0, mitk::Image::ImportMemoryManagementType::CopyMemory); + } + } + } + catch (mitk::Exception &e) + { + std::string errorMessage = "Caught unexpected exception "; + errorMessage.append(e.what()); + MITK_ERROR << errorMessage; + + float* dummyData = new float[m_Conf->GetReconstructionLines() * m_Conf->GetSamplesPerLine() * m_Conf->GetInputDim()[2]]; + output->SetImportVolume(dummyData, 0, 0, mitk::Image::ImportMemoryManagementType::CopyMemory); + } + } +#endif + m_TimeOfHeaderInitialization.Modified(); + + auto end = std::chrono::high_resolution_clock::now(); + MITK_INFO << "Beamforming of " << output->GetDimension(2) << " Images completed in " << ((float)std::chrono::duration_cast(end - begin).count()) / 1000000 << "ms" << std::endl; +} diff --git a/Modules/PhotoacousticsAlgorithms/source/filters/mitkBeamformingSettings.cpp b/Modules/PhotoacousticsAlgorithms/source/filters/mitkBeamformingSettings.cpp new file mode 100644 index 0000000000..b052f15a5c --- /dev/null +++ b/Modules/PhotoacousticsAlgorithms/source/filters/mitkBeamformingSettings.cpp @@ -0,0 +1,105 @@ +/*=================================================================== +mitkBeamformingSettings +The Medical Imaging Interaction Toolkit (MITK) + +Copyright (c) German Cancer Research Center, +Division of Medical and Biological Informatics. +All rights reserved. + +This software is distributed WITHOUT ANY WARRANTY; without +even the implied warranty of MERCHANTABILITY or FITNESS FOR +A PARTICULAR PURPOSE. + +See LICENSE.txt or http://www.mitk.org for details. + +===================================================================*/ + +#include "mitkBeamformingSettings.h" +#include "mitkBeamformingUtils.h" +#include "itkMutexLock.h" + +mitk::BeamformingSettings::BeamformingSettings(std::string xmlFile) +{ + if((xmlFile.length())>0) + { + MITK_ERROR << "Not implemented yet."; + mitkThrow() << "Not implemented yet."; + } +} + +mitk::BeamformingSettings::BeamformingSettings(float pitchInMeters, + float speedOfSound, + float timeSpacing, + float angle, + bool isPhotoacousticImage, + unsigned int samplesPerLine, + unsigned int reconstructionLines, + unsigned int* inputDim, + float reconstructionDepth, + bool useGPU, + unsigned int GPUBatchSize, + DelayCalc delayCalculationMethod, + Apodization apod, + unsigned int apodizationArraySize, + BeamformingAlgorithm algorithm +) : + m_PitchInMeters(pitchInMeters), + m_SpeedOfSound(speedOfSound), + m_TimeSpacing(timeSpacing), + m_Angle(angle), + m_IsPhotoacousticImage(isPhotoacousticImage), + m_SamplesPerLine(samplesPerLine), + m_ReconstructionLines(reconstructionLines), + m_ReconstructionDepth(reconstructionDepth), + m_UseGPU(useGPU), + m_GPUBatchSize(GPUBatchSize), + m_DelayCalculationMethod(delayCalculationMethod), + m_Apod(apod), + m_ApodizationArraySize(apodizationArraySize), + m_Algorithm(algorithm) +{ + if (inputDim == nullptr) + { + MITK_ERROR << "No input dimension given."; + mitkThrow() << "No input dimension given."; + } + + switch (GetApod()) + { + case BeamformingSettings::Apodization::Hann: + m_ApodizationFunction = mitk::BeamformingUtils::VonHannFunction(GetApodizationArraySize()); + break; + case BeamformingSettings::Apodization::Hamm: + m_ApodizationFunction = mitk::BeamformingUtils::HammFunction(GetApodizationArraySize()); + break; + case BeamformingSettings::Apodization::Box: + default: + m_ApodizationFunction = mitk::BeamformingUtils::BoxFunction(GetApodizationArraySize()); + break; + } + + m_InputDim = new unsigned int[3]{ inputDim[0], inputDim[1], inputDim[2] }; + + m_TransducerElements = m_InputDim[0]; +} + +mitk::BeamformingSettings::~BeamformingSettings() +{ + MITK_INFO << "Destructing beamforming settings..."; + //Free memory + if (m_ApodizationFunction != nullptr) + { + MITK_INFO << "Deleting apodization function..."; + delete[] m_ApodizationFunction; + MITK_INFO << "Deleting apodization function...[Done]"; + } + + if (m_InputDim != nullptr) + { + MITK_INFO << "Deleting input dim..."; + delete[] m_InputDim; + MITK_INFO << "Deleting input dim...[Done]"; + } + + MITK_INFO << "Destructing beamforming settings...[Done]"; +} diff --git a/Modules/PhotoacousticsAlgorithms/source/filters/mitkCastToFloatImageFilter.cpp b/Modules/PhotoacousticsAlgorithms/source/filters/mitkCastToFloatImageFilter.cpp new file mode 100644 index 0000000000..cc26284be8 --- /dev/null +++ b/Modules/PhotoacousticsAlgorithms/source/filters/mitkCastToFloatImageFilter.cpp @@ -0,0 +1,92 @@ +/*=================================================================== +mitkCastToFloatImageFilter +The Medical Imaging Interaction Toolkit (MITK) + +Copyright (c) German Cancer Research Center, +Division of Medical and Biological Informatics. +All rights reserved. + +This software is distributed WITHOUT ANY WARRANTY; without +even the implied warranty of MERCHANTABILITY or FITNESS FOR +A PARTICULAR PURPOSE. + +See LICENSE.txt or http://www.mitk.org for details. + +===================================================================*/ + +#include "mitkCastToFloatImageFilter.h" +#include "mitkImageReadAccessor.h" +#include "mitkImageWriteAccessor.h" + +mitk::CastToFloatImageFilter::CastToFloatImageFilter() +{ + MITK_INFO << "Instantiating CastToFloatImageFilter..."; + SetNumberOfIndexedInputs(1); + SetNumberOfIndexedOutputs(1); + MITK_INFO << "Instantiating CastToFloatImageFilter...[Done]"; +} + +mitk::CastToFloatImageFilter::~CastToFloatImageFilter() +{ + MITK_INFO << "Destructed CastToFloatImageFilter."; +} + +template +float* CastData(const void* inputArray, unsigned long length) +{ + float* outputArray = new float[length]; + for (unsigned long i = 0; i < length; ++i) + { + outputArray[i] = (float)((TPixelType*)inputArray)[i]; + } + return outputArray; +} + +void mitk::CastToFloatImageFilter::GenerateData() +{ + mitk::Image::Pointer inputImage = GetInput(); + mitk::Image::Pointer outputImage = GetOutput(); + + std::string type = inputImage->GetPixelType().GetTypeAsString(); + + if (type == "scalar (float)" || type == " (float)") + { + outputImage->Initialize(mitk::MakeScalarPixelType(), inputImage->GetDimension(), inputImage->GetDimensions()); + outputImage->SetSpacing(inputImage->GetGeometry()->GetSpacing()); + outputImage->SetImportVolume(mitk::ImageWriteAccessor(inputImage).GetData(), 0, 0, mitk::Image::ImportMemoryManagementType::CopyMemory); + MITK_INFO << "Input is already float type. Nothing to do here."; + return; + } + if (outputImage.IsNull()) + { + outputImage = mitk::Image::New(); + } + + unsigned long length = 1; + for (unsigned int i = 0, limit = inputImage->GetDimension(); i < limit; ++i) + length *= inputImage->GetDimensions()[i]; + + float* outputData; + + mitk::ImageReadAccessor readAccess(inputImage); + + if (type == "scalar (short)" || type == " (short)") + outputData = CastData(readAccess.GetData(), length); + else if (type == "scalar (unsigned short)" || type == " (unsigned short)" || type == " (unsigned_short)" || type == "scalar (unsigned_short)") + outputData = CastData(readAccess.GetData(), length); + else if (type == "scalar (int)" || type == " (int)") + outputData = CastData(readAccess.GetData(), length); + else if (type == "scalar (unsigned int)" || type == " (unsigned int)" || type == " (unsigned_int)" || type == "scalar (unsigned_int)") + outputData = CastData(readAccess.GetData(), length); + else if (type == "scalar (double)" || type == " (double)") + outputData = CastData(readAccess.GetData(), length); + else if (type == "scalar (long)" || type == " (long)") + outputData = CastData(readAccess.GetData(), length); + else + mitkThrow() << "The given image has a datatype that is not yet supported. Given datatype: " << type; + + outputImage->Initialize(mitk::MakeScalarPixelType(), inputImage->GetDimension(), inputImage->GetDimensions()); + outputImage->SetSpacing(inputImage->GetGeometry()->GetSpacing()); + outputImage->SetImportVolume(outputData, 0, 0, mitk::Image::ImportMemoryManagementType::CopyMemory); + delete[] outputData; +} diff --git a/Modules/PhotoacousticsAlgorithms/source/filters/mitkCropImageFilter.cpp b/Modules/PhotoacousticsAlgorithms/source/filters/mitkCropImageFilter.cpp new file mode 100644 index 0000000000..3996284f79 --- /dev/null +++ b/Modules/PhotoacousticsAlgorithms/source/filters/mitkCropImageFilter.cpp @@ -0,0 +1,120 @@ +/*=================================================================== +mitkCropImageFilter +The Medical Imaging Interaction Toolkit (MITK) + +Copyright (c) German Cancer Research Center, +Division of Medical and Biological Informatics. +All rights reserved. + +This software is distributed WITHOUT ANY WARRANTY; without +even the implied warranty of MERCHANTABILITY or FITNESS FOR +A PARTICULAR PURPOSE. + +See LICENSE.txt or http://www.mitk.org for details. + +===================================================================*/ + +#include "mitkCropImageFilter.h" +#include +#include + +mitk::CropImageFilter::CropImageFilter() : + m_XPixelsCropStart(0), + m_YPixelsCropStart(0), + m_ZPixelsCropStart(0), + m_XPixelsCropEnd(0), + m_YPixelsCropEnd(0), + m_ZPixelsCropEnd(0) +{ + MITK_INFO << "Instantiating CropImageFilter..."; + SetNumberOfIndexedInputs(1); + SetNumberOfIndexedOutputs(1); + MITK_INFO << "Instantiating CropImageFilter...[Done]"; +} + +mitk::CropImageFilter::~CropImageFilter() +{ + MITK_INFO << "Destructed CastToFloatImageFilter."; +} + +void mitk::CropImageFilter::SanityCheckPreconditions() +{ + mitk::Image::Pointer inputImage = GetInput(); + + std::string type = inputImage->GetPixelType().GetTypeAsString(); + if (!(type == "scalar (float)" || type == " (float)")) + { + MITK_ERROR << "This filter can currently only handle float type images."; + mitkThrow() << "This filter can currently only handle float type images."; + } + + if (m_XPixelsCropStart + m_XPixelsCropEnd >= inputImage->GetDimension(0)) + { + MITK_ERROR << "X Crop area too large for selected input image"; + mitkThrow() << "X Crop area too large for selected input image"; + } + if (m_YPixelsCropStart + m_YPixelsCropEnd >= inputImage->GetDimension(1)) + { + MITK_ERROR << "Y Crop area too large for selected input image"; + mitkThrow() << "Y Crop area too large for selected input image"; + } + + if (inputImage->GetDimension() == 3) + { + if (m_ZPixelsCropStart + m_ZPixelsCropEnd >= inputImage->GetDimension(2)) + { + MITK_INFO << m_ZPixelsCropStart + m_ZPixelsCropEnd << "vs" << inputImage->GetDimension(2); + MITK_ERROR << "Z Crop area too large for selected input image"; + mitkThrow() << "Z Crop area too large for selected input image"; + } + } + else + { + if (m_ZPixelsCropStart + m_ZPixelsCropEnd > 0) + { + mitkThrow() << "Z Crop area too large for selected input image"; + } + } +} + +void mitk::CropImageFilter::GenerateData() +{ + mitk::Image::Pointer inputImage = GetInput(); + mitk::Image::Pointer outputImage = GetOutput(); + + SanityCheckPreconditions(); + unsigned int* outputDim; + unsigned int* inputDim = inputImage->GetDimensions(); + if (inputImage->GetDimension() == 2) + outputDim = new unsigned int[2]{ inputImage->GetDimension(0) - m_XPixelsCropStart - m_XPixelsCropEnd, + inputImage->GetDimension(1) - m_YPixelsCropStart - m_YPixelsCropEnd }; + else + outputDim = new unsigned int[3]{ inputImage->GetDimension(0) - m_XPixelsCropStart - m_XPixelsCropEnd, + inputImage->GetDimension(1) - m_YPixelsCropStart - m_YPixelsCropEnd, + inputImage->GetDimension(2) - m_ZPixelsCropStart - m_ZPixelsCropEnd }; + + outputImage->Initialize(mitk::MakeScalarPixelType(), inputImage->GetDimension(), outputDim); + outputImage->SetSpacing(inputImage->GetGeometry()->GetSpacing()); + + ImageReadAccessor accR(inputImage); + const float* inputData = (const float*)(accR.GetData()); + + ImageWriteAccessor accW(outputImage); + float* outputData = (float*)(accW.GetData()); + + unsigned int zLength = inputImage->GetDimension() == 3 ? outputDim[2] : 0; + + for (unsigned int sl = 0; sl < zLength; ++sl) + { + for (unsigned int l = 0; l < outputDim[0]; ++l) + { + for (unsigned int s = 0; s < outputDim[1]; ++s) + { + outputData[l + s*outputDim[0] + sl*outputDim[0] * outputDim[1]] = + inputData[(l + m_XPixelsCropStart) + (s + m_YPixelsCropStart)*inputDim[0] + + (sl + m_ZPixelsCropStart)*inputDim[0] * inputDim[1]]; + } + } + } + delete[] outputDim; +} diff --git a/Modules/PhotoacousticsAlgorithms/source/filters/mitkImageSliceSelectionFilter.cpp b/Modules/PhotoacousticsAlgorithms/source/filters/mitkImageSliceSelectionFilter.cpp new file mode 100644 index 0000000000..b90b5a726e --- /dev/null +++ b/Modules/PhotoacousticsAlgorithms/source/filters/mitkImageSliceSelectionFilter.cpp @@ -0,0 +1,34 @@ +/*=================================================================== +mitkImageSliceSelectionFilter +The Medical Imaging Interaction Toolkit (MITK) + +Copyright (c) German Cancer Research Center, +Division of Medical and Biological Informatics. +All rights reserved. + +This software is distributed WITHOUT ANY WARRANTY; without +even the implied warranty of MERCHANTABILITY or FITNESS FOR +A PARTICULAR PURPOSE. + +See LICENSE.txt or http://www.mitk.org for details. + +===================================================================*/ + +#include "mitkImageSliceSelectionFilter.h" + +mitk::ImageSliceSelectionFilter::ImageSliceSelectionFilter() +{ + MITK_INFO << "Instantiating CropImageFilter..."; + SetNumberOfIndexedInputs(1); + SetNumberOfIndexedOutputs(1); + MITK_INFO << "Instantiating CropImageFilter...[Done]"; +} + +mitk::ImageSliceSelectionFilter::~ImageSliceSelectionFilter() +{ + MITK_INFO << "Destructed CastToFloatImageFilter."; +} + +void mitk::ImageSliceSelectionFilter::GenerateData() +{ +} diff --git a/Modules/PhotoacousticsAlgorithms/source/mitkPhotoacousticBeamformingFilter.cpp b/Modules/PhotoacousticsAlgorithms/source/mitkPhotoacousticBeamformingFilter.cpp deleted file mode 100644 index 20abc2c982..0000000000 --- a/Modules/PhotoacousticsAlgorithms/source/mitkPhotoacousticBeamformingFilter.cpp +++ /dev/null @@ -1,801 +0,0 @@ -/*=================================================================== -mitkPhotoacousticBeamformingFilter -The Medical Imaging Interaction Toolkit (MITK) - -Copyright (c) German Cancer Research Center, -Division of Medical and Biological Informatics. -All rights reserved. - -This software is distributed WITHOUT ANY WARRANTY; without -even the implied warranty of MERCHANTABILITY or FITNESS FOR -A PARTICULAR PURPOSE. - -See LICENSE.txt or http://www.mitk.org for details. - -===================================================================*/ - -#include "mitkProperties.h" -#include "mitkImageReadAccessor.h" -#include -#include -#include -#include -#include -#include "mitkImageCast.h" -#include "mitkPhotoacousticBeamformingFilter.h" - -mitk::BeamformingFilter::BeamformingFilter() : m_OutputData(nullptr), m_InputData(nullptr), m_Message("noMessage") -{ - this->SetNumberOfIndexedInputs(1); - this->SetNumberOfRequiredInputs(1); - - m_ProgressHandle = [](int, std::string) {}; - - m_BeamformingOclFilter = mitk::PhotoacousticOCLBeamformingFilter::New(); - - m_VonHannFunction = VonHannFunction(m_Conf.apodizationArraySize); - m_HammFunction = HammFunction(m_Conf.apodizationArraySize); - m_BoxFunction = BoxFunction(m_Conf.apodizationArraySize); -} - -void mitk::BeamformingFilter::SetProgressHandle(std::function progressHandle) -{ - m_ProgressHandle = progressHandle; -} - -mitk::BeamformingFilter::~BeamformingFilter() -{ - delete[] m_VonHannFunction; - delete[] m_HammFunction; - delete[] m_BoxFunction; -} - -void mitk::BeamformingFilter::GenerateInputRequestedRegion() -{ - Superclass::GenerateInputRequestedRegion(); - - mitk::Image* output = this->GetOutput(); - mitk::Image* input = const_cast (this->GetInput()); - if (!output->IsInitialized()) - { - return; - } - - input->SetRequestedRegionToLargestPossibleRegion(); - //GenerateTimeInInputRegion(output, input); -} - -void mitk::BeamformingFilter::GenerateOutputInformation() -{ - mitk::Image::ConstPointer input = this->GetInput(); - mitk::Image::Pointer output = this->GetOutput(); - - if ((output->IsInitialized()) && (this->GetMTime() <= m_TimeOfHeaderInitialization.GetMTime())) - return; - - itkDebugMacro(<< "GenerateOutputInformation()"); - - unsigned int dim[] = { m_Conf.ReconstructionLines, m_Conf.SamplesPerLine, input->GetDimension(2) }; - output->Initialize(mitk::MakeScalarPixelType(), 3, dim); - - mitk::Vector3D spacing; - spacing[0] = m_Conf.Pitch * m_Conf.TransducerElements * 1000 / m_Conf.ReconstructionLines; - spacing[1] = (m_Conf.TimeSpacing * m_Conf.inputDim[1]) / 2 * m_Conf.SpeedOfSound * 1000 / m_Conf.SamplesPerLine; - spacing[2] = 1; - - output->GetGeometry()->SetSpacing(spacing); - output->GetGeometry()->Modified(); - output->SetPropertyList(input->GetPropertyList()->Clone()); - - m_TimeOfHeaderInitialization.Modified(); -} - - -void mitk::BeamformingFilter::GenerateData() -{ - GenerateOutputInformation(); - mitk::Image::Pointer input = this->GetInput(); - mitk::Image::Pointer output = this->GetOutput(); - - if (!output->IsInitialized()) - return; - - float* ApodWindow; - if (m_ConfOld.apodizationArraySize != m_Conf.apodizationArraySize) - { - delete[] m_VonHannFunction; - delete[] m_HammFunction; - delete[] m_BoxFunction; - m_VonHannFunction = VonHannFunction(m_Conf.apodizationArraySize); - m_HammFunction = HammFunction(m_Conf.apodizationArraySize); - m_BoxFunction = BoxFunction(m_Conf.apodizationArraySize); - - m_ConfOld = m_Conf; - } - - // set the appropiate apodization window - switch (m_Conf.Apod) - { - case BeamformingSettings::Apodization::Hann: - ApodWindow = m_VonHannFunction; - break; - case BeamformingSettings::Apodization::Hamm: - ApodWindow = m_HammFunction; - break; - case BeamformingSettings::Apodization::Box: - ApodWindow = m_BoxFunction; - break; - default: - ApodWindow = m_BoxFunction; - break; - } - - auto begin = std::chrono::high_resolution_clock::now(); // debbuging the performance... - - if (!m_Conf.UseGPU) - { - int progInterval = output->GetDimension(2) / 20 > 1 ? output->GetDimension(2) / 20 : 1; - // the interval at which we update the gui progress bar - - float inputDim[2] = { (float)input->GetDimension(0), (float)input->GetDimension(1) }; - float outputDim[2] = { (float)output->GetDimension(0), (float)output->GetDimension(1) }; - - for (unsigned int i = 0; i < output->GetDimension(2); ++i) // seperate Slices should get Beamforming seperately applied - { - mitk::ImageReadAccessor inputReadAccessor(input, input->GetSliceData(i)); - - // first, we check whether the dara is float, other formats are unsupported - if (input->GetPixelType().GetTypeAsString() == "scalar (float)" || input->GetPixelType().GetTypeAsString() == " (float)") - { - m_InputData = (float*)inputReadAccessor.GetData(); - } - else - { - MITK_INFO << "Pixel type is not float, abort"; - return; - } - - m_OutputData = new float[m_Conf.ReconstructionLines*m_Conf.SamplesPerLine]; - - // fill the image with zeros - for (int l = 0; l < outputDim[0]; ++l) - { - for (int s = 0; s < outputDim[1]; ++s) - { - m_OutputData[l*(short)outputDim[1] + s] = 0; - } - } - - std::thread *threads = new std::thread[(short)outputDim[0]]; - - // every line will be beamformed in a seperate thread - if (m_Conf.Algorithm == BeamformingSettings::BeamformingAlgorithm::DAS) - { - if (m_Conf.DelayCalculationMethod == BeamformingSettings::DelayCalc::QuadApprox) - { - for (short line = 0; line < outputDim[0]; ++line) - { - threads[line] = std::thread(&BeamformingFilter::DASQuadraticLine, this, m_InputData, m_OutputData, inputDim, outputDim, line, ApodWindow, m_Conf.apodizationArraySize); - } - } - else if (m_Conf.DelayCalculationMethod == BeamformingSettings::DelayCalc::Spherical) - { - for (short line = 0; line < outputDim[0]; ++line) - { - threads[line] = std::thread(&BeamformingFilter::DASSphericalLine, this, m_InputData, m_OutputData, inputDim, outputDim, line, ApodWindow, m_Conf.apodizationArraySize); - } - } - } - else if (m_Conf.Algorithm == BeamformingSettings::BeamformingAlgorithm::DMAS) - { - if (m_Conf.DelayCalculationMethod == BeamformingSettings::DelayCalc::QuadApprox) - { - for (short line = 0; line < outputDim[0]; ++line) - { - threads[line] = std::thread(&BeamformingFilter::DMASQuadraticLine, this, m_InputData, m_OutputData, inputDim, outputDim, line, ApodWindow, m_Conf.apodizationArraySize); - } - } - else if (m_Conf.DelayCalculationMethod == BeamformingSettings::DelayCalc::Spherical) - { - for (short line = 0; line < outputDim[0]; ++line) - { - threads[line] = std::thread(&BeamformingFilter::DMASSphericalLine, this, m_InputData, m_OutputData, inputDim, outputDim, line, ApodWindow, m_Conf.apodizationArraySize); - } - } - } - else if (m_Conf.Algorithm == BeamformingSettings::BeamformingAlgorithm::sDMAS) - { - if (m_Conf.DelayCalculationMethod == BeamformingSettings::DelayCalc::QuadApprox) - { - for (short line = 0; line < outputDim[0]; ++line) - { - threads[line] = std::thread(&BeamformingFilter::sDMASQuadraticLine, this, m_InputData, m_OutputData, inputDim, outputDim, line, ApodWindow, m_Conf.apodizationArraySize); - } - } - else if (m_Conf.DelayCalculationMethod == BeamformingSettings::DelayCalc::Spherical) - { - for (short line = 0; line < outputDim[0]; ++line) - { - threads[line] = std::thread(&BeamformingFilter::sDMASSphericalLine, this, m_InputData, m_OutputData, inputDim, outputDim, line, ApodWindow, m_Conf.apodizationArraySize); - } - } - } - // wait for all lines to finish - for (short line = 0; line < outputDim[0]; ++line) - { - threads[line].join(); - } - - output->SetSlice(m_OutputData, i); - - if (i % progInterval == 0) - m_ProgressHandle((int)((i + 1) / (float)output->GetDimension(2) * 100), "performing reconstruction"); - - delete[] m_OutputData; - m_OutputData = nullptr; - m_InputData = nullptr; - } - } - #if defined(PHOTOACOUSTICS_USE_GPU) || DOXYGEN - else - { - try - { - // first, we check whether the data is float, other formats are unsupported - if (!(input->GetPixelType().GetTypeAsString() == "scalar (float)" || input->GetPixelType().GetTypeAsString() == " (float)")) - { - MITK_ERROR << "Pixel type is not float, abort"; - return; - } - - unsigned long availableMemory = m_BeamformingOclFilter->GetDeviceMemory(); - - unsigned int batchSize = 16; - unsigned int batches = (unsigned int)((float)input->GetDimension(2)/batchSize) + (input->GetDimension(2)%batchSize > 0); - - unsigned int batchDim[] = { input->GetDimension(0), input->GetDimension(1), batchSize }; - unsigned int batchDimLast[] = { input->GetDimension(0), input->GetDimension(1), input->GetDimension(2) % batchSize }; - - // the following safeguard is probably only needed for absurdly small GPU memory - for (batchSize = 16; - (unsigned long)batchSize * - ((unsigned long)(batchDim[0] * batchDim[1]) * 4 + // single input image (float) - (unsigned long)(m_Conf.ReconstructionLines * m_Conf.SamplesPerLine) * 4) // single output image (float) - > availableMemory - - (unsigned long)(m_Conf.ReconstructionLines / 2 * m_Conf.SamplesPerLine) * 2 - // Delays buffer (unsigned short) - (unsigned long)(m_Conf.ReconstructionLines * m_Conf.SamplesPerLine) * 3 * 2 - // UsedLines buffer (unsigned short) - 50 * 1024 * 1024; // 50 MB buffer for local data, system purposes etc - --batchSize) - {} - if (batchSize < 1) - { - MITK_ERROR << "device memory too small for GPU beamforming"; - return; - } - - mitk::ImageReadAccessor copy(input); - - for(unsigned int i = 0; i < batches; ++i) - { - m_ProgressHandle(input->GetDimension(2)/batches * i, "performing reconstruction"); - - mitk::Image::Pointer inputBatch = mitk::Image::New(); - if(i == batches - 1 && (input->GetDimension(2)%batchSize > 0)) - { - inputBatch->Initialize(mitk::MakeScalarPixelType(), 3, batchDimLast); - m_Conf.inputDim[2] = batchDimLast[2]; - } - else - { - inputBatch->Initialize(mitk::MakeScalarPixelType(), 3, batchDim); - m_Conf.inputDim[2] = batchDim[2]; - } - - inputBatch->SetSpacing(input->GetGeometry()->GetSpacing()); - - inputBatch->SetImportVolume(&(((float*)copy.GetData())[input->GetDimension(0) * input->GetDimension(1) * batchSize * i])); - - m_BeamformingOclFilter->SetApodisation(ApodWindow, m_Conf.apodizationArraySize); - m_BeamformingOclFilter->SetConfig(m_Conf); - m_BeamformingOclFilter->SetInput(inputBatch); - m_BeamformingOclFilter->Update(); - - void* out = m_BeamformingOclFilter->GetOutput(); - - for(unsigned int slice = 0; slice < m_Conf.inputDim[2]; ++slice) - { - output->SetImportSlice( - &(((float*)out)[m_Conf.ReconstructionLines * m_Conf.SamplesPerLine * slice]), - batchSize * i + slice, 0, 0, mitk::Image::ImportMemoryManagementType::CopyMemory); - } - } - } - catch (mitk::Exception &e) - { - std::string errorMessage = "Caught unexpected exception "; - errorMessage.append(e.what()); - MITK_ERROR << errorMessage; - - float* dummyData = new float[m_Conf.ReconstructionLines * m_Conf.SamplesPerLine * m_Conf.inputDim[2]]; - output->SetImportVolume(dummyData, 0, 0, mitk::Image::ImportMemoryManagementType::ManageMemory); - - m_Message = "An openCL error occurred; all GPU operations in this and the next session may be corrupted."; - } - } - #endif - m_TimeOfHeaderInitialization.Modified(); - - auto end = std::chrono::high_resolution_clock::now(); - MITK_INFO << "Beamforming of " << output->GetDimension(2) << " Images completed in " << ((float)std::chrono::duration_cast(end - begin).count()) / 1000000 << "ms" << std::endl; -} - -float* mitk::BeamformingFilter::VonHannFunction(int samples) -{ - float* ApodWindow = new float[samples]; - - for (int n = 0; n < samples; ++n) - { - ApodWindow[n] = (1 - cos(2 * itk::Math::pi * n / (samples - 1))) / 2; - } - - return ApodWindow; -} - -float* mitk::BeamformingFilter::HammFunction(int samples) -{ - float* ApodWindow = new float[samples]; - - for (int n = 0; n < samples; ++n) - { - ApodWindow[n] = 0.54 - 0.46*cos(2 * itk::Math::pi*n / (samples - 1)); - } - - return ApodWindow; -} - -float* mitk::BeamformingFilter::BoxFunction(int samples) -{ - float* ApodWindow = new float[samples]; - - for (int n = 0; n < samples; ++n) - { - ApodWindow[n] = 1; - } - - return ApodWindow; -} - -void mitk::BeamformingFilter::DASQuadraticLine(float* input, float* output, float inputDim[2], float outputDim[2], const short& line, float* apodisation, const short& apodArraySize) -{ - float& inputS = inputDim[1]; - float& inputL = inputDim[0]; - - float& outputS = outputDim[1]; - float& outputL = outputDim[0]; - - short AddSample = 0; - short maxLine = 0; - short minLine = 0; - float delayMultiplicator = 0; - float l_i = 0; - float s_i = 0; - - float part = 0.07 * inputL; - float tan_phi = std::tan(m_Conf.Angle / 360 * 2 * itk::Math::pi); - float part_multiplicator = tan_phi * m_Conf.TimeSpacing * m_Conf.SpeedOfSound / m_Conf.Pitch * inputL / m_Conf.TransducerElements; - float apod_mult = 1; - - short usedLines = (maxLine - minLine); - - //quadratic delay - l_i = line / outputL * inputL; - - for (short sample = 0; sample < outputS; ++sample) - { - s_i = (float)sample / outputS * inputS / 2; - - part = part_multiplicator*s_i; - - if (part < 1) - part = 1; - - maxLine = (short)std::min((l_i + part) + 1, inputL); - minLine = (short)std::max((l_i - part), 0.0f); - usedLines = (maxLine - minLine); - - apod_mult = (float)apodArraySize / (float)usedLines; - - delayMultiplicator = pow((1 / (m_Conf.TimeSpacing*m_Conf.SpeedOfSound) * (m_Conf.Pitch*m_Conf.TransducerElements) / inputL), 2) / s_i / 2; - - for (short l_s = minLine; l_s < maxLine; ++l_s) - { - AddSample = delayMultiplicator * pow((l_s - l_i), 2) + s_i + (1 - m_Conf.isPhotoacousticImage)*s_i; - if (AddSample < inputS && AddSample >= 0) - output[sample*(short)outputL + line] += input[l_s + AddSample*(short)inputL] * apodisation[(short)((l_s - minLine)*apod_mult)]; - else - --usedLines; - } - output[sample*(short)outputL + line] = output[sample*(short)outputL + line] / usedLines; - } -} - -void mitk::BeamformingFilter::DASSphericalLine(float* input, float* output, float inputDim[2], float outputDim[2], const short& line, float* apodisation, const short& apodArraySize) -{ - float& inputS = inputDim[1]; - float& inputL = inputDim[0]; - - float& outputS = outputDim[1]; - float& outputL = outputDim[0]; - - short AddSample = 0; - short maxLine = 0; - short minLine = 0; - float l_i = 0; - float s_i = 0; - - float part = 0.07 * inputL; - float tan_phi = std::tan(m_Conf.Angle / 360 * 2 * itk::Math::pi); - float part_multiplicator = tan_phi * m_Conf.TimeSpacing * m_Conf.SpeedOfSound / m_Conf.Pitch * inputL / (float)m_Conf.TransducerElements; - float apod_mult = 1; - - short usedLines = (maxLine - minLine); - - //exact delay - - l_i = (float)line / outputL * inputL; - - for (short sample = 0; sample < outputS; ++sample) - { - s_i = (float)sample / outputS * inputS / 2; - - part = part_multiplicator*s_i; - - if (part < 1) - part = 1; - - maxLine = (short)std::min((l_i + part) + 1, inputL); - minLine = (short)std::max((l_i - part), 0.0f); - usedLines = (maxLine - minLine); - - apod_mult = (float)apodArraySize / (float)usedLines; - - for (short l_s = minLine; l_s < maxLine; ++l_s) - { - AddSample = (int)sqrt( - pow(s_i, 2) - + - pow((1 / (m_Conf.TimeSpacing*m_Conf.SpeedOfSound) * (((float)l_s - l_i)*m_Conf.Pitch*(float)m_Conf.TransducerElements) / inputL), 2) - ) + (1 - m_Conf.isPhotoacousticImage)*s_i; - if (AddSample < inputS && AddSample >= 0) - output[sample*(short)outputL + line] += input[l_s + AddSample*(short)inputL] * apodisation[(short)((l_s - minLine)*apod_mult)]; - else - --usedLines; - } - output[sample*(short)outputL + line] = output[sample*(short)outputL + line] / usedLines; - } -} - - -void mitk::BeamformingFilter::DMASQuadraticLine(float* input, float* output, float inputDim[2], float outputDim[2], const short& line, float* apodisation, const short& apodArraySize) -{ - float& inputS = inputDim[1]; - float& inputL = inputDim[0]; - - float& outputS = outputDim[1]; - float& outputL = outputDim[0]; - - short maxLine = 0; - short minLine = 0; - float delayMultiplicator = 0; - float l_i = 0; - float s_i = 0; - - float part = 0.07 * inputL; - float tan_phi = std::tan(m_Conf.Angle / 360 * 2 * itk::Math::pi); - float part_multiplicator = tan_phi * m_Conf.TimeSpacing * m_Conf.SpeedOfSound / m_Conf.Pitch * inputL / (float)m_Conf.TransducerElements; - float apod_mult = 1; - - float mult = 0; - short usedLines = (maxLine - minLine); - - //quadratic delay - l_i = line / outputL * inputL; - - for (short sample = 0; sample < outputS; ++sample) - { - s_i = sample / outputS * inputS / 2; - - part = part_multiplicator*s_i; - - if (part < 1) - part = 1; - - maxLine = (short)std::min((l_i + part) + 1, inputL); - minLine = (short)std::max((l_i - part), 0.0f); - usedLines = (maxLine - minLine); - - apod_mult = (float)apodArraySize / (float)usedLines; - - delayMultiplicator = pow((1 / (m_Conf.TimeSpacing*m_Conf.SpeedOfSound) * (m_Conf.Pitch*m_Conf.TransducerElements) / inputL), 2) / s_i / 2; - - //calculate the AddSamples beforehand to save some time - short* AddSample = new short[maxLine - minLine]; - for (short l_s = 0; l_s < maxLine - minLine; ++l_s) - { - AddSample[l_s] = (short)(delayMultiplicator * pow((minLine + l_s - l_i), 2) + s_i) + (1 - m_Conf.isPhotoacousticImage)*s_i; - } - - float s_1 = 0; - float s_2 = 0; - - for (short l_s1 = minLine; l_s1 < maxLine - 1; ++l_s1) - { - if (AddSample[l_s1 - minLine] < inputS && AddSample[l_s1 - minLine] >= 0) - { - for (short l_s2 = l_s1 + 1; l_s2 < maxLine; ++l_s2) - { - if (AddSample[l_s2 - minLine] < inputS && AddSample[l_s2 - minLine] >= 0) - { - s_2 = input[l_s2 + AddSample[l_s2 - minLine] * (short)inputL]; - s_1 = input[l_s1 + AddSample[l_s1 - minLine] * (short)inputL]; - - mult = s_2 * apodisation[(int)((l_s2 - minLine)*apod_mult)] * s_1 * apodisation[(int)((l_s1 - minLine)*apod_mult)]; - output[sample*(short)outputL + line] += sqrt(fabs(mult)) * ((mult > 0) - (mult < 0)); - } - } - } - else - --usedLines; - } - - output[sample*(short)outputL + line] = output[sample*(short)outputL + line] / (float)(pow(usedLines, 2) - (usedLines - 1)); - - delete[] AddSample; - } -} - -void mitk::BeamformingFilter::DMASSphericalLine(float* input, float* output, float inputDim[2], float outputDim[2], const short& line, float* apodisation, const short& apodArraySize) -{ - float& inputS = inputDim[1]; - float& inputL = inputDim[0]; - - float& outputS = outputDim[1]; - float& outputL = outputDim[0]; - - short maxLine = 0; - short minLine = 0; - float l_i = 0; - float s_i = 0; - - float part = 0.07 * inputL; - float tan_phi = std::tan(m_Conf.Angle / 360 * 2 * itk::Math::pi); - float part_multiplicator = tan_phi * m_Conf.TimeSpacing * m_Conf.SpeedOfSound / m_Conf.Pitch * inputL / (float)m_Conf.TransducerElements; - float apod_mult = 1; - - float mult = 0; - - short usedLines = (maxLine - minLine); - - //exact delay - - l_i = (float)line / outputL * inputL; - - for (short sample = 0; sample < outputS; ++sample) - { - s_i = (float)sample / outputS * inputS / 2; - - part = part_multiplicator*s_i; - - if (part < 1) - part = 1; - - maxLine = (short)std::min((l_i + part) + 1, inputL); - minLine = (short)std::max((l_i - part), 0.0f); - usedLines = (maxLine - minLine); - - apod_mult = (float)apodArraySize / (float)usedLines; - - //calculate the AddSamples beforehand to save some time - short* AddSample = new short[maxLine - minLine]; - for (short l_s = 0; l_s < maxLine - minLine; ++l_s) - { - AddSample[l_s] = (short)sqrt( - pow(s_i, 2) - + - pow((1 / (m_Conf.TimeSpacing*m_Conf.SpeedOfSound) * (((float)minLine + (float)l_s - l_i)*m_Conf.Pitch*(float)m_Conf.TransducerElements) / inputL), 2) - ) + (1 - m_Conf.isPhotoacousticImage)*s_i; - } - - float s_1 = 0; - float s_2 = 0; - - for (short l_s1 = minLine; l_s1 < maxLine - 1; ++l_s1) - { - if (AddSample[l_s1 - minLine] < inputS && AddSample[l_s1 - minLine] >= 0) - { - for (short l_s2 = l_s1 + 1; l_s2 < maxLine; ++l_s2) - { - if (AddSample[l_s2 - minLine] < inputS && AddSample[l_s2 - minLine] >= 0) - { - s_2 = input[l_s2 + AddSample[l_s2 - minLine] * (short)inputL]; - s_1 = input[l_s1 + AddSample[l_s1 - minLine] * (short)inputL]; - - mult = s_2 * apodisation[(int)((l_s2 - minLine)*apod_mult)] * s_1 * apodisation[(int)((l_s1 - minLine)*apod_mult)]; - output[sample*(short)outputL + line] += sqrt(fabs(mult)) * ((mult > 0) - (mult < 0)); - } - } - } - else - --usedLines; - } - - output[sample*(short)outputL + line] = output[sample*(short)outputL + line] / (float)(pow(usedLines, 2) - (usedLines - 1)); - - delete[] AddSample; - } -} - -void mitk::BeamformingFilter::sDMASQuadraticLine(float* input, float* output, float inputDim[2], float outputDim[2], const short& line, float* apodisation, const short& apodArraySize) -{ - float& inputS = inputDim[1]; - float& inputL = inputDim[0]; - - float& outputS = outputDim[1]; - float& outputL = outputDim[0]; - - short maxLine = 0; - short minLine = 0; - float delayMultiplicator = 0; - float l_i = 0; - float s_i = 0; - - float part = 0.07 * inputL; - float tan_phi = std::tan(m_Conf.Angle / 360 * 2 * itk::Math::pi); - float part_multiplicator = tan_phi * m_Conf.TimeSpacing * m_Conf.SpeedOfSound / m_Conf.Pitch * inputL / (float)m_Conf.TransducerElements; - float apod_mult = 1; - - float mult = 0; - short usedLines = (maxLine - minLine); - - //quadratic delay - l_i = line / outputL * inputL; - - for (short sample = 0; sample < outputS; ++sample) - { - s_i = sample / outputS * inputS / 2; - - part = part_multiplicator*s_i; - - if (part < 1) - part = 1; - - maxLine = (short)std::min((l_i + part) + 1, inputL); - minLine = (short)std::max((l_i - part), 0.0f); - usedLines = (maxLine - minLine); - - apod_mult = (float)apodArraySize / (float)usedLines; - - delayMultiplicator = pow((1 / (m_Conf.TimeSpacing*m_Conf.SpeedOfSound) * (m_Conf.Pitch*m_Conf.TransducerElements) / inputL), 2) / s_i / 2; - - //calculate the AddSamples beforehand to save some time - short* AddSample = new short[maxLine - minLine]; - for (short l_s = 0; l_s < maxLine - minLine; ++l_s) - { - AddSample[l_s] = (short)(delayMultiplicator * pow((minLine + l_s - l_i), 2) + s_i) + (1 - m_Conf.isPhotoacousticImage)*s_i; - } - - float s_1 = 0; - float s_2 = 0; - float sign = 0; - - for (short l_s1 = minLine; l_s1 < maxLine - 1; ++l_s1) - { - if (AddSample[l_s1 - minLine] < inputS && AddSample[l_s1 - minLine] >= 0) - { - s_1 = input[l_s1 + AddSample[l_s1 - minLine] * (short)inputL]; - sign += s_1; - - for (short l_s2 = l_s1 + 1; l_s2 < maxLine; ++l_s2) - { - if (AddSample[l_s2 - minLine] < inputS && AddSample[l_s2 - minLine] >= 0) - { - s_2 = input[l_s2 + AddSample[l_s2 - minLine] * (short)inputL]; - - mult = s_2 * apodisation[(int)((l_s2 - minLine)*apod_mult)] * s_1 * apodisation[(int)((l_s1 - minLine)*apod_mult)]; - output[sample*(short)outputL + line] += sqrt(fabs(mult)) * ((mult > 0) - (mult < 0)); - } - } - } - else - --usedLines; - } - - output[sample*(short)outputL + line] = output[sample*(short)outputL + line] / (float)(pow(usedLines, 2) - (usedLines - 1)) * ((sign > 0) - (sign < 0)); - - delete[] AddSample; - } -} - -void mitk::BeamformingFilter::sDMASSphericalLine(float* input, float* output, float inputDim[2], float outputDim[2], const short& line, float* apodisation, const short& apodArraySize) -{ - float& inputS = inputDim[1]; - float& inputL = inputDim[0]; - - float& outputS = outputDim[1]; - float& outputL = outputDim[0]; - - short maxLine = 0; - short minLine = 0; - float l_i = 0; - float s_i = 0; - - float part = 0.07 * inputL; - float tan_phi = std::tan(m_Conf.Angle / 360 * 2 * itk::Math::pi); - float part_multiplicator = tan_phi * m_Conf.TimeSpacing * m_Conf.SpeedOfSound / m_Conf.Pitch * inputL / (float)m_Conf.TransducerElements; - float apod_mult = 1; - - float mult = 0; - - short usedLines = (maxLine - minLine); - - //exact delay - - l_i = (float)line / outputL * inputL; - - for (short sample = 0; sample < outputS; ++sample) - { - s_i = (float)sample / outputS * inputS / 2; - - part = part_multiplicator*s_i; - - if (part < 1) - part = 1; - - maxLine = (short)std::min((l_i + part) + 1, inputL); - minLine = (short)std::max((l_i - part), 0.0f); - usedLines = (maxLine - minLine); - - apod_mult = (float)apodArraySize / (float)usedLines; - - //calculate the AddSamples beforehand to save some time - short* AddSample = new short[maxLine - minLine]; - for (short l_s = 0; l_s < maxLine - minLine; ++l_s) - { - AddSample[l_s] = (short)sqrt( - pow(s_i, 2) - + - pow((1 / (m_Conf.TimeSpacing*m_Conf.SpeedOfSound) * (((float)minLine + (float)l_s - l_i)*m_Conf.Pitch*(float)m_Conf.TransducerElements) / inputL), 2) - ) + (1 - m_Conf.isPhotoacousticImage)*s_i; - } - - float s_1 = 0; - float s_2 = 0; - float sign = 0; - - for (short l_s1 = minLine; l_s1 < maxLine - 1; ++l_s1) - { - if (AddSample[l_s1 - minLine] < inputS && AddSample[l_s1 - minLine] >= 0) - { - s_1 = input[l_s1 + AddSample[l_s1 - minLine] * (short)inputL]; - sign += s_1; - - for (short l_s2 = l_s1 + 1; l_s2 < maxLine; ++l_s2) - { - if (AddSample[l_s2 - minLine] < inputS && AddSample[l_s2 - minLine] >= 0) - { - s_2 = input[l_s2 + AddSample[l_s2 - minLine] * (short)inputL]; - - mult = s_2 * apodisation[(int)((l_s2 - minLine)*apod_mult)] * s_1 * apodisation[(int)((l_s1 - minLine)*apod_mult)]; - output[sample*(short)outputL + line] += sqrt(fabs(mult)) * ((mult > 0) - (mult < 0)); - } - } - } - else - --usedLines; - } - - output[sample*(short)outputL + line] = output[sample*(short)outputL + line] / (float)(pow(usedLines, 2) - (usedLines - 1)) * ((sign > 0) - (sign < 0)); - - delete[] AddSample; - } -} diff --git a/Modules/PhotoacousticsAlgorithms/source/mitkPhotoacousticImage.cpp b/Modules/PhotoacousticsAlgorithms/source/mitkPhotoacousticImage.cpp deleted file mode 100644 index 4a268f824d..0000000000 --- a/Modules/PhotoacousticsAlgorithms/source/mitkPhotoacousticImage.cpp +++ /dev/null @@ -1,517 +0,0 @@ -/*=================================================================== - -The Medical Imaging Interaction Toolkit (MITK) - -Copyright (c) German Cancer Research Center, -Division of Medical and Biological Informatics. -All rights reserved. - -This software is distributed WITHOUT ANY WARRANTY; without -even the implied warranty of MERCHANTABILITY or FITNESS FOR -A PARTICULAR PURPOSE. - -See LICENSE.txt or http://www.mitk.org for details. - -===================================================================*/ - -#include "mitkPhotoacousticImage.h" -#include "../ITKFilter/ITKUltrasound/itkBModeImageFilter.h" -#include "../ITKFilter/itkPhotoacousticBModeImageFilter.h" -#include "mitkImageCast.h" -#include "mitkITKImageImport.h" -#include "mitkPhotoacousticBeamformingFilter.h" -#include -#include -#include "./OpenCLFilter/mitkPhotoacousticBModeFilter.h" - - -// itk dependencies -#include "itkImage.h" -#include "itkResampleImageFilter.h" -#include "itkCastImageFilter.h" -#include "itkCropImageFilter.h" -#include "itkRescaleIntensityImageFilter.h" -#include "itkIntensityWindowingImageFilter.h" -#include -#include "itkMultiplyImageFilter.h" -#include "itkBSplineInterpolateImageFunction.h" -#include - -// needed itk image filters -#include "mitkITKImageImport.h" -#include "itkFFTShiftImageFilter.h" -#include "itkMultiplyImageFilter.h" -#include "itkComplexToModulusImageFilter.h" -#include -#include "../ITKFilter/ITKUltrasound/itkFFT1DComplexConjugateToRealImageFilter.h" -#include "../ITKFilter/ITKUltrasound/itkFFT1DRealToComplexConjugateImageFilter.h" - -mitk::PhotoacousticImage::PhotoacousticImage() : m_BeamformingFilter(BeamformingFilter::New()) -{ - MITK_INFO << "[PhotoacousticImage Debug] created that image"; -} - -mitk::PhotoacousticImage::~PhotoacousticImage() -{ - MITK_INFO << "[PhotoacousticImage Debug] destroyed that image"; -} - -mitk::Image::Pointer mitk::PhotoacousticImage::ApplyBmodeFilter(mitk::Image::Pointer inputImage, BModeMethod method, bool UseGPU, bool UseLogFilter, float resampleSpacing) -{ - // the image needs to be of floating point type for the envelope filter to work; the casting is done automatically by the CastToItkImage - typedef itk::Image< float, 3 > itkFloatImageType; - typedef itk::IdentityTransform TransformType; - - if (method == BModeMethod::Abs) - { - mitk::Image::Pointer input; - mitk::Image::Pointer out; - if (inputImage->GetPixelType().GetTypeAsString() == "scalar (float)" || inputImage->GetPixelType().GetTypeAsString() == " (float)") - input = inputImage; - else - input = ApplyCropping(inputImage, 0, 0, 0, 0, 0, inputImage->GetDimension(2) - 1); - - if (!UseGPU) - { - PhotoacousticBModeFilter::Pointer filter = PhotoacousticBModeFilter::New(); - filter->SetParameters(UseLogFilter); - filter->SetInput(input); - filter->Update(); - - out = filter->GetOutput(); - - if (resampleSpacing == 0) - return out; - } - #ifdef PHOTOACOUSTICS_USE_GPU - else - { - PhotoacousticOCLBModeFilter::Pointer filter = PhotoacousticOCLBModeFilter::New(); - filter->SetParameters(UseLogFilter); - filter->SetInput(input); - filter->Update(); - - out = filter->GetOutput(); - - if (resampleSpacing == 0) - return out; - } - #endif - - typedef itk::ResampleImageFilter < itkFloatImageType, itkFloatImageType > ResampleImageFilter; - ResampleImageFilter::Pointer resampleImageFilter = ResampleImageFilter::New(); - - itkFloatImageType::Pointer itkImage; - mitk::CastToItkImage(out, itkImage); - itkFloatImageType::SpacingType outputSpacing; - itkFloatImageType::SizeType inputSize = itkImage->GetLargestPossibleRegion().GetSize(); - itkFloatImageType::SizeType outputSize = inputSize; - - outputSpacing[0] = itkImage->GetSpacing()[0]; - outputSpacing[1] = resampleSpacing; - outputSpacing[2] = itkImage->GetSpacing()[2]; - - outputSize[1] = inputSize[1] * itkImage->GetSpacing()[1] / outputSpacing[1]; - - typedef itk::IdentityTransform TransformType; - resampleImageFilter->SetInput(itkImage); - resampleImageFilter->SetSize(outputSize); - resampleImageFilter->SetOutputSpacing(outputSpacing); - resampleImageFilter->SetTransform(TransformType::New()); - - resampleImageFilter->UpdateLargestPossibleRegion(); - return mitk::GrabItkImageMemory(resampleImageFilter->GetOutput()); - } - else if (method == BModeMethod::EnvelopeDetection) - { - typedef itk::BModeImageFilter < itkFloatImageType, itkFloatImageType > BModeFilterType; - BModeFilterType::Pointer bModeFilter = BModeFilterType::New(); // LogFilter - - typedef itk::PhotoacousticBModeImageFilter < itkFloatImageType, itkFloatImageType > PhotoacousticBModeImageFilter; - PhotoacousticBModeImageFilter::Pointer photoacousticBModeFilter = PhotoacousticBModeImageFilter::New(); // No LogFilter - - typedef itk::ResampleImageFilter < itkFloatImageType, itkFloatImageType > ResampleImageFilter; - ResampleImageFilter::Pointer resampleImageFilter = ResampleImageFilter::New(); - - itkFloatImageType::Pointer itkImage; - - mitk::CastToItkImage(inputImage, itkImage); - - itkFloatImageType::Pointer bmode; - - if (UseLogFilter) - { - bModeFilter->SetInput(itkImage); - bModeFilter->SetDirection(1); - bmode = bModeFilter->GetOutput(); - } - else - { - photoacousticBModeFilter->SetInput(itkImage); - photoacousticBModeFilter->SetDirection(1); - bmode = photoacousticBModeFilter->GetOutput(); - } - - // resampleSpacing == 0 means: do no resampling - if (resampleSpacing == 0) - { - return mitk::GrabItkImageMemory(bmode); - } - - itkFloatImageType::SpacingType outputSpacing; - itkFloatImageType::SizeType inputSize = itkImage->GetLargestPossibleRegion().GetSize(); - itkFloatImageType::SizeType outputSize = inputSize; - - outputSpacing[0] = itkImage->GetSpacing()[0]; - outputSpacing[1] = resampleSpacing; - outputSpacing[2] = itkImage->GetSpacing()[2]; - - outputSize[1] = inputSize[1] * itkImage->GetSpacing()[1] / outputSpacing[1]; - - resampleImageFilter->SetInput(bmode); - resampleImageFilter->SetSize(outputSize); - resampleImageFilter->SetOutputSpacing(outputSpacing); - resampleImageFilter->SetTransform(TransformType::New()); - - resampleImageFilter->UpdateLargestPossibleRegion(); - return mitk::GrabItkImageMemory(resampleImageFilter->GetOutput()); - } - return nullptr; -} - -/*mitk::Image::Pointer mitk::PhotoacousticImage::ApplyScatteringCompensation(mitk::Image::Pointer inputImage, int scattering) -{ - typedef itk::Image< float, 3 > itkFloatImageType; - typedef itk::MultiplyImageFilter MultiplyImageFilterType; - - itkFloatImageType::Pointer itkImage; - mitk::CastToItkImage(inputImage, itkImage); - - MultiplyImageFilterType::Pointer multiplyFilter = MultiplyImageFilterType::New(); - multiplyFilter->SetInput1(itkImage); - multiplyFilter->SetInput2(m_FluenceCompResizedItk.at(m_ScatteringCoefficient)); - - return mitk::GrabItkImageMemory(multiplyFilter->GetOutput()); -}*/ - -mitk::Image::Pointer mitk::PhotoacousticImage::ApplyResampling(mitk::Image::Pointer inputImage, unsigned int outputSize[2]) -{ - typedef itk::Image< float, 3 > itkFloatImageType; - - typedef itk::ResampleImageFilter < itkFloatImageType, itkFloatImageType > ResampleImageFilter; - ResampleImageFilter::Pointer resampleImageFilter = ResampleImageFilter::New(); - typedef itk::LinearInterpolateImageFunction T_Interpolator; - itkFloatImageType::Pointer itkImage; - - mitk::CastToItkImage(inputImage, itkImage); - - itkFloatImageType::SpacingType outputSpacingItk; - itkFloatImageType::SizeType inputSizeItk = itkImage->GetLargestPossibleRegion().GetSize(); - itkFloatImageType::SizeType outputSizeItk = inputSizeItk; - - outputSizeItk[0] = outputSize[0]; - outputSizeItk[1] = outputSize[1]; - outputSizeItk[2] = inputSizeItk[2]; - - outputSpacingItk[0] = itkImage->GetSpacing()[0] * (static_cast(inputSizeItk[0]) / static_cast(outputSizeItk[0])); - outputSpacingItk[1] = itkImage->GetSpacing()[1] * (static_cast(inputSizeItk[1]) / static_cast(outputSizeItk[1])); - outputSpacingItk[2] = itkImage->GetSpacing()[2]; - - typedef itk::IdentityTransform TransformType; - T_Interpolator::Pointer _pInterpolator = T_Interpolator::New(); - resampleImageFilter->SetInput(itkImage); - resampleImageFilter->SetSize(outputSizeItk); - resampleImageFilter->SetOutputSpacing(outputSpacingItk); - resampleImageFilter->SetTransform(TransformType::New()); - resampleImageFilter->SetInterpolator(_pInterpolator); - - resampleImageFilter->UpdateLargestPossibleRegion(); - return mitk::GrabItkImageMemory(resampleImageFilter->GetOutput()); -} - -mitk::Image::Pointer mitk::PhotoacousticImage::ApplyCropping(mitk::Image::Pointer inputImage, int above, int below, int right, int left, int minSlice, int maxSlice) -{ - - unsigned int inputDim[3] = { inputImage->GetDimension(0), inputImage->GetDimension(1), inputImage->GetDimension(2) }; - unsigned int outputDim[3] = { inputImage->GetDimension(0) - left - right, inputImage->GetDimension(1) - (unsigned int)above - (unsigned int)below, (unsigned int)maxSlice - (unsigned int)minSlice + 1 }; - - void* inputData; - float* outputData = new float[outputDim[0] * outputDim[1] * outputDim[2]]; - - ImageReadAccessor acc(inputImage); - inputData = const_cast(acc.GetData()); - - // convert the data to float by default - // as of now only float, short, double are used at all. - if (inputImage->GetPixelType().GetTypeAsString() == "scalar (float)" || inputImage->GetPixelType().GetTypeAsString() == " (float)") - { - // copy the data into the cropped image - for (unsigned short sl = 0; sl < outputDim[2]; ++sl) - { - for (unsigned short l = 0; l < outputDim[0]; ++l) - { - for (unsigned short s = 0; s < outputDim[1]; ++s) - { - outputData[l + s*(unsigned short)outputDim[0] + sl*outputDim[0] * outputDim[1]] = (float)((float*)inputData)[(l + left) + (s + above)*(unsigned short)inputDim[0] + (sl + minSlice)*inputDim[0] * inputDim[1]]; - } - } - } - } - else if (inputImage->GetPixelType().GetTypeAsString() == "scalar (short)" || inputImage->GetPixelType().GetTypeAsString() == " (short)") - { - // copy the data to the cropped image - for (unsigned short sl = 0; sl < outputDim[2]; ++sl) - { - for (unsigned short l = 0; l < outputDim[0]; ++l) - { - for (unsigned short s = 0; s < outputDim[1]; ++s) - { - outputData[l + s*(unsigned short)outputDim[0] + sl*outputDim[0] * outputDim[1]] = (float)((short*)inputData)[(l + left) + (s + above)*(unsigned short)inputDim[0] + (sl + minSlice)*inputDim[0] * inputDim[1]]; - } - } - } - } - else if (inputImage->GetPixelType().GetTypeAsString() == "scalar (double)" || inputImage->GetPixelType().GetTypeAsString() == " (double)") - { - // copy the data to the cropped image - for (unsigned short sl = 0; sl < outputDim[2]; ++sl) - { - for (unsigned short l = 0; l < outputDim[0]; ++l) - { - for (unsigned short s = 0; s < outputDim[1]; ++s) - { - outputData[l + s*(unsigned short)outputDim[0] + sl*outputDim[0] * outputDim[1]] = (float)((double*)inputData)[(l + left) + (s + above)*(unsigned short)inputDim[0] + (sl + minSlice)*inputDim[0] * inputDim[1]]; - } - } - } - } - else - { - MITK_INFO << "Could not determine pixel type"; - } - - mitk::Image::Pointer output = mitk::Image::New(); - output->Initialize(mitk::MakeScalarPixelType(), 3, outputDim); - output->SetSpacing(inputImage->GetGeometry()->GetSpacing()); - output->SetImportVolume(outputData, 0, 0, mitk::Image::CopyMemory); - delete[] outputData; - return output; -} - -mitk::Image::Pointer mitk::PhotoacousticImage::ApplyBeamforming(mitk::Image::Pointer inputImage, BeamformingSettings config, std::string& message, std::function progressHandle) -{ - Image::Pointer processedImage = inputImage; - if (inputImage->GetDimension() != 3) - { - processedImage->Initialize(mitk::MakeScalarPixelType(), 3, inputImage->GetDimensions()); - processedImage->SetSpacing(inputImage->GetGeometry()->GetSpacing()); - - mitk::ImageReadAccessor copy(inputImage); - - processedImage->SetImportVolume(copy.GetData()); - } - - config.RecordTime = config.RecordTime - (float)(config.upperCutoff) / (float)inputImage->GetDimension(1) * config.RecordTime; // adjust the recorded time lost by cropping - progressHandle(0, "converting image"); - if (!config.partial) - { - config.CropBounds[0] = 0; - config.CropBounds[1] = inputImage->GetDimension(2) - 1; - } - processedImage = ApplyCropping(inputImage, config.upperCutoff, 0, 0, 0, config.CropBounds[0], config.CropBounds[1]); - - config.inputDim[0] = processedImage->GetDimension(0); - config.inputDim[1] = processedImage->GetDimension(1); - config.inputDim[2] = processedImage->GetDimension(2); - - // perform the beamforming - m_BeamformingFilter->SetInput(processedImage); - m_BeamformingFilter->Configure(config); - m_BeamformingFilter->SetProgressHandle(progressHandle); - m_BeamformingFilter->UpdateLargestPossibleRegion(); - - processedImage = m_BeamformingFilter->GetOutput(); - message = m_BeamformingFilter->GetMessageString(); - - return processedImage; -} - -mitk::Image::Pointer mitk::PhotoacousticImage::BandpassFilter(mitk::Image::Pointer data, float recordTime, - float BPHighPass, float BPLowPass, - float alphaHighPass, float alphaLowPass) -{ - bool powerOfTwo = false; - int finalPower = 0; - for (int i = 1; pow(2, i) <= data->GetDimension(1); ++i) - { - finalPower = i; - if (pow(2, i) == data->GetDimension(1)) - { - powerOfTwo = true; - } - } - if (!powerOfTwo) - { - unsigned int dim[2] = { data->GetDimension(0), (unsigned int)pow(2,finalPower+1)}; - data = ApplyResampling(data, dim); - } - - MITK_INFO << data->GetDimension(0); - - // do a fourier transform, multiply with an appropriate window for the filter, and transform back - typedef float PixelType; - typedef itk::Image< PixelType, 3 > RealImageType; - RealImageType::Pointer image; - - mitk::CastToItkImage(data, image); - - typedef itk::FFT1DRealToComplexConjugateImageFilter ForwardFFTFilterType; - typedef ForwardFFTFilterType::OutputImageType ComplexImageType; - ForwardFFTFilterType::Pointer forwardFFTFilter = ForwardFFTFilterType::New(); - forwardFFTFilter->SetInput(image); - forwardFFTFilter->SetDirection(1); - try - { - forwardFFTFilter->UpdateOutputInformation(); - } - catch (itk::ExceptionObject & error) - { - std::cerr << "Error: " << error << std::endl; - MITK_WARN << "Bandpass could not be applied"; - return data; - } - - float singleVoxel = 1 / (recordTime / data->GetDimension(1)) / 2 / 1000; - float cutoffPixelHighPass = std::min(BPHighPass / singleVoxel, (float)data->GetDimension(1) / 2); - float cutoffPixelLowPass = std::min(BPLowPass / singleVoxel, (float)data->GetDimension(1) / 2 - cutoffPixelHighPass); - - RealImageType::Pointer fftMultiplicator = BPFunction(data, cutoffPixelHighPass, cutoffPixelLowPass, alphaHighPass, alphaLowPass); - - typedef itk::MultiplyImageFilter< ComplexImageType, - RealImageType, - ComplexImageType > - MultiplyFilterType; - MultiplyFilterType::Pointer multiplyFilter = MultiplyFilterType::New(); - multiplyFilter->SetInput1(forwardFFTFilter->GetOutput()); - multiplyFilter->SetInput2(fftMultiplicator); - - /*itk::ComplexToModulusImageFilter::Pointer toReal = itk::ComplexToModulusImageFilter::New(); - toReal->SetInput(forwardFFTFilter->GetOutput()); - return GrabItkImageMemory(toReal->GetOutput()); - return GrabItkImageMemory(fftMultiplicator); *///DEBUG - - typedef itk::FFT1DComplexConjugateToRealImageFilter< ComplexImageType, RealImageType > InverseFilterType; - InverseFilterType::Pointer inverseFFTFilter = InverseFilterType::New(); - inverseFFTFilter->SetInput(multiplyFilter->GetOutput()); - inverseFFTFilter->SetDirection(1); - - return GrabItkImageMemory(inverseFFTFilter->GetOutput()); -} - -itk::Image::Pointer mitk::PhotoacousticImage::BPFunction(mitk::Image::Pointer reference, - int cutoffFrequencyPixelHighPass, - int cutoffFrequencyPixelLowPass, - float alphaHighPass, float alphaLowPass) -{ - float* imageData = new float[reference->GetDimension(0)*reference->GetDimension(1)]; - - float width = reference->GetDimension(1) / 2.0 - (float)cutoffFrequencyPixelHighPass - (float)cutoffFrequencyPixelLowPass; - float center = (float)cutoffFrequencyPixelHighPass / 2.0 + width / 2.0; - - for (unsigned int n = 0; n < reference->GetDimension(1); ++n) - { - imageData[reference->GetDimension(0)*n] = 0; - } - for (int n = 0; n < width; ++n) - { - imageData[reference->GetDimension(0)*n] = 1; - if (n <= (alphaHighPass*(width - 1)) / 2.0) - { - if (alphaHighPass > 0.00001) - { - imageData[reference->GetDimension(0)*(int)(n + center - (width / 2))] = - (1 + cos(itk::Math::pi*(2 * n / (alphaHighPass*(width - 1)) - 1))) / 2; - } - else - { - imageData[reference->GetDimension(0)*(int)(n + center - (width / 2))] = 1; - } - } - else if (n >= (width - 1)*(1 - alphaLowPass / 2)) //??? - { - if (alphaLowPass > 0.00001) - { - imageData[reference->GetDimension(0)*(int)(n + center - (width / 2))] = - (1 + cos(itk::Math::pi*(2 * n / (alphaLowPass*(width - 1)) + 1 - 2 / alphaLowPass))) / 2; - } - else - { - imageData[reference->GetDimension(0)*(int)(n + center - (width / 2))] = 1; - } - } - //MITK_INFO << "n:" << n << " is " << imageData[reference->GetDimension(0)*(int)(n + center - (width / 2))]; - } - MITK_INFO << "width: " << width << ", center: " << center << ", alphaHighPass: " << alphaHighPass << ", alphaLowPass: " << alphaLowPass; - - // mirror the first half of the image - for (unsigned int n = reference->GetDimension(1) / 2; n < reference->GetDimension(1); ++n) - { - imageData[reference->GetDimension(0)*n] = imageData[(reference->GetDimension(1) - (n + 1)) * reference->GetDimension(0)]; - } - - - // copy and paste to all lines - for (unsigned int line = 1; line < reference->GetDimension(0); ++line) - { - for (unsigned int sample = 0; sample < reference->GetDimension(1); ++sample) - { - imageData[reference->GetDimension(0)*sample + line] = imageData[reference->GetDimension(0)*sample]; - } - } - - typedef itk::Image< float, 3U > ImageType; - ImageType::RegionType region; - ImageType::IndexType start; - start.Fill(0); - - region.SetIndex(start); - - ImageType::SizeType size; - size[0] = reference->GetDimension(0); - size[1] = reference->GetDimension(1); - size[2] = reference->GetDimension(2); - - region.SetSize(size); - - ImageType::SpacingType SpacingItk; - SpacingItk[0] = reference->GetGeometry()->GetSpacing()[0]; - SpacingItk[1] = reference->GetGeometry()->GetSpacing()[1]; - SpacingItk[2] = reference->GetGeometry()->GetSpacing()[2]; - - ImageType::Pointer image = ImageType::New(); - image->SetRegions(region); - image->Allocate(); - image->FillBuffer(itk::NumericTraits::Zero); - image->SetSpacing(SpacingItk); - - ImageType::IndexType pixelIndex; - - for (unsigned int slice = 0; slice < reference->GetDimension(2); ++slice) - { - for (unsigned int line = 0; line < reference->GetDimension(0); ++line) - { - for (unsigned int sample = 0; sample < reference->GetDimension(1); ++sample) - { - pixelIndex[0] = line; - pixelIndex[1] = sample; - pixelIndex[2] = slice; - - image->SetPixel(pixelIndex, imageData[line + sample*reference->GetDimension(0)]); - } - } - } - - delete[] imageData; - - return image; -} diff --git a/Modules/PhotoacousticsAlgorithms/source/utils/mitkBeamformingUtils.cpp b/Modules/PhotoacousticsAlgorithms/source/utils/mitkBeamformingUtils.cpp new file mode 100644 index 0000000000..b000906692 --- /dev/null +++ b/Modules/PhotoacousticsAlgorithms/source/utils/mitkBeamformingUtils.cpp @@ -0,0 +1,563 @@ +/*=================================================================== +mitkPhotoacousticBeamformingFilter +The Medical Imaging Interaction Toolkit (MITK) + +Copyright (c) German Cancer Research Center, +Division of Medical and Biological Informatics. +All rights reserved. + +This software is distributed WITHOUT ANY WARRANTY; without +even the implied warranty of MERCHANTABILITY or FITNESS FOR +A PARTICULAR PURPOSE. + +See LICENSE.txt or http://www.mitk.org for details. + +===================================================================*/ + +#include "mitkProperties.h" +#include "mitkImageReadAccessor.h" +#include +#include +#include +#include +#include +#include "mitkImageCast.h" +#include "mitkBeamformingUtils.h" + +mitk::BeamformingUtils::BeamformingUtils() +{ +} + +mitk::BeamformingUtils::~BeamformingUtils() +{ +} + +float* mitk::BeamformingUtils::VonHannFunction(int samples) +{ + float* ApodWindow = new float[samples]; + + for (int n = 0; n < samples; ++n) + { + ApodWindow[n] = (1 - cos(2 * itk::Math::pi * n / (samples - 1))) / 2; + } + + return ApodWindow; +} + +float* mitk::BeamformingUtils::HammFunction(int samples) +{ + float* ApodWindow = new float[samples]; + + for (int n = 0; n < samples; ++n) + { + ApodWindow[n] = 0.54 - 0.46*cos(2 * itk::Math::pi*n / (samples - 1)); + } + + return ApodWindow; +} + +float* mitk::BeamformingUtils::BoxFunction(int samples) +{ + float* ApodWindow = new float[samples]; + + for (int n = 0; n < samples; ++n) + { + ApodWindow[n] = 1; + } + + return ApodWindow; +} + +void mitk::BeamformingUtils::DASQuadraticLine( + float* input, float* output, float inputDim[2], float outputDim[2], + const short& line, const mitk::BeamformingSettings::Pointer config) +{ + const float* apodisation = config->GetApodizationFunction(); + const short apodArraySize = config->GetApodizationArraySize(); + float& inputS = inputDim[1]; + float& inputL = inputDim[0]; + + float& outputS = outputDim[1]; + float& outputL = outputDim[0]; + + short AddSample = 0; + short maxLine = 0; + short minLine = 0; + float delayMultiplicator = 0; + float l_i = 0; + float s_i = 0; + + float part = 0; + float tan_phi = std::tan(config->GetAngle() / 360 * 2 * itk::Math::pi); + float part_multiplicator = tan_phi * config->GetTimeSpacing() * config->GetSpeedOfSound() / + config->GetPitchInMeters() * inputL / config->GetTransducerElements(); + float apod_mult = 1; + + short usedLines = (maxLine - minLine); + + float percentOfImageReconstructed = (float)(config->GetReconstructionDepth()) / + (float)(inputS * config->GetSpeedOfSound() * config->GetTimeSpacing() / (float)(2 - (int)config->GetIsPhotoacousticImage())); + percentOfImageReconstructed = percentOfImageReconstructed <= 1 ? percentOfImageReconstructed : 1; + + l_i = (float)line / outputL * inputL; + + for (short sample = 0; sample < outputS; ++sample) + { + s_i = (float)sample / outputS * inputS / (float)(2 - (int)config->GetIsPhotoacousticImage()) * percentOfImageReconstructed; + + part = part_multiplicator*s_i; + + if (part < 1) + part = 1; + + maxLine = (short)std::min((l_i + part) + 1, inputL); + minLine = (short)std::max((l_i - part), 0.0f); + usedLines = (maxLine - minLine); + + apod_mult = (float)apodArraySize / (float)usedLines; + + delayMultiplicator = pow((1 / (config->GetTimeSpacing()*config->GetSpeedOfSound()) * + (config->GetPitchInMeters()*config->GetTransducerElements()) / inputL), 2) / s_i / 2; + + for (short l_s = minLine; l_s < maxLine; ++l_s) + { + AddSample = delayMultiplicator * pow((l_s - l_i), 2) + s_i + (1 - config->GetIsPhotoacousticImage())*s_i; + if (AddSample < inputS && AddSample >= 0) + output[sample*(short)outputL + line] += input[l_s + AddSample*(short)inputL] * + apodisation[(short)((l_s - minLine)*apod_mult)]; + else + --usedLines; + } + output[sample*(short)outputL + line] = output[sample*(short)outputL + line] / usedLines; + } +} + +void mitk::BeamformingUtils::DASSphericalLine( + float* input, float* output, float inputDim[2], float outputDim[2], + const short& line, const mitk::BeamformingSettings::Pointer config) +{ + const float* apodisation = config->GetApodizationFunction(); + const short apodArraySize = config->GetApodizationArraySize(); + + float& inputS = inputDim[1]; + float& inputL = inputDim[0]; + + float& outputS = outputDim[1]; + float& outputL = outputDim[0]; + + short AddSample = 0; + short maxLine = 0; + short minLine = 0; + float l_i = 0; + float s_i = 0; + + float part = 0.07 * inputL; + float tan_phi = std::tan(config->GetAngle() / 360 * 2 * itk::Math::pi); + float part_multiplicator = tan_phi * config->GetTimeSpacing() * + config->GetSpeedOfSound() / config->GetPitchInMeters() * inputL / (float)config->GetTransducerElements(); + float apod_mult = 1; + + short usedLines = (maxLine - minLine); + + float percentOfImageReconstructed = (float)(config->GetReconstructionDepth()) / + (float)(inputS * config->GetSpeedOfSound() * config->GetTimeSpacing() / (float)(2 - (int)config->GetIsPhotoacousticImage())); + percentOfImageReconstructed = percentOfImageReconstructed <= 1 ? percentOfImageReconstructed : 1; + + l_i = (float)line / outputL * inputL; + + for (short sample = 0; sample < outputS; ++sample) + { + s_i = (float)sample / outputS * inputS / (float)(2 - (int)config->GetIsPhotoacousticImage()) * percentOfImageReconstructed; + + part = part_multiplicator*s_i; + + if (part < 1) + part = 1; + + maxLine = (short)std::min((l_i + part) + 1, inputL); + minLine = (short)std::max((l_i - part), 0.0f); + usedLines = (maxLine - minLine); + + apod_mult = (float)apodArraySize / (float)usedLines; + + for (short l_s = minLine; l_s < maxLine; ++l_s) + { + AddSample = (int)sqrt( + pow(s_i, 2) + + + pow((1 / (config->GetTimeSpacing()*config->GetSpeedOfSound()) * + (((float)l_s - l_i)*config->GetPitchInMeters()*(float)config->GetTransducerElements()) / inputL), 2) + ) + (1 - config->GetIsPhotoacousticImage())*s_i; + if (AddSample < inputS && AddSample >= 0) + output[sample*(short)outputL + line] += input[l_s + AddSample*(short)inputL] * + apodisation[(short)((l_s - minLine)*apod_mult)]; + else + --usedLines; + } + output[sample*(short)outputL + line] = output[sample*(short)outputL + line] / usedLines; + } +} + +void mitk::BeamformingUtils::DMASQuadraticLine( + float* input, float* output, float inputDim[2], float outputDim[2], + const short& line, const mitk::BeamformingSettings::Pointer config) +{ + const float* apodisation = config->GetApodizationFunction(); + const short apodArraySize = config->GetApodizationArraySize(); + + float& inputS = inputDim[1]; + float& inputL = inputDim[0]; + + float& outputS = outputDim[1]; + float& outputL = outputDim[0]; + + short maxLine = 0; + short minLine = 0; + float delayMultiplicator = 0; + float l_i = 0; + float s_i = 0; + + float part = 0.07 * inputL; + float tan_phi = std::tan(config->GetAngle() / 360 * 2 * itk::Math::pi); + float part_multiplicator = tan_phi * config->GetTimeSpacing() * + config->GetSpeedOfSound() / config->GetPitchInMeters() * inputL / (float)config->GetTransducerElements(); + float apod_mult = 1; + + float mult = 0; + short usedLines = (maxLine - minLine); + + float percentOfImageReconstructed = (float)(config->GetReconstructionDepth()) / + (float)(inputS * config->GetSpeedOfSound() * config->GetTimeSpacing() / (float)(2 - (int)config->GetIsPhotoacousticImage())); + percentOfImageReconstructed = percentOfImageReconstructed <= 1 ? percentOfImageReconstructed : 1; + + l_i = (float)line / outputL * inputL; + + for (short sample = 0; sample < outputS; ++sample) + { + s_i = (float)sample / outputS * inputS / (float)(2 - (int)config->GetIsPhotoacousticImage()) * percentOfImageReconstructed; + + part = part_multiplicator*s_i; + + if (part < 1) + part = 1; + + maxLine = (short)std::min((l_i + part) + 1, inputL); + minLine = (short)std::max((l_i - part), 0.0f); + usedLines = (maxLine - minLine); + + apod_mult = (float)apodArraySize / (float)usedLines; + + delayMultiplicator = pow((1 / (config->GetTimeSpacing()*config->GetSpeedOfSound()) * + (config->GetPitchInMeters()*config->GetTransducerElements()) / inputL), 2) / s_i / 2; + + //calculate the AddSamples beforehand to save some time + short* AddSample = new short[maxLine - minLine]; + for (short l_s = 0; l_s < maxLine - minLine; ++l_s) + { + AddSample[l_s] = (short)(delayMultiplicator * pow((minLine + l_s - l_i), 2) + s_i) + + (1 - config->GetIsPhotoacousticImage())*s_i; + } + + float s_1 = 0; + float s_2 = 0; + + for (short l_s1 = minLine; l_s1 < maxLine - 1; ++l_s1) + { + if (AddSample[l_s1 - minLine] < inputS && AddSample[l_s1 - minLine] >= 0) + { + for (short l_s2 = l_s1 + 1; l_s2 < maxLine; ++l_s2) + { + if (AddSample[l_s2 - minLine] < inputS && AddSample[l_s2 - minLine] >= 0) + { + s_2 = input[l_s2 + AddSample[l_s2 - minLine] * (short)inputL]; + s_1 = input[l_s1 + AddSample[l_s1 - minLine] * (short)inputL]; + + mult = s_2 * apodisation[(int)((l_s2 - minLine)*apod_mult)] * s_1 * apodisation[(int)((l_s1 - minLine)*apod_mult)]; + output[sample*(short)outputL + line] += sqrt(fabs(mult)) * ((mult > 0) - (mult < 0)); + } + } + } + else + --usedLines; + } + + output[sample*(short)outputL + line] = output[sample*(short)outputL + line] / (float)(pow(usedLines, 2) - (usedLines - 1)); + + delete[] AddSample; + } +} + +void mitk::BeamformingUtils::DMASSphericalLine( + float* input, float* output, float inputDim[2], float outputDim[2], + const short& line, const mitk::BeamformingSettings::Pointer config) +{ + const float* apodisation = config->GetApodizationFunction(); + const short apodArraySize = config->GetApodizationArraySize(); + + float& inputS = inputDim[1]; + float& inputL = inputDim[0]; + + float& outputS = outputDim[1]; + float& outputL = outputDim[0]; + + short maxLine = 0; + short minLine = 0; + float l_i = 0; + float s_i = 0; + + float part = 0.07 * inputL; + float tan_phi = std::tan(config->GetAngle() / 360 * 2 * itk::Math::pi); + float part_multiplicator = tan_phi * config->GetTimeSpacing() * + config->GetSpeedOfSound() / config->GetPitchInMeters() * inputL / (float)config->GetTransducerElements(); + float apod_mult = 1; + + float mult = 0; + + short usedLines = (maxLine - minLine); + + float percentOfImageReconstructed = (float)(config->GetReconstructionDepth()) / + (float)(inputS * config->GetSpeedOfSound() * config->GetTimeSpacing() / (float)(2 - (int)config->GetIsPhotoacousticImage())); + percentOfImageReconstructed = percentOfImageReconstructed <= 1 ? percentOfImageReconstructed : 1; + + l_i = (float)line / outputL * inputL; + + for (short sample = 0; sample < outputS; ++sample) + { + s_i = (float)sample / outputS * inputS / (float)(2 - (int)config->GetIsPhotoacousticImage()) * percentOfImageReconstructed; + + part = part_multiplicator*s_i; + + if (part < 1) + part = 1; + + maxLine = (short)std::min((l_i + part) + 1, inputL); + minLine = (short)std::max((l_i - part), 0.0f); + usedLines = (maxLine - minLine); + + apod_mult = (float)apodArraySize / (float)usedLines; + + //calculate the AddSamples beforehand to save some time + short* AddSample = new short[maxLine - minLine]; + for (short l_s = 0; l_s < maxLine - minLine; ++l_s) + { + AddSample[l_s] = (short)sqrt( + pow(s_i, 2) + + + pow((1 / (config->GetTimeSpacing()*config->GetSpeedOfSound()) * + (((float)minLine + (float)l_s - l_i)*config->GetPitchInMeters()*(float)config->GetTransducerElements()) / inputL), 2) + ) + (1 - config->GetIsPhotoacousticImage())*s_i; + } + + float s_1 = 0; + float s_2 = 0; + + for (short l_s1 = minLine; l_s1 < maxLine - 1; ++l_s1) + { + if (AddSample[l_s1 - minLine] < inputS && AddSample[l_s1 - minLine] >= 0) + { + for (short l_s2 = l_s1 + 1; l_s2 < maxLine; ++l_s2) + { + if (AddSample[l_s2 - minLine] < inputS && AddSample[l_s2 - minLine] >= 0) + { + s_2 = input[l_s2 + AddSample[l_s2 - minLine] * (short)inputL]; + s_1 = input[l_s1 + AddSample[l_s1 - minLine] * (short)inputL]; + + mult = s_2 * apodisation[(int)((l_s2 - minLine)*apod_mult)] * s_1 * apodisation[(int)((l_s1 - minLine)*apod_mult)]; + output[sample*(short)outputL + line] += sqrt(fabs(mult)) * ((mult > 0) - (mult < 0)); + } + } + } + else + --usedLines; + } + + output[sample*(short)outputL + line] = output[sample*(short)outputL + line] / (float)(pow(usedLines, 2) - (usedLines - 1)); + + delete[] AddSample; + } +} + +void mitk::BeamformingUtils::sDMASQuadraticLine( + float* input, float* output, float inputDim[2], float outputDim[2], + const short& line, const mitk::BeamformingSettings::Pointer config) +{ + const float* apodisation = config->GetApodizationFunction(); + const short apodArraySize = config->GetApodizationArraySize(); + + float& inputS = inputDim[1]; + float& inputL = inputDim[0]; + + float& outputS = outputDim[1]; + float& outputL = outputDim[0]; + + short maxLine = 0; + short minLine = 0; + float delayMultiplicator = 0; + float l_i = 0; + float s_i = 0; + + float part = 0.07 * inputL; + float tan_phi = std::tan(config->GetAngle() / 360 * 2 * itk::Math::pi); + float part_multiplicator = tan_phi * config->GetTimeSpacing() * config->GetSpeedOfSound() / + config->GetPitchInMeters() * inputL / (float)config->GetTransducerElements(); + float apod_mult = 1; + + float mult = 0; + short usedLines = (maxLine - minLine); + + float percentOfImageReconstructed = (float)(config->GetReconstructionDepth()) / + (float)(inputS * config->GetSpeedOfSound() * config->GetTimeSpacing() / (float)(2 - (int)config->GetIsPhotoacousticImage())); + percentOfImageReconstructed = percentOfImageReconstructed <= 1 ? percentOfImageReconstructed : 1; + + l_i = (float)line / outputL * inputL; + + for (short sample = 0; sample < outputS; ++sample) + { + s_i = (float)sample / outputS * inputS / (float)(2 - (int)config->GetIsPhotoacousticImage()) * percentOfImageReconstructed; + + part = part_multiplicator*s_i; + + if (part < 1) + part = 1; + + maxLine = (short)std::min((l_i + part) + 1, inputL); + minLine = (short)std::max((l_i - part), 0.0f); + usedLines = (maxLine - minLine); + + apod_mult = (float)apodArraySize / (float)usedLines; + + delayMultiplicator = pow((1 / (config->GetTimeSpacing()*config->GetSpeedOfSound()) * + (config->GetPitchInMeters()*config->GetTransducerElements()) / inputL), 2) / s_i / 2; + + //calculate the AddSamples beforehand to save some time + short* AddSample = new short[maxLine - minLine]; + for (short l_s = 0; l_s < maxLine - minLine; ++l_s) + { + AddSample[l_s] = (short)(delayMultiplicator * pow((minLine + l_s - l_i), 2) + s_i) + + (1 - config->GetIsPhotoacousticImage())*s_i; + } + + float s_1 = 0; + float s_2 = 0; + float sign = 0; + + for (short l_s1 = minLine; l_s1 < maxLine - 1; ++l_s1) + { + if (AddSample[l_s1 - minLine] < inputS && AddSample[l_s1 - minLine] >= 0) + { + s_1 = input[l_s1 + AddSample[l_s1 - minLine] * (short)inputL]; + sign += s_1; + + for (short l_s2 = l_s1 + 1; l_s2 < maxLine; ++l_s2) + { + if (AddSample[l_s2 - minLine] < inputS && AddSample[l_s2 - minLine] >= 0) + { + s_2 = input[l_s2 + AddSample[l_s2 - minLine] * (short)inputL]; + + mult = s_2 * apodisation[(int)((l_s2 - minLine)*apod_mult)] * s_1 * apodisation[(int)((l_s1 - minLine)*apod_mult)]; + output[sample*(short)outputL + line] += sqrt(fabs(mult)) * ((mult > 0) - (mult < 0)); + } + } + } + else + --usedLines; + } + + output[sample*(short)outputL + line] = output[sample*(short)outputL + line] / (float)(pow(usedLines, 2) - (usedLines - 1)) * ((sign > 0) - (sign < 0)); + + delete[] AddSample; + } +} + +void mitk::BeamformingUtils::sDMASSphericalLine( + float* input, float* output, float inputDim[2], float outputDim[2], + const short& line, const mitk::BeamformingSettings::Pointer config) +{ + const float* apodisation = config->GetApodizationFunction(); + const short apodArraySize = config->GetApodizationArraySize(); + + float& inputS = inputDim[1]; + float& inputL = inputDim[0]; + + float& outputS = outputDim[1]; + float& outputL = outputDim[0]; + + short maxLine = 0; + short minLine = 0; + float l_i = 0; + float s_i = 0; + + float part = 0.07 * inputL; + float tan_phi = std::tan(config->GetAngle() / 360 * 2 * itk::Math::pi); + float part_multiplicator = tan_phi * config->GetTimeSpacing() * config->GetSpeedOfSound() / + config->GetPitchInMeters() * inputL / (float)config->GetTransducerElements(); + float apod_mult = 1; + + float mult = 0; + + short usedLines = (maxLine - minLine); + + float percentOfImageReconstructed = (float)(config->GetReconstructionDepth()) / + (float)(inputS * config->GetSpeedOfSound() * config->GetTimeSpacing() / (float)(2 - (int)config->GetIsPhotoacousticImage())); + percentOfImageReconstructed = percentOfImageReconstructed <= 1 ? percentOfImageReconstructed : 1; + + l_i = (float)line / outputL * inputL; + + for (short sample = 0; sample < outputS; ++sample) + { + s_i = (float)sample / outputS * inputS / (float)(2 - (int)config->GetIsPhotoacousticImage()) * percentOfImageReconstructed; + + part = part_multiplicator*s_i; + + if (part < 1) + part = 1; + + maxLine = (short)std::min((l_i + part) + 1, inputL); + minLine = (short)std::max((l_i - part), 0.0f); + usedLines = (maxLine - minLine); + + apod_mult = (float)apodArraySize / (float)usedLines; + + //calculate the AddSamples beforehand to save some time + short* AddSample = new short[maxLine - minLine]; + for (short l_s = 0; l_s < maxLine - minLine; ++l_s) + { + AddSample[l_s] = (short)sqrt( + pow(s_i, 2) + + + pow((1 / (config->GetTimeSpacing()*config->GetSpeedOfSound()) * + (((float)minLine + (float)l_s - l_i)*config->GetPitchInMeters()*(float)config->GetTransducerElements()) / inputL), 2) + ) + (1 - config->GetIsPhotoacousticImage())*s_i; + } + + float s_1 = 0; + float s_2 = 0; + float sign = 0; + + for (short l_s1 = minLine; l_s1 < maxLine - 1; ++l_s1) + { + if (AddSample[l_s1 - minLine] < inputS && AddSample[l_s1 - minLine] >= 0) + { + s_1 = input[l_s1 + AddSample[l_s1 - minLine] * (short)inputL]; + sign += s_1; + + for (short l_s2 = l_s1 + 1; l_s2 < maxLine; ++l_s2) + { + if (AddSample[l_s2 - minLine] < inputS && AddSample[l_s2 - minLine] >= 0) + { + s_2 = input[l_s2 + AddSample[l_s2 - minLine] * (short)inputL]; + + mult = s_2 * apodisation[(int)((l_s2 - minLine)*apod_mult)] * s_1 * apodisation[(int)((l_s1 - minLine)*apod_mult)]; + output[sample*(short)outputL + line] += sqrt(fabs(mult)) * ((mult > 0) - (mult < 0)); + } + } + } + else + --usedLines; + } + + output[sample*(short)outputL + line] = output[sample*(short)outputL + line] / (float)(pow(usedLines, 2) - (usedLines - 1)) * ((sign > 0) - (sign < 0)); + + delete[] AddSample; + } +} diff --git a/Modules/PhotoacousticsAlgorithms/source/utils/mitkPhotoacousticFilterService.cpp b/Modules/PhotoacousticsAlgorithms/source/utils/mitkPhotoacousticFilterService.cpp new file mode 100644 index 0000000000..2ab8cbca1c --- /dev/null +++ b/Modules/PhotoacousticsAlgorithms/source/utils/mitkPhotoacousticFilterService.cpp @@ -0,0 +1,233 @@ +/*=================================================================== + +The Medical Imaging Interaction Toolkit (MITK) + +Copyright (c) German Cancer Research Center, +Division of Medical and Biological Informatics. +All rights reserved. + +This software is distributed WITHOUT ANY WARRANTY; without +even the implied warranty of MERCHANTABILITY or FITNESS FOR +A PARTICULAR PURPOSE. + +See LICENSE.txt or http://www.mitk.org for details. + +===================================================================*/ + +#include "mitkPhotoacousticFilterService.h" + +#include "mitkITKImageImport.h" +#include +#include +#include "./OpenCLFilter/mitkPhotoacousticBModeFilter.h" +#include "mitkConvert2Dto3DImageFilter.h" +#include +#include "../ITKFilter/ITKUltrasound/itkBModeImageFilter.h" +#include "../ITKFilter/itkPhotoacousticBModeImageFilter.h" +#include + +// itk dependencies +#include "itkImage.h" +#include "itkResampleImageFilter.h" +#include "itkCastImageFilter.h" +#include "itkCropImageFilter.h" +#include "itkRescaleIntensityImageFilter.h" +#include "itkIntensityWindowingImageFilter.h" +#include +#include "itkBSplineInterpolateImageFunction.h" +#include + +// needed itk image filters +#include "mitkImageCast.h" + +mitk::PhotoacousticFilterService::PhotoacousticFilterService() +{ + MITK_INFO << "[PhotoacousticFilterService] created filter service"; +} + +mitk::PhotoacousticFilterService::~PhotoacousticFilterService() +{ + MITK_INFO << "[PhotoacousticFilterService] destructed filter service"; +} + +mitk::Image::Pointer mitk::PhotoacousticFilterService::ApplyBmodeFilter( + mitk::Image::Pointer inputImage, + BModeMethod method, bool UseLogFilter) +{ + // the image needs to be of floating point type for the envelope filter to work; the casting is done automatically by the CastToItkImage + typedef itk::Image< float, 3 > itkFloatImageType; + + auto floatImage = ConvertToFloat(inputImage); + + if (method == BModeMethod::Abs) + { + PhotoacousticBModeFilter::Pointer filter = PhotoacousticBModeFilter::New(); + filter->UseLogFilter(UseLogFilter); + filter->SetInput(floatImage); + filter->Update(); + return filter->GetOutput(); + } + + typedef itk::BModeImageFilter < itkFloatImageType, itkFloatImageType > BModeFilterType; + BModeFilterType::Pointer bModeFilter = BModeFilterType::New(); // LogFilter + + typedef itk::PhotoacousticBModeImageFilter < itkFloatImageType, itkFloatImageType > PhotoacousticBModeImageFilter; + PhotoacousticBModeImageFilter::Pointer photoacousticBModeFilter = PhotoacousticBModeImageFilter::New(); // No LogFilter + + typedef itk::ResampleImageFilter < itkFloatImageType, itkFloatImageType > ResampleImageFilter; + ResampleImageFilter::Pointer resampleImageFilter = ResampleImageFilter::New(); + + itkFloatImageType::Pointer itkImage; + + mitk::CastToItkImage(floatImage, itkImage); + + if (UseLogFilter) + { + bModeFilter->SetInput(itkImage); + bModeFilter->SetDirection(1); + itkImage = bModeFilter->GetOutput(); + } + else + { + photoacousticBModeFilter->SetInput(itkImage); + photoacousticBModeFilter->SetDirection(1); + itkImage = photoacousticBModeFilter->GetOutput(); + } + + return mitk::GrabItkImageMemory(itkImage); +} + +mitk::Image::Pointer mitk::PhotoacousticFilterService::ApplyResampling( + mitk::Image::Pointer inputImage, + double outputSpacing[2]) +{ + typedef itk::Image< float, 3 > itkFloatImageType; + + auto floatImage = ConvertToFloat(inputImage); + + typedef itk::ResampleImageFilter < itkFloatImageType, itkFloatImageType > ResampleImageFilter; + ResampleImageFilter::Pointer resampleImageFilter = ResampleImageFilter::New(); + + itkFloatImageType::Pointer itkImage; + + mitk::CastToItkImage(floatImage, itkImage); + + itkFloatImageType::SpacingType outputSpacingItk; + itkFloatImageType::SizeType inputSizeItk = itkImage->GetLargestPossibleRegion().GetSize(); + itkFloatImageType::SizeType outputSizeItk = inputSizeItk; + + outputSpacingItk[0] = outputSpacing[0]; + outputSpacingItk[1] = outputSpacing[1]; + outputSpacingItk[2] = itkImage->GetSpacing()[2]; + + outputSizeItk[0] = outputSizeItk[0] * (floatImage->GetGeometry()->GetSpacing()[0] / outputSpacing[0]); + outputSizeItk[1] = outputSizeItk[1] * (floatImage->GetGeometry()->GetSpacing()[1] / outputSpacing[1]); + + resampleImageFilter->SetInput(itkImage); + resampleImageFilter->SetSize(outputSizeItk); + resampleImageFilter->SetOutputSpacing(outputSpacingItk); + + resampleImageFilter->UpdateLargestPossibleRegion(); + return mitk::GrabItkImageMemory(resampleImageFilter->GetOutput()); +} + +mitk::Image::Pointer mitk::PhotoacousticFilterService::ApplyCropping( + mitk::Image::Pointer inputImage, + int above, int below, + int right, int left, + int zStart, int zEnd) +{ + try + { + auto floatImage = ConvertToFloat(inputImage); + mitk::CropImageFilter::Pointer cropImageFilter = mitk::CropImageFilter::New(); + cropImageFilter->SetInput(floatImage); + cropImageFilter->SetXPixelsCropStart(left); + cropImageFilter->SetXPixelsCropEnd(right); + cropImageFilter->SetYPixelsCropStart(above); + cropImageFilter->SetYPixelsCropEnd(below); + cropImageFilter->SetZPixelsCropStart(zStart); + cropImageFilter->SetZPixelsCropEnd(zEnd); + cropImageFilter->Update(); + return cropImageFilter->GetOutput(); + } + catch (mitk::Exception &e) + { + std::string errorMessage = "Caught unexpected exception "; + errorMessage.append(e.what()); + MITK_ERROR << errorMessage; + + return inputImage; + } +} + +mitk::Image::Pointer mitk::PhotoacousticFilterService::ApplyBeamforming( + mitk::Image::Pointer inputImage, + BeamformingSettings::Pointer config, + std::function progressHandle) +{ + Image::Pointer processedImage = mitk::Image::New(); + + if (inputImage->GetDimension() != 3) + { + mitk::Convert2Dto3DImageFilter::Pointer dimensionImageFilter = mitk::Convert2Dto3DImageFilter::New(); + dimensionImageFilter->SetInput(inputImage); + dimensionImageFilter->Update(); + processedImage = dimensionImageFilter->GetOutput(); + } + else + { + processedImage = inputImage; + } + + m_BeamformingFilter = mitk::BeamformingFilter::New(config); + m_BeamformingFilter->SetInput(ConvertToFloat(processedImage)); + m_BeamformingFilter->SetProgressHandle(progressHandle); + m_BeamformingFilter->UpdateLargestPossibleRegion(); + + processedImage = m_BeamformingFilter->GetOutput(); + + return processedImage; +} + +mitk::Image::Pointer mitk::PhotoacousticFilterService::ApplyBandpassFilter( + mitk::Image::Pointer data, + float BPHighPass, float BPLowPass, + float alphaHighPass, float alphaLowPass) +{ + try + { + auto floatData = ConvertToFloat(data); + mitk::BandpassFilter::Pointer bandpassFilter = mitk::BandpassFilter::New(); + bandpassFilter->SetInput(floatData); + bandpassFilter->SetHighPass(BPHighPass); + bandpassFilter->SetLowPass(BPLowPass); + bandpassFilter->SetHighPassAlpha(alphaHighPass); + bandpassFilter->SetLowPassAlpha(alphaLowPass); + bandpassFilter->Update(); + return bandpassFilter->GetOutput(); + } + + catch (mitk::Exception &e) + { + std::string errorMessage = "Caught unexpected exception "; + errorMessage.append(e.what()); + MITK_ERROR << errorMessage; + + return data; + } +} + +mitk::Image::Pointer mitk::PhotoacousticFilterService::ConvertToFloat(mitk::Image::Pointer inputImage) +{ + if ((inputImage->GetPixelType().GetTypeAsString() == "scalar (float)" || + inputImage->GetPixelType().GetTypeAsString() == " (float)")) + { + return inputImage; + } + + mitk::CastToFloatImageFilter::Pointer castToFloatImageFilter = mitk::CastToFloatImageFilter::New(); + castToFloatImageFilter->SetInput(inputImage); + castToFloatImageFilter->Update(); + return castToFloatImageFilter->GetOutput(); +} diff --git a/Modules/PhotoacousticsAlgorithms/test/CMakeLists.txt b/Modules/PhotoacousticsAlgorithms/test/CMakeLists.txt new file mode 100644 index 0000000000..153cd81e2e --- /dev/null +++ b/Modules/PhotoacousticsAlgorithms/test/CMakeLists.txt @@ -0,0 +1 @@ +MITK_CREATE_MODULE_TESTS() diff --git a/Modules/PhotoacousticsLib/test/files.cmake b/Modules/PhotoacousticsAlgorithms/test/files.cmake similarity index 54% copy from Modules/PhotoacousticsLib/test/files.cmake copy to Modules/PhotoacousticsAlgorithms/test/files.cmake index ae1e015083..2fe7e9fa64 100644 --- a/Modules/PhotoacousticsLib/test/files.cmake +++ b/Modules/PhotoacousticsAlgorithms/test/files.cmake @@ -1,40 +1,24 @@ set(MODULE_TESTS # IMPORTANT: If you plan to deactivate / comment out a test please write a bug number to the commented out line of code. # # Example: #mitkMyTest #this test is commented out because of bug 12345 # # It is important that the bug is open and that the test will be activated again before the bug is closed. This assures that # no test is forgotten after it was commented out. If there is no bug for your current problem, please add a new one and # mark it as critical. ################## ON THE FENCE TESTS ################################################# # none ################## DISABLED TESTS ##################################################### ################# RUNNING TESTS ####################################################### - mitkSlicedVolumeGeneratorTest.cpp - mitkPhotoacousticTissueGeneratorTest.cpp - mitkPhotoacousticVectorTest.cpp - mitkPhotoacoustic3dVolumeTest.cpp - mitkPhotoacousticVolumeTest.cpp - mitkPhotoacousticVesselTest.cpp - mitkPhotoacousticVesselTreeTest.cpp - mitkPhotoacousticVesselMeanderStrategyTest.cpp - mitkMcxyzXmlTest.cpp - mitkPhotoacousticComposedVolumeTest.cpp - mitkPhotoacousticNoiseGeneratorTest.cpp - mitkPhotoacousticIOTest.cpp - mitkMCThreadHandlerTest.cpp - mitkSimulationBatchGeneratorTest.cpp - mitkPropertyCalculatorTest.cpp -) -set(RESOURCE_FILES - pointsource.xml - circlesource.xml - rectanglesource.xml - twopointsources.xml - allsources.xml + mitkPAFilterServiceTest.cpp + mitkCastToFloatImageFilterTest.cpp + mitkBandpassFilterTest.cpp + mitkBeamformingFilterTest.cpp + mitkCropImageFilterTest.cpp ) +set(RESOURCE_FILES) diff --git a/Modules/PhotoacousticsAlgorithms/test/mitkBandpassFilterTest.cpp b/Modules/PhotoacousticsAlgorithms/test/mitkBandpassFilterTest.cpp new file mode 100644 index 0000000000..b3b319f35b --- /dev/null +++ b/Modules/PhotoacousticsAlgorithms/test/mitkBandpassFilterTest.cpp @@ -0,0 +1,194 @@ +/*=================================================================== + +The Medical Imaging Interaction Toolkit (MITK) + +Copyright (c) German Cancer Research Center, +Division of Medical and Biological Informatics. +All rights reserved. + +This software is distributed WITHOUT ANY WARRANTY; without +even the implied warranty of MERCHANTABILITY or FITNESS FOR +A PARTICULAR PURPOSE. + +See LICENSE.txt or http://www.mitk.org for details. + +===================================================================*/ +#define _USE_MATH_DEFINES +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "../ITKFilter/ITKUltrasound/itkFFT1DRealToComplexConjugateImageFilter.h" +#include "mitkImageCast.h" +#include "mitkITKImageImport.h" +#include "itkComplexToModulusImageFilter.h" + +class mitkBandpassFilterTestSuite : public mitk::TestFixture +{ + CPPUNIT_TEST_SUITE(mitkBandpassFilterTestSuite); + MITK_TEST(testHighPass); + MITK_TEST(testLowPass); + CPPUNIT_TEST_SUITE_END(); + +private: + + mitk::BandpassFilter::Pointer m_BandpassFilter; + const unsigned int NUM_ITERATIONS = 15; + const unsigned int DATA_XY_DIM = 512; + const unsigned int DATA_Z_DIM = 8; + const float TIME_SPACING = 0.00625; // [us] + const float FREQUENCY_RESOLUTION = 2 * M_PI / (TIME_SPACING * DATA_XY_DIM); // [MHz] + const float MAX_FREQUENCY = FREQUENCY_RESOLUTION * DATA_XY_DIM / 2.f; // [MHz] + const float HIGHPASS_FREQENCY = MAX_FREQUENCY * 0.8f; // [MHz] + const float LOWPASS_FREQENCY = MAX_FREQUENCY * 0.1f; // [MHz] + const float ALPHA = 0; // 0 = box, 1 = von Hann; changing this may make the test invalid + const float EPSILON_FFT = 0.00001f; + +public: + + void setUp() override + { + m_BandpassFilter = mitk::BandpassFilter::New(); + } + + void test(float HighPass, float LowPass, float HighPassAlpha, float LowPassAlpha, bool useLow, bool useHigh) + { + std::random_device r; + std::default_random_engine randGen(r()); + std::uniform_real_distribution randDistrHighPass(HighPass * 0.01f, HighPass * 0.2f); + std::uniform_real_distribution randDistrLowPass(LowPass * 1.5f, LowPass * 2.f); + float* data = new float[DATA_XY_DIM*DATA_XY_DIM*DATA_Z_DIM]; + + mitk::Image::Pointer inputImage = mitk::Image::New(); + unsigned int dimension[3]{ DATA_XY_DIM, DATA_XY_DIM, DATA_Z_DIM }; + inputImage->Initialize(mitk::MakeScalarPixelType(), 3, dimension); + mitk::Vector3D spacing; + spacing[0] = 1; + spacing[1] = TIME_SPACING; + spacing[2] = 1; + inputImage->SetSpacing(spacing); + + for (unsigned int iteration = 0; iteration < NUM_ITERATIONS; ++iteration) + { + // fill image with zeros + for (unsigned int i = 0; i < DATA_XY_DIM*DATA_XY_DIM*DATA_Z_DIM; ++i) + { + data[i] = 0; + } + + // write specific frequencies to the image + if (useHigh) + addFrequency(randDistrHighPass(randGen), TIME_SPACING, data, dimension); + if (useLow) + addFrequency(randDistrLowPass(randGen), TIME_SPACING, data, dimension); + + inputImage->SetImportVolume(data, 0, 0, mitk::Image::ImportMemoryManagementType::CopyMemory); + + m_BandpassFilter->SetInput(inputImage); + + if (!useHigh) + HighPass = 0; + m_BandpassFilter->SetHighPass(HighPass); + + if(!useLow) + LowPass = MAX_FREQUENCY; + m_BandpassFilter->SetLowPass(LowPass); + + m_BandpassFilter->SetHighPassAlpha(HighPassAlpha); + m_BandpassFilter->SetLowPassAlpha(LowPassAlpha); + + m_BandpassFilter->Update(); + mitk::Image::Pointer outputImage = m_BandpassFilter->GetOutput(); + + // do a fourier transform, and check whether the part of the image that has been filtered is zero + typedef itk::Image< float, 3 > RealImageType; + RealImageType::Pointer image; + + mitk::CastToItkImage(outputImage, image); + + typedef itk::FFT1DRealToComplexConjugateImageFilter ForwardFFTFilterType; + // typedef ForwardFFTFilterType::OutputImageType ComplexImageType; + ForwardFFTFilterType::Pointer forwardFFTFilter = ForwardFFTFilterType::New(); + forwardFFTFilter->SetInput(image); + forwardFFTFilter->SetDirection(1); + forwardFFTFilter->UpdateOutputInformation(); + forwardFFTFilter->Update(); + + auto fftResult = forwardFFTFilter->GetOutput(); + + // the resulting image should consist only of zeros, as we filtered the frequencies out + for (unsigned int z = 0; z < DATA_Z_DIM; ++z) + { + for (unsigned int y = 0; y < DATA_XY_DIM / 2; ++y) + { + if (y < (unsigned int)std::floor(HighPass / FREQUENCY_RESOLUTION) || y > (unsigned int)std::ceil(LowPass / FREQUENCY_RESOLUTION)) + { + for (unsigned int x = 0; x < DATA_XY_DIM; ++x) + { + // unsigned int outPos = x + y * DATA_XY_DIM + z * DATA_XY_DIM * DATA_XY_DIM; + std::complex value = fftResult->GetPixel({ x,y,z }); + CPPUNIT_ASSERT_MESSAGE(std::string("Expected 0, got (" + std::to_string(value.real()) + " + " + std::to_string(value.imag()) + "i) at " + std::to_string(x)+"-"+std::to_string(y)+"-"+std::to_string(z)), + (abs(value.real()) < EPSILON_FFT) && (abs(value.imag() < EPSILON_FFT))); + } + } + } + + for (unsigned int y = DATA_XY_DIM / 2; y < DATA_XY_DIM; ++y) + { + if (y > DATA_XY_DIM - (unsigned int)std::floor(HighPass / FREQUENCY_RESOLUTION) || y < DATA_XY_DIM - (unsigned int)std::ceil(LowPass / FREQUENCY_RESOLUTION)) + { + for (unsigned int x = 0; x < DATA_XY_DIM; ++x) + { + // unsigned int outPos = x + y * DATA_XY_DIM + z * DATA_XY_DIM * DATA_XY_DIM; + std::complex value = fftResult->GetPixel({ x,y,z }); + CPPUNIT_ASSERT_MESSAGE(std::string("Expected 0, got (" + std::to_string(value.real()) + " + " + std::to_string(value.imag()) + "i) at " + std::to_string(x) + "-" + std::to_string(y) + "-" + std::to_string(z)), + (abs(value.real()) < EPSILON_FFT) && (abs(value.imag() < EPSILON_FFT))); + } + } + } + } + } + + delete[] data; + } + + // write a fixed-frequency signal to the image + void addFrequency(float freq, float timeSpacing, float* data, unsigned int* dim) + { + for (unsigned int z = 0; z < dim[2]; ++z) + { + for (unsigned int y = 0; y < dim[1]; ++y) + { + for (unsigned int x = 0; x < dim[0]; ++x) + { + data[x + y*dim[0] + z*dim[0] * dim[1]] += std::sin(freq * timeSpacing * y); + } + } + } + } + + void testHighPass() + { + MITK_INFO << "Performing HighPass test"; + test(HIGHPASS_FREQENCY, 0, ALPHA, ALPHA, false, true); + } + + void testLowPass() + { + MITK_INFO << "Performing LowPass test"; + test(0, LOWPASS_FREQENCY, ALPHA, ALPHA, true, false); + } + + void tearDown() override + { + m_BandpassFilter = nullptr; + } +}; + +MITK_TEST_SUITE_REGISTRATION(mitkBandpassFilter) diff --git a/Modules/PhotoacousticsAlgorithms/test/mitkBeamformingFilterTest.cpp b/Modules/PhotoacousticsAlgorithms/test/mitkBeamformingFilterTest.cpp new file mode 100644 index 0000000000..8258e991e2 --- /dev/null +++ b/Modules/PhotoacousticsAlgorithms/test/mitkBeamformingFilterTest.cpp @@ -0,0 +1,280 @@ +/*=================================================================== + +The Medical Imaging Interaction Toolkit (MITK) + +Copyright (c) German Cancer Research Center, +Division of Medical and Biological Informatics. +All rights reserved. + +This software is distributed WITHOUT ANY WARRANTY; without +even the implied warranty of MERCHANTABILITY or FITNESS FOR +A PARTICULAR PURPOSE. + +See LICENSE.txt or http://www.mitk.org for details. + +===================================================================*/ + +#include +#include +#include +#include +#include +#include +#include +#include + +class SyntheticPAImageData +{ +public: + SyntheticPAImageData(float spacing_x, float spacing_y, unsigned int samples, unsigned int num_transducers, float speedOfSound) + { + m_Spacing_x = spacing_x; + m_Spacing_y = spacing_y; + m_TransducerElements = num_transducers; + m_Samples = samples; + m_SpeedOfSound = speedOfSound; + m_Data = new float[m_Samples*m_TransducerElements]; + + for (size_t i = 0; i < m_Samples * m_TransducerElements; ++i) + { + m_Data[i] = 0; + } + } + + ~SyntheticPAImageData() + { + delete[] m_Data; + } + + void AddWave(float origin_depth, float origin_x, float base_value= 10000) + { + AddLine(origin_depth, origin_x, base_value); + AddLine(origin_depth + 0.0001f, origin_x, -base_value); + } + + const float* GetData() + { + return (const float*)m_Data; + } + +private: + void AddLine(float origin_depth, unsigned int origin_x, float base_value = 10000) + { + for (unsigned int x = 0; x < m_TransducerElements; ++x) + { + float distance = std::abs((int)x - (int)origin_x); + float delay_in_seconds = std::sqrt(std::pow(origin_depth, 2) + std::pow(distance*m_Spacing_x, 2)) / m_SpeedOfSound; + int pixels = std::round(delay_in_seconds / m_Spacing_y); + + for (int index = -4; index < 9; ++index) + { + if ((int)pixels + index < (int)m_Samples && (int)pixels + index > 0) + { + m_Data[(size_t)(x + (pixels + index)*m_TransducerElements)] += base_value / std::sqrt(distance + 1); + } + } + } + } + + float* m_Data; + float m_Spacing_x; + float m_Spacing_y; + unsigned int m_TransducerElements; + unsigned int m_Samples; + float m_SpeedOfSound; +}; + +class mitkBeamformingFilterTestSuite : public mitk::TestFixture +{ + CPPUNIT_TEST_SUITE(mitkBeamformingFilterTestSuite); + MITK_TEST(testBeamformingCPU_DAS); + MITK_TEST(testBeamformingGPU_DAS); + MITK_TEST(testBeamformingCPU_DMAS); + MITK_TEST(testBeamformingGPU_DMAS); + MITK_TEST(testBeamformingCPU_sDMAS); + MITK_TEST(testBeamformingGPU_sDMAS); + CPPUNIT_TEST_SUITE_END(); + +private: + + mitk::BeamformingFilter::Pointer m_BeamformingFilter; + const unsigned int NUM_ITERATIONS = 15; + const unsigned int SAMPLES = 5000 * 2; + const unsigned int RECONSTRUCTED_SAMPLES = 2048; + const unsigned int ELEMENTS = 128; + const unsigned int RECONSTRUCTED_LINES = 128; + const float SPEED_OF_SOUND = 1540; // m/s + const float SPACING_X = 0.3; // mm + const float SPACING_Y = 0.00625 / 2; // us + const unsigned int GPU_BATCH_SIZE = 16; + +public: + + void setUp() override + { + + } + + void test() + { + std::random_device r; + std::default_random_engine randGen(r()); + float maxDepthInMeters = SAMPLES * (SPACING_Y / 1000000) * SPEED_OF_SOUND / 2; + std::uniform_real_distribution randDistrDepth(maxDepthInMeters*0.1, maxDepthInMeters*0.8); + + for (unsigned int iteration = 0; iteration < NUM_ITERATIONS; ++iteration) + { + // create some synthetic input data + float depth_in_meters1 = randDistrDepth(randGen); + float depth_in_meters2 = randDistrDepth(randGen); + float depth_in_meters3 = randDistrDepth(randGen); + + unsigned int element1 = 29; + unsigned int element2 = 63; + unsigned int element3 = 98; + + SyntheticPAImageData image(SPACING_X / 1000.f, SPACING_Y / 1000000.f, SAMPLES, ELEMENTS, SPEED_OF_SOUND); + image.AddWave(depth_in_meters1, 29); + image.AddWave(depth_in_meters2, 63); + image.AddWave(depth_in_meters3, 98); + + mitk::Image::Pointer inputImage = mitk::Image::New(); + unsigned int dimension[3]{ ELEMENTS, SAMPLES, 1 }; + inputImage->Initialize(mitk::MakeScalarPixelType(), 3, dimension); + mitk::Vector3D spacing; + spacing[0] = SPACING_X; + spacing[1] = SPACING_Y; + spacing[2] = 1; + inputImage->SetSpacing(spacing); + inputImage->SetImportVolume((const void*)image.GetData(), mitk::Image::CopyMemory); + + // setup the beamforming filter + m_BeamformingFilter->SetInput(inputImage); + m_BeamformingFilter->Update(); + + mitk::Image::Pointer outputImage = m_BeamformingFilter->GetOutput(); + mitk::ImageReadAccessor readAccess(outputImage); + const float* outputData = (const float*)readAccess.GetData(); + + unsigned int pos1[3] = { element1, (unsigned int)std::round(depth_in_meters1 * 1000.f / outputImage->GetGeometry()->GetSpacing()[1]), 0 }; + unsigned int pos2[3] = { element2, (unsigned int)std::round(depth_in_meters2 * 1000.f / outputImage->GetGeometry()->GetSpacing()[1]), 0 }; + unsigned int pos3[3] = { element3, (unsigned int)std::round(depth_in_meters3 * 1000.f / outputImage->GetGeometry()->GetSpacing()[1]), 0 }; + + double average = 0; + + for (unsigned int i = 0; i < RECONSTRUCTED_LINES*RECONSTRUCTED_SAMPLES; ++i) + { + average += outputData[i] / (RECONSTRUCTED_LINES*RECONSTRUCTED_SAMPLES); + } + + CPPUNIT_ASSERT_MESSAGE(std::string("Iteration " + std::to_string(iteration) + ": first point source incorrectly reconstructed; should be > average*100, is " + + std::to_string(abs(outputData[pos1[0] + pos1[1] * RECONSTRUCTED_LINES]))) + " < " + std::to_string(average) + "*100" + , abs(outputData[pos1[0] + pos1[1]*RECONSTRUCTED_LINES] / average) > 100); + CPPUNIT_ASSERT_MESSAGE(std::string("Iteration " + std::to_string(iteration) + ": second point source incorrectly reconstructed; should be > average*100, is " + + std::to_string(abs(outputData[pos2[0] + pos2[1] * RECONSTRUCTED_LINES]))) + " < " + std::to_string(average) + "*100" + , abs(outputData[pos2[0] + pos2[1] * RECONSTRUCTED_LINES] / average) > 100); + CPPUNIT_ASSERT_MESSAGE(std::string("Iteration " + std::to_string(iteration) + ": third point source incorrectly reconstructed; should be > average*100, is " + + std::to_string(abs(outputData[pos3[0] + pos3[1] * RECONSTRUCTED_LINES]))) + " < " + std::to_string(average) + "*100" + , abs(outputData[pos3[0] + pos3[1] * RECONSTRUCTED_LINES] / average) > 100); + } + } + + mitk::BeamformingSettings::Pointer createConfig(bool UseGPU, unsigned int* inputDim, mitk::BeamformingSettings::BeamformingAlgorithm alg) + { + return mitk::BeamformingSettings::New(SPACING_X / 1000, + SPEED_OF_SOUND, + SPACING_Y / 1000000, + 27.f, + true, + RECONSTRUCTED_SAMPLES, + RECONSTRUCTED_LINES, + inputDim, + SPEED_OF_SOUND * (SPACING_Y / 1000000) * SAMPLES, + UseGPU, + GPU_BATCH_SIZE, + mitk::BeamformingSettings::DelayCalc::Spherical, + mitk::BeamformingSettings::Apodization::Box, + ELEMENTS * 2, + alg); + } + + void testBeamformingCPU_DAS() + { + MITK_INFO << "Started DAS test on CPU"; + unsigned int* inputDim = new unsigned int[3]; + inputDim[0] = ELEMENTS; + inputDim[1] = SAMPLES; + inputDim[2] = 1; + m_BeamformingFilter = mitk::BeamformingFilter::New(createConfig(false, inputDim, mitk::BeamformingSettings::BeamformingAlgorithm::DAS)); + + test(); + delete[] inputDim; + } + + void testBeamformingGPU_DAS() + { + MITK_INFO << "Started DAS test on GPU"; + unsigned int* inputDim = new unsigned int[3]; + inputDim[0] = ELEMENTS; + inputDim[1] = SAMPLES; + inputDim[2] = 1; + m_BeamformingFilter = mitk::BeamformingFilter::New(createConfig(true, inputDim, mitk::BeamformingSettings::BeamformingAlgorithm::DAS)); + + test(); + delete[] inputDim; + } + + void testBeamformingCPU_sDMAS() + { + MITK_INFO << "Started sDMAS test on CPU"; + unsigned int* inputDim = new unsigned int[3]; + inputDim[0] = ELEMENTS; + inputDim[1] = SAMPLES; + inputDim[2] = 1; + m_BeamformingFilter = mitk::BeamformingFilter::New(createConfig(false, inputDim, mitk::BeamformingSettings::BeamformingAlgorithm::sDMAS)); + + test(); + delete[] inputDim; + } + + void testBeamformingGPU_sDMAS() + { + MITK_INFO << "Started sDMAS test on GPU"; + unsigned int* inputDim = new unsigned int[3]; + inputDim[0] = ELEMENTS; + inputDim[1] = SAMPLES; + inputDim[2] = 1; + m_BeamformingFilter = mitk::BeamformingFilter::New(createConfig(true, inputDim, mitk::BeamformingSettings::BeamformingAlgorithm::sDMAS)); + + test(); + delete[] inputDim; + } + + void testBeamformingCPU_DMAS() + { + MITK_INFO << "Started DMAS test on CPU"; + unsigned int* inputDim = new unsigned int[3]; + inputDim[0] = ELEMENTS; + inputDim[1] = SAMPLES; + inputDim[2] = 1; + m_BeamformingFilter = mitk::BeamformingFilter::New(createConfig(false, inputDim, mitk::BeamformingSettings::BeamformingAlgorithm::DMAS)); + + test(); + delete[] inputDim; + } + + void testBeamformingGPU_DMAS() + { + MITK_INFO << "Started DMAS test on GPU"; + unsigned int* inputDim = new unsigned int[3]; + inputDim[0] = ELEMENTS; + inputDim[1] = SAMPLES; + inputDim[2] = 1; + m_BeamformingFilter = mitk::BeamformingFilter::New(createConfig(true, inputDim, mitk::BeamformingSettings::BeamformingAlgorithm::DMAS)); + + test(); + delete[] inputDim; + } +}; + +MITK_TEST_SUITE_REGISTRATION(mitkBeamformingFilter) diff --git a/Modules/PhotoacousticsAlgorithms/test/mitkCastToFloatImageFilterTest.cpp b/Modules/PhotoacousticsAlgorithms/test/mitkCastToFloatImageFilterTest.cpp new file mode 100644 index 0000000000..ab43018f02 --- /dev/null +++ b/Modules/PhotoacousticsAlgorithms/test/mitkCastToFloatImageFilterTest.cpp @@ -0,0 +1,108 @@ +/*=================================================================== + +The Medical Imaging Interaction Toolkit (MITK) + +Copyright (c) German Cancer Research Center, +Division of Medical and Biological Informatics. +All rights reserved. + +This software is distributed WITHOUT ANY WARRANTY; without +even the implied warranty of MERCHANTABILITY or FITNESS FOR +A PARTICULAR PURPOSE. + +See LICENSE.txt or http://www.mitk.org for details. + +===================================================================*/ + +#include +#include +#include +#include +#include + +class mitkCastToFloatImageFilterTestSuite : public mitk::TestFixture +{ + CPPUNIT_TEST_SUITE(mitkCastToFloatImageFilterTestSuite); + MITK_TEST(testShortConversion); + MITK_TEST(testIntConversion); + MITK_TEST(testLongConversion); + MITK_TEST(testFloatConversion); + MITK_TEST(testDoubleConversion); + CPPUNIT_TEST_SUITE_END(); + +private: + + mitk::CastToFloatImageFilter::Pointer m_CastToFloatImageFilter; + const unsigned int NUM_ITERATIONS = 15; + const unsigned int DATA_DIM = 10; + +public: + + void setUp() override + { + m_CastToFloatImageFilter = mitk::CastToFloatImageFilter::New(); + } + + template + void test() + { + TPixelType* data = new TPixelType[DATA_DIM*DATA_DIM*DATA_DIM]; + + for (unsigned int i = 0; i < DATA_DIM*DATA_DIM*DATA_DIM; ++i) + { + data[i] = (TPixelType)i; + } + + mitk::Image::Pointer inputImage = mitk::Image::New(); + unsigned int dimension[3]{ DATA_DIM, DATA_DIM, DATA_DIM }; + inputImage->Initialize(mitk::MakeScalarPixelType(), 3, dimension); + inputImage->SetImportVolume(data); + + for (unsigned int iteration = 0; iteration < NUM_ITERATIONS; ++iteration) + { + m_CastToFloatImageFilter->SetInput(inputImage); + m_CastToFloatImageFilter->Update(); + mitk::Image::Pointer outputImage = m_CastToFloatImageFilter->GetOutput(); + mitk::ImageReadAccessor readAccess(outputImage); + const float* outputData = (const float*)readAccess.GetData(); + for (unsigned int i = 0; i < DATA_DIM*DATA_DIM*DATA_DIM; ++i) + { + CPPUNIT_ASSERT_MESSAGE(std::string("expected " + std::to_string(data[i]) + " but was " + std::to_string(outputData[i])), abs(outputData[i] - data[i]) < mitk::eps); + } + } + } + + void testFloatConversion() + { + test(); + } + + void testShortConversion() + { + test(); + test(); + } + + void testIntConversion() + { + test(); + test(); + } + + void testDoubleConversion() + { + test(); + } + + void testLongConversion() + { + test(); + } + + void tearDown() override + { + m_CastToFloatImageFilter = nullptr; + } +}; + +MITK_TEST_SUITE_REGISTRATION(mitkCastToFloatImageFilter) diff --git a/Modules/PhotoacousticsAlgorithms/test/mitkCropImageFilterTest.cpp b/Modules/PhotoacousticsAlgorithms/test/mitkCropImageFilterTest.cpp new file mode 100644 index 0000000000..808382eb97 --- /dev/null +++ b/Modules/PhotoacousticsAlgorithms/test/mitkCropImageFilterTest.cpp @@ -0,0 +1,127 @@ +/*=================================================================== + +The Medical Imaging Interaction Toolkit (MITK) + +Copyright (c) German Cancer Research Center, +Division of Medical and Biological Informatics. +All rights reserved. + +This software is distributed WITHOUT ANY WARRANTY; without +even the implied warranty of MERCHANTABILITY or FITNESS FOR +A PARTICULAR PURPOSE. + +See LICENSE.txt or http://www.mitk.org for details. + +===================================================================*/ + +#include +#include +#include +#include +#include +#include + +class mitkCropImageFilterTestSuite : public mitk::TestFixture +{ + CPPUNIT_TEST_SUITE(mitkCropImageFilterTestSuite); + MITK_TEST(testCropImage); + CPPUNIT_TEST_SUITE_END(); + +private: + + mitk::CropImageFilter::Pointer m_CropImageFilter; + const unsigned int NUM_ITERATIONS = 5; + const unsigned int DATA_DIM = 25; + +public: + + void setUp() override + { + m_CropImageFilter = mitk::CropImageFilter::New(); + } + + void test() + { + std::random_device r; + std::default_random_engine randGen(r()); + std::uniform_int_distribution randDistr(0, (DATA_DIM / 2) - 1); + + float* data = new float[DATA_DIM*DATA_DIM*DATA_DIM]; + + for (unsigned int i = 0; i < DATA_DIM*DATA_DIM*DATA_DIM; ++i) + { + data[i] = (float)i; + } + + mitk::Image::Pointer inputImage = mitk::Image::New(); + unsigned int dimension[3]{ DATA_DIM, DATA_DIM, DATA_DIM }; + inputImage->Initialize(mitk::MakeScalarPixelType(), 3, dimension); + inputImage->SetImportVolume(data, 0, 0, mitk::Image::ImportMemoryManagementType::CopyMemory); + + for (unsigned int iteration = 0; iteration < NUM_ITERATIONS; ++iteration) + { + unsigned int XPixelsCropStart = randDistr(randGen); + unsigned int YPixelsCropStart = randDistr(randGen); + unsigned int ZPixelsCropStart = randDistr(randGen); + unsigned int XPixelsCropEnd = randDistr(randGen); + unsigned int YPixelsCropEnd = randDistr(randGen); + unsigned int ZPixelsCropEnd = randDistr(randGen); + + unsigned int newXDim = DATA_DIM - XPixelsCropStart - XPixelsCropEnd; + unsigned int newYDim = DATA_DIM - YPixelsCropStart - YPixelsCropEnd; + unsigned int newZDim = DATA_DIM - ZPixelsCropStart - ZPixelsCropEnd; + + m_CropImageFilter->SetInput(inputImage); + + m_CropImageFilter->SetXPixelsCropStart(XPixelsCropStart); + m_CropImageFilter->SetYPixelsCropStart(YPixelsCropStart); + m_CropImageFilter->SetZPixelsCropStart(ZPixelsCropStart); + m_CropImageFilter->SetXPixelsCropEnd(XPixelsCropEnd); + m_CropImageFilter->SetYPixelsCropEnd(YPixelsCropEnd); + m_CropImageFilter->SetZPixelsCropEnd(ZPixelsCropEnd); + + m_CropImageFilter->Update(); + mitk::Image::Pointer outputImage = m_CropImageFilter->GetOutput(); + + mitk::ImageReadAccessor readAccess(outputImage); + const float* outputData = (const float*)readAccess.GetData(); + + CPPUNIT_ASSERT_MESSAGE(std::string("expected x size to be " + std::to_string(newXDim) + " but was " + std::to_string(outputImage->GetDimension(0))), newXDim == outputImage->GetDimension(0)); + CPPUNIT_ASSERT_MESSAGE(std::string("expected y size to be " + std::to_string(newYDim) + " but was " + std::to_string(outputImage->GetDimension(1))), newYDim == outputImage->GetDimension(1)); + CPPUNIT_ASSERT_MESSAGE(std::string("expected z size to be " + std::to_string(newZDim) + " but was " + std::to_string(outputImage->GetDimension(2))), newZDim == outputImage->GetDimension(2)); + + for (unsigned int z = 0; z < newZDim; ++z) + { + for (unsigned int y = 0; y < newYDim; ++y) + { + for (unsigned int x = 0; x < newXDim; ++x) + { + unsigned int origPos = (x + XPixelsCropStart) + (y + YPixelsCropStart) * DATA_DIM + (z + ZPixelsCropStart) * DATA_DIM * DATA_DIM; + unsigned int outPos = x + y * newXDim + z * newXDim * newYDim; + CPPUNIT_ASSERT_MESSAGE(std::string("expected " + std::to_string(data[origPos]) + + " but was " + std::to_string(outputData[outPos])), + abs((float)outputData[outPos] - (float)data[origPos]) < mitk::eps); + } + } + } + } + + delete[] data; + } + + void testCropImage() + { + for (int repetition = 0; repetition < 20; ++repetition) + { + MITK_INFO << "[" << (repetition + 1) << "/20]"; + test(); + } + } + + void tearDown() override + { + m_CropImageFilter = nullptr; + } +}; + +MITK_TEST_SUITE_REGISTRATION(mitkCropImageFilter) diff --git a/Modules/PhotoacousticsAlgorithms/test/mitkPAFilterServiceTest.cpp b/Modules/PhotoacousticsAlgorithms/test/mitkPAFilterServiceTest.cpp new file mode 100644 index 0000000000..70d0dd7f1b --- /dev/null +++ b/Modules/PhotoacousticsAlgorithms/test/mitkPAFilterServiceTest.cpp @@ -0,0 +1,123 @@ +/*=================================================================== + +The Medical Imaging Interaction Toolkit (MITK) + +Copyright (c) German Cancer Research Center, +Division of Medical and Biological Informatics. +All rights reserved. + +This software is distributed WITHOUT ANY WARRANTY; without +even the implied warranty of MERCHANTABILITY or FITNESS FOR +A PARTICULAR PURPOSE. + +See LICENSE.txt or http://www.mitk.org for details. + +===================================================================*/ + +#include +#include + +#include +#include +#include +#include + +// us +#include +#include +#include +#include +#include + +#include +#include + +class mitkPAFilterServiceTestSuite : public mitk::TestFixture +{ + CPPUNIT_TEST_SUITE(mitkPAFilterServiceTestSuite); + MITK_TEST(testRunning); + CPPUNIT_TEST_SUITE_END(); + +private: + + mitk::PhotoacousticFilterService::Pointer m_PhotoacousticFilterService; + mitk::BeamformingSettings::Pointer m_BeamformingSettings; + unsigned int* inputDimensions; + const int xDim = 16; + const int yDim = 128; + const int length = yDim * xDim; + +public: + + void setUp() override + { + m_PhotoacousticFilterService = mitk::PhotoacousticFilterService::New(); + m_BeamformingSettings = CreateBeamformingSettings(); + } + + mitk::BeamformingSettings::Pointer CreateBeamformingSettings() + { + inputDimensions = new unsigned int[length]; + inputDimensions[0] = yDim; + inputDimensions[1] = xDim; + mitk::BeamformingSettings::Pointer outputSettings = mitk::BeamformingSettings::New( + (float)(0.3 / 1000), + (float)(1500), + (float)(0.0125 / 1000000), + 27, + true, + 3000, + 128, + inputDimensions, + yDim * (0.0125 / 1000000) * (1500), + false, + 16, + mitk::BeamformingSettings::DelayCalc::Spherical, + mitk::BeamformingSettings::Apodization::Box, + 128, + mitk::BeamformingSettings::BeamformingAlgorithm::DAS); + + return outputSettings; + } + + void testRunning() + { + float* testArray = new float[length]; + for (int i = 0; i < length; ++i) + { + testArray[i] = 0; + } + + mitk::PixelType pixelType = mitk::MakeScalarPixelType(); + mitk::Image::Pointer testImage = mitk::Image::New(); + testImage->Initialize(pixelType, 2, inputDimensions); + testImage->SetImportSlice(testArray, 0, 0, 0, mitk::Image::ImportMemoryManagementType::CopyMemory); + delete[] testArray; + + mitk::ImageReadAccessor readAccessInput(testImage); + const float* inputArray = (const float*)readAccessInput.GetData(); + + for (int i = 0; i < length; ++i) + { + CPPUNIT_ASSERT_MESSAGE(std::string("Input array already not correct: " + std::to_string(inputArray[i])), abs(inputArray[i]) < 1e-5f); + } + + auto output = m_PhotoacousticFilterService->ApplyBeamforming(testImage, m_BeamformingSettings); + + mitk::ImageReadAccessor readAccess(output); + const float* outputArray = (const float*)readAccess.GetData(); + + for (int i = 0; i < length; ++i) + { + CPPUNIT_ASSERT_MESSAGE(std::string("Output array not correct: " + std::to_string(abs(outputArray[i]))), abs(outputArray[i]) < 1e-5f); + } + } + + void tearDown() override + { + m_PhotoacousticFilterService = nullptr; + delete[] inputDimensions; + } +}; + +MITK_TEST_SUITE_REGISTRATION(mitkPAFilterService) diff --git a/Modules/PhotoacousticsLib/CMakeLists.txt b/Modules/PhotoacousticsLib/CMakeLists.txt index 8c89b2658e..0ae41f8f5c 100644 --- a/Modules/PhotoacousticsLib/CMakeLists.txt +++ b/Modules/PhotoacousticsLib/CMakeLists.txt @@ -1,12 +1,15 @@ MITK_CREATE_MODULE( INCLUDE_DIRS PUBLIC include INTERNAL_INCLUDE_DIRS ${INCLUDE_DIRS_INTERNAL} DEPENDS PUBLIC MitkAlgorithmsExt tinyxml PACKAGE_DEPENDS tinyxml PUBLIC ITK ) add_subdirectory(MitkMCxyz) +add_subdirectory(MitkTissueBatchGenerator) +add_subdirectory(MitkPAPhantomGenerator) + add_subdirectory(test) diff --git a/Modules/PhotoacousticsLib/MitkMCxyz/MitkMCxyz.cpp b/Modules/PhotoacousticsLib/MitkMCxyz/MitkMCxyz.cpp index a557042cb0..6a375706a9 100644 --- a/Modules/PhotoacousticsLib/MitkMCxyz/MitkMCxyz.cpp +++ b/Modules/PhotoacousticsLib/MitkMCxyz/MitkMCxyz.cpp @@ -1,1472 +1,1474 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ // Please retain the following copyright notice /****************************************************************** * based on mcxyz.c Oct2014 * * mcxyz.c, in ANSI Standard C programing language * * created 2010, 2012 by * Steven L. JACQUES * Ting LI * Oregon Health & Science University * *******************************************************************/ #include #include #include #include #include #include #include #include #include #include #include "mitkCommandLineParser.h" #include "mitkIOUtil.h" #include "mitkImageCast.h" #include #include #include #include #include #include #include #include #ifdef _WIN32 #include #else #include #include #endif #define ls 1.0E-7 /* Moving photon a little bit off the voxel face */ #define PI 3.1415926 #define ALIVE 1 /* if photon not yet terminated */ #define DEAD 0 /* if photon is to be terminated */ #define THRESHOLD 0.01 /* used in roulette */ #define CHANCE 0.1 /* used in roulette */ #define SQR(x) (x*x) #define SIGN(x) ((x)>=0 ? 1:-1) #define ONE_MINUS_COSZERO 1.0E-12 /* If 1-cos(theta) <= ONE_MINUS_COSZERO, fabs(theta) <= 1e-6 rad. */ /* If 1+cos(theta) <= ONE_MINUS_COSZERO, fabs(PI-theta) <= 1e-6 rad. */ /* Struct for storing x,y and z coordinates */ struct Location { int x, y, z; double absorb; }; struct Location initLocation(int x, int y, int z, double absorb) { struct Location loc; loc.x = x; loc.y = y; loc.z = z; loc.absorb = absorb; return loc; } class DetectorVoxel { public: Location location; std::vector* recordedPhotonRoute = new std::vector(); double* fluenceContribution; double m_PhotonNormalizationValue; long m_NumberPhotonsCurrent; DetectorVoxel(Location location, long totalNumberOfVoxels, double photonNormalizationValue) { this->location = location; this->fluenceContribution = (double *)malloc(totalNumberOfVoxels * sizeof(double)); for (int j = 0; j < totalNumberOfVoxels; j++) fluenceContribution[j] = 0; // ensure fluenceContribution[] starts empty. m_NumberPhotonsCurrent = 0; m_PhotonNormalizationValue = photonNormalizationValue; } }; bool verbose(false); class InputValues { private: std::string inputFilename; int tissueIterator; long long ix, iy, iz; public: int mcflag, launchflag, boundaryflag; double xfocus, yfocus, zfocus; double ux0, uy0, uz0; double radius; double waist; double xs, ys, zs; /* launch position */ int Nx, Ny, Nz, numberOfTissueTypes; /* # of bins */ char* tissueType; double* muaVector; double* musVector; double* gVector; double* normalizationVector; double xSpacing, ySpacing, zSpacing; double simulationTimeFromFile; long long Nphotons; long totalNumberOfVoxels; double* totalFluence; std::string myname; DetectorVoxel* detectorVoxel; mitk::Image::Pointer m_inputImage; mitk::Image::Pointer m_normalizationImage; InputValues() { tissueType = nullptr; muaVector = nullptr; musVector = nullptr; gVector = nullptr; detectorVoxel = nullptr; normalizationVector = nullptr; mcflag = 0; launchflag = 0; boundaryflag = 0; } double GetNormalizationValue(int x, int y, int z) { if (normalizationVector) return normalizationVector[z*Ny*Nx + x*Ny + y]; else return 1; } void LoadValues(std::string localInputFilename, float yOffset, std::string normalizationFilename, bool simulatePVFC) { inputFilename = localInputFilename; try { + if (verbose) std::cout << "Loading input image..." << std::endl; m_inputImage = mitk::IOUtil::Load(inputFilename); + if (verbose) std::cout << "Loading input image... [Done]" << std::endl; } catch (...) { if (verbose) std::cout << "No .nrrd file found ... switching to legacy mode." << std::endl; } try { if (simulatePVFC && !normalizationFilename.empty()) m_normalizationImage = mitk::IOUtil::Load(normalizationFilename); } catch (...) { if (verbose) std::cout << "No normalization .nrrd file found ... will not normalize PVFC" << std::endl; } if (m_normalizationImage.IsNotNull()) { mitk::ImageReadAccessor readAccess3(m_normalizationImage, m_normalizationImage->GetVolumeData(0)); normalizationVector = (double *)readAccess3.GetData(); } if (m_inputImage.IsNotNull()) // load stuff from nrrd file { simulationTimeFromFile = 0; Nx = m_inputImage->GetDimensions()[1]; Ny = m_inputImage->GetDimensions()[0]; Nz = m_inputImage->GetDimensions()[2]; xSpacing = m_inputImage->GetGeometry(0)->GetSpacing()[0]; ySpacing = m_inputImage->GetGeometry(0)->GetSpacing()[1]; zSpacing = m_inputImage->GetGeometry(0)->GetSpacing()[2]; mcflag = std::stoi(m_inputImage->GetProperty("mcflag")->GetValueAsString().c_str()); // mcflag, 0 = uniform, 1 = Gaussian, 2 = iso-pt, 4 = monospectral fraunhofer setup launchflag = std::stoi(m_inputImage->GetProperty("launchflag")->GetValueAsString().c_str());// 0 = let mcxyz calculate trajectory, 1 = manually set launch vector boundaryflag = std::stoi(m_inputImage->GetProperty("boundaryflag")->GetValueAsString().c_str());// 0 = no boundaries, 1 = escape at boundaries, 2 = escape at surface only xs = std::stod(m_inputImage->GetProperty("launchPointX")->GetValueAsString().c_str()); ys = std::stod(m_inputImage->GetProperty("launchPointY")->GetValueAsString().c_str()) + yOffset; zs = std::stod(m_inputImage->GetProperty("launchPointZ")->GetValueAsString().c_str()); xfocus = std::stod(m_inputImage->GetProperty("focusPointX")->GetValueAsString().c_str()); yfocus = std::stod(m_inputImage->GetProperty("focusPointY")->GetValueAsString().c_str()); zfocus = std::stod(m_inputImage->GetProperty("focusPointZ")->GetValueAsString().c_str()); ux0 = std::stod(m_inputImage->GetProperty("trajectoryVectorX")->GetValueAsString().c_str()); uy0 = std::stod(m_inputImage->GetProperty("trajectoryVectorY")->GetValueAsString().c_str()); uz0 = std::stod(m_inputImage->GetProperty("trajectoryVectorZ")->GetValueAsString().c_str()); radius = std::stod(m_inputImage->GetProperty("radius")->GetValueAsString().c_str()); waist = std::stod(m_inputImage->GetProperty("waist")->GetValueAsString().c_str()); totalNumberOfVoxels = Nx*Ny*Nz; if (verbose) std::cout << totalNumberOfVoxels << " = sizeof totalNumberOfVoxels" << std::endl; muaVector = (double *)malloc(totalNumberOfVoxels * sizeof(double)); /* tissue structure */ musVector = (double *)malloc(totalNumberOfVoxels * sizeof(double)); /* tissue structure */ gVector = (double *)malloc(totalNumberOfVoxels * sizeof(double)); /* tissue structure */ mitk::ImageReadAccessor readAccess0(m_inputImage, m_inputImage->GetVolumeData(0)); muaVector = (double *)readAccess0.GetData(); mitk::ImageReadAccessor readAccess1(m_inputImage, m_inputImage->GetVolumeData(1)); musVector = (double *)readAccess1.GetData(); mitk::ImageReadAccessor readAccess2(m_inputImage, m_inputImage->GetVolumeData(2)); gVector = (double *)readAccess2.GetData(); } else { mitkThrow() << "No longer support loading of binary tissue files."; } } }; class ReturnValues { private: long i1 = 0, i2 = 31; // used Random Generator long ma[56]; // used Random Generator /* ma[0] is not used. */ long mj, mk; short i, ii; public: long long Nphotons; double* totalFluence; std::string myname; DetectorVoxel* detectorVoxel; ReturnValues() { detectorVoxel = nullptr; Nphotons = 0; totalFluence = nullptr; } /* SUBROUTINES */ /************************************************************************** * RandomGen * A random number generator that generates uniformly * distributed random numbers between 0 and 1 inclusive. * The algorithm is based on: * W.H. Press, S.A. Teukolsky, W.T. Vetterling, and B.P. * Flannery, "Numerical Recipes in C," Cambridge University * Press, 2nd edition, (1992). * and * D.E. Knuth, "Seminumerical Algorithms," 2nd edition, vol. 2 * of "The Art of Computer Programming", Addison-Wesley, (1981). * * When Type is 0, sets Seed as the seed. Make sure 0 b) m = a; else m = b; return m; } /*********************************************************** * min2 ****/ double min2(double a, double b) { double m; if (a >= b) m = b; else m = a; return m; } /*********************************************************** * min3 ****/ double min3(double a, double b, double c) { double m; if (a <= min2(b, c)) m = a; else if (b <= min2(a, c)) m = b; else m = c; return m; } /******************** * my version of FindVoxelFace for no scattering. * s = ls + FindVoxelFace2(x,y,z, tempx, tempy, tempz, dx, dy, dz, ux, uy, uz); ****/ double FindVoxelFace2(double x1, double y1, double z1, double /*x2*/, double /*y2*/, double /*z2*/, double dx, double dy, double dz, double ux, double uy, double uz) { int ix1 = floor(x1 / dx); int iy1 = floor(y1 / dy); int iz1 = floor(z1 / dz); int ix2, iy2, iz2; if (ux >= 0) ix2 = ix1 + 1; else ix2 = ix1; if (uy >= 0) iy2 = iy1 + 1; else iy2 = iy1; if (uz >= 0) iz2 = iz1 + 1; else iz2 = iz1; double xs = fabs((ix2*dx - x1) / ux); double ys = fabs((iy2*dy - y1) / uy); double zs = fabs((iz2*dz - z1) / uz); double s = min3(xs, ys, zs); return s; } /*********************************************************** * FRESNEL REFLECTANCE * Computes reflectance as photon passes from medium 1 to * medium 2 with refractive indices n1,n2. Incident * angle a1 is specified by cosine value ca1 = cos(a1). * Program returns value of transmitted angle a1 as * value in *ca2_Ptr = cos(a2). ****/ double RFresnel(double n1, /* incident refractive index.*/ double n2, /* transmit refractive index.*/ double ca1, /* cosine of the incident */ /* angle a1, 00. */ { double r; if (n1 == n2) { /** matched boundary. **/ *ca2_Ptr = ca1; r = 0.0; } else if (ca1 > (1.0 - 1.0e-12)) { /** normal incidence. **/ *ca2_Ptr = ca1; r = (n2 - n1) / (n2 + n1); r *= r; } else if (ca1 < 1.0e-6) { /** very slanted. **/ *ca2_Ptr = 0.0; r = 1.0; } else { /** general. **/ double sa1, sa2; /* sine of incident and transmission angles. */ double ca2; /* cosine of transmission angle. */ sa1 = sqrt(1 - ca1*ca1); sa2 = n1*sa1 / n2; if (sa2 >= 1.0) { /* double check for total internal reflection. */ *ca2_Ptr = 0.0; r = 1.0; } else { double cap, cam; /* cosines of sum ap or diff am of the two */ /* angles: ap = a1 + a2, am = a1 - a2. */ double sap, sam; /* sines. */ *ca2_Ptr = ca2 = sqrt(1 - sa2*sa2); cap = ca1*ca2 - sa1*sa2; /* c+ = cc - ss. */ cam = ca1*ca2 + sa1*sa2; /* c- = cc + ss. */ sap = sa1*ca2 + ca1*sa2; /* s+ = sc + cs. */ sam = sa1*ca2 - ca1*sa2; /* s- = sc - cs. */ r = 0.5*sam*sam*(cam*cam + cap*cap) / (sap*sap*cam*cam); /* rearranged for speed. */ } } return(r); } /******** END SUBROUTINE **********/ /*********************************************************** * the boundary is the face of some voxel * find the the photon's hitting position on the nearest face of the voxel and update the step size. ****/ double FindVoxelFace(double x1, double y1, double z1, double x2, double y2, double z2, double dx, double dy, double dz, double ux, double uy, double uz) { double x_1 = x1 / dx; double y_1 = y1 / dy; double z_1 = z1 / dz; double x_2 = x2 / dx; double y_2 = y2 / dy; double z_2 = z2 / dz; double fx_1 = floor(x_1); double fy_1 = floor(y_1); double fz_1 = floor(z_1); double fx_2 = floor(x_2); double fy_2 = floor(y_2); double fz_2 = floor(z_2); double x = 0, y = 0, z = 0, x0 = 0, y0 = 0, z0 = 0, s = 0; if ((fx_1 != fx_2) && (fy_1 != fy_2) && (fz_1 != fz_2)) { //#10 fx_2 = fx_1 + SIGN(fx_2 - fx_1);//added fy_2 = fy_1 + SIGN(fy_2 - fy_1);//added fz_2 = fz_1 + SIGN(fz_2 - fz_1);//added x = (max2(fx_1, fx_2) - x_1) / ux; y = (max2(fy_1, fy_2) - y_1) / uy; z = (max2(fz_1, fz_2) - z_1) / uz; if (x == min3(x, y, z)) { x0 = max2(fx_1, fx_2); y0 = (x0 - x_1) / ux*uy + y_1; z0 = (x0 - x_1) / ux*uz + z_1; } else if (y == min3(x, y, z)) { y0 = max2(fy_1, fy_2); x0 = (y0 - y_1) / uy*ux + x_1; z0 = (y0 - y_1) / uy*uz + z_1; } else { z0 = max2(fz_1, fz_2); y0 = (z0 - z_1) / uz*uy + y_1; x0 = (z0 - z_1) / uz*ux + x_1; } } else if ((fx_1 != fx_2) && (fy_1 != fy_2)) { //#2 fx_2 = fx_1 + SIGN(fx_2 - fx_1);//added fy_2 = fy_1 + SIGN(fy_2 - fy_1);//added x = (max2(fx_1, fx_2) - x_1) / ux; y = (max2(fy_1, fy_2) - y_1) / uy; if (x == min2(x, y)) { x0 = max2(fx_1, fx_2); y0 = (x0 - x_1) / ux*uy + y_1; z0 = (x0 - x_1) / ux*uz + z_1; } else { y0 = max2(fy_1, fy_2); x0 = (y0 - y_1) / uy*ux + x_1; z0 = (y0 - y_1) / uy*uz + z_1; } } else if ((fy_1 != fy_2) && (fz_1 != fz_2)) { //#3 fy_2 = fy_1 + SIGN(fy_2 - fy_1);//added fz_2 = fz_1 + SIGN(fz_2 - fz_1);//added y = (max2(fy_1, fy_2) - y_1) / uy; z = (max2(fz_1, fz_2) - z_1) / uz; if (y == min2(y, z)) { y0 = max2(fy_1, fy_2); x0 = (y0 - y_1) / uy*ux + x_1; z0 = (y0 - y_1) / uy*uz + z_1; } else { z0 = max2(fz_1, fz_2); x0 = (z0 - z_1) / uz*ux + x_1; y0 = (z0 - z_1) / uz*uy + y_1; } } else if ((fx_1 != fx_2) && (fz_1 != fz_2)) { //#4 fx_2 = fx_1 + SIGN(fx_2 - fx_1);//added fz_2 = fz_1 + SIGN(fz_2 - fz_1);//added x = (max2(fx_1, fx_2) - x_1) / ux; z = (max2(fz_1, fz_2) - z_1) / uz; if (x == min2(x, z)) { x0 = max2(fx_1, fx_2); y0 = (x0 - x_1) / ux*uy + y_1; z0 = (x0 - x_1) / ux*uz + z_1; } else { z0 = max2(fz_1, fz_2); x0 = (z0 - z_1) / uz*ux + x_1; y0 = (z0 - z_1) / uz*uy + y_1; } } else if (fx_1 != fx_2) { //#5 fx_2 = fx_1 + SIGN(fx_2 - fx_1);//added x0 = max2(fx_1, fx_2); y0 = (x0 - x_1) / ux*uy + y_1; z0 = (x0 - x_1) / ux*uz + z_1; } else if (fy_1 != fy_2) { //#6 fy_2 = fy_1 + SIGN(fy_2 - fy_1);//added y0 = max2(fy_1, fy_2); x0 = (y0 - y_1) / uy*ux + x_1; z0 = (y0 - y_1) / uy*uz + z_1; } else { //#7 z0 = max2(fz_1, fz_2); fz_2 = fz_1 + SIGN(fz_2 - fz_1);//added x0 = (z0 - z_1) / uz*ux + x_1; y0 = (z0 - z_1) / uz*uy + y_1; } //s = ( (x0-fx_1)*dx + (y0-fy_1)*dy + (z0-fz_1)*dz )/3; //s = sqrt( SQR((x0-x_1)*dx) + SQR((y0-y_1)*dy) + SQR((z0-z_1)*dz) ); //s = sqrt(SQR(x0-x_1)*SQR(dx) + SQR(y0-y_1)*SQR(dy) + SQR(z0-z_1)*SQR(dz)); s = sqrt(SQR((x0 - x_1)*dx) + SQR((y0 - y_1)*dy) + SQR((z0 - z_1)*dz)); return (s); } }; /* DECLARE FUNCTIONS */ void runMonteCarlo(InputValues* inputValues, ReturnValues* returnValue, int thread, mitk::pa::MonteCarloThreadHandler::Pointer threadHandler); int detector_x = -1; int detector_z = -1; bool interpretAsTime = true; bool simulatePVFC = false; int requestedNumberOfPhotons = 100000; float requestedSimulationTime = 0; // in minutes int concurentThreadsSupported = -1; float yOffset = 0; // in mm bool saveLegacy = false; std::string normalizationFilename; std::string inputFilename; std::string outputFilename; mitk::pa::Probe::Pointer m_PhotoacousticProbe; int main(int argc, char * argv[]) { mitkCommandLineParser parser; // set general information parser.setCategory("MITK-Photoacoustics"); parser.setTitle("Mitk MCxyz"); parser.setDescription("Runs Monte Carlo simulations on inputed tissues."); parser.setContributor("CAI, DKFZ based on code by Jacques and Li"); // how should arguments be prefixed parser.setArgumentPrefix("--", "-"); // add each argument, unless specified otherwise each argument is optional // see mitkCommandLineParser::addArgument for more information parser.beginGroup("Required I/O parameters"); parser.addArgument( "input", "i", mitkCommandLineParser::InputFile, "Input tissue file", "input tissue file (*.nrrd)", us::Any(), false); parser.addArgument( "output", "o", mitkCommandLineParser::OutputFile, "Output fluence file", "where to save the simulated fluence (*.nrrd)", us::Any(), false); parser.endGroup(); parser.beginGroup("Optional parameters"); parser.addArgument( "verbose", "v", mitkCommandLineParser::Bool, "Verbose Output", "Whether to produce verbose, or rather debug output"); parser.addArgument( "detector-x", "dx", mitkCommandLineParser::Int, "Detector voxel x position", "Determines the x position of the detector voxel (default: -1 = dont use detector voxel)", -1); parser.addArgument( "detector-z", "dz", mitkCommandLineParser::Int, "Detector voxel z position", "Determines the z position of the detector voxel (default: -1 = dont use detector voxel)", -1); parser.addArgument( "number-of-photons", "n", mitkCommandLineParser::Int, "Number of photons", "Specifies the number of photons (default: 100000). Simulation stops after that number. Use -t --timer to define a timer instead"); parser.addArgument( "timer", "t", mitkCommandLineParser::Float, "Simulation time in min", "Specifies the amount of time for simutation (default: 0). Simulation stops after that number of minutes. -n --number-of-photons is the override and default behavior and defines the maximum number of photons instead. If no simulation time or number of photons is specified the file time is taken."); parser.addArgument( "y-offset", "yo", mitkCommandLineParser::Float, "Probe Y-Offset in mm", "Specifies an offset of the photoacoustic probe in the y direction depending on the initial probe position (default: 0) in mm."); parser.addArgument( "jobs", "j", mitkCommandLineParser::Int, "Number of jobs", "Specifies the number of jobs for simutation (default: -1 which starts as many jobs as supported)."); parser.addArgument( "probe-xml", "p", mitkCommandLineParser::InputFile, "Xml definition of the probe", "Specifies the absolute path of the location of the xml definition file of the probe design."); parser.addArgument("normalization-file", "nf", mitkCommandLineParser::InputFile, "Input normalization file", "The input normalization file is used for normalization of the number of photons in the PVFC calculations."); parser.endGroup(); // parse arguments, this method returns a mapping of long argument names and their values std::map parsedArgs = parser.parseArguments(argc, argv); if (parsedArgs.size() == 0) return EXIT_FAILURE; // parse, cast and set required arguments inputFilename = us::any_cast(parsedArgs["input"]); // strip ending inputFilename = inputFilename.substr(0, inputFilename.find("_H.mci")); inputFilename = inputFilename.substr(0, inputFilename.find("_T.bin")); outputFilename = us::any_cast(parsedArgs["output"]); // add .nrrd if not there std::string suffix = ".nrrd"; if (outputFilename.compare(outputFilename.size() - suffix.size(), suffix.size(), suffix) != 0) outputFilename = outputFilename + suffix; // default values for optional arguments // parse, cast and set optional arguments if given if (parsedArgs.count("verbose")) { verbose = us::any_cast(parsedArgs["verbose"]); } if (parsedArgs.count("detector-x")) { detector_x = us::any_cast(parsedArgs["detector-x"]); } if (parsedArgs.count("detector-z")) { detector_z = us::any_cast(parsedArgs["detector-z"]); } if (parsedArgs.count("timer")) { requestedSimulationTime = us::any_cast(parsedArgs["timer"]); if (requestedSimulationTime > 0) interpretAsTime = true; } if (parsedArgs.count("y-offset")) { yOffset = us::any_cast(parsedArgs["y-offset"]); } if (parsedArgs.count("number-of-photons")) { requestedNumberOfPhotons = us::any_cast(parsedArgs["number-of-photons"]); if (requestedNumberOfPhotons > 0) interpretAsTime = false; } if (parsedArgs.count("jobs")) { concurentThreadsSupported = us::any_cast(parsedArgs["jobs"]); } if (parsedArgs.count("probe-xml")) { std::string inputXmlProbeDesign = us::any_cast(parsedArgs["probe-xml"]); m_PhotoacousticProbe = mitk::pa::Probe::New(inputXmlProbeDesign, verbose); if (!m_PhotoacousticProbe->IsValid()) { std::cerr << "Xml File was not valid. Simulation failed." << std::endl; return EXIT_FAILURE; } } if (parsedArgs.count("normalization-file")) { normalizationFilename = us::any_cast(parsedArgs["normalization-file"]); } if (concurentThreadsSupported == 0 || concurentThreadsSupported == -1) { concurentThreadsSupported = std::thread::hardware_concurrency(); if (concurentThreadsSupported == 0) { std::cout << "Could not determine number of available threads. Launching only one." << std::endl; concurentThreadsSupported = 1; } } if (detector_x != -1 && detector_z != -1) { if (verbose) std::cout << "Performing PVFC calculation for x=" << detector_x << " and z=" << detector_z << std::endl; simulatePVFC = true; } else { if (verbose) std::cout << "Will not perform PVFC calculation due to x=" << detector_x << " and/or z=" << detector_z << std::endl; } InputValues allInput = InputValues(); allInput.LoadValues(inputFilename, yOffset, normalizationFilename, simulatePVFC); std::vector allValues(concurentThreadsSupported); auto* threads = new std::thread[concurentThreadsSupported]; for (long i = 0; i < concurentThreadsSupported; i++) { auto* tmp = new ReturnValues(); allValues.push_back(*tmp); } if (verbose) std::cout << "Initializing MonteCarloThreadHandler" << std::endl; long timeMetric; if (interpretAsTime) { if (requestedSimulationTime < mitk::eps) requestedSimulationTime = allInput.simulationTimeFromFile; timeMetric = requestedSimulationTime * 60 * 1000; } else { timeMetric = requestedNumberOfPhotons; } mitk::pa::MonteCarloThreadHandler::Pointer threadHandler = mitk::pa::MonteCarloThreadHandler::New(timeMetric, interpretAsTime); if (simulatePVFC) threadHandler->SetPackageSize(1000); if (verbose) std::cout << "\nStarting simulation ...\n" << std::endl; auto simulationStartTime = std::chrono::system_clock::now(); for (int i = 0; i < concurentThreadsSupported; i++) { threads[i] = std::thread(runMonteCarlo, &allInput, &allValues[i], (i + 1), threadHandler); } for (int i = 0; i < concurentThreadsSupported; i++) { threads[i].join(); } auto simulationFinishTime = std::chrono::system_clock::now(); auto simulationTimeElapsed = simulationFinishTime - simulationStartTime; if (verbose) std::cout << "\n\nFinished simulation\n\n" << std::endl; std::cout << "total time for simulation: " << (int)std::chrono::duration_cast(simulationTimeElapsed).count() << "sec " << std::endl; /**** SAVE Convert data to relative fluence rate [cm^-2] and save. *****/ if (!simulatePVFC) { if (verbose) std::cout << "Allocating memory for normal simulation result ... "; auto* finalTotalFluence = (double *)malloc(allInput.totalNumberOfVoxels * sizeof(double)); if (verbose) std::cout << "[OK]" << std::endl; if (verbose) std::cout << "Cleaning memory for normal simulation result ..."; for (int i = 0; i < allInput.totalNumberOfVoxels; i++) { finalTotalFluence[i] = 0; } if (verbose) std::cout << "[OK]" << std::endl; if (verbose) std::cout << "Calculating resulting fluence ... "; double tdx = 0, tdy = 0, tdz = 0; long long tNphotons = 0; for (int t = 0; t < concurentThreadsSupported; t++) { tdx = allInput.xSpacing; tdy = allInput.ySpacing; tdz = allInput.zSpacing; tNphotons += allValues[t].Nphotons; for (int voxelNumber = 0; voxelNumber < allInput.totalNumberOfVoxels; voxelNumber++) { finalTotalFluence[voxelNumber] += allValues[t].totalFluence[voxelNumber]; } } if (verbose) std::cout << "[OK]" << std::endl; std::cout << "total number of photons simulated: " << tNphotons << std::endl; // Normalize deposition (A) to yield fluence rate (F). double temp = tdx*tdy*tdz*tNphotons; for (int i = 0; i < allInput.totalNumberOfVoxels; i++) { finalTotalFluence[i] /= temp*allInput.muaVector[i]; } if (verbose) std::cout << "Saving normal simulated fluence result to " << outputFilename << " ... "; mitk::Image::Pointer resultImage = mitk::Image::New(); mitk::PixelType TPixel = mitk::MakeScalarPixelType(); auto* dimensionsOfImage = new unsigned int[3]; // Copy dimensions dimensionsOfImage[0] = allInput.Ny; dimensionsOfImage[1] = allInput.Nx; dimensionsOfImage[2] = allInput.Nz; resultImage->Initialize(TPixel, 3, dimensionsOfImage); mitk::Vector3D spacing; spacing[0] = allInput.ySpacing; spacing[1] = allInput.xSpacing; spacing[2] = allInput.zSpacing; resultImage->SetSpacing(spacing); resultImage->SetImportVolume(finalTotalFluence, 0, 0, mitk::Image::CopyMemory); resultImage->GetPropertyList()->SetFloatProperty("y-offset", yOffset); mitk::CoreServices::GetPropertyPersistence()->AddInfo(mitk::PropertyPersistenceInfo::New("y-offset")); mitk::IOUtil::Save(resultImage, outputFilename); if (verbose) std::cout << "[OK]" << std::endl; if (verbose) { std::cout << "x spacing = " << tdx << std::endl; std::cout << "y spacing = " << tdy << std::endl; std::cout << "z spacing = " << tdz << std::endl; std::cout << "total number of voxels = " << allInput.totalNumberOfVoxels << std::endl; std::cout << "number of photons = " << (int)tNphotons << std::endl; } } else // if simulate PVFC { if (verbose) std::cout << "Allocating memory for PVFC simulation result ... "; double* detectorFluence = ((double*)malloc(allInput.totalNumberOfVoxels * sizeof(double))); if (verbose) std::cout << "[OK]" << std::endl; if (verbose) std::cout << "Cleaning memory for PVFC simulation result ..."; for (int i = 0; i < allInput.totalNumberOfVoxels; i++) { detectorFluence[i] = 0; } if (verbose) std::cout << "[OK]" << std::endl; if (verbose) std::cout << "Calculating resulting PVFC fluence ... "; double tdx = 0, tdy = 0, tdz = 0; long long tNphotons = 0; long pvfcPhotons = 0; for (int t = 0; t < concurentThreadsSupported; t++) { tdx = allInput.xSpacing; tdy = allInput.ySpacing; tdz = allInput.zSpacing; tNphotons += allValues[t].Nphotons; pvfcPhotons += allValues[t].detectorVoxel->m_NumberPhotonsCurrent; for (int voxelNumber = 0; voxelNumber < allInput.totalNumberOfVoxels; voxelNumber++) { detectorFluence[voxelNumber] += allValues[t].detectorVoxel->fluenceContribution[voxelNumber]; } } if (verbose) std::cout << "[OK]" << std::endl; std::cout << "total number of photons simulated: " << tNphotons << std::endl; // Normalize deposition (A) to yield fluence rate (F). double temp = tdx*tdy*tdz*tNphotons; for (int i = 0; i < allInput.totalNumberOfVoxels; i++) { detectorFluence[i] /= temp*allInput.muaVector[i]; } if (verbose) std::cout << "Saving PVFC ..."; std::stringstream detectorname(""); double detectorX = allValues[0].detectorVoxel->location.x; double detectorY = allValues[0].detectorVoxel->location.y; double detectorZ = allValues[0].detectorVoxel->location.z; detectorname << detectorX << "," << detectorY << "," << detectorZ << "FluenceContribution.nrrd"; // Save the binary file std::string outputFileBase = outputFilename.substr(0, outputFilename.find(".nrrd")); outputFilename = outputFileBase + "_p" + detectorname.str().c_str(); mitk::Image::Pointer pvfcImage = mitk::Image::New(); auto* dimensionsOfPvfcImage = new unsigned int[3]; // Copy dimensions dimensionsOfPvfcImage[0] = allInput.Ny; dimensionsOfPvfcImage[1] = allInput.Nx; dimensionsOfPvfcImage[2] = allInput.Nz; pvfcImage->Initialize(mitk::MakeScalarPixelType(), 3, dimensionsOfPvfcImage); mitk::Vector3D pvfcSpacing; pvfcSpacing[0] = allInput.ySpacing; pvfcSpacing[1] = allInput.xSpacing; pvfcSpacing[2] = allInput.zSpacing; pvfcImage->SetSpacing(pvfcSpacing); pvfcImage->SetImportVolume(detectorFluence, 0, 0, mitk::Image::CopyMemory); pvfcImage->GetPropertyList()->SetFloatProperty("detector-x", detectorX); mitk::CoreServices::GetPropertyPersistence()->AddInfo(mitk::PropertyPersistenceInfo::New("detector-x")); pvfcImage->GetPropertyList()->SetFloatProperty("detector-y", detectorY); mitk::CoreServices::GetPropertyPersistence()->AddInfo(mitk::PropertyPersistenceInfo::New("detector-y")); pvfcImage->GetPropertyList()->SetFloatProperty("detector-z", detectorZ); mitk::CoreServices::GetPropertyPersistence()->AddInfo(mitk::PropertyPersistenceInfo::New("detector-z")); pvfcImage->GetPropertyList()->SetFloatProperty("normalization-factor", allValues[0].detectorVoxel->m_PhotonNormalizationValue); mitk::CoreServices::GetPropertyPersistence()->AddInfo(mitk::PropertyPersistenceInfo::New("normalization-factor")); pvfcImage->GetPropertyList()->SetFloatProperty("simulated-photons", pvfcPhotons); mitk::CoreServices::GetPropertyPersistence()->AddInfo(mitk::PropertyPersistenceInfo::New("simulated-photons")); mitk::IOUtil::Save(pvfcImage, outputFilename); if (verbose) std::cout << "[OK]" << std::endl; if (verbose) { std::cout << "x spacing = " << tdx << std::endl; std::cout << "y spacing = " << tdy << std::endl; std::cout << "z spacing = " << tdz << std::endl; std::cout << "total number of voxels = " << allInput.totalNumberOfVoxels << std::endl; std::cout << "number of photons = " << (int)tNphotons << std::endl; } } exit(EXIT_SUCCESS); } /* end of main */ /* CORE FUNCTION */ void runMonteCarlo(InputValues* inputValues, ReturnValues* returnValue, int thread, mitk::pa::MonteCarloThreadHandler::Pointer threadHandler) { if (verbose) std::cout << "Thread " << thread << ": Locking Mutex ..." << std::endl; if (verbose) std::cout << "[OK]" << std::endl; if (verbose) std::cout << "Initializing ... "; /* Propagation parameters */ double x, y, z; /* photon position */ double ux, uy, uz; /* photon trajectory as cosines */ double uxx, uyy, uzz; /* temporary values used during SPIN */ double s; /* step sizes. s = -log(RND)/mus [cm] */ double sleft; /* dimensionless */ double costheta; /* cos(theta) */ double sintheta; /* sin(theta) */ double cospsi; /* cos(psi) */ double sinpsi; /* sin(psi) */ double psi; /* azimuthal angle */ long photonIterator = 0; /* current photon */ double W; /* photon weight */ double absorb; /* weighted deposited in a step due to absorption */ short photon_status; /* flag = ALIVE=1 or DEAD=0 */ bool sv; /* Are they in the same voxel? */ /* dummy variables */ double rnd; /* assigned random value 0-1 */ double r, phi; /* dummy values */ long i, j; /* dummy indices */ double tempx, tempy, tempz; /* temporary variables, used during photon step. */ int ix, iy, iz; /* Added. Used to track photons */ double temp; /* dummy variable */ int bflag; /* boundary flag: 0 = photon inside volume. 1 = outside volume */ int CNT = 0; returnValue->totalFluence = (double *)malloc(inputValues->totalNumberOfVoxels * sizeof(double)); /* relative fluence rate [W/cm^2/W.delivered] */ if (detector_x != -1 && detector_z != -1) { if (detector_x<0 || detector_x>inputValues->Nx) { std::cout << "Requested detector x position not valid. Needs to be >= 0 and <= " << inputValues->Nx << std::endl; exit(EXIT_FAILURE); } if (detector_z<1 || detector_z>inputValues->Nz) { std::cout << "Requested detector z position not valid. Needs to be > 0 and <= " << inputValues->Nz << std::endl; exit(EXIT_FAILURE); } double photonNormalizationValue = 1 / inputValues->GetNormalizationValue(detector_x, inputValues->Ny / 2, detector_z); returnValue->detectorVoxel = new DetectorVoxel(initLocation(detector_x, inputValues->Ny / 2, detector_z, 0), inputValues->totalNumberOfVoxels, photonNormalizationValue); } /**** ======================== MAJOR CYCLE ============================ *****/ auto duration = std::chrono::system_clock::now().time_since_epoch(); returnValue->RandomGen(0, (std::chrono::duration_cast(duration).count() + thread) % 32000, nullptr); /* initiate with seed = 1, or any long integer. */ for (j = 0; j < inputValues->totalNumberOfVoxels; j++) returnValue->totalFluence[j] = 0; // ensure F[] starts empty. /**** RUN Launch N photons, initializing each one before progation. *****/ long photonsToSimulate = 0; do { photonsToSimulate = threadHandler->GetNextWorkPackage(); if (returnValue->detectorVoxel != nullptr) { photonsToSimulate = photonsToSimulate * returnValue->detectorVoxel->m_PhotonNormalizationValue; } if (verbose) MITK_INFO << "Photons to simulate: " << photonsToSimulate; photonIterator = 0L; do { /**** LAUNCH Initialize photon position and trajectory. *****/ photonIterator += 1; /* increment photon count */ W = 1.0; /* set photon weight to one */ photon_status = ALIVE; /* Launch an ALIVE photon */ CNT = 0; /**** SET SOURCE* Launch collimated beam at x,y center.****/ /****************************/ /* Initial position. */ if (m_PhotoacousticProbe.IsNotNull()) { double rnd1 = -1; double rnd2 = -1; double rnd3 = -1; double rnd4 = -1; double rnd5 = -1; double rnd6 = -1; double rnd7 = -1; double rnd8 = -1; while ((rnd1 = returnValue->RandomGen(1, 0, nullptr)) <= 0.0); while ((rnd2 = returnValue->RandomGen(1, 0, nullptr)) <= 0.0); while ((rnd3 = returnValue->RandomGen(1, 0, nullptr)) <= 0.0); while ((rnd4 = returnValue->RandomGen(1, 0, nullptr)) <= 0.0); while ((rnd5 = returnValue->RandomGen(1, 0, nullptr)) <= 0.0); while ((rnd6 = returnValue->RandomGen(1, 0, nullptr)) <= 0.0); while ((rnd7 = returnValue->RandomGen(1, 0, nullptr)) <= 0.0); while ((rnd8 = returnValue->RandomGen(1, 0, nullptr)) <= 0.0); mitk::pa::LightSource::PhotonInformation info = m_PhotoacousticProbe->GetNextPhoton(rnd1, rnd2, rnd3, rnd4, rnd5, rnd6, rnd7, rnd8); x = info.xPosition; y = yOffset + info.yPosition; z = info.zPosition; ux = info.xAngle; uy = info.yAngle; uz = info.zAngle; if (verbose) std::cout << "Created photon at position (" << x << "|" << y << "|" << z << ") with angles (" << ux << "|" << uy << "|" << uz << ")." << std::endl; } else { /* trajectory */ if (inputValues->launchflag == 1) // manually set launch { x = inputValues->xs; y = inputValues->ys; z = inputValues->zs; ux = inputValues->ux0; uy = inputValues->uy0; uz = inputValues->uz0; } else // use mcflag { if (inputValues->mcflag == 0) // uniform beam { // set launch point and width of beam while ((rnd = returnValue->RandomGen(1, 0, nullptr)) <= 0.0); // avoids rnd = 0 r = inputValues->radius*sqrt(rnd); // radius of beam at launch point while ((rnd = returnValue->RandomGen(1, 0, nullptr)) <= 0.0); // avoids rnd = 0 phi = rnd*2.0*PI; x = inputValues->xs + r*cos(phi); y = inputValues->ys + r*sin(phi); z = inputValues->zs; // set trajectory toward focus while ((rnd = returnValue->RandomGen(1, 0, nullptr)) <= 0.0); // avoids rnd = 0 r = inputValues->waist*sqrt(rnd); // radius of beam at focus while ((rnd = returnValue->RandomGen(1, 0, nullptr)) <= 0.0); // avoids rnd = 0 phi = rnd*2.0*PI; // !!!!!!!!!!!!!!!!!!!!!!! setting input values will braek inputValues->xfocus = r*cos(phi); inputValues->yfocus = r*sin(phi); temp = sqrt((x - inputValues->xfocus)*(x - inputValues->xfocus) + (y - inputValues->yfocus)*(y - inputValues->yfocus) + inputValues->zfocus*inputValues->zfocus); ux = -(x - inputValues->xfocus) / temp; uy = -(y - inputValues->yfocus) / temp; uz = sqrt(1 - ux*ux + uy*uy); } else if (inputValues->mcflag == 5) // Multispectral DKFZ prototype { // set launch point and width of beam while ((rnd = returnValue->RandomGen(1, 0, nullptr)) <= 0.0); //offset in x direction in cm (random) x = (rnd*2.5) - 1.25; while ((rnd = returnValue->RandomGen(1, 0, nullptr)) <= 0.0); double b = ((rnd)-0.5); y = (b > 0 ? yOffset + 1.5 : yOffset - 1.5); z = 0.1; ux = 0; while ((rnd = returnValue->RandomGen(1, 0, nullptr)) <= 0.0); //Angle of beam in y direction uy = sin((rnd*0.42) - 0.21 + (b < 0 ? 1.0 : -1.0) * 0.436); while ((rnd = returnValue->RandomGen(1, 0, nullptr)) <= 0.0); // angle of beam in x direction ux = sin((rnd*0.42) - 0.21); uz = sqrt(1 - ux*ux - uy*uy); } else if (inputValues->mcflag == 4) // Monospectral prototype DKFZ { // set launch point and width of beam while ((rnd = returnValue->RandomGen(1, 0, nullptr)) <= 0.0); //offset in x direction in cm (random) x = (rnd*2.5) - 1.25; while ((rnd = returnValue->RandomGen(1, 0, nullptr)) <= 0.0); double b = ((rnd)-0.5); y = (b > 0 ? yOffset + 0.83 : yOffset - 0.83); z = 0.1; ux = 0; while ((rnd = returnValue->RandomGen(1, 0, nullptr)) <= 0.0); //Angle of beam in y direction uy = sin((rnd*0.42) - 0.21 + (b < 0 ? 1.0 : -1.0) * 0.375); while ((rnd = returnValue->RandomGen(1, 0, nullptr)) <= 0.0); // angle of beam in x direction ux = sin((rnd*0.42) - 0.21); uz = sqrt(1 - ux*ux - uy*uy); } else { // isotropic pt source costheta = 1.0 - 2.0 * returnValue->RandomGen(1, 0, nullptr); sintheta = sqrt(1.0 - costheta*costheta); psi = 2.0 * PI * returnValue->RandomGen(1, 0, nullptr); cospsi = cos(psi); if (psi < PI) sinpsi = sqrt(1.0 - cospsi*cospsi); else sinpsi = -sqrt(1.0 - cospsi*cospsi); x = inputValues->xs; y = inputValues->ys; z = inputValues->zs; ux = sintheta*cospsi; uy = sintheta*sinpsi; uz = costheta; } } // end use mcflag } /****************************/ /* Get tissue voxel properties of launchpoint. * If photon beyond outer edge of defined voxels, * the tissue equals properties of outermost voxels. * Therefore, set outermost voxels to infinite background value. */ ix = (int)(inputValues->Nx / 2 + x / inputValues->xSpacing); iy = (int)(inputValues->Ny / 2 + y / inputValues->ySpacing); iz = (int)(z / inputValues->zSpacing); if (ix >= inputValues->Nx) ix = inputValues->Nx - 1; if (iy >= inputValues->Ny) iy = inputValues->Ny - 1; if (iz >= inputValues->Nz) iz = inputValues->Nz - 1; if (ix < 0) ix = 0; if (iy < 0) iy = 0; if (iz < 0) iz = 0; /* Get the tissue type of located voxel */ i = (long)(iz*inputValues->Ny*inputValues->Nx + ix*inputValues->Ny + iy); bflag = 1; // initialize as 1 = inside volume, but later check as photon propagates. if (returnValue->detectorVoxel != nullptr) returnValue->detectorVoxel->recordedPhotonRoute->clear(); /* HOP_DROP_SPIN_CHECK Propagate one photon until it dies as determined by ROULETTE. *******/ do { /**** HOP Take step to new position s = dimensionless stepsize x, uy, uz are cosines of current photon trajectory *****/ while ((rnd = returnValue->RandomGen(1, 0, nullptr)) <= 0.0); /* yields 0 < rnd <= 1 */ sleft = -log(rnd); /* dimensionless step */ CNT += 1; do { // while sleft>0 s = sleft / inputValues->musVector[i]; /* Step size [cm].*/ tempx = x + s*ux; /* Update positions. [cm] */ tempy = y + s*uy; tempz = z + s*uz; sv = returnValue->SameVoxel(x, y, z, tempx, tempy, tempz, inputValues->xSpacing, inputValues->ySpacing, inputValues->zSpacing); if (sv) /* photon in same voxel */ { x = tempx; /* Update positions. */ y = tempy; z = tempz; /**** DROP Drop photon weight (W) into local bin. *****/ absorb = W*(1 - exp(-inputValues->muaVector[i] * s)); /* photon weight absorbed at this step */ W -= absorb; /* decrement WEIGHT by amount absorbed */ // If photon within volume of heterogeneity, deposit energy in F[]. // Normalize F[] later, when save output. if (bflag) { i = (long)(iz*inputValues->Ny*inputValues->Nx + ix*inputValues->Ny + iy); returnValue->totalFluence[i] += absorb; // only save data if blag==1, i.e., photon inside simulation cube //For each detectorvoxel if (returnValue->detectorVoxel != nullptr) { //Add photon position to the recorded photon route returnValue->detectorVoxel->recordedPhotonRoute->push_back(initLocation(ix, iy, iz, absorb)); //If the photon is currently at the detector position if ((returnValue->detectorVoxel->location.x == ix) && ((returnValue->detectorVoxel->location.y == iy) || (returnValue->detectorVoxel->location.y - 1 == iy)) && (returnValue->detectorVoxel->location.z == iz)) { //For each voxel in the recorded photon route for (unsigned int routeIndex = 0; routeIndex < returnValue->detectorVoxel->recordedPhotonRoute->size(); routeIndex++) { //increment the fluence contribution at that particular position i = (long)(returnValue->detectorVoxel->recordedPhotonRoute->at(routeIndex).z*inputValues->Ny*inputValues->Nx + returnValue->detectorVoxel->recordedPhotonRoute->at(routeIndex).x*inputValues->Ny + returnValue->detectorVoxel->recordedPhotonRoute->at(routeIndex).y); returnValue->detectorVoxel->fluenceContribution[i] += returnValue->detectorVoxel->recordedPhotonRoute->at(routeIndex).absorb; } //Clear the recorded photon route returnValue->detectorVoxel->m_NumberPhotonsCurrent++; returnValue->detectorVoxel->recordedPhotonRoute->clear(); } } } /* Update sleft */ sleft = 0; /* dimensionless step remaining */ } else /* photon has crossed voxel boundary */ { /* step to voxel face + "littlest step" so just inside new voxel. */ s = ls + returnValue->FindVoxelFace2(x, y, z, tempx, tempy, tempz, inputValues->xSpacing, inputValues->ySpacing, inputValues->zSpacing, ux, uy, uz); /**** DROP Drop photon weight (W) into local bin. *****/ absorb = W*(1 - exp(-inputValues->muaVector[i] * s)); /* photon weight absorbed at this step */ W -= absorb; /* decrement WEIGHT by amount absorbed */ // If photon within volume of heterogeneity, deposit energy in F[]. // Normalize F[] later, when save output. if (bflag) { // only save data if bflag==1, i.e., photon inside simulation cube //For each detectorvoxel if (returnValue->detectorVoxel != nullptr) { //Add photon position to the recorded photon route returnValue->detectorVoxel->recordedPhotonRoute->push_back(initLocation(ix, iy, iz, absorb)); //If the photon is currently at the detector position if ((returnValue->detectorVoxel->location.x == ix) && ((returnValue->detectorVoxel->location.y == iy) || (returnValue->detectorVoxel->location.y - 1 == iy)) && (returnValue->detectorVoxel->location.z == iz)) { //For each voxel in the recorded photon route for (unsigned int routeIndex = 0; routeIndex < returnValue->detectorVoxel->recordedPhotonRoute->size(); routeIndex++) { //increment the fluence contribution at that particular position i = (long)(returnValue->detectorVoxel->recordedPhotonRoute->at(routeIndex).z*inputValues->Ny*inputValues->Nx + returnValue->detectorVoxel->recordedPhotonRoute->at(routeIndex).x*inputValues->Ny + returnValue->detectorVoxel->recordedPhotonRoute->at(routeIndex).y); returnValue->detectorVoxel->fluenceContribution[i] += returnValue->detectorVoxel->recordedPhotonRoute->at(routeIndex).absorb; } //Clear the recorded photon route returnValue->detectorVoxel->m_NumberPhotonsCurrent++; returnValue->detectorVoxel->recordedPhotonRoute->clear(); } } i = (long)(iz*inputValues->Ny*inputValues->Nx + ix*inputValues->Ny + iy); returnValue->totalFluence[i] += absorb; } /* Update sleft */ sleft -= s*inputValues->musVector[i]; /* dimensionless step remaining */ if (sleft <= ls) sleft = 0; /* Update positions. */ x += s*ux; y += s*uy; z += s*uz; // pointers to voxel containing optical properties ix = (int)(inputValues->Nx / 2 + x / inputValues->xSpacing); iy = (int)(inputValues->Ny / 2 + y / inputValues->ySpacing); iz = (int)(z / inputValues->zSpacing); bflag = 1; // Boundary flag. Initialize as 1 = inside volume, then check. if (inputValues->boundaryflag == 0) { // Infinite medium. // Check if photon has wandered outside volume. // If so, set tissue type to boundary value, but let photon wander. // Set blag to zero, so DROP does not deposit energy. if (iz >= inputValues->Nz) { iz = inputValues->Nz - 1; bflag = 0; } if (ix >= inputValues->Nx) { ix = inputValues->Nx - 1; bflag = 0; } if (iy >= inputValues->Ny) { iy = inputValues->Ny - 1; bflag = 0; } if (iz < 0) { iz = 0; bflag = 0; } if (ix < 0) { ix = 0; bflag = 0; } if (iy < 0) { iy = 0; bflag = 0; } } else if (inputValues->boundaryflag == 1) { // Escape at boundaries if (iz >= inputValues->Nz) { iz = inputValues->Nz - 1; photon_status = DEAD; sleft = 0; } if (ix >= inputValues->Nx) { ix = inputValues->Nx - 1; photon_status = DEAD; sleft = 0; } if (iy >= inputValues->Ny) { iy = inputValues->Ny - 1; photon_status = DEAD; sleft = 0; } if (iz < 0) { iz = 0; photon_status = DEAD; sleft = 0; } if (ix < 0) { ix = 0; photon_status = DEAD; sleft = 0; } if (iy < 0) { iy = 0; photon_status = DEAD; sleft = 0; } } else if (inputValues->boundaryflag == 2) { // Escape at top surface, no x,y bottom z boundaries if (iz >= inputValues->Nz) { iz = inputValues->Nz - 1; bflag = 0; } if (ix >= inputValues->Nx) { ix = inputValues->Nx - 1; bflag = 0; } if (iy >= inputValues->Ny) { iy = inputValues->Ny - 1; bflag = 0; } if (iz < 0) { iz = 0; photon_status = DEAD; sleft = 0; } if (ix < 0) { ix = 0; bflag = 0; } if (iy < 0) { iy = 0; bflag = 0; } } // update pointer to tissue type i = (long)(iz*inputValues->Ny*inputValues->Nx + ix*inputValues->Ny + iy); } //(sv) /* same voxel */ } while (sleft > 0); //do...while /**** SPIN Scatter photon into new trajectory defined by theta and psi. Theta is specified by cos(theta), which is determined based on the Henyey-Greenstein scattering function. Convert theta and psi into cosines ux, uy, uz. *****/ /* Sample for costheta */ while ((rnd = returnValue->RandomGen(1, 0, nullptr)) <= 0.0); if (inputValues->gVector[i] == 0.0) { costheta = 2.0 * rnd - 1.0; } else { double temp = (1.0 - inputValues->gVector[i] * inputValues->gVector[i]) / (1.0 - inputValues->gVector[i] + 2 * inputValues->gVector[i] * rnd); costheta = (1.0 + inputValues->gVector[i] * inputValues->gVector[i] - temp*temp) / (2.0*inputValues->gVector[i]); } sintheta = sqrt(1.0 - costheta*costheta); /* sqrt() is faster than sin(). */ /* Sample psi. */ psi = 2.0*PI*returnValue->RandomGen(1, 0, nullptr); cospsi = cos(psi); if (psi < PI) sinpsi = sqrt(1.0 - cospsi*cospsi); /* sqrt() is faster than sin(). */ else sinpsi = -sqrt(1.0 - cospsi*cospsi); /* New trajectory. */ if (1 - fabs(uz) <= ONE_MINUS_COSZERO) { /* close to perpendicular. */ uxx = sintheta * cospsi; uyy = sintheta * sinpsi; uzz = costheta * SIGN(uz); /* SIGN() is faster than division. */ } else { /* usually use this option */ temp = sqrt(1.0 - uz * uz); uxx = sintheta * (ux * uz * cospsi - uy * sinpsi) / temp + ux * costheta; uyy = sintheta * (uy * uz * cospsi + ux * sinpsi) / temp + uy * costheta; uzz = -sintheta * cospsi * temp + uz * costheta; } /* Update trajectory */ ux = uxx; uy = uyy; uz = uzz; /**** CHECK ROULETTE If photon weight below THRESHOLD, then terminate photon using Roulette technique. Photon has CHANCE probability of having its weight increased by factor of 1/CHANCE, and 1-CHANCE probability of terminating. *****/ if (W < THRESHOLD) { if (returnValue->RandomGen(1, 0, nullptr) <= CHANCE) W /= CHANCE; else photon_status = DEAD; } } while (photon_status == ALIVE); /* end STEP_CHECK_HOP_SPIN */ /* if ALIVE, continue propagating */ /* If photon DEAD, then launch new photon. */ } while (photonIterator < photonsToSimulate); /* end RUN */ returnValue->Nphotons += photonsToSimulate; } while (photonsToSimulate > 0); if (verbose) std::cout << "------------------------------------------------------" << std::endl; if (verbose) std::cout << "Thread " << thread << " is finished." << std::endl; } diff --git a/Modules/PhotoacousticsLib/MitkPAPhantomGenerator/CMakeLists.txt b/Modules/PhotoacousticsLib/MitkPAPhantomGenerator/CMakeLists.txt new file mode 100644 index 0000000000..8c8dcb7fba --- /dev/null +++ b/Modules/PhotoacousticsLib/MitkPAPhantomGenerator/CMakeLists.txt @@ -0,0 +1,11 @@ +OPTION(BUILD_PhotoacousticPhantomGenerator "Build MiniApp for generating a PA phantom in silico" ON) + +IF(BUILD_PhotoacousticPhantomGenerator) + PROJECT( MitkPAPhantomGenerator ) + mitk_create_executable(PAPhantomGenerator + DEPENDS MitkCommandLine MitkCore MitkPhotoacousticsLib + PACKAGE_DEPENDS + CPP_FILES PAPhantomGenerator.cpp) + + install(TARGETS ${EXECUTABLE_TARGET} RUNTIME DESTINATION bin) + ENDIF() diff --git a/Modules/PhotoacousticsLib/MitkPAPhantomGenerator/PAPhantomGenerator.cpp b/Modules/PhotoacousticsLib/MitkPAPhantomGenerator/PAPhantomGenerator.cpp new file mode 100644 index 0000000000..cb83119f46 --- /dev/null +++ b/Modules/PhotoacousticsLib/MitkPAPhantomGenerator/PAPhantomGenerator.cpp @@ -0,0 +1,230 @@ +/*=================================================================== + +The Medical Imaging Interaction Toolkit (MITK) + +Copyright (c) German Cancer Research Center, +Division of Medical and Biological Informatics. +All rights reserved. + +This software is distributed WITHOUT ANY WARRANTY; without +even the implied warranty of MERCHANTABILITY or FITNESS FOR +A PARTICULAR PURPOSE. + +See LICENSE.txt or http://www.mitk.org for details. + +===================================================================*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +using namespace mitk::pa; + +TissueGeneratorParameters::Pointer CreatePhantom_04_04_18_Parameters() +{ + auto returnParameters = TissueGeneratorParameters::New(); + returnParameters->SetAirThicknessInMillimeters(12); + returnParameters->SetMinBackgroundAbsorption(0.1); + returnParameters->SetMaxBackgroundAbsorption(0.1); + returnParameters->SetBackgroundAnisotropy(0.9); + returnParameters->SetBackgroundScattering(15); + returnParameters->SetCalculateNewVesselPositionCallback(&VesselMeanderStrategy::CalculateNewPositionInStraightLine); + returnParameters->SetDoPartialVolume(true); + returnParameters->SetMinNumberOfVessels(1); + returnParameters->SetMaxNumberOfVessels(8); + returnParameters->SetMinVesselAbsorption(1); + returnParameters->SetMaxVesselAbsorption(10); + returnParameters->SetMinVesselAnisotropy(0.9); + returnParameters->SetMaxVesselAnisotropy(0.9); + returnParameters->SetMinVesselBending(0.1); + returnParameters->SetMaxVesselBending(0.3); + returnParameters->SetMinVesselRadiusInMillimeters(0.25); + returnParameters->SetMaxVesselRadiusInMillimeters(4); + returnParameters->SetMinVesselScattering(15); + returnParameters->SetMaxVesselScattering(15); + returnParameters->SetMinVesselZOrigin(1.6); + returnParameters->SetMaxVesselZOrigin(4); + returnParameters->SetVesselBifurcationFrequency(5000); + returnParameters->SetRandomizePhysicalProperties(false); + returnParameters->SetSkinThicknessInMillimeters(0); + returnParameters->SetUseRngSeed(false); + returnParameters->SetVoxelSpacingInCentimeters(0.03); + returnParameters->SetXDim(140); + returnParameters->SetYDim(100); + returnParameters->SetZDim(180); + //returnParameters->SetVoxelSpacingInCentimeters(0.015); + //returnParameters->SetXDim(280); + //returnParameters->SetYDim(200); + //returnParameters->SetZDim(360); + returnParameters->SetForceVesselsMoveAlongYDirection(true); + //returnParameters->SetVoxelSpacingInCentimeters(0.0075); + //returnParameters->SetXDim(560); + //returnParameters->SetYDim(400); + //returnParameters->SetZDim(720); + return returnParameters; +} + +struct InputParameters +{ + std::string saveFolderPath; + std::string identifyer; + std::string exePath; + std::string probePath; + bool empty; + bool verbose; +}; + +InputParameters parseInput(int argc, char* argv[]) +{ + MITK_INFO << "Parsing arguments..."; + mitkCommandLineParser parser; + + parser.setCategory("MITK-Photoacoustics"); + parser.setTitle("Mitk Tissue Batch Generator"); + parser.setDescription("Creates in silico tissue in batch processing and automatically calculates fluence values for the central slice of the volume."); + parser.setContributor("Computer Assisted Medical Interventions, DKFZ"); + + parser.setArgumentPrefix("--", "-"); + + parser.beginGroup("Required parameters"); + parser.addArgument( + "savePath", "s", mitkCommandLineParser::InputDirectory, + "Input save folder (directory)", "input save folder", + us::Any(), false); + parser.addArgument( + "mitkMcxyz", "m", mitkCommandLineParser::OutputFile, + "MitkMcxyz binary (file)", "path to the MitkMcxyz binary", + us::Any(), false); + parser.endGroup(); + + parser.beginGroup("Optional parameters"); + parser.addArgument( + "probe", "p", mitkCommandLineParser::OutputFile, + "xml probe file (file)", "file to the definition of the used probe (*.xml)", + us::Any()); + parser.addArgument( + "verbose", "v", mitkCommandLineParser::Bool, + "Verbose Output", "Whether to produce verbose, or rather debug output"); + parser.addArgument( + "identifyer", "i", mitkCommandLineParser::String, + "Generator identifyer (string)", "A unique identifyer for the calculation instance"); + parser.addArgument( + "empty-volume", "e", mitkCommandLineParser::Bool, + "omit vessel structures (boolean flag)", "Whether to create an empty volume with no structures inside."); + parser.endGroup(); + + InputParameters input; + + std::map parsedArgs = parser.parseArguments(argc, argv); + if (parsedArgs.size() == 0) + exit(-1); + + if (parsedArgs.count("empty-volume")) + { + input.empty = us::any_cast(parsedArgs["empty-volume"]); + } + else + { + input.empty = false; + } + + if (parsedArgs.count("verbose")) + { + input.verbose = us::any_cast(parsedArgs["verbose"]); + } + else + { + input.verbose = false; + } + + if (parsedArgs.count("savePath")) + { + input.saveFolderPath = us::any_cast(parsedArgs["savePath"]); + } + + if (parsedArgs.count("mitkMcxyz")) + { + input.exePath = us::any_cast(parsedArgs["mitkMcxyz"]); + } + + if (parsedArgs.count("probe")) + { + input.probePath = us::any_cast(parsedArgs["probe"]); + } + + if (parsedArgs.count("identifyer")) + { + input.identifyer = us::any_cast(parsedArgs["identifyer"]); + } + else + { + auto uid = mitk::UIDGenerator("", 8); + input.identifyer = uid.GetUID(); + } + MITK_INFO << "Parsing arguments...[Done]"; + return input; +} + +int main(int argc, char * argv[]) +{ + auto input = parseInput(argc, argv); + auto parameters = CreatePhantom_04_04_18_Parameters(); + if (input.empty) + { + parameters->SetMaxNumberOfVessels(0); + parameters->SetMinNumberOfVessels(0); + } + MITK_INFO(input.verbose) << "Generating tissue.."; + auto resultTissue = InSilicoTissueGenerator::GenerateInSilicoData(parameters); + MITK_INFO(input.verbose) << "Generating tissue..[Done]"; + + auto inputfolder = std::string(input.saveFolderPath + "input/"); + auto outputfolder = std::string(input.saveFolderPath + "output/"); + if (!itksys::SystemTools::FileIsDirectory(inputfolder)) + { + itksys::SystemTools::MakeDirectory(inputfolder); + } + if (!itksys::SystemTools::FileIsDirectory(outputfolder)) + { + itksys::SystemTools::MakeDirectory(outputfolder); + } + + std::string savePath = input.saveFolderPath + "input/Phantom_" + input.identifyer + + ".nrrd"; + mitk::IOUtil::Save(resultTissue->ConvertToMitkImage(), savePath); + std::string outputPath = input.saveFolderPath + "output/Phantom_" + input.identifyer + + "/"; + + resultTissue = nullptr; + + if (!itksys::SystemTools::FileIsDirectory(outputPath)) + { + itksys::SystemTools::MakeDirectory(outputPath); + } + + outputPath = outputPath + "Fluence_Phantom_" + input.identifyer; + + MITK_INFO(input.verbose) << "Simulating fluence.."; + + int result = -4; + + std::string cmdString = std::string(input.exePath + " -i " + savePath + " -o " + + (outputPath + ".nrrd") + + " -yo " + "0" + " -p " + input.probePath + + " -n 10000000"); + + MITK_INFO << "Executing: " << cmdString; + + result = std::system(cmdString.c_str()); + + MITK_INFO << result; + MITK_INFO(input.verbose) << "Simulating fluence..[Done]"; +} diff --git a/Modules/PhotoacousticsLib/MitkPAPhantomGenerator/files.cmake b/Modules/PhotoacousticsLib/MitkPAPhantomGenerator/files.cmake new file mode 100644 index 0000000000..07f708562d --- /dev/null +++ b/Modules/PhotoacousticsLib/MitkPAPhantomGenerator/files.cmake @@ -0,0 +1,3 @@ +set(CPP_FILES + PAPhantomGenerator.cpp +) diff --git a/Modules/PhotoacousticsLib/MitkTissueBatchGenerator/CMakeLists.txt b/Modules/PhotoacousticsLib/MitkTissueBatchGenerator/CMakeLists.txt new file mode 100644 index 0000000000..73a948b462 --- /dev/null +++ b/Modules/PhotoacousticsLib/MitkTissueBatchGenerator/CMakeLists.txt @@ -0,0 +1,11 @@ +OPTION(BUILD_PhotoacousticTissueBatchGenerator "Build MiniApp for batch generating of photoacoustic tissue" ON) + +IF(BUILD_PhotoacousticTissueBatchGenerator) + PROJECT( MitkTissueBatchGenerator ) + mitk_create_executable(TissueBatchGenerator + DEPENDS MitkCommandLine MitkCore MitkPhotoacousticsLib + PACKAGE_DEPENDS + CPP_FILES TissueBatchGenerator.cpp) + + install(TARGETS ${EXECUTABLE_TARGET} RUNTIME DESTINATION bin) + ENDIF() diff --git a/Modules/PhotoacousticsLib/MitkTissueBatchGenerator/TissueBatchGenerator.cpp b/Modules/PhotoacousticsLib/MitkTissueBatchGenerator/TissueBatchGenerator.cpp new file mode 100644 index 0000000000..61d6b94ab9 --- /dev/null +++ b/Modules/PhotoacousticsLib/MitkTissueBatchGenerator/TissueBatchGenerator.cpp @@ -0,0 +1,394 @@ +/*=================================================================== + +The Medical Imaging Interaction Toolkit (MITK) + +Copyright (c) German Cancer Research Center, +Division of Medical and Biological Informatics. +All rights reserved. + +This software is distributed WITHOUT ANY WARRANTY; without +even the implied warranty of MERCHANTABILITY or FITNESS FOR +A PARTICULAR PURPOSE. + +See LICENSE.txt or http://www.mitk.org for details. + +===================================================================*/ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +using namespace mitk::pa; + +TissueGeneratorParameters::Pointer CreateMultiHB_13_02_18_Parameters() +{ + auto returnParameters = TissueGeneratorParameters::New(); + returnParameters->SetAirThicknessInMillimeters(1.8); + returnParameters->SetMinBackgroundAbsorption(0.001); + returnParameters->SetMaxBackgroundAbsorption(0.2); + returnParameters->SetBackgroundAnisotropy(0.9); + returnParameters->SetBackgroundScattering(15); + returnParameters->SetCalculateNewVesselPositionCallback(&VesselMeanderStrategy::CalculateRandomlyDivergingPosition); + returnParameters->SetDoPartialVolume(true); + returnParameters->SetMinNumberOfVessels(1); + returnParameters->SetMaxNumberOfVessels(7); + returnParameters->SetMinVesselAbsorption(1); + returnParameters->SetMaxVesselAbsorption(12); + returnParameters->SetMinVesselAnisotropy(0.9); + returnParameters->SetMaxVesselAnisotropy(0.9); + returnParameters->SetMinVesselBending(0); + returnParameters->SetMaxVesselBending(0.2); + returnParameters->SetMinVesselRadiusInMillimeters(0.5); + returnParameters->SetMaxVesselRadiusInMillimeters(6); + returnParameters->SetMinVesselScattering(15); + returnParameters->SetMaxVesselScattering(15); + returnParameters->SetMinVesselZOrigin(1); + returnParameters->SetMaxVesselZOrigin(3); + returnParameters->SetVesselBifurcationFrequency(5000); + returnParameters->SetRandomizePhysicalProperties(false); + returnParameters->SetSkinThicknessInMillimeters(0); + returnParameters->SetUseRngSeed(false); + returnParameters->SetVoxelSpacingInCentimeters(0.06); + returnParameters->SetXDim(70); + returnParameters->SetYDim(100); + returnParameters->SetZDim(100); + returnParameters->SetMCflag(4); + return returnParameters; +} + +TissueGeneratorParameters::Pointer CreateBaselineHB_13_02_18_Parameters() +{ + auto returnParameters = TissueGeneratorParameters::New(); + returnParameters->SetAirThicknessInMillimeters(1.8); + returnParameters->SetMinBackgroundAbsorption(0.001); + returnParameters->SetMaxBackgroundAbsorption(0.2); + returnParameters->SetBackgroundAnisotropy(0.9); + returnParameters->SetBackgroundScattering(15); + returnParameters->SetCalculateNewVesselPositionCallback(&VesselMeanderStrategy::CalculateRandomlyDivergingPosition); + returnParameters->SetDoPartialVolume(true); + returnParameters->SetMinNumberOfVessels(1); + returnParameters->SetMaxNumberOfVessels(1); + returnParameters->SetMinVesselAbsorption(4.73); + returnParameters->SetMaxVesselAbsorption(4.73); + returnParameters->SetMinVesselAnisotropy(0.9); + returnParameters->SetMaxVesselAnisotropy(0.9); + returnParameters->SetMinVesselBending(0); + returnParameters->SetMaxVesselBending(0.2); + returnParameters->SetMinVesselRadiusInMillimeters(3); + returnParameters->SetMaxVesselRadiusInMillimeters(3); + returnParameters->SetMinVesselScattering(15); + returnParameters->SetMaxVesselScattering(15); + returnParameters->SetMinVesselZOrigin(1); + returnParameters->SetMaxVesselZOrigin(3); + returnParameters->SetVesselBifurcationFrequency(5000); + returnParameters->SetRandomizePhysicalProperties(false); + returnParameters->SetSkinThicknessInMillimeters(0); + returnParameters->SetUseRngSeed(false); + returnParameters->SetVoxelSpacingInCentimeters(0.06); + returnParameters->SetXDim(70); + returnParameters->SetYDim(100); + returnParameters->SetZDim(100); + returnParameters->SetMCflag(4); + return returnParameters; +} + +TissueGeneratorParameters::Pointer CreateSingleVesselHeterogeneousBackground_08_02_18_Parameters() +{ + auto returnParameters = TissueGeneratorParameters::New(); + returnParameters->SetAirThicknessInMillimeters(1.8); + returnParameters->SetMinBackgroundAbsorption(0.001); + returnParameters->SetMaxBackgroundAbsorption(0.2); + returnParameters->SetBackgroundAnisotropy(0.9); + returnParameters->SetBackgroundScattering(15); + returnParameters->SetCalculateNewVesselPositionCallback(&VesselMeanderStrategy::CalculateRandomlyDivergingPosition); + returnParameters->SetDoPartialVolume(true); + returnParameters->SetMinNumberOfVessels(1); + returnParameters->SetMaxNumberOfVessels(1); + returnParameters->SetMinVesselAbsorption(1); + returnParameters->SetMaxVesselAbsorption(12); + returnParameters->SetMinVesselAnisotropy(0.9); + returnParameters->SetMaxVesselAnisotropy(0.9); + returnParameters->SetMinVesselBending(0); + returnParameters->SetMaxVesselBending(0.2); + returnParameters->SetMinVesselRadiusInMillimeters(0.5); + returnParameters->SetMaxVesselRadiusInMillimeters(6); + returnParameters->SetMinVesselScattering(15); + returnParameters->SetMaxVesselScattering(15); + returnParameters->SetMinVesselZOrigin(1); + returnParameters->SetMaxVesselZOrigin(3); + returnParameters->SetVesselBifurcationFrequency(5000); + returnParameters->SetRandomizePhysicalProperties(false); + returnParameters->SetSkinThicknessInMillimeters(0); + returnParameters->SetUseRngSeed(false); + returnParameters->SetVoxelSpacingInCentimeters(0.06); + returnParameters->SetXDim(70); + returnParameters->SetYDim(100); + returnParameters->SetZDim(100); + returnParameters->SetMCflag(4); + return returnParameters; +} + +TissueGeneratorParameters::Pointer CreateMultivessel_19_12_17_Parameters() +{ + auto returnParameters = TissueGeneratorParameters::New(); + returnParameters->SetAirThicknessInMillimeters(12); + returnParameters->SetMinBackgroundAbsorption(0.1); + returnParameters->SetMaxBackgroundAbsorption(0.1); + returnParameters->SetBackgroundAnisotropy(0.9); + returnParameters->SetBackgroundScattering(15); + returnParameters->SetCalculateNewVesselPositionCallback(&VesselMeanderStrategy::CalculateRandomlyDivergingPosition); + returnParameters->SetDoPartialVolume(true); + returnParameters->SetMinNumberOfVessels(1); + returnParameters->SetMaxNumberOfVessels(7); + returnParameters->SetMinVesselAbsorption(2); + returnParameters->SetMaxVesselAbsorption(8); + returnParameters->SetMinVesselAnisotropy(0.9); + returnParameters->SetMaxVesselAnisotropy(0.9); + returnParameters->SetMinVesselBending(0.1); + returnParameters->SetMaxVesselBending(0.3); + returnParameters->SetMinVesselRadiusInMillimeters(0.5); + returnParameters->SetMaxVesselRadiusInMillimeters(4); + returnParameters->SetMinVesselScattering(15); + returnParameters->SetMaxVesselScattering(15); + returnParameters->SetMinVesselZOrigin(2.2); + returnParameters->SetMaxVesselZOrigin(4); + returnParameters->SetVesselBifurcationFrequency(5000); + returnParameters->SetRandomizePhysicalProperties(false); + returnParameters->SetSkinThicknessInMillimeters(0); + returnParameters->SetUseRngSeed(false); + returnParameters->SetVoxelSpacingInCentimeters(0.06); + returnParameters->SetXDim(70); + returnParameters->SetYDim(100); + returnParameters->SetZDim(100); + return returnParameters; +} + +TissueGeneratorParameters::Pointer CreateMultivessel_19_10_17_Parameters() +{ + auto returnParameters = TissueGeneratorParameters::New(); + returnParameters->SetAirThicknessInMillimeters(12); + returnParameters->SetMinBackgroundAbsorption(0.1); + returnParameters->SetMaxBackgroundAbsorption(0.1); + returnParameters->SetBackgroundAnisotropy(0.9); + returnParameters->SetBackgroundScattering(15); + returnParameters->SetCalculateNewVesselPositionCallback(&VesselMeanderStrategy::CalculateRandomlyDivergingPosition); + returnParameters->SetDoPartialVolume(true); + returnParameters->SetMinNumberOfVessels(1); + returnParameters->SetMaxNumberOfVessels(7); + returnParameters->SetMinVesselAbsorption(2); + returnParameters->SetMaxVesselAbsorption(8); + returnParameters->SetMinVesselAnisotropy(0.9); + returnParameters->SetMaxVesselAnisotropy(0.9); + returnParameters->SetMinVesselBending(0.1); + returnParameters->SetMaxVesselBending(0.3); + returnParameters->SetMinVesselRadiusInMillimeters(0.5); + returnParameters->SetMaxVesselRadiusInMillimeters(4); + returnParameters->SetMinVesselScattering(15); + returnParameters->SetMaxVesselScattering(15); + returnParameters->SetMinVesselZOrigin(2.2); + returnParameters->SetMaxVesselZOrigin(4); + returnParameters->SetVesselBifurcationFrequency(5000); + returnParameters->SetRandomizePhysicalProperties(false); + returnParameters->SetSkinThicknessInMillimeters(0); + returnParameters->SetUseRngSeed(false); + returnParameters->SetVoxelSpacingInCentimeters(0.03); + returnParameters->SetXDim(140); + returnParameters->SetYDim(200); + returnParameters->SetZDim(180); + return returnParameters; +} + +TissueGeneratorParameters::Pointer CreateSinglevessel_19_10_17_Parameters() +{ + auto returnParameters = TissueGeneratorParameters::New(); + returnParameters->SetAirThicknessInMillimeters(12); + returnParameters->SetMinBackgroundAbsorption(0.1); + returnParameters->SetMaxBackgroundAbsorption(0.1); + returnParameters->SetBackgroundAnisotropy(0.9); + returnParameters->SetBackgroundScattering(15); + returnParameters->SetCalculateNewVesselPositionCallback(&VesselMeanderStrategy::CalculateRandomlyDivergingPosition); + returnParameters->SetDoPartialVolume(true); + returnParameters->SetMinNumberOfVessels(1); + returnParameters->SetMaxNumberOfVessels(1); + returnParameters->SetMinVesselAbsorption(2); + returnParameters->SetMaxVesselAbsorption(8); + returnParameters->SetMinVesselAnisotropy(0.9); + returnParameters->SetMaxVesselAnisotropy(0.9); + returnParameters->SetMinVesselBending(0.1); + returnParameters->SetMaxVesselBending(0.3); + returnParameters->SetMinVesselRadiusInMillimeters(0.5); + returnParameters->SetMaxVesselRadiusInMillimeters(4); + returnParameters->SetMinVesselScattering(15); + returnParameters->SetMaxVesselScattering(15); + returnParameters->SetMinVesselZOrigin(2.2); + returnParameters->SetMaxVesselZOrigin(4); + returnParameters->SetVesselBifurcationFrequency(5000); + returnParameters->SetRandomizePhysicalProperties(false); + returnParameters->SetSkinThicknessInMillimeters(0); + returnParameters->SetUseRngSeed(false); + returnParameters->SetVoxelSpacingInCentimeters(0.03); + returnParameters->SetXDim(140); + returnParameters->SetYDim(200); + returnParameters->SetZDim(180); + return returnParameters; +} + +struct InputParameters +{ + std::string saveFolderPath; + std::string identifyer; + std::string exePath; + std::string probePath; + bool verbose; +}; + +InputParameters parseInput(int argc, char* argv[]) +{ + MITK_INFO << "Paring arguments..."; + mitkCommandLineParser parser; + // set general information + parser.setCategory("MITK-Photoacoustics"); + parser.setTitle("Mitk Tissue Batch Generator"); + parser.setDescription("Creates in silico tissue in batch processing and automatically calculates fluence values for the central slice of the volume."); + parser.setContributor("Computer Assisted Medical Interventions, DKFZ"); + + // how should arguments be prefixed + parser.setArgumentPrefix("--", "-"); + // add each argument, unless specified otherwise each argument is optional + // see mitkCommandLineParser::addArgument for more information + parser.beginGroup("Required parameters"); + parser.addArgument( + "savePath", "s", mitkCommandLineParser::InputDirectory, + "Input save folder (directory)", "input save folder", + us::Any(), false); + parser.addArgument( + "mitkMcxyz", "m", mitkCommandLineParser::OutputFile, + "MitkMcxyz binary (file)", "path to the MitkMcxyz binary", + us::Any(), false); + parser.endGroup(); + parser.beginGroup("Optional parameters"); + parser.addArgument( + "probe", "p", mitkCommandLineParser::OutputFile, + "xml probe file (file)", "file to the definition of the used probe (*.xml)", + us::Any()); + parser.addArgument( + "verbose", "v", mitkCommandLineParser::Bool, + "Verbose Output", "Whether to produce verbose, or rather debug output"); + parser.addArgument( + "identifyer", "i", mitkCommandLineParser::String, + "Generator identifyer (string)", "A unique identifyer for the calculation instance"); + + InputParameters input; + + std::map parsedArgs = parser.parseArguments(argc, argv); + if (parsedArgs.size() == 0) + exit(-1); + + if (parsedArgs.count("verbose")) + { + MITK_INFO << "verbose"; + input.verbose = us::any_cast(parsedArgs["verbose"]); + } + else + { + input.verbose = false; + } + + if (parsedArgs.count("savePath")) + { + MITK_INFO << "savePath"; + input.saveFolderPath = us::any_cast(parsedArgs["savePath"]); + } + + if (parsedArgs.count("mitkMcxyz")) + { + MITK_INFO << "mitkMcxyz"; + input.exePath = us::any_cast(parsedArgs["mitkMcxyz"]); + } + + if (parsedArgs.count("probe")) + { + MITK_INFO << "probe"; + input.probePath = us::any_cast(parsedArgs["probe"]); + } + + if (parsedArgs.count("identifyer")) + { + MITK_INFO << "identifyer"; + input.identifyer = us::any_cast(parsedArgs["identifyer"]); + } + else + { + MITK_INFO << "generating identifyer"; + auto uid = mitk::UIDGenerator("", 8); + input.identifyer = uid.GetUID(); + } + MITK_INFO << "Paring arguments...[Done]"; + return input; +} + +int main(int argc, char * argv[]) +{ + auto input = parseInput(argc, argv); + unsigned int iterationNumber = 0; + + while (true) + { + auto parameters = CreateBaselineHB_13_02_18_Parameters(); + MITK_INFO(input.verbose) << "Generating tissue.."; + auto resultTissue = InSilicoTissueGenerator::GenerateInSilicoData(parameters); + MITK_INFO(input.verbose) << "Generating tissue..[Done]"; + + auto inputfolder = std::string(input.saveFolderPath + "input/"); + auto outputfolder = std::string(input.saveFolderPath + "output/"); + if (!itksys::SystemTools::FileIsDirectory(inputfolder)) + { + itksys::SystemTools::MakeDirectory(inputfolder); + } + if (!itksys::SystemTools::FileIsDirectory(outputfolder)) + { + itksys::SystemTools::MakeDirectory(outputfolder); + } + + std::string savePath = input.saveFolderPath + "input/BaselineHB_" + input.identifyer + + "_" + std::to_string(iterationNumber) + ".nrrd"; + mitk::IOUtil::Save(resultTissue->ConvertToMitkImage(), savePath); + std::string outputPath = input.saveFolderPath + "output/BaselineHB_" + input.identifyer + + "_" + std::to_string(iterationNumber) + "/"; + + if (!itksys::SystemTools::FileIsDirectory(outputPath)) + { + itksys::SystemTools::MakeDirectory(outputPath); + } + + outputPath = outputPath + "Fluence_BaselineHB_" + input.identifyer + "_" + std::to_string(iterationNumber); + + MITK_INFO(input.verbose) << "Simulating fluence.."; + for(double yo = -1.8; yo <= 1.81; yo=yo+0.12) + { + std::string yo_string = std::to_string(round(yo*100)/100.0); + int result = -4; + if(!input.probePath.empty()) + result = std::system(std::string(input.exePath + " -i " + savePath + " -o " + + (outputPath + "_yo" + yo_string + ".nrrd") + + " -yo " + yo_string + " -p " + input.probePath + + " -n 100000000").c_str()); + else + result = std::system(std::string(input.exePath + " -i " + savePath + " -o " + + (outputPath + "_yo" + yo_string + ".nrrd") + + " -yo " + yo_string + " -n 100000000").c_str()); + MITK_INFO << "yo: " << yo_string << ": " << result; + } + + MITK_INFO(input.verbose) << "Simulating fluence..[Done]"; + + iterationNumber++; + } +} diff --git a/Modules/PhotoacousticsLib/MitkTissueBatchGenerator/files.cmake b/Modules/PhotoacousticsLib/MitkTissueBatchGenerator/files.cmake new file mode 100644 index 0000000000..d4e42c83d5 --- /dev/null +++ b/Modules/PhotoacousticsLib/MitkTissueBatchGenerator/files.cmake @@ -0,0 +1,3 @@ +set(CPP_FILES + TissueBatchGenerator.cpp +) diff --git a/Modules/PhotoacousticsLib/files.cmake b/Modules/PhotoacousticsLib/files.cmake index 8b14417421..3f62323168 100644 --- a/Modules/PhotoacousticsLib/files.cmake +++ b/Modules/PhotoacousticsLib/files.cmake @@ -1,52 +1,56 @@ SET(H_FILES include/mitkPAPropertyCalculator.h include/mitkPAVector.h include/mitkPATissueGeneratorParameters.h include/mitkPAInSilicoTissueVolume.h + include/mitkPAPhantomTissueGenerator.h include/mitkPATissueGenerator.h include/mitkPAVesselTree.h include/mitkPAVessel.h + include/mitkPAVesselDrawer.h include/mitkPAVesselMeanderStrategy.h include/mitkPANoiseGenerator.h include/mitkPAVolume.h include/mitkPAComposedVolume.h include/mitkPASlicedVolumeGenerator.h include/mitkPAProbe.h include/mitkPALightSource.h include/mitkPAIOUtil.h include/mitkPAMonteCarloThreadHandler.h include/mitkPASimulationBatchGenerator.h include/mitkPAFluenceYOffsetPair.h include/mitkPAVolumeManipulator.h include/mitkPAVesselProperties.h include/mitkPASimulationBatchGeneratorParameters.h include/mitkPAExceptions.h ) set(CPP_FILES Domain/Vessel/mitkPAVesselTree.cpp Domain/Vessel/mitkPAVessel.cpp Domain/Vessel/mitkPAVesselMeanderStrategy.cpp Domain/Vessel/mitkPAVesselProperties.cpp Domain/Volume/mitkPAInSilicoTissueVolume.cpp Domain/Volume/mitkPAVolume.cpp Domain/Volume/mitkPAComposedVolume.cpp Domain/Volume/mitkPAFluenceYOffsetPair.cpp Generator/mitkPATissueGenerator.cpp + Generator/mitkPAPhantomTissueGenerator.cpp Generator/mitkPANoiseGenerator.cpp Generator/mitkPASlicedVolumeGenerator.cpp Generator/mitkPASimulationBatchGenerator.cpp Generator/mitkPASimulationBatchGeneratorParameters.cpp IO/mitkPAIOUtil.cpp Utils/mitkPAPropertyCalculator.cpp Utils/mitkPAVector.cpp Utils/mitkPATissueGeneratorParameters.cpp Utils/mitkPAVolumeManipulator.cpp Utils/ProbeDesign/mitkPAProbe.cpp Utils/ProbeDesign/mitkPALightSource.cpp Utils/Thread/mitkPAMonteCarloThreadHandler.cpp + Utils/mitkPAVesselDrawer.cpp ) set(RESOURCE_FILES spectralLIB.dat ) diff --git a/Modules/PhotoacousticsLib/include/mitkPAInSilicoTissueVolume.h b/Modules/PhotoacousticsLib/include/mitkPAInSilicoTissueVolume.h index db6ed6a7a2..732bb1040e 100644 --- a/Modules/PhotoacousticsLib/include/mitkPAInSilicoTissueVolume.h +++ b/Modules/PhotoacousticsLib/include/mitkPAInSilicoTissueVolume.h @@ -1,140 +1,164 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef MITKPHOTOACOUSTICVOLUME_H #define MITKPHOTOACOUSTICVOLUME_H #include #include #include #include #include //Includes for smart pointer usage #include "mitkCommon.h" #include "itkLightObject.h" namespace mitk { namespace pa { class MITKPHOTOACOUSTICSLIB_EXPORT InSilicoTissueVolume : public itk::LightObject { public: mitkClassMacroItkParent(InSilicoTissueVolume, itk::LightObject) - mitkNewMacro1Param(Self, TissueGeneratorParameters::Pointer) + mitkNewMacro2Param(Self, TissueGeneratorParameters::Pointer, std::mt19937*) - enum SegmentationType + enum SegmentationType { AIR = -1, BACKGROUND = 0, VESSEL = 1, FAT = 2, SKIN = 3 }; /** * @brief ConvertToMitkImage * @return a pointer to an mitk image containing this volume. */ mitk::Image::Pointer ConvertToMitkImage(); /** * @brief SetVolumeValues sets the values for aborption, scattering and anisotropy at the specified voxel location. * * @param x * @param y * @param z * @param absorption * @param scattering * @param anisotropy * @param segmentType */ - void SetVolumeValues(int x, int y, int z, double absorption, double scattering, double anisotropy, SegmentationType segmentType = SegmentationType::BACKGROUND); + void SetVolumeValues(int x, int y, int z, double absorption, double scattering, double anisotropy, SegmentationType segmentType); + + /** + * @brief SetVolumeValues sets the values for aborption, scattering and anisotropy at the specified voxel location. + * + * @param x + * @param y + * @param z + * @param absorption + * @param scattering + * @param anisotropy + */ + void SetVolumeValues(int x, int y, int z, double absorption, double scattering, double anisotropy); /** * @brief IsInsideVolume * * @param x * @param y * @param z * @return true if the voxel location is inside the volume */ bool IsInsideVolume(int x, int y, int z); /** * @brief AddDoubleProperty adds a persistent property to the volume, which will be exported to the mitk image. * * @param label * @param value */ void AddDoubleProperty(std::string label, double value); /** * @brief AddIntProperty adds a persistent property to the volume, which will be exported to the mitk image. * * @param label * @param value */ void AddIntProperty(std::string label, int value); Volume::Pointer GetAbsorptionVolume(); Volume::Pointer GetScatteringVolume(); Volume::Pointer GetAnisotropyVolume(); Volume::Pointer GetSegmentationVolume(); + void SetAbsorptionVolume(Volume::Pointer volume); + void SetScatteringVolume(Volume::Pointer volume); + void SetAnisotropyVolume(Volume::Pointer volume); + void SetSegmentationVolume(Volume::Pointer volume); + + double GetSpacing(); + void SetSpacing(double spacing); + void FinalizeVolume(); itkGetMacro(TissueParameters, TissueGeneratorParameters::Pointer); itkGetMacro(TDim, unsigned int); static InSilicoTissueVolume::Pointer New(mitk::pa::Volume::Pointer absorptionVolume, Volume::Pointer scatteringVolume, Volume::Pointer anisotropyVolume, Volume::Pointer segmentationVolume, TissueGeneratorParameters::Pointer tissueParameters, mitk::PropertyList::Pointer propertyList); protected: - InSilicoTissueVolume(TissueGeneratorParameters::Pointer parameters); + InSilicoTissueVolume(TissueGeneratorParameters::Pointer parameters, std::mt19937* rng); InSilicoTissueVolume(Volume::Pointer absorptionVolume, Volume::Pointer scatteringVolume, Volume::Pointer anisotropyVolume, Volume::Pointer segmentationVolume, TissueGeneratorParameters::Pointer tissueParameters, mitk::PropertyList::Pointer propertyList); ~InSilicoTissueVolume() override; mitk::pa::Volume::Pointer m_AbsorptionVolume; mitk::pa::Volume::Pointer m_ScatteringVolume; mitk::pa::Volume::Pointer m_AnisotropyVolume; mitk::pa::Volume::Pointer m_SegmentationVolume; TissueGeneratorParameters::Pointer m_TissueParameters; unsigned int m_TDim; + double m_InitialBackgroundAbsorption; + + std::mt19937* m_Rng; + void RandomizeTissueCoefficients(long rngSeed, bool useRngSeed, double percentage); mitk::PropertyList::Pointer m_PropertyList; private: void FillZLayer(int x, int y, double startIdx, double endIdx, double absorption, double scattering, double anisotropy, SegmentationType segmentationType); void AddSkinAndAirLayers(); void UpdatePropertyList(); }; } } #endif // MITKPHOTOACOUSTICVOLUME_H diff --git a/Modules/PhotoacousticsLib/include/mitkPAPhantomTissueGenerator.h b/Modules/PhotoacousticsLib/include/mitkPAPhantomTissueGenerator.h new file mode 100644 index 0000000000..f60c62de7c --- /dev/null +++ b/Modules/PhotoacousticsLib/include/mitkPAPhantomTissueGenerator.h @@ -0,0 +1,52 @@ +/*=================================================================== + +The Medical Imaging Interaction Toolkit (MITK) + +Copyright (c) German Cancer Research Center, +Division of Medical and Biological Informatics. +All rights reserved. + +This software is distributed WITHOUT ANY WARRANTY; without +even the implied warranty of MERCHANTABILITY or FITNESS FOR +A PARTICULAR PURPOSE. + +See LICENSE.txt or http://www.mitk.org for details. + +===================================================================*/ + +#ifndef mitkPhotoacousticPhantomTissueGenerator_h +#define mitkPhotoacousticPhantomTissueGenerator_h + +#include +#include +#include +#include + +#include +#include + +#include "mitkPAVesselTree.h" +#include "mitkPAInSilicoTissueVolume.h" + +#include "mitkCommon.h" + +namespace mitk { + namespace pa { + class MITKPHOTOACOUSTICSLIB_EXPORT PhantomTissueGenerator final + { + public: + + /** + * @brief GenerateInSilicoData This method will return a InSilicoTissueVolume created in terms of the given parameters. + * @param parameters + * @return + */ + static InSilicoTissueVolume::Pointer GeneratePhantomData(TissueGeneratorParameters::Pointer parameters); + + private: + PhantomTissueGenerator(); + virtual ~PhantomTissueGenerator(); + }; + } +} +#endif diff --git a/Modules/PhotoacousticsLib/include/mitkPATissueGeneratorParameters.h b/Modules/PhotoacousticsLib/include/mitkPATissueGeneratorParameters.h index 9d8e833f29..2ab5fcf444 100644 --- a/Modules/PhotoacousticsLib/include/mitkPATissueGeneratorParameters.h +++ b/Modules/PhotoacousticsLib/include/mitkPATissueGeneratorParameters.h @@ -1,214 +1,217 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef MITKPHOTOACOUSTICTISSUEGENERATORPARAMETERS_H #define MITKPHOTOACOUSTICTISSUEGENERATORPARAMETERS_H #include #include //Includes for smart pointer usage #include "mitkCommon.h" #include "itkLightObject.h" namespace mitk { namespace pa { class MITKPHOTOACOUSTICSLIB_EXPORT TissueGeneratorParameters : public itk::Object { public: mitkClassMacroItkParent(TissueGeneratorParameters, itk::Object) itkFactorylessNewMacro(Self) /** * Callback function definition of a VesselMeanderStrategy */ typedef void (VesselMeanderStrategy::*CalculateNewVesselPositionCallback) (Vector::Pointer, Vector::Pointer, double, std::mt19937*); itkGetMacro(XDim, int) itkGetMacro(YDim, int) itkGetMacro(ZDim, int) itkGetMacro(VoxelSpacingInCentimeters, double) - itkGetMacro(VolumeSmoothingSigma, double) - itkGetMacro(DoVolumeSmoothing, bool) + itkGetMacro(DoPartialVolume, bool) itkGetMacro(UseRngSeed, bool) itkGetMacro(RngSeed, long) itkGetMacro(RandomizePhysicalProperties, bool) itkGetMacro(RandomizePhysicalPropertiesPercentage, double) + itkGetMacro(ForceVesselsMoveAlongYDirection, bool) - itkGetMacro(BackgroundAbsorption, double) + itkGetMacro(MinBackgroundAbsorption, double) + itkGetMacro(MaxBackgroundAbsorption, double) itkGetMacro(BackgroundScattering, double) itkGetMacro(BackgroundAnisotropy, double) itkGetMacro(AirAbsorption, double) itkGetMacro(AirScattering, double) itkGetMacro(AirAnisotropy, double) itkGetMacro(AirThicknessInMillimeters, double) itkGetMacro(SkinAbsorption, double) itkGetMacro(SkinScattering, double) itkGetMacro(SkinAnisotropy, double) itkGetMacro(SkinThicknessInMillimeters, double) itkGetMacro(CalculateNewVesselPositionCallback, CalculateNewVesselPositionCallback) itkGetMacro(MinNumberOfVessels, int) itkGetMacro(MaxNumberOfVessels, int) itkGetMacro(MinVesselBending, double) itkGetMacro(MaxVesselBending, double) itkGetMacro(MinVesselAbsorption, double) itkGetMacro(MaxVesselAbsorption, double) itkGetMacro(MinVesselRadiusInMillimeters, double) itkGetMacro(MaxVesselRadiusInMillimeters, double) itkGetMacro(VesselBifurcationFrequency, int) itkGetMacro(MinVesselScattering, double) itkGetMacro(MaxVesselScattering, double) itkGetMacro(MinVesselAnisotropy, double) itkGetMacro(MaxVesselAnisotropy, double) itkGetMacro(MinVesselZOrigin, double) itkGetMacro(MaxVesselZOrigin, double) itkGetMacro(MCflag, double) itkGetMacro(MCLaunchflag, double) itkGetMacro(MCBoundaryflag, double) itkGetMacro(MCLaunchPointX, double) itkGetMacro(MCLaunchPointY, double) itkGetMacro(MCLaunchPointZ, double) itkGetMacro(MCFocusPointX, double) itkGetMacro(MCFocusPointY, double) itkGetMacro(MCFocusPointZ, double) itkGetMacro(MCTrajectoryVectorX, double) itkGetMacro(MCTrajectoryVectorY, double) itkGetMacro(MCTrajectoryVectorZ, double) itkGetMacro(MCRadius, double) itkGetMacro(MCWaist, double) itkSetMacro(XDim, int) itkSetMacro(YDim, int) itkSetMacro(ZDim, int) itkSetMacro(VoxelSpacingInCentimeters, double) - itkSetMacro(VolumeSmoothingSigma, double) - itkSetMacro(DoVolumeSmoothing, bool) + itkSetMacro(DoPartialVolume, bool) itkSetMacro(UseRngSeed, bool) itkSetMacro(RngSeed, long) itkSetMacro(RandomizePhysicalProperties, bool) itkSetMacro(RandomizePhysicalPropertiesPercentage, double) + itkSetMacro(ForceVesselsMoveAlongYDirection, bool) - itkSetMacro(BackgroundAbsorption, double) + itkSetMacro(MinBackgroundAbsorption, double) + itkSetMacro(MaxBackgroundAbsorption, double) itkSetMacro(BackgroundScattering, double) itkSetMacro(BackgroundAnisotropy, double) itkSetMacro(AirAbsorption, double) itkSetMacro(AirScattering, double) itkSetMacro(AirAnisotropy, double) itkSetMacro(AirThicknessInMillimeters, double) itkSetMacro(SkinAbsorption, double) itkSetMacro(SkinScattering, double) itkSetMacro(SkinAnisotropy, double) itkSetMacro(SkinThicknessInMillimeters, double) itkSetMacro(CalculateNewVesselPositionCallback, CalculateNewVesselPositionCallback) itkSetMacro(MinNumberOfVessels, int) itkSetMacro(MaxNumberOfVessels, int) itkSetMacro(MinVesselBending, double) itkSetMacro(MaxVesselBending, double) itkSetMacro(MinVesselAbsorption, double) itkSetMacro(MaxVesselAbsorption, double) itkSetMacro(MinVesselRadiusInMillimeters, double) itkSetMacro(MaxVesselRadiusInMillimeters, double) itkSetMacro(VesselBifurcationFrequency, int) itkSetMacro(MinVesselScattering, double) itkSetMacro(MaxVesselScattering, double) itkSetMacro(MinVesselAnisotropy, double) itkSetMacro(MaxVesselAnisotropy, double) itkSetMacro(MinVesselZOrigin, double) itkSetMacro(MaxVesselZOrigin, double) itkSetMacro(MCflag, double) itkSetMacro(MCLaunchflag, double) itkSetMacro(MCBoundaryflag, double) itkSetMacro(MCLaunchPointX, double) itkSetMacro(MCLaunchPointY, double) itkSetMacro(MCLaunchPointZ, double) itkSetMacro(MCFocusPointX, double) itkSetMacro(MCFocusPointY, double) itkSetMacro(MCFocusPointZ, double) itkSetMacro(MCTrajectoryVectorX, double) itkSetMacro(MCTrajectoryVectorY, double) itkSetMacro(MCTrajectoryVectorZ, double) itkSetMacro(MCRadius, double) itkSetMacro(MCWaist, double) protected: TissueGeneratorParameters(); - ~TissueGeneratorParameters() override; + ~TissueGeneratorParameters(); private: int m_XDim; int m_YDim; int m_ZDim; double m_VoxelSpacingInCentimeters; - double m_VolumeSmoothingSigma; - bool m_DoVolumeSmoothing; + bool m_DoPartialVolume; bool m_UseRngSeed; long m_RngSeed; bool m_RandomizePhysicalProperties; double m_RandomizePhysicalPropertiesPercentage; + bool m_ForceVesselsMoveAlongYDirection; - double m_BackgroundAbsorption; + double m_MinBackgroundAbsorption; + double m_MaxBackgroundAbsorption; double m_BackgroundScattering; double m_BackgroundAnisotropy; double m_AirAbsorption; double m_AirScattering; double m_AirAnisotropy; double m_AirThicknessInMillimeters; double m_SkinAbsorption; double m_SkinScattering; double m_SkinAnisotropy; double m_SkinThicknessInMillimeters; CalculateNewVesselPositionCallback m_CalculateNewVesselPositionCallback; int m_MinNumberOfVessels; int m_MaxNumberOfVessels; double m_MinVesselBending; double m_MaxVesselBending; double m_MinVesselAbsorption; double m_MaxVesselAbsorption; double m_MinVesselRadiusInMillimeters; double m_MaxVesselRadiusInMillimeters; int m_VesselBifurcationFrequency; double m_MinVesselScattering; double m_MaxVesselScattering; double m_MinVesselAnisotropy; double m_MaxVesselAnisotropy; double m_MinVesselZOrigin; double m_MaxVesselZOrigin; double m_MCflag; double m_MCLaunchflag; double m_MCBoundaryflag; double m_MCLaunchPointX; double m_MCLaunchPointY; double m_MCLaunchPointZ; double m_MCFocusPointX; double m_MCFocusPointY; double m_MCFocusPointZ; double m_MCTrajectoryVectorX; double m_MCTrajectoryVectorY; double m_MCTrajectoryVectorZ; double m_MCRadius; double m_MCWaist; }; } } #endif // MITKPHOTOACOUSTICTISSUEGENERATORPARAMETERS_H diff --git a/Modules/PhotoacousticsLib/include/mitkPAVector.h b/Modules/PhotoacousticsLib/include/mitkPAVector.h index 8e20ce895a..b4901b7248 100644 --- a/Modules/PhotoacousticsLib/include/mitkPAVector.h +++ b/Modules/PhotoacousticsLib/include/mitkPAVector.h @@ -1,132 +1,136 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef MITKSMARTVECTOR_H #define MITKSMARTVECTOR_H #include #include #include //Includes for smart pointer usage #include "mitkCommon.h" #include "itkLightObject.h" namespace mitk { namespace pa { class MITKPHOTOACOUSTICSLIB_EXPORT Vector : public itk::LightObject { public: mitkClassMacroItkParent(Vector, itk::LightObject) itkFactorylessNewMacro(Self) /** * @brief GetNorm calculates the length of this vector. * @return the euclidean norm */ double GetNorm(); double GetElement(unsigned short index); void SetElement(unsigned short index, double value); /** * @brief Normalize normalizes this vector. After calling this GetNorm() will return 1. */ void Normalize(); void SetValue(Vector::Pointer value); /** * @brief RandomizeByPercentage alters this vector randomly by [-percentage, percentage] of the bendingFactor. * * @param percentage * @param bendingFactor */ void RandomizeByPercentage(double percentage, double bendingFactor, std::mt19937* rng); /** * @brief Randomize randomizes this vector to be [lowerLimit, upperLimit] in each element * * @param xLowerLimit * @param xUpperLimit * @param yLowerLimit * @param yUpperLimit * @param zLowerLimit * @param zUpperLimit */ void Randomize(double xLowerLimit, double xUpperLimit, double yLowerLimit, double yUpperLimit, double zLowerLimit, double zUpperLimit, std::mt19937* rng); /** * @brief Randomize randomizes this vector to be [0, limit] in each element * * @param xLimit * @param yLimit * @param zLimit */ void Randomize(double xLimit, double yLimit, double zLimit, std::mt19937* rng); /** * @brief Randomize randomizes this vector to be [-1, 1] in each element */ void Randomize(std::mt19937* rng); /** * @brief Rotate rotates this Vector around the x, y and z axis with the given angles in radians * * @param thetaChange rotation of the inclination angle in radians * @param phiChange rotation of the azimuthal angle in radians */ void Rotate(double xAngle, double yAngle); /** * @brief Scale scales this Vector with the given factor * * @param factor the scaling factor * * If a negative number is provided, the direction of the vector will be inverted. */ void Scale(double factor); /** * @brief Clone create a deep copy of this vector * * @return a new vector with the same values. */ Vector::Pointer Clone(); + void Subtract(Vector::Pointer other); + + void Add(Vector::Pointer other); + protected: Vector(); ~Vector() override; void PrintSelf(std::ostream& os, itk::Indent indent) const override; private: mitk::Vector3D m_Vector; }; /** * @brief Equal A function comparing two vectors for beeing equal * * @param rightHandSide A Vector to be compared * @param leftHandSide A Vector to be compared * @param eps tolarence for comparison. You can use mitk::eps in most cases. * @param verbose flag indicating if the user wants detailed console output or not. * @return true, if all subsequent comparisons are true, false otherwise */ MITKPHOTOACOUSTICSLIB_EXPORT bool Equal(const Vector::Pointer leftHandSide, const Vector::Pointer rightHandSide, double eps, bool verbose); } } #endif // MITKSMARTVECTOR_H diff --git a/Modules/PhotoacousticsLib/include/mitkPAVessel.h b/Modules/PhotoacousticsLib/include/mitkPAVessel.h index f49289e5a6..f7e4b777a7 100644 --- a/Modules/PhotoacousticsLib/include/mitkPAVessel.h +++ b/Modules/PhotoacousticsLib/include/mitkPAVessel.h @@ -1,116 +1,118 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef MITKVESSEL_H #define MITKVESSEL_H #include "mitkVector.h" #include "mitkPAVesselMeanderStrategy.h" #include "mitkPAInSilicoTissueVolume.h" #include "mitkPAVector.h" #include "mitkPAVesselProperties.h" +#include "mitkPAVesselDrawer.h" #include //Includes for smart pointer usage #include "mitkCommon.h" #include "itkLightObject.h" namespace mitk { namespace pa { class MITKPHOTOACOUSTICSLIB_EXPORT Vessel : public itk::LightObject { public: mitkClassMacroItkParent(Vessel, itk::LightObject) mitkNewMacro1Param(Self, VesselProperties::Pointer) /** * Callback function definition of a VesselMeanderStrategy */ typedef void (VesselMeanderStrategy::*CalculateNewVesselPositionCallback) (Vector::Pointer, Vector::Pointer, double, std::mt19937*); /** * @brief ExpandVessel makes this Vessel expand one step in its current direction. * After expanding, the vessel will draw itself into the given InSilicoTissueVolume. * * @param volume volume for the vessel to draw itself in * @param calculateNewPosition a callback function of the VesselMeanderStrategy class. * It is used to calculate the final position after taking the step. * @param bendingFactor a metric of how much the Vessel should bend. If set to 0 the vessel will go in a straight line. */ void ExpandVessel(mitk::pa::InSilicoTissueVolume::Pointer volume, CalculateNewVesselPositionCallback calculateNewPosition, double bendingFactor, std::mt19937* rng); /** * @brief CanBifurcate * @return true if the Vessel is ready to Bifurcate() */ bool CanBifurcate(); /** * @brief Bifurcate bifurcates this vessel into two new ones. Makes sure that the volume of the vessels stays the same. * * @return a new vessel split up from the current one. */ Vessel::Pointer Bifurcate(std::mt19937* rng); /** * @brief IsFinished * @return true if the vessel cannot expand any further */ bool IsFinished(); itkGetConstMacro(VesselProperties, VesselProperties::Pointer); protected: Vessel(VesselProperties::Pointer parameters); ~Vessel() override; private: - const double MINIMUM_VESSEL_RADIUS = 1; + const double MINIMUM_VESSEL_RADIUS = 0.1; const double SCALING_FACTOR = 0.33; const double NEW_RADIUS_MINIMUM_RELATIVE_SIZE = 0.6; const double NEW_RADIUS_MAXIMUM_RELATIVE_SIZE = 0.8; - void DrawVesselInVolume(Vector::Pointer toPosition, mitk::pa::InSilicoTissueVolume::Pointer volume); - VesselProperties::Pointer m_VesselProperties; - VesselMeanderStrategy::Pointer m_VesselMeanderStrategy; bool m_Finished; double m_WalkedDistance; std::uniform_real_distribution<> m_RangeDistribution; std::uniform_real_distribution<> m_SignDistribution; std::uniform_real_distribution<> m_RadiusRangeDistribution; int GetSign(std::mt19937* rng); + + VesselProperties::Pointer m_VesselProperties; + + VesselDrawer::Pointer m_VesselDrawer; }; /** * @brief Equal A function comparing two vessels for beeing equal * * @param rightHandSide A vessel to be compared * @param leftHandSide A vessel to be compared * @param eps tolarence for comparison. You can use mitk::eps in most cases. * @param verbose flag indicating if the user wants detailed console output or not. * @return true, if all subsequent comparisons are true, false otherwise */ MITKPHOTOACOUSTICSLIB_EXPORT bool Equal(const Vessel::Pointer leftHandSide, const Vessel::Pointer rightHandSide, double eps, bool verbose); } } #endif // MITKVESSEL_H diff --git a/Modules/PhotoacousticsLib/include/mitkPAVesselDrawer.h b/Modules/PhotoacousticsLib/include/mitkPAVesselDrawer.h new file mode 100644 index 0000000000..e201d468a5 --- /dev/null +++ b/Modules/PhotoacousticsLib/include/mitkPAVesselDrawer.h @@ -0,0 +1,53 @@ +/*=================================================================== + +The Medical Imaging Interaction Toolkit (MITK) + +Copyright (c) German Cancer Research Center, +Division of Medical and Biological Informatics. +All rights reserved. + +This software is distributed WITHOUT ANY WARRANTY; without +even the implied warranty of MERCHANTABILITY or FITNESS FOR +A PARTICULAR PURPOSE. + +See LICENSE.txt or http://www.mitk.org for details. + +===================================================================*/ + +#ifndef MITKVESSELDRAWER_H +#define MITKVESSELDRAWER_H + +#include "mitkVector.h" +#include "mitkPAVesselMeanderStrategy.h" +#include "mitkPAInSilicoTissueVolume.h" +#include "mitkPAVector.h" +#include "mitkPAVesselProperties.h" + +#include + +//Includes for smart pointer usage +#include "mitkCommon.h" +#include "itkLightObject.h" + +namespace mitk { + namespace pa { + class MITKPHOTOACOUSTICSLIB_EXPORT VesselDrawer : public itk::LightObject + { + public: + mitkClassMacroItkParent(VesselDrawer, itk::LightObject); + itkFactorylessNewMacro(Self); + + void DrawVesselInVolume( + VesselProperties::Pointer properties, + InSilicoTissueVolume::Pointer volume); + + protected: + VesselDrawer(); + virtual ~VesselDrawer(); + + private: + }; + + } +} +#endif // MITKVESSELDRAWER_H diff --git a/Modules/PhotoacousticsLib/include/mitkPAVesselProperties.h b/Modules/PhotoacousticsLib/include/mitkPAVesselProperties.h index fa80d8386c..f19be3a4d2 100644 --- a/Modules/PhotoacousticsLib/include/mitkPAVesselProperties.h +++ b/Modules/PhotoacousticsLib/include/mitkPAVesselProperties.h @@ -1,80 +1,83 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef MITKPhotoacousticVesselParameters_H #define MITKPhotoacousticVesselParameters_H #include #include //Includes for smart pointer usage #include "mitkCommon.h" #include "itkLightObject.h" namespace mitk { namespace pa { class MITKPHOTOACOUSTICSLIB_EXPORT VesselProperties : public itk::Object { public: - mitkClassMacroItkParent(VesselProperties, itk::Object) - itkFactorylessNewMacro(Self) - mitkNewMacro1Param(Self, Self::Pointer) + mitkClassMacroItkParent(VesselProperties, itk::Object); + itkFactorylessNewMacro(Self); + mitkNewMacro1Param(Self, Self::Pointer); - itkGetMacro(PositionVector, Vector::Pointer) - itkGetMacro(DirectionVector, Vector::Pointer) - itkGetMacro(RadiusInVoxel, double) - itkGetMacro(AbsorptionCoefficient, double) - itkGetMacro(ScatteringCoefficient, double) - itkGetMacro(AnisotopyCoefficient, double) - itkGetMacro(BifurcationFrequency, double) + itkGetMacro(PositionVector, Vector::Pointer); + itkGetMacro(DirectionVector, Vector::Pointer); + itkGetMacro(RadiusInVoxel, double); + itkGetMacro(AbsorptionCoefficient, double); + itkGetMacro(ScatteringCoefficient, double); + itkGetMacro(AnisotopyCoefficient, double); + itkGetMacro(BifurcationFrequency, double); + itkGetMacro(DoPartialVolume, bool); - itkSetMacro(PositionVector, Vector::Pointer) - itkSetMacro(DirectionVector, Vector::Pointer) - itkSetMacro(RadiusInVoxel, double) - itkSetMacro(AbsorptionCoefficient, double) - itkSetMacro(ScatteringCoefficient, double) - itkSetMacro(AnisotopyCoefficient, double) - itkSetMacro(BifurcationFrequency, double) + itkSetMacro(PositionVector, Vector::Pointer); + itkSetMacro(DirectionVector, Vector::Pointer); + itkSetMacro(RadiusInVoxel, double); + itkSetMacro(AbsorptionCoefficient, double); + itkSetMacro(ScatteringCoefficient, double); + itkSetMacro(AnisotopyCoefficient, double); + itkSetMacro(BifurcationFrequency, double); + itkSetMacro(DoPartialVolume, bool); protected: VesselProperties(); VesselProperties(Self::Pointer other); ~VesselProperties() override; private: Vector::Pointer m_PositionVector; Vector::Pointer m_DirectionVector; double m_RadiusInVoxel; double m_AbsorptionCoefficient; double m_ScatteringCoefficient; double m_AnisotopyCoefficient; double m_BifurcationFrequency; + bool m_DoPartialVolume; }; /** * @brief Equal A function comparing two VesselProperty instances for beeing equal * * @param rightHandSide A VesselProperty to be compared * @param leftHandSide A Vesselproperty to be compared * @param eps tolarence for comparison. You can use mitk::eps in most cases. * @param verbose flag indicating if the user wants detailed console output or not. * @return true, if all subsequent comparisons are true, false otherwise */ MITKPHOTOACOUSTICSLIB_EXPORT bool Equal(const VesselProperties::Pointer leftHandSide, const VesselProperties::Pointer rightHandSide, double eps, bool verbose); } } #endif // MITKPhotoacousticVesselParameters_H diff --git a/Modules/PhotoacousticsLib/include/mitkPAVolume.h b/Modules/PhotoacousticsLib/include/mitkPAVolume.h index 01b62542ea..e6f60707a6 100644 --- a/Modules/PhotoacousticsLib/include/mitkPAVolume.h +++ b/Modules/PhotoacousticsLib/include/mitkPAVolume.h @@ -1,142 +1,149 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef MITKPHOTOACOUSTIC3dVOLUME_H #define MITKPHOTOACOUSTIC3dVOLUME_H #include "MitkPhotoacousticsLibExports.h" //Includes for smart pointer usage #include #include namespace mitk { namespace pa { /** * @brief The Volume class is designed to encapsulate volumetric information and to provide convenience methods * for data access and image conversions. */ class MITKPHOTOACOUSTICSLIB_EXPORT Volume : public itk::LightObject { public: - mitkClassMacroItkParent(Volume, itk::LightObject) - - /** - *@brief returns smartpointer reference to a new instance of this objects. - * The given data array will be freed upon calling this constructor. - *@param data - *@param xDim - *@param yDim - *@param zDim - *@return smartpointer reference to a new instance of this object - */ - static Volume::Pointer New(double* data, unsigned int xDim, unsigned int yDim, unsigned int zDim); + mitkClassMacroItkParent(Volume, itk::LightObject); + + /** + *@brief returns smartpointer reference to a new instance of this objects. + * The given data array will be freed upon calling this constructor. + *@param data + *@param xDim + *@param yDim + *@param zDim + *@return smartpointer reference to a new instance of this object + */ + static Volume::Pointer New(double* data, unsigned int xDim, unsigned int yDim, unsigned int zDim, double spacing); + + static Volume::Pointer New(mitk::Image::Pointer image); /** * @brief GetData. Returns data at wanted position. For performance reasons, this method will not check, * if the specified position it within the array. Please use the GetXDim(), GetYDim() and GetZDim() methods * to check for yourself if necessary. * * @param x * @param y * @param z * @return the data contained within the data array held by this Volume at * positon x|y|z. */ double GetData(unsigned int x, unsigned int y, unsigned int z); /** * Returns a const reference to the data encapsuled by this class. */ double* GetData() const; /** * @brief SetData * @param data * @param x * @param y * @param z */ void SetData(double data, unsigned int x, unsigned int y, unsigned int z); /** * @brief GetXDim * @return size of x dimension of this Volume */ unsigned int GetXDim(); /** * @brief GetYDim * @return size of y dimension of this Volume */ unsigned int GetYDim(); /** * @brief GetZDim * @return size of z dimension of this Volume */ unsigned int GetZDim(); /** *@brief returns the Volume instance as an mitk image */ Image::Pointer AsMitkImage(); /** * @brief DeepCopy * @return a deep copy of this Volume. the old volume remains intact and memory is NOT shared * between the objects. */ Volume::Pointer DeepCopy(); /** *@brief convenience method to enable consistent access to the dat array *@return a 1d index from 3d pixel coordinates */ - int GetIndex(unsigned int x, unsigned int y, unsigned int z); + long long GetIndex(unsigned int x, unsigned int y, unsigned int z); + + double GetSpacing(); + + void SetSpacing(double spacing); protected: /** * @brief Initialize initializes this volume with the given pointer to the data array. * It is assumed, that the array is of dimension xDim|yDim|zDim. * The Photoacoustic3DVolume will handle memory management of the array and delete it on * constructor call. * * @param data a pointer to the data array * @param xDim x dimension of the data * @param yDim y dimension of the data * @param zDim z dimension of the data */ - Volume(double* data, unsigned int xDim, unsigned int yDim, unsigned int zDim); - ~Volume() override; + Volume(double* data, unsigned int xDim, unsigned int yDim, unsigned int zDim, double spacing); + Volume(mitk::Image::Pointer image); + virtual ~Volume(); const int NUMBER_OF_SPATIAL_DIMENSIONS = 3; Image::Pointer m_InternalMitkImage; // this data is kept to enable fast access unsigned int m_XDim; unsigned int m_YDim; unsigned int m_ZDim; double* m_FastAccessDataPointer; }; } } #endif // MITKPHOTOACOUSTIC3dVOLUME_H diff --git a/Modules/PhotoacousticsLib/include/mitkPAVolumeManipulator.h b/Modules/PhotoacousticsLib/include/mitkPAVolumeManipulator.h index 0aa1999d61..3a02d50974 100644 --- a/Modules/PhotoacousticsLib/include/mitkPAVolumeManipulator.h +++ b/Modules/PhotoacousticsLib/include/mitkPAVolumeManipulator.h @@ -1,53 +1,58 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef MITKPHOTOACOUSTIC3DVOLUMEMANIPULATOR_H #define MITKPHOTOACOUSTIC3DVOLUMEMANIPULATOR_H #include #include #include "mitkPAVolume.h" +#include "mitkPAInSilicoTissueVolume.h" namespace mitk { namespace pa { class MITKPHOTOACOUSTICSLIB_EXPORT VolumeManipulator final { public: /** * @brief ThresholdImage applies a binary threshold filter to this image. * @param threshold */ - static void ThresholdImage(mitk::pa::Volume::Pointer image, double threshold); + static void ThresholdImage(Volume::Pointer image, double threshold); /** * @brief Multiplies the image with a given factor * @param factor */ - static void MultiplyImage(mitk::pa::Volume::Pointer image, double factor); + static void MultiplyImage(Volume::Pointer image, double factor); - static void GaussianBlur3D(mitk::pa::Volume::Pointer paVolume, double sigma); + static void GaussianBlur3D(Volume::Pointer paVolume, double sigma); - static void Log10Image(mitk::pa::Volume::Pointer image); + static void Log10Image(Volume::Pointer image); + + static void RescaleImage(InSilicoTissueVolume::Pointer image, double ratio); + + static Volume::Pointer RescaleImage(Volume::Pointer image, double ratio, double sigma); private: VolumeManipulator(); virtual ~VolumeManipulator(); }; } } #endif // MITKPHOTOACOUSTIC3DVOLUMEMANIPULATOR_H diff --git a/Modules/PhotoacousticsLib/src/Domain/Vessel/mitkPAVessel.cpp b/Modules/PhotoacousticsLib/src/Domain/Vessel/mitkPAVessel.cpp index 236cf7c25e..c3c98426de 100644 --- a/Modules/PhotoacousticsLib/src/Domain/Vessel/mitkPAVessel.cpp +++ b/Modules/PhotoacousticsLib/src/Domain/Vessel/mitkPAVessel.cpp @@ -1,167 +1,115 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkPAVessel.h" #include #include mitk::pa::Vessel::Vessel(VesselProperties::Pointer initialProperties) : m_RangeDistribution(itk::Math::pi / 16, itk::Math::pi / 8), m_SignDistribution(-1, 1) { m_Finished = false; //Copy this so it may be reused for other vessels. m_VesselProperties = VesselProperties::New(initialProperties); m_RadiusRangeDistribution = std::uniform_real_distribution<>(NEW_RADIUS_MINIMUM_RELATIVE_SIZE, NEW_RADIUS_MAXIMUM_RELATIVE_SIZE); m_VesselMeanderStrategy = VesselMeanderStrategy::New(); m_WalkedDistance = 0; + m_VesselDrawer = VesselDrawer::New(); } mitk::pa::Vessel::~Vessel() { m_VesselProperties = nullptr; m_VesselMeanderStrategy = nullptr; } void mitk::pa::Vessel::ExpandVessel(InSilicoTissueVolume::Pointer volume, CalculateNewVesselPositionCallback calculateNewPosition, double bendingFactor, std::mt19937* rng) { - Vector::Pointer oldPosition = m_VesselProperties->GetPositionVector()->Clone(); + m_VesselDrawer->DrawVesselInVolume(m_VesselProperties, volume); (m_VesselMeanderStrategy->*calculateNewPosition)(m_VesselProperties->GetPositionVector(), m_VesselProperties->GetDirectionVector(), bendingFactor, rng); - DrawVesselInVolume(oldPosition, volume); + m_WalkedDistance += (m_VesselProperties->GetDirectionVector()->GetNorm() / volume->GetSpacing()); } bool mitk::pa::Vessel::CanBifurcate() { return m_VesselProperties->GetBifurcationFrequency() < m_WalkedDistance; } int mitk::pa::Vessel::GetSign(std::mt19937 *rng) { if (m_SignDistribution(*rng) < 0) return -1; return 1; } mitk::pa::Vessel::Pointer mitk::pa::Vessel::Bifurcate(std::mt19937* rng) { VesselProperties::Pointer vesselParams = VesselProperties::New(m_VesselProperties); double thetaChange = m_RangeDistribution(*rng) * GetSign(rng); double phiChange = m_RangeDistribution(*rng) * GetSign(rng); vesselParams->GetDirectionVector()->Rotate(thetaChange, phiChange); m_VesselProperties->GetDirectionVector()->Rotate(-thetaChange, -phiChange); double newRadius = m_RadiusRangeDistribution(*rng)*m_VesselProperties->GetRadiusInVoxel(); vesselParams->SetRadiusInVoxel(newRadius); m_VesselProperties->SetRadiusInVoxel( sqrt(m_VesselProperties->GetRadiusInVoxel()*m_VesselProperties->GetRadiusInVoxel() - newRadius*newRadius)); m_WalkedDistance = 0; return Vessel::New(vesselParams); } -void mitk::pa::Vessel::DrawVesselInVolume(Vector::Pointer fromPosition, - InSilicoTissueVolume::Pointer volume) -{ - Vector::Pointer diffVector = Vector::New(); - Vector::Pointer toPosition = m_VesselProperties->GetPositionVector(); - diffVector->SetElement(0, fromPosition->GetElement(0) - toPosition->GetElement(0)); - diffVector->SetElement(1, fromPosition->GetElement(1) - toPosition->GetElement(1)); - diffVector->SetElement(2, fromPosition->GetElement(2) - toPosition->GetElement(2)); - - //1/SCALING_FACTOR steps along the direction vector are taken and drawn into the image. - Vector::Pointer stepSize = Vector::New(); - stepSize->SetValue(m_VesselProperties->GetDirectionVector()); - stepSize->Scale(SCALING_FACTOR); - - while (diffVector->GetNorm() >= SCALING_FACTOR) - { - m_WalkedDistance += stepSize->GetNorm(); - - fromPosition->SetElement(0, fromPosition->GetElement(0) + stepSize->GetElement(0)); - fromPosition->SetElement(1, fromPosition->GetElement(1) + stepSize->GetElement(1)); - fromPosition->SetElement(2, fromPosition->GetElement(2) + stepSize->GetElement(2)); - - int xPos = fromPosition->GetElement(0); - int yPos = fromPosition->GetElement(1); - int zPos = fromPosition->GetElement(2); - - if (!volume->IsInsideVolume(xPos, yPos, zPos)) - { - m_VesselProperties->SetRadiusInVoxel(0); - break; - } - - double radius = m_VesselProperties->GetRadiusInVoxel(); - - for (int x = xPos - radius; x <= xPos + radius; x++) - for (int y = yPos - radius; y <= yPos + radius; y++) - for (int z = zPos - radius; z <= zPos + radius; z++) - { - if (radius*radius >= (x - xPos)*(x - xPos) + (y - yPos)*(y - yPos) + (z - zPos)*(z - zPos)) - { - volume->SetVolumeValues(x, y, z, m_VesselProperties->GetAbsorptionCoefficient(), - m_VesselProperties->GetScatteringCoefficient(), - m_VesselProperties->GetAnisotopyCoefficient(), - mitk::pa::InSilicoTissueVolume::SegmentationType::VESSEL); - } - } - - diffVector->SetElement(0, fromPosition->GetElement(0) - toPosition->GetElement(0)); - diffVector->SetElement(1, fromPosition->GetElement(1) - toPosition->GetElement(1)); - diffVector->SetElement(2, fromPosition->GetElement(2) - toPosition->GetElement(2)); - } -} - bool mitk::pa::Vessel::IsFinished() { return m_VesselProperties->GetRadiusInVoxel() < MINIMUM_VESSEL_RADIUS; } bool mitk::pa::Equal(const Vessel::Pointer leftHandSide, const Vessel::Pointer rightHandSide, double eps, bool verbose) { MITK_INFO(verbose) << "=== mitk::pa::Vessel Equal ==="; if (rightHandSide.IsNull() || leftHandSide.IsNull()) { MITK_INFO(verbose) << "Cannot compare nullpointers"; return false; } if (leftHandSide->IsFinished() != rightHandSide->IsFinished()) { MITK_INFO(verbose) << "Not same finished state."; return false; } if (leftHandSide->CanBifurcate() != rightHandSide->CanBifurcate()) { MITK_INFO(verbose) << "Not same bifurcation state."; return false; } if (!Equal(leftHandSide->GetVesselProperties(), rightHandSide->GetVesselProperties(), eps, verbose)) { MITK_INFO(verbose) << "Vesselproperties not equal"; return false; } return true; } diff --git a/Modules/PhotoacousticsLib/src/Domain/Vessel/mitkPAVesselMeanderStrategy.cpp b/Modules/PhotoacousticsLib/src/Domain/Vessel/mitkPAVesselMeanderStrategy.cpp index 699235bdac..14229f6577 100644 --- a/Modules/PhotoacousticsLib/src/Domain/Vessel/mitkPAVesselMeanderStrategy.cpp +++ b/Modules/PhotoacousticsLib/src/Domain/Vessel/mitkPAVesselMeanderStrategy.cpp @@ -1,53 +1,47 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkPAVesselMeanderStrategy.h" mitk::pa::VesselMeanderStrategy::VesselMeanderStrategy() { } mitk::pa::VesselMeanderStrategy::~VesselMeanderStrategy() { } void mitk::pa::VesselMeanderStrategy::CalculateNewPositionInStraightLine( - Vector::Pointer position, Vector::Pointer direction, double /*bendingFactor*/, std::mt19937* rng) + Vector::Pointer /*position*/, Vector::Pointer direction, double /*bendingFactor*/, std::mt19937* rng) { if (direction->GetNorm() <= mitk::eps) { direction->Randomize(rng); } - - position->SetElement(0, position->GetElement(0) + direction->GetElement(0)); - position->SetElement(1, position->GetElement(1) + direction->GetElement(1)); - position->SetElement(2, position->GetElement(2) + direction->GetElement(2)); } void mitk::pa::VesselMeanderStrategy::CalculateRandomlyDivergingPosition( - Vector::Pointer position, Vector::Pointer direction, double bendingFactor, std::mt19937* rng) + Vector::Pointer /*position*/, Vector::Pointer direction, double bendingFactor, std::mt19937* rng) { if (direction->GetNorm() <= mitk::eps) { direction->Randomize(rng); } direction->RandomizeByPercentage(RANDOMIZATION_PERCENTAGE, bendingFactor, rng); - position->SetElement(0, position->GetElement(0) + direction->GetElement(0)); - position->SetElement(1, position->GetElement(1) + direction->GetElement(1)); - position->SetElement(2, position->GetElement(2) + direction->GetElement(2)); + direction->Normalize(); } diff --git a/Modules/PhotoacousticsLib/src/Domain/Vessel/mitkPAVesselProperties.cpp b/Modules/PhotoacousticsLib/src/Domain/Vessel/mitkPAVesselProperties.cpp index 63ebd6de6d..c3800ef171 100644 --- a/Modules/PhotoacousticsLib/src/Domain/Vessel/mitkPAVesselProperties.cpp +++ b/Modules/PhotoacousticsLib/src/Domain/Vessel/mitkPAVesselProperties.cpp @@ -1,101 +1,109 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkPAVesselProperties.h" mitk::pa::VesselProperties::VesselProperties() { m_PositionVector = Vector::New(); m_DirectionVector = Vector::New(); m_RadiusInVoxel = 0; m_AbsorptionCoefficient = 0; m_ScatteringCoefficient = 0; m_AnisotopyCoefficient = 0; m_BifurcationFrequency = 0; + m_DoPartialVolume = false; } mitk::pa::VesselProperties::VesselProperties(Self::Pointer other) { m_PositionVector = other->GetPositionVector()->Clone(); m_DirectionVector = other->GetDirectionVector()->Clone(); m_RadiusInVoxel = other->GetRadiusInVoxel(); m_AbsorptionCoefficient = other->GetAbsorptionCoefficient(); m_ScatteringCoefficient = other->GetScatteringCoefficient(); m_AnisotopyCoefficient = other->GetAnisotopyCoefficient(); m_BifurcationFrequency = other->GetBifurcationFrequency(); + m_DoPartialVolume = other->GetDoPartialVolume(); } mitk::pa::VesselProperties::~VesselProperties() { m_PositionVector = nullptr; m_DirectionVector = nullptr; } bool mitk::pa::Equal(const VesselProperties::Pointer leftHandSide, const VesselProperties::Pointer rightHandSide, double eps, bool verbose) { MITK_INFO(verbose) << "=== mitk::pa::VesselProperties Equal ==="; if (rightHandSide.IsNull() || leftHandSide.IsNull()) { MITK_INFO(verbose) << "Cannot compare nullpointers"; return false; } if (leftHandSide->GetAbsorptionCoefficient() - rightHandSide->GetAbsorptionCoefficient() > eps) { MITK_INFO(verbose) << "Not the same AbsorptionCoefficient."; return false; } if (leftHandSide->GetAnisotopyCoefficient() - rightHandSide->GetAnisotopyCoefficient() > eps) { MITK_INFO(verbose) << "Not the same AnisotropyCoefficient."; return false; } if (leftHandSide->GetBifurcationFrequency() - rightHandSide->GetBifurcationFrequency() > eps) { MITK_INFO(verbose) << "Not the same BifurcationFrequency."; return false; } if (leftHandSide->GetRadiusInVoxel() - rightHandSide->GetRadiusInVoxel() > eps) { MITK_INFO(verbose) << "Not the same RadiusInVoxel."; return false; } if (leftHandSide->GetScatteringCoefficient() - rightHandSide->GetScatteringCoefficient() > eps) { MITK_INFO(verbose) << "Not the same ScatteringCoefficient."; return false; } if (!Equal(leftHandSide->GetPositionVector(), rightHandSide->GetPositionVector(), eps, verbose)) { MITK_INFO(verbose) << "PositionVector not equal"; return false; } if (!Equal(leftHandSide->GetDirectionVector(), rightHandSide->GetDirectionVector(), eps, verbose)) { - MITK_INFO(verbose) << "PositionVector not equal"; + MITK_INFO(verbose) << "DirectionVector not equal"; + return false; + } + + if (!(leftHandSide->GetDoPartialVolume() == rightHandSide->GetDoPartialVolume())) + { + MITK_INFO(verbose) << "GetDoPartialVolume not equal"; return false; } return true; } diff --git a/Modules/PhotoacousticsLib/src/Domain/Volume/mitkPAInSilicoTissueVolume.cpp b/Modules/PhotoacousticsLib/src/Domain/Volume/mitkPAInSilicoTissueVolume.cpp index 3d7b5501c1..9c7f308207 100644 --- a/Modules/PhotoacousticsLib/src/Domain/Volume/mitkPAInSilicoTissueVolume.cpp +++ b/Modules/PhotoacousticsLib/src/Domain/Volume/mitkPAInSilicoTissueVolume.cpp @@ -1,325 +1,387 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include #include #include #include #include #include #include #include #include -mitk::pa::InSilicoTissueVolume::InSilicoTissueVolume(TissueGeneratorParameters::Pointer parameters) +mitk::pa::InSilicoTissueVolume::InSilicoTissueVolume(TissueGeneratorParameters::Pointer parameters, std::mt19937* rng) { { unsigned int xDim = parameters->GetXDim(); unsigned int yDim = parameters->GetYDim(); unsigned int zDim = parameters->GetZDim(); - m_TDim = 4; + m_TDim = 3; unsigned int size = xDim * yDim * zDim; auto* absorptionArray = new double[size]; auto* scatteringArray = new double[size]; auto* anisotropyArray = new double[size]; auto* segmentationArray = new double[size]; + m_InitialBackgroundAbsorption = (parameters->GetMinBackgroundAbsorption() + parameters->GetMaxBackgroundAbsorption()) / 2; + m_Rng = rng; + for (unsigned int index = 0; index < size; index++) { - absorptionArray[index] = parameters->GetBackgroundAbsorption(); + absorptionArray[index] = m_InitialBackgroundAbsorption; scatteringArray[index] = parameters->GetBackgroundScattering(); anisotropyArray[index] = parameters->GetBackgroundAnisotropy(); segmentationArray[index] = SegmentationType::BACKGROUND; } - m_AbsorptionVolume = Volume::New(absorptionArray, xDim, yDim, zDim); - m_ScatteringVolume = Volume::New(scatteringArray, xDim, yDim, zDim); - m_AnisotropyVolume = Volume::New(anisotropyArray, xDim, yDim, zDim); - m_SegmentationVolume = Volume::New(segmentationArray, xDim, yDim, zDim); + m_AbsorptionVolume = Volume::New(absorptionArray, xDim, yDim, zDim, parameters->GetVoxelSpacingInCentimeters()); + m_ScatteringVolume = Volume::New(scatteringArray, xDim, yDim, zDim, parameters->GetVoxelSpacingInCentimeters()); + m_AnisotropyVolume = Volume::New(anisotropyArray, xDim, yDim, zDim, parameters->GetVoxelSpacingInCentimeters()); + m_SegmentationVolume = Volume::New(segmentationArray, xDim, yDim, zDim, parameters->GetVoxelSpacingInCentimeters()); } m_TissueParameters = parameters; m_PropertyList = mitk::PropertyList::New(); UpdatePropertyList(); } void mitk::pa::InSilicoTissueVolume::UpdatePropertyList() { //Set properties AddIntProperty("mcflag", m_TissueParameters->GetMCflag()); AddIntProperty("launchflag", m_TissueParameters->GetMCLaunchflag()); AddIntProperty("boundaryflag", m_TissueParameters->GetMCBoundaryflag()); AddDoubleProperty("launchPointX", m_TissueParameters->GetMCLaunchPointX()); AddDoubleProperty("launchPointY", m_TissueParameters->GetMCLaunchPointY()); AddDoubleProperty("launchPointZ", m_TissueParameters->GetMCLaunchPointZ()); AddDoubleProperty("focusPointX", m_TissueParameters->GetMCFocusPointX()); AddDoubleProperty("focusPointY", m_TissueParameters->GetMCFocusPointY()); AddDoubleProperty("focusPointZ", m_TissueParameters->GetMCFocusPointZ()); AddDoubleProperty("trajectoryVectorX", m_TissueParameters->GetMCTrajectoryVectorX()); AddDoubleProperty("trajectoryVectorY", m_TissueParameters->GetMCTrajectoryVectorY()); AddDoubleProperty("trajectoryVectorZ", m_TissueParameters->GetMCTrajectoryVectorZ()); AddDoubleProperty("radius", m_TissueParameters->GetMCRadius()); AddDoubleProperty("waist", m_TissueParameters->GetMCWaist()); - if (m_TissueParameters->GetDoVolumeSmoothing()) - AddDoubleProperty("sigma", m_TissueParameters->GetVolumeSmoothingSigma()); - else - AddDoubleProperty("sigma", 0); - AddDoubleProperty("standardTissueAbsorption", m_TissueParameters->GetBackgroundAbsorption()); + AddDoubleProperty("partialVolume", m_TissueParameters->GetDoPartialVolume()); + AddDoubleProperty("standardTissueAbsorptionMin", m_TissueParameters->GetMinBackgroundAbsorption()); + AddDoubleProperty("standardTissueAbsorptionMax", m_TissueParameters->GetMaxBackgroundAbsorption()); AddDoubleProperty("standardTissueScattering", m_TissueParameters->GetBackgroundScattering()); AddDoubleProperty("standardTissueAnisotropy", m_TissueParameters->GetBackgroundAnisotropy()); AddDoubleProperty("airThickness", m_TissueParameters->GetAirThicknessInMillimeters()); AddDoubleProperty("skinThickness", m_TissueParameters->GetSkinThicknessInMillimeters()); } mitk::pa::InSilicoTissueVolume::InSilicoTissueVolume( Volume::Pointer absorptionVolume, Volume::Pointer scatteringVolume, Volume::Pointer anisotropyVolume, Volume::Pointer segmentationVolume, TissueGeneratorParameters::Pointer tissueParameters, mitk::PropertyList::Pointer propertyList) { m_AbsorptionVolume = absorptionVolume; m_ScatteringVolume = scatteringVolume; m_AnisotropyVolume = anisotropyVolume; m_SegmentationVolume = segmentationVolume; m_TissueParameters = tissueParameters; m_PropertyList = propertyList; if (m_SegmentationVolume.IsNotNull()) m_TDim = 4; else m_TDim = 3; } +double mitk::pa::InSilicoTissueVolume::GetSpacing() +{ + return m_AbsorptionVolume->GetSpacing(); +} + +void mitk::pa::InSilicoTissueVolume::SetSpacing(double spacing) +{ + m_AbsorptionVolume->SetSpacing(spacing); + m_ScatteringVolume->SetSpacing(spacing); + m_AnisotropyVolume->SetSpacing(spacing); + m_SegmentationVolume->SetSpacing(spacing); +} + void mitk::pa::InSilicoTissueVolume::AddDoubleProperty(std::string label, double value) { m_PropertyList->SetDoubleProperty(label.c_str(), value); mitk::CoreServices::GetPropertyPersistence()->AddInfo(mitk::PropertyPersistenceInfo::New(label)); } void mitk::pa::InSilicoTissueVolume::AddIntProperty(std::string label, int value) { m_PropertyList->SetIntProperty(label.c_str(), value); mitk::CoreServices::GetPropertyPersistence()->AddInfo(mitk::PropertyPersistenceInfo::New(label)); } mitk::Image::Pointer mitk::pa::InSilicoTissueVolume::ConvertToMitkImage() { mitk::Image::Pointer resultImage = mitk::Image::New(); mitk::PixelType TPixel = mitk::MakeScalarPixelType(); auto* dimensionsOfImage = new unsigned int[4]; // Copy dimensions dimensionsOfImage[0] = m_TissueParameters->GetYDim(); dimensionsOfImage[1] = m_TissueParameters->GetXDim(); dimensionsOfImage[2] = m_TissueParameters->GetZDim(); dimensionsOfImage[3] = m_TDim; - MITK_INFO << "Initializing image..."; resultImage->Initialize(TPixel, 4, dimensionsOfImage, 1); - MITK_INFO << "Initializing image...[Done]"; mitk::Vector3D spacing; spacing.Fill(m_TissueParameters->GetVoxelSpacingInCentimeters()); resultImage->SetSpacing(spacing); - MITK_INFO << "Set Import Volumes..."; - //Copy memory, deal with cleaning up memory yourself! resultImage->SetImportVolume(m_AbsorptionVolume->GetData(), 0, 0, mitk::Image::CopyMemory); resultImage->SetImportVolume(m_ScatteringVolume->GetData(), 1, 0, mitk::Image::CopyMemory); resultImage->SetImportVolume(m_AnisotropyVolume->GetData(), 2, 0, mitk::Image::CopyMemory); resultImage->SetImportVolume(m_SegmentationVolume->GetData(), 3, 0, mitk::Image::CopyMemory); - MITK_INFO << "Set Import Volumes...[Done]"; resultImage->SetPropertyList(m_PropertyList); return resultImage; } mitk::pa::InSilicoTissueVolume::Pointer mitk::pa::InSilicoTissueVolume::New( Volume::Pointer absorptionVolume, Volume::Pointer scatteringVolume, Volume::Pointer anisotropyVolume, Volume::Pointer segmentationVolume, TissueGeneratorParameters::Pointer tissueParameters, mitk::PropertyList::Pointer propertyList) { InSilicoTissueVolume::Pointer smartPtr = new InSilicoTissueVolume( absorptionVolume, scatteringVolume, anisotropyVolume, segmentationVolume, tissueParameters, propertyList); smartPtr->UnRegister(); return smartPtr; } mitk::pa::InSilicoTissueVolume::~InSilicoTissueVolume() { m_AbsorptionVolume = nullptr; m_ScatteringVolume = nullptr; m_AnisotropyVolume = nullptr; m_SegmentationVolume = nullptr; m_TissueParameters = nullptr; m_PropertyList = nullptr; } +void mitk::pa::InSilicoTissueVolume::SetVolumeValues(int x, int y, int z, double absorption, double scattering, double anisotropy) +{ + if (IsInsideVolume(x, y, z)) + { + m_AbsorptionVolume->SetData(absorption, x, y, z); + m_ScatteringVolume->SetData(scattering, x, y, z); + m_AnisotropyVolume->SetData(anisotropy, x, y, z); + } +} + void mitk::pa::InSilicoTissueVolume::SetVolumeValues(int x, int y, int z, double absorption, double scattering, double anisotropy, SegmentationType segmentType) { if (IsInsideVolume(x, y, z)) { m_AbsorptionVolume->SetData(absorption, x, y, z); m_ScatteringVolume->SetData(scattering, x, y, z); m_AnisotropyVolume->SetData(anisotropy, x, y, z); m_SegmentationVolume->SetData(segmentType, x, y, z); } } bool mitk::pa::InSilicoTissueVolume::IsInsideVolume(int x, int y, int z) { return x >= 0 && x < m_TissueParameters->GetXDim() && y >= 0 && y < m_TissueParameters->GetYDim() && z >= 0 && z < m_TissueParameters->GetZDim(); } mitk::pa::Volume::Pointer mitk::pa::InSilicoTissueVolume::GetAbsorptionVolume() { return m_AbsorptionVolume; } mitk::pa::Volume::Pointer mitk::pa::InSilicoTissueVolume::GetSegmentationVolume() { return m_SegmentationVolume; } void mitk::pa::InSilicoTissueVolume::FinalizeVolume() { AddSkinAndAirLayers(); // If specified, randomize all tissue parameters if (m_TissueParameters->GetRandomizePhysicalProperties()) + { RandomizeTissueCoefficients(m_TissueParameters->GetUseRngSeed(), m_TissueParameters->GetRngSeed(), m_TissueParameters->GetRandomizePhysicalPropertiesPercentage()); + } + + unsigned int xDim = m_TissueParameters->GetXDim(); + unsigned int yDim = m_TissueParameters->GetYDim(); + unsigned int zDim = m_TissueParameters->GetZDim(); + + std::uniform_real_distribution randomBackgroundAbsorptionDistribution( + m_TissueParameters->GetMinBackgroundAbsorption(), m_TissueParameters->GetMaxBackgroundAbsorption()); + + for (unsigned int z = 0; z < zDim; z++) + { + for (unsigned int y = 0; y < yDim; y++) + { + for (unsigned int x = 0; x < xDim; x++) + { + if (fabs(m_AbsorptionVolume->GetData(x, y, z) - m_InitialBackgroundAbsorption) < mitk::eps) + { + m_AbsorptionVolume->SetData(randomBackgroundAbsorptionDistribution(*m_Rng), x, y, z); + } + } + } + } } void mitk::pa::InSilicoTissueVolume::AddSkinAndAirLayers() { //Calculate the index location according to thickness in cm double airvoxel = (m_TissueParameters->GetAirThicknessInMillimeters() / m_TissueParameters->GetVoxelSpacingInCentimeters()) / 10; double skinvoxel = airvoxel + (m_TissueParameters->GetSkinThicknessInMillimeters() / m_TissueParameters->GetVoxelSpacingInCentimeters()) / 10; for (int y = 0; y < m_TissueParameters->GetYDim(); y++) { for (int x = 0; x < m_TissueParameters->GetXDim(); x++) { // Add air from index 0 to airvoxel if (m_TissueParameters->GetAirThicknessInMillimeters() > mitk::eps) { FillZLayer(x, y, 0, airvoxel, m_TissueParameters->GetAirAbsorption(), m_TissueParameters->GetAirScattering(), m_TissueParameters->GetAirAnisotropy(), SegmentationType::AIR); } //Add skin from index airvoxel to skinvoxel if (m_TissueParameters->GetSkinThicknessInMillimeters() > mitk::eps) { FillZLayer(x, y, airvoxel, skinvoxel, m_TissueParameters->GetSkinAbsorption(), m_TissueParameters->GetSkinScattering(), m_TissueParameters->GetSkinAnisotropy(), SegmentationType::SKIN); } } } } void mitk::pa::InSilicoTissueVolume::FillZLayer(int x, int y, double startIdx, double endIdx, double absorption, double scattering, double anisotropy, SegmentationType segmentationType) { for (int z = startIdx; z < endIdx; z++) { if (IsInsideVolume(x, y, z)) { if (endIdx - z < 1) { //Simulate partial volume effects m_AbsorptionVolume->SetData((1 - (endIdx - z)) * m_AbsorptionVolume->GetData(x, y, z) + (endIdx - z) * absorption, x, y, z); m_ScatteringVolume->SetData((1 - (endIdx - z)) * m_ScatteringVolume->GetData(x, y, z) + (endIdx - z) * scattering, x, y, z); m_AnisotropyVolume->SetData((1 - (endIdx - z)) * m_AnisotropyVolume->GetData(x, y, z) + (endIdx - z) * anisotropy, x, y, z); if (endIdx - z > 0.5) { //Only put the segmentation label if more than half of the partial volume is the wanted tissue type m_SegmentationVolume->SetData(segmentationType, x, y, z); } } else { m_AbsorptionVolume->SetData(absorption, x, y, z); m_ScatteringVolume->SetData(scattering, x, y, z); m_AnisotropyVolume->SetData(anisotropy, x, y, z); m_SegmentationVolume->SetData(segmentationType, x, y, z); } } } } void mitk::pa::InSilicoTissueVolume::RandomizeTissueCoefficients(long rngSeed, bool useRngSeed, double percentage) { std::mt19937 rng; std::random_device randomDevice; if (useRngSeed) { rng.seed(rngSeed); } else { if (randomDevice.entropy() > 0.1) { rng.seed(randomDevice()); } else { rng.seed(std::chrono::duration_cast(std::chrono::high_resolution_clock::now().time_since_epoch()).count()); } } std::normal_distribution<> percentageDistribution(1, percentage / 100); for (int y = 0; y < m_TissueParameters->GetYDim(); y++) { for (int x = 0; x < m_TissueParameters->GetXDim(); x++) { for (int z = 0; z < m_TissueParameters->GetZDim(); z++) { m_AbsorptionVolume->SetData(m_AbsorptionVolume->GetData(x, y, z) * percentageDistribution(rng), x, y, z); m_ScatteringVolume->SetData(m_ScatteringVolume->GetData(x, y, z) * percentageDistribution(rng), x, y, z); } } } } mitk::pa::Volume::Pointer mitk::pa::InSilicoTissueVolume::GetScatteringVolume() { return m_ScatteringVolume; } mitk::pa::Volume::Pointer mitk::pa::InSilicoTissueVolume::GetAnisotropyVolume() { return m_AnisotropyVolume; } + +void mitk::pa::InSilicoTissueVolume::SetAbsorptionVolume(Volume::Pointer volume) +{ + m_AbsorptionVolume = volume; +} + +void mitk::pa::InSilicoTissueVolume::SetScatteringVolume(Volume::Pointer volume) +{ + m_ScatteringVolume = volume; +} + +void mitk::pa::InSilicoTissueVolume::SetAnisotropyVolume(Volume::Pointer volume) +{ + m_AnisotropyVolume = volume; +} + +void mitk::pa::InSilicoTissueVolume::SetSegmentationVolume(Volume::Pointer volume) +{ + m_SegmentationVolume = volume; +} diff --git a/Modules/PhotoacousticsLib/src/Domain/Volume/mitkPAVolume.cpp b/Modules/PhotoacousticsLib/src/Domain/Volume/mitkPAVolume.cpp index 5e4e013fcb..8ae9e32bb2 100644 --- a/Modules/PhotoacousticsLib/src/Domain/Volume/mitkPAVolume.cpp +++ b/Modules/PhotoacousticsLib/src/Domain/Volume/mitkPAVolume.cpp @@ -1,115 +1,153 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkPAVolume.h" #include #include #include mitk::pa::Volume::Volume(double* data, - unsigned int xDim, unsigned int yDim, unsigned int zDim) + unsigned int xDim, unsigned int yDim, unsigned int zDim, double spacing) { + MITK_INFO << "Initialized by data*"; if (data == nullptr) mitkThrow() << "You may not initialize a mitk::Volume with a nullptr"; m_InternalMitkImage = mitk::Image::New(); auto* dimensions = new unsigned int[NUMBER_OF_SPATIAL_DIMENSIONS]; dimensions[0] = yDim; dimensions[1] = xDim; dimensions[2] = zDim; m_XDim = xDim; m_YDim = yDim; m_ZDim = zDim; mitk::PixelType pixelType = mitk::MakeScalarPixelType(); m_InternalMitkImage->Initialize(pixelType, NUMBER_OF_SPATIAL_DIMENSIONS, dimensions); m_InternalMitkImage->SetImportVolume(data, Image::ImportMemoryManagementType::CopyMemory); + SetSpacing(spacing); + m_FastAccessDataPointer = GetData(); delete data; } +mitk::pa::Volume::Volume(mitk::Image::Pointer image) +{ + MITK_INFO << "Initialized by mitk::Image"; + + if (image.IsNull()) + mitkThrow() << "You may not initialize a mitk::Volume with a null reference to an mitk image"; + + unsigned int* dimensions = image->GetDimensions(); + m_YDim = dimensions[0]; + m_XDim = dimensions[1]; + m_ZDim = dimensions[2]; + + m_InternalMitkImage = image; + + m_FastAccessDataPointer = GetData(); +} + +double mitk::pa::Volume::GetSpacing() +{ + return m_InternalMitkImage->GetGeometry()->GetSpacing()[0]; +} + +void mitk::pa::Volume::SetSpacing(double spacing) +{ + const mitk::ScalarType spacingArray[]{ spacing, spacing, spacing }; + m_InternalMitkImage->SetSpacing(spacingArray); +} + mitk::pa::Volume::~Volume() { m_InternalMitkImage = nullptr; } -mitk::pa::Volume::Pointer mitk::pa::Volume::New(double* data, unsigned int xDim, unsigned int yDim, unsigned int zDim) +mitk::pa::Volume::Pointer mitk::pa::Volume::New(double* data, unsigned int xDim, unsigned int yDim, unsigned int zDim, double spacing) +{ + mitk::pa::Volume::Pointer smartPtr = new mitk::pa::Volume(data, xDim, yDim, zDim, spacing); + smartPtr->UnRegister(); + return smartPtr; +} + +mitk::pa::Volume::Pointer mitk::pa::Volume::New(mitk::Image::Pointer image) { - mitk::pa::Volume::Pointer smartPtr = new mitk::pa::Volume(data, xDim, yDim, zDim); + mitk::pa::Volume::Pointer smartPtr = new mitk::pa::Volume(image); smartPtr->UnRegister(); return smartPtr; } mitk::Image::Pointer mitk::pa::Volume::AsMitkImage() { return m_InternalMitkImage; } mitk::pa::Volume::Pointer mitk::pa::Volume::DeepCopy() { long length = GetXDim()*GetYDim()*GetZDim(); auto* data = new double[length]; memcpy(data, GetData(), length * sizeof(double)); - return mitk::pa::Volume::New(data, GetXDim(), GetYDim(), GetZDim()); + return mitk::pa::Volume::New(data, GetXDim(), GetYDim(), GetZDim(), GetSpacing()); } double mitk::pa::Volume::GetData(unsigned int x, unsigned int y, unsigned int z) { return m_FastAccessDataPointer[GetIndex(x, y, z)]; } void mitk::pa::Volume::SetData(double data, unsigned int x, unsigned int y, unsigned int z) { m_FastAccessDataPointer[GetIndex(x, y, z)] = data; } unsigned int mitk::pa::Volume::GetXDim() { return m_XDim; } unsigned int mitk::pa::Volume::GetYDim() { return m_YDim; } unsigned int mitk::pa::Volume::GetZDim() { return m_ZDim; } double* mitk::pa::Volume::GetData() const { mitk::ImageWriteAccessor imgRead(m_InternalMitkImage, m_InternalMitkImage->GetVolumeData()); return (double*)imgRead.GetData(); } -int mitk::pa::Volume::GetIndex(unsigned int x, unsigned int y, unsigned int z) +long long mitk::pa::Volume::GetIndex(unsigned int x, unsigned int y, unsigned int z) { #ifdef _DEBUG if (x < 0 || x >(GetXDim() - 1) || y < 0 || y >(GetYDim() - 1) || z < 0 || z >(GetZDim() - 1)) { MITK_ERROR << "Index out of bounds at " << x << "|" << y << "|" << z; mitkThrow() << "Index out of bounds exception!"; } #endif - return z * m_XDim * m_YDim + x * m_YDim + y; + return ((long long)z) * m_XDim * m_YDim + ((long long)x) * m_YDim + ((long long)y); } diff --git a/Modules/PhotoacousticsLib/src/Generator/mitkPAPhantomTissueGenerator.cpp b/Modules/PhotoacousticsLib/src/Generator/mitkPAPhantomTissueGenerator.cpp new file mode 100644 index 0000000000..167e30b0bb --- /dev/null +++ b/Modules/PhotoacousticsLib/src/Generator/mitkPAPhantomTissueGenerator.cpp @@ -0,0 +1,157 @@ +/*=================================================================== + +The Medical Imaging Interaction Toolkit (MITK) + +Copyright (c) German Cancer Research Center, +Division of Medical and Biological Informatics. +All rights reserved. + +This software is distributed WITHOUT ANY WARRANTY; without +even the implied warranty of MERCHANTABILITY or FITNESS FOR +A PARTICULAR PURPOSE. + +See LICENSE.txt or http://www.mitk.org for details. + +===================================================================*/ + +#include "mitkPAPhantomTissueGenerator.h" +#include "mitkPAVector.h" +#include "mitkPAVolumeManipulator.h" + +mitk::pa::InSilicoTissueVolume::Pointer mitk::pa::PhantomTissueGenerator::GeneratePhantomData( + TissueGeneratorParameters::Pointer parameters) +{ + MITK_DEBUG << "Initializing Empty Volume"; + + const double RESAMPLING_FACTOR = 2; + + if (parameters->GetDoPartialVolume()) + { + parameters->SetXDim(parameters->GetXDim() * RESAMPLING_FACTOR); + parameters->SetYDim(parameters->GetYDim() * RESAMPLING_FACTOR); + parameters->SetZDim(parameters->GetZDim() * RESAMPLING_FACTOR); + parameters->SetVesselBifurcationFrequency(parameters->GetVesselBifurcationFrequency() * RESAMPLING_FACTOR); + parameters->SetVoxelSpacingInCentimeters(parameters->GetVoxelSpacingInCentimeters() / RESAMPLING_FACTOR); + } + + parameters->SetVesselBifurcationFrequency(10000); + + std::mt19937 randomNumberGenerator; + std::random_device randomDevice; + if (parameters->GetUseRngSeed()) + { + randomNumberGenerator.seed(parameters->GetRngSeed()); + } + else + { + if (randomDevice.entropy() > 0.1) + { + randomNumberGenerator.seed(randomDevice()); + } + else + { + randomNumberGenerator.seed(std::chrono::duration_cast(std::chrono::high_resolution_clock::now().time_since_epoch()).count()); + } + } + + auto generatedVolume = mitk::pa::InSilicoTissueVolume::New(parameters, &randomNumberGenerator); + + const unsigned int NUMER_OF_VESSELS = 5; + const double START_DEPTH_IN_CM = 2.10; + const double START_X_IN_CM = 0.76; + const double RADIUS_IN_MM = 0.5; + const double INCREMENT_XZ_IN_CM = 0.50; + double ABSORPTION_PER_CM = parameters->GetMinVesselAbsorption(); + + generatedVolume->AddIntProperty("numberOfVessels", NUMER_OF_VESSELS); + generatedVolume->AddIntProperty("bifurcationFrequency", parameters->GetVesselBifurcationFrequency()); + + for (unsigned int vesselNumber = 0; vesselNumber < NUMER_OF_VESSELS; vesselNumber++) + { + Vector::Pointer initialPosition = Vector::New(); + Vector::Pointer initialDirection = Vector::New(); + + double initialRadius = RADIUS_IN_MM / parameters->GetVoxelSpacingInCentimeters() / 10; + std::stringstream radiusString; + radiusString << "vessel_" << vesselNumber + 1 << "_radius"; + generatedVolume->AddDoubleProperty(radiusString.str(), initialRadius); + + double absorptionCoefficient = ABSORPTION_PER_CM; + std::stringstream absorptionString; + absorptionString << "vessel_" << vesselNumber + 1 << "_absorption"; + generatedVolume->AddDoubleProperty(absorptionString.str(), absorptionCoefficient); + + double bendingFactor = 0; + std::stringstream bendingString; + bendingString << "vessel_" << vesselNumber + 1 << "_bendingFactor"; + generatedVolume->AddDoubleProperty(bendingString.str(), bendingFactor); + + double vesselScattering = 15; + std::stringstream scatteringString; + scatteringString << "vessel_" << vesselNumber + 1 << "_scattering"; + generatedVolume->AddDoubleProperty(scatteringString.str(), vesselScattering); + + double vesselAnisotropy = 0.9; + std::stringstream anisotropyString; + anisotropyString << "vessel_" << vesselNumber + 1 << "_anisotropy"; + generatedVolume->AddDoubleProperty(anisotropyString.str(), vesselAnisotropy); + + /*The vessel tree shall start at one of the 4 sides of the volume. + * The vessels will always be completely contained in the volume + * when starting to meander. + * They will meander in a direction perpendicular to the + * plane they started from (within the limits of the + * DIRECTION_VECTOR_INITIAL_VARIANCE) + */ + + double zposition = (START_DEPTH_IN_CM + (vesselNumber*INCREMENT_XZ_IN_CM)) / parameters->GetVoxelSpacingInCentimeters(); + + double xposition = (START_X_IN_CM + (vesselNumber*INCREMENT_XZ_IN_CM)) / parameters->GetVoxelSpacingInCentimeters(); + + initialPosition->Randomize(xposition, xposition, 0, 0, zposition, zposition, &randomNumberGenerator); + initialDirection->Randomize(0, 0, 1, 1, 0, 0, &randomNumberGenerator); + + initialDirection->Normalize(); + MITK_INFO << initialPosition->GetElement(0) << " | " << initialPosition->GetElement(1) << " | " << initialPosition->GetElement(2); + MITK_INFO << initialDirection->GetElement(0) << " | " << initialDirection->GetElement(1) << " | " << initialDirection->GetElement(2); + + VesselProperties::Pointer vesselParams = VesselProperties::New(); + vesselParams->SetDirectionVector(initialDirection); + vesselParams->SetPositionVector(initialPosition); + vesselParams->SetRadiusInVoxel(initialRadius); + vesselParams->SetAbsorptionCoefficient(absorptionCoefficient); + vesselParams->SetScatteringCoefficient(vesselScattering); + vesselParams->SetAnisotopyCoefficient(vesselAnisotropy); + vesselParams->SetBifurcationFrequency(parameters->GetVesselBifurcationFrequency()); + vesselParams->SetDoPartialVolume(parameters->GetDoPartialVolume()); + + VesselTree::Pointer vesselTree = VesselTree::New(vesselParams); + + while (!vesselTree->IsFinished()) + { + vesselTree->Step(generatedVolume, parameters->GetCalculateNewVesselPositionCallback(), bendingFactor, &randomNumberGenerator); + } + } + + if (parameters->GetDoPartialVolume()) + { + VolumeManipulator::RescaleImage(generatedVolume, (1.0 / RESAMPLING_FACTOR)); + parameters->SetXDim(parameters->GetXDim() / RESAMPLING_FACTOR); + parameters->SetYDim(parameters->GetYDim() / RESAMPLING_FACTOR); + parameters->SetZDim(parameters->GetZDim() / RESAMPLING_FACTOR); + parameters->SetVesselBifurcationFrequency(parameters->GetVesselBifurcationFrequency() / RESAMPLING_FACTOR); + parameters->SetVoxelSpacingInCentimeters(parameters->GetVoxelSpacingInCentimeters() * RESAMPLING_FACTOR); + } + + generatedVolume->FinalizeVolume(); + + return generatedVolume; +} + +mitk::pa::PhantomTissueGenerator::PhantomTissueGenerator() +{ +} + +mitk::pa::PhantomTissueGenerator::~PhantomTissueGenerator() +{ +} diff --git a/Modules/PhotoacousticsLib/src/Generator/mitkPASlicedVolumeGenerator.cpp b/Modules/PhotoacousticsLib/src/Generator/mitkPASlicedVolumeGenerator.cpp index c310767c39..cd6cf1ea7a 100644 --- a/Modules/PhotoacousticsLib/src/Generator/mitkPASlicedVolumeGenerator.cpp +++ b/Modules/PhotoacousticsLib/src/Generator/mitkPASlicedVolumeGenerator.cpp @@ -1,118 +1,118 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkPASlicedVolumeGenerator.h" #include mitk::pa::SlicedVolumeGenerator::SlicedVolumeGenerator(int centralYSlice, bool precorrect, Volume::Pointer precorrectionVolume, bool inverse) { m_CentralYSlice = centralYSlice; m_Precorrect = precorrect; m_Inverse = inverse; m_PrecorrectionVolume = nullptr; if (m_Precorrect) { m_PrecorrectionVolume = precorrectionVolume; } } mitk::pa::SlicedVolumeGenerator::~SlicedVolumeGenerator() { m_CentralYSlice = -1; m_Precorrect = false; m_PrecorrectionVolume = nullptr; } mitk::pa::Volume::Pointer mitk::pa::SlicedVolumeGenerator::GetSlicedFluenceImageFromComposedVolume( ComposedVolume::Pointer composedVolume) { int fluenceComponents = composedVolume->GetNumberOfFluenceComponents(); int xDim = composedVolume->GetGroundTruthVolume()->GetAbsorptionVolume()->GetXDim(); int zDim = composedVolume->GetGroundTruthVolume()->GetAbsorptionVolume()->GetZDim(); auto* imageArray = new double[xDim*zDim*fluenceComponents]; for (int fluenceComponentIdx = 0; fluenceComponentIdx < fluenceComponents; fluenceComponentIdx++) for (int z = 0; z < zDim; z++) for (int x = 0; x < xDim; x++) { int index = z * xDim * fluenceComponents + x * fluenceComponents + fluenceComponentIdx; imageArray[index] = composedVolume->GetFluenceValue(fluenceComponentIdx, x, m_CentralYSlice, z); if (m_Precorrect) { imageArray[index] = imageArray[index] / m_PrecorrectionVolume->GetData(x, m_CentralYSlice, z); } if (m_Inverse) { if (std::abs(imageArray[index] - 0) >= mitk::eps) imageArray[index] = 1 / imageArray[index]; else imageArray[index] = INFINITY; } } - return mitk::pa::Volume::New(imageArray, xDim, fluenceComponents, zDim); + return mitk::pa::Volume::New(imageArray, xDim, fluenceComponents, zDim, composedVolume->GetGroundTruthVolume()->GetSpacing()); } mitk::pa::Volume::Pointer mitk::pa::SlicedVolumeGenerator::GetSlicedSignalImageFromComposedVolume( ComposedVolume::Pointer composedVolume) { int fluenceComponents = composedVolume->GetNumberOfFluenceComponents(); int xDim = composedVolume->GetGroundTruthVolume()->GetAbsorptionVolume()->GetXDim(); int zDim = composedVolume->GetGroundTruthVolume()->GetAbsorptionVolume()->GetZDim(); auto* imageArray = new double[xDim*zDim*fluenceComponents]; for (int fluenceComponentIdx = 0; fluenceComponentIdx < fluenceComponents; fluenceComponentIdx++) for (int z = 0; z < zDim; z++) for (int x = 0; x < xDim; x++) { int y = m_CentralYSlice + composedVolume->GetYOffsetForFluenceComponentInPixels(fluenceComponentIdx); imageArray[z * xDim * fluenceComponents + x * fluenceComponents + fluenceComponentIdx] = composedVolume->GetFluenceValue(fluenceComponentIdx, x, m_CentralYSlice, z) * composedVolume->GetGroundTruthVolume()->GetAbsorptionVolume()->GetData(x, y, z); } - return mitk::pa::Volume::New(imageArray, xDim, fluenceComponents, zDim); + return mitk::pa::Volume::New(imageArray, xDim, fluenceComponents, zDim, composedVolume->GetGroundTruthVolume()->GetSpacing()); } mitk::pa::Volume::Pointer mitk::pa::SlicedVolumeGenerator::GetSlicedGroundTruthImageFromComposedVolume( ComposedVolume::Pointer composedVolume) { int fluenceComponents = composedVolume->GetNumberOfFluenceComponents(); int xDim = composedVolume->GetGroundTruthVolume()->GetAbsorptionVolume()->GetXDim(); int zDim = composedVolume->GetGroundTruthVolume()->GetAbsorptionVolume()->GetZDim(); auto* imageArray = new double[xDim*zDim*fluenceComponents]; for (int fluenceComponentIdx = 0; fluenceComponentIdx < fluenceComponents; fluenceComponentIdx++) for (int z = 0; z < zDim; z++) for (int x = 0; x < xDim; x++) { int y = m_CentralYSlice + composedVolume->GetYOffsetForFluenceComponentInPixels(fluenceComponentIdx); imageArray[z * xDim * fluenceComponents + x * fluenceComponents + fluenceComponentIdx] = composedVolume->GetGroundTruthVolume()->GetAbsorptionVolume()->GetData(x, y, z); } - return mitk::pa::Volume::New(imageArray, xDim, fluenceComponents, zDim); + return mitk::pa::Volume::New(imageArray, xDim, fluenceComponents, zDim, composedVolume->GetGroundTruthVolume()->GetSpacing()); } diff --git a/Modules/PhotoacousticsLib/src/Generator/mitkPATissueGenerator.cpp b/Modules/PhotoacousticsLib/src/Generator/mitkPATissueGenerator.cpp index c2704dce5a..cbe23af73d 100644 --- a/Modules/PhotoacousticsLib/src/Generator/mitkPATissueGenerator.cpp +++ b/Modules/PhotoacousticsLib/src/Generator/mitkPATissueGenerator.cpp @@ -1,160 +1,199 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkPATissueGenerator.h" #include "mitkPAVector.h" #include "mitkPAVolumeManipulator.h" mitk::pa::InSilicoTissueVolume::Pointer mitk::pa::InSilicoTissueGenerator::GenerateInSilicoData( TissueGeneratorParameters::Pointer parameters) { MITK_DEBUG << "Initializing Empty Volume"; - auto generatedVolume = mitk::pa::InSilicoTissueVolume::New(parameters); + const double RESAMPLING_FACTOR = 2; - const double DIRECTION_VECTOR_INITIAL_VARIANCE = 0.2; + if (parameters->GetDoPartialVolume()) + { + parameters->SetXDim(parameters->GetXDim() * RESAMPLING_FACTOR); + parameters->SetYDim(parameters->GetYDim() * RESAMPLING_FACTOR); + parameters->SetZDim(parameters->GetZDim() * RESAMPLING_FACTOR); + parameters->SetVesselBifurcationFrequency(parameters->GetVesselBifurcationFrequency() * RESAMPLING_FACTOR); + parameters->SetVoxelSpacingInCentimeters(parameters->GetVoxelSpacingInCentimeters() / RESAMPLING_FACTOR); + } std::mt19937 randomNumberGenerator; std::random_device randomDevice; if (parameters->GetUseRngSeed()) { randomNumberGenerator.seed(parameters->GetRngSeed()); } else { if (randomDevice.entropy() > 0.1) { randomNumberGenerator.seed(randomDevice()); } else { randomNumberGenerator.seed(std::chrono::duration_cast(std::chrono::high_resolution_clock::now().time_since_epoch()).count()); } } - std::uniform_int_distribution randomNumVesselDistribution(parameters->GetMinNumberOfVessels(), parameters->GetMaxNumberOfVessels()); - std::uniform_real_distribution randomBendingDistribution(parameters->GetMinVesselBending(), parameters->GetMaxVesselBending()); - std::uniform_real_distribution randomAbsorptionDistribution(parameters->GetMinVesselAbsorption(), parameters->GetMaxVesselAbsorption()); - std::uniform_real_distribution randomRadiusDistribution(parameters->GetMinVesselRadiusInMillimeters(), parameters->GetMaxVesselRadiusInMillimeters()); - std::uniform_real_distribution randomScatteringDistribution(parameters->GetMinVesselScattering(), parameters->GetMaxVesselScattering()); - std::uniform_real_distribution randomAnisotropyDistribution(parameters->GetMinVesselAnisotropy(), parameters->GetMaxVesselAnisotropy()); + auto generatedVolume = mitk::pa::InSilicoTissueVolume::New(parameters, &randomNumberGenerator); + + double DIRECTION_VECTOR_INITIAL_VARIANCE = 0.2; + + if(parameters->GetForceVesselsMoveAlongYDirection()) + DIRECTION_VECTOR_INITIAL_VARIANCE = 0; + + std::uniform_int_distribution randomNumVesselDistribution( + parameters->GetMinNumberOfVessels(), parameters->GetMaxNumberOfVessels()); + std::uniform_real_distribution randomBendingDistribution( + parameters->GetMinVesselBending(), parameters->GetMaxVesselBending()); + std::uniform_real_distribution randomAbsorptionDistribution( + parameters->GetMinVesselAbsorption(), parameters->GetMaxVesselAbsorption()); + std::uniform_real_distribution randomRadiusDistribution( + parameters->GetMinVesselRadiusInMillimeters(), parameters->GetMaxVesselRadiusInMillimeters()); + std::uniform_real_distribution randomScatteringDistribution( + parameters->GetMinVesselScattering(), parameters->GetMaxVesselScattering()); + std::uniform_real_distribution randomAnisotropyDistribution( + parameters->GetMinVesselAnisotropy(), parameters->GetMaxVesselAnisotropy()); std::uniform_int_distribution borderTypeDistribution(0, 3); int numberOfBloodVessels = randomNumVesselDistribution(randomNumberGenerator); generatedVolume->AddIntProperty("numberOfVessels", numberOfBloodVessels); generatedVolume->AddIntProperty("bifurcationFrequency", parameters->GetVesselBifurcationFrequency()); MITK_INFO << "Simulating " << numberOfBloodVessels << " vessel structures"; for (int vesselNumber = 0; vesselNumber < numberOfBloodVessels; vesselNumber++) { Vector::Pointer initialPosition = Vector::New(); Vector::Pointer initialDirection = Vector::New(); double initialRadius = randomRadiusDistribution(randomNumberGenerator) / parameters->GetVoxelSpacingInCentimeters() / 10; std::stringstream radiusString; radiusString << "vessel_" << vesselNumber + 1 << "_radius"; generatedVolume->AddDoubleProperty(radiusString.str(), initialRadius); double absorptionCoefficient = randomAbsorptionDistribution(randomNumberGenerator); std::stringstream absorptionString; absorptionString << "vessel_" << vesselNumber + 1 << "_absorption"; generatedVolume->AddDoubleProperty(absorptionString.str(), absorptionCoefficient); double bendingFactor = randomBendingDistribution(randomNumberGenerator); std::stringstream bendingString; bendingString << "vessel_" << vesselNumber + 1 << "_bendingFactor"; generatedVolume->AddDoubleProperty(bendingString.str(), bendingFactor); double vesselScattering = randomScatteringDistribution(randomNumberGenerator); std::stringstream scatteringString; scatteringString << "vessel_" << vesselNumber + 1 << "_scattering"; generatedVolume->AddDoubleProperty(scatteringString.str(), vesselScattering); double vesselAnisotropy = randomAnisotropyDistribution(randomNumberGenerator); std::stringstream anisotropyString; anisotropyString << "vessel_" << vesselNumber + 1 << "_anisotropy"; generatedVolume->AddDoubleProperty(anisotropyString.str(), vesselAnisotropy); /*The vessel tree shall start at one of the 4 sides of the volume. * The vessels will always be completely contained in the volume * when starting to meander. * They will meander in a direction perpendicular to the * plane they started from (within the limits of the * DIRECTION_VECTOR_INITIAL_VARIANCE) */ int borderType = borderTypeDistribution(randomNumberGenerator); + + if(parameters->GetForceVesselsMoveAlongYDirection()) + borderType = 2; + switch (borderType) { case 0: - initialPosition->Randomize(0, 0, initialRadius, parameters->GetYDim() - initialRadius, parameters->GetMinVesselZOrigin() / parameters->GetVoxelSpacingInCentimeters(), + initialPosition->Randomize(0, 0, initialRadius, parameters->GetYDim() - initialRadius, + parameters->GetMinVesselZOrigin() / parameters->GetVoxelSpacingInCentimeters(), parameters->GetMaxVesselZOrigin() / parameters->GetVoxelSpacingInCentimeters(), &randomNumberGenerator); initialDirection->Randomize(1, 2, -DIRECTION_VECTOR_INITIAL_VARIANCE, DIRECTION_VECTOR_INITIAL_VARIANCE, -DIRECTION_VECTOR_INITIAL_VARIANCE, DIRECTION_VECTOR_INITIAL_VARIANCE, &randomNumberGenerator); break; case 1: - initialPosition->Randomize(parameters->GetXDim(), parameters->GetXDim(), initialRadius, parameters->GetYDim() - initialRadius, parameters->GetMinVesselZOrigin() / parameters->GetVoxelSpacingInCentimeters(), + initialPosition->Randomize(parameters->GetXDim() - 1, parameters->GetXDim() - 1, initialRadius, parameters->GetYDim() - initialRadius, + parameters->GetMinVesselZOrigin() / parameters->GetVoxelSpacingInCentimeters(), parameters->GetMaxVesselZOrigin() / parameters->GetVoxelSpacingInCentimeters(), &randomNumberGenerator); initialDirection->Randomize(-2, -1, -DIRECTION_VECTOR_INITIAL_VARIANCE, DIRECTION_VECTOR_INITIAL_VARIANCE, -DIRECTION_VECTOR_INITIAL_VARIANCE, DIRECTION_VECTOR_INITIAL_VARIANCE, &randomNumberGenerator); break; case 2: - initialPosition->Randomize(initialRadius, parameters->GetXDim() - initialRadius, 0, 0, parameters->GetMinVesselZOrigin() / parameters->GetVoxelSpacingInCentimeters(), + initialPosition->Randomize(initialRadius, parameters->GetXDim() - initialRadius, 0, 0, + parameters->GetMinVesselZOrigin() / parameters->GetVoxelSpacingInCentimeters(), parameters->GetMaxVesselZOrigin() / parameters->GetVoxelSpacingInCentimeters(), &randomNumberGenerator); initialDirection->Randomize(-DIRECTION_VECTOR_INITIAL_VARIANCE, DIRECTION_VECTOR_INITIAL_VARIANCE, 1, 2, -DIRECTION_VECTOR_INITIAL_VARIANCE, DIRECTION_VECTOR_INITIAL_VARIANCE, &randomNumberGenerator); break; case 3: - initialPosition->Randomize(initialRadius, parameters->GetXDim() - initialRadius, parameters->GetYDim(), parameters->GetYDim(), parameters->GetMinVesselZOrigin() / parameters->GetVoxelSpacingInCentimeters(), + initialPosition->Randomize(initialRadius, parameters->GetXDim() - initialRadius, parameters->GetYDim() - 1, parameters->GetYDim() - 1, + parameters->GetMinVesselZOrigin() / parameters->GetVoxelSpacingInCentimeters(), parameters->GetMaxVesselZOrigin() / parameters->GetVoxelSpacingInCentimeters(), &randomNumberGenerator); initialDirection->Randomize(-DIRECTION_VECTOR_INITIAL_VARIANCE, DIRECTION_VECTOR_INITIAL_VARIANCE, -2, -1, -DIRECTION_VECTOR_INITIAL_VARIANCE, DIRECTION_VECTOR_INITIAL_VARIANCE, &randomNumberGenerator); break; } + initialDirection->Normalize(); + MITK_INFO << initialPosition->GetElement(0) << " | " << initialPosition->GetElement(1) << " | " << initialPosition->GetElement(2); + MITK_INFO << initialDirection->GetElement(0) << " | " << initialDirection->GetElement(1) << " | " << initialDirection->GetElement(2); + VesselProperties::Pointer vesselParams = VesselProperties::New(); vesselParams->SetDirectionVector(initialDirection); vesselParams->SetPositionVector(initialPosition); vesselParams->SetRadiusInVoxel(initialRadius); vesselParams->SetAbsorptionCoefficient(absorptionCoefficient); vesselParams->SetScatteringCoefficient(vesselScattering); vesselParams->SetAnisotopyCoefficient(vesselAnisotropy); vesselParams->SetBifurcationFrequency(parameters->GetVesselBifurcationFrequency()); + vesselParams->SetDoPartialVolume(parameters->GetDoPartialVolume()); VesselTree::Pointer vesselTree = VesselTree::New(vesselParams); while (!vesselTree->IsFinished()) { vesselTree->Step(generatedVolume, parameters->GetCalculateNewVesselPositionCallback(), bendingFactor, &randomNumberGenerator); } } - mitk::pa::VolumeManipulator::GaussianBlur3D(generatedVolume->GetAbsorptionVolume(), parameters->GetVolumeSmoothingSigma()); - mitk::pa::VolumeManipulator::GaussianBlur3D(generatedVolume->GetScatteringVolume(), parameters->GetVolumeSmoothingSigma()); - mitk::pa::VolumeManipulator::GaussianBlur3D(generatedVolume->GetAnisotropyVolume(), parameters->GetVolumeSmoothingSigma()); + if (parameters->GetDoPartialVolume()) + { + VolumeManipulator::RescaleImage(generatedVolume, (1.0 / RESAMPLING_FACTOR)); + parameters->SetXDim(parameters->GetXDim() / RESAMPLING_FACTOR); + parameters->SetYDim(parameters->GetYDim() / RESAMPLING_FACTOR); + parameters->SetZDim(parameters->GetZDim() / RESAMPLING_FACTOR); + parameters->SetVesselBifurcationFrequency(parameters->GetVesselBifurcationFrequency() / RESAMPLING_FACTOR); + parameters->SetVoxelSpacingInCentimeters(parameters->GetVoxelSpacingInCentimeters() * RESAMPLING_FACTOR); + } generatedVolume->FinalizeVolume(); return generatedVolume; } mitk::pa::InSilicoTissueGenerator::InSilicoTissueGenerator() { } mitk::pa::InSilicoTissueGenerator::~InSilicoTissueGenerator() { } diff --git a/Modules/PhotoacousticsLib/src/IO/mitkPAIOUtil.cpp b/Modules/PhotoacousticsLib/src/IO/mitkPAIOUtil.cpp index 3da84124e7..06f2f84d8b 100644 --- a/Modules/PhotoacousticsLib/src/IO/mitkPAIOUtil.cpp +++ b/Modules/PhotoacousticsLib/src/IO/mitkPAIOUtil.cpp @@ -1,258 +1,240 @@ #include "mitkPAIOUtil.h" #include "mitkIOUtil.h" #include "mitkImageReadAccessor.h" #include #include #include #include "mitkPAComposedVolume.h" #include "mitkPASlicedVolumeGenerator.h" #include "mitkPANoiseGenerator.h" #include "mitkPAVolumeManipulator.h" #include #include #include static std::vector splitString(const std::string &s, const char* delim) { std::vector elems; std::stringstream ss(s); std::string item; while (std::getline(ss, item, *delim)) { int numb; std::stringstream(item) >> numb; elems.push_back(numb); } return elems; } bool mitk::pa::IOUtil::DoesFileHaveEnding(std::string const &fullString, std::string const &ending) { if (fullString.length() == 0 || ending.length() == 0 || fullString.length() < ending.length()) return false; return (0 == fullString.compare(fullString.length() - ending.length(), ending.length(), ending)); } mitk::pa::IOUtil::IOUtil() {} mitk::pa::IOUtil::~IOUtil() {} mitk::pa::Volume::Pointer mitk::pa::IOUtil::LoadNrrd(std::string filename, double blur) { if (filename.empty() || filename == "") return nullptr; - auto inputImage = mitk::IOUtil::Load(filename); + mitk::Image::Pointer inputImage = mitk::IOUtil::Load(filename); if (inputImage.IsNull()) return nullptr; - int xDim = inputImage->GetDimensions()[1]; - int yDim = inputImage->GetDimensions()[0]; - int zDim = inputImage->GetDimensions()[2]; + auto returnImage = Volume::New(inputImage); - mitk::ImageReadAccessor readAccess(inputImage, inputImage->GetVolumeData(0)); - - auto* dataArray = new double[xDim*yDim*zDim]; - const auto* srcData = (const double*)readAccess.GetData(); - memcpy(dataArray, srcData, xDim*yDim*zDim * sizeof(double)); - - auto returnImage = mitk::pa::Volume::New(dataArray, xDim, yDim, zDim); - - mitk::pa::VolumeManipulator::GaussianBlur3D(returnImage, blur); + VolumeManipulator::GaussianBlur3D(returnImage, blur); return returnImage; } std::map mitk::pa::IOUtil::LoadFluenceContributionMaps(std::string foldername, double blur, int* progress, bool doLog10) { std::map resultMap; itk::Directory::Pointer directoryHandler = itk::Directory::New(); directoryHandler->Load(foldername.c_str()); for (unsigned int fileIndex = 0, numFiles = directoryHandler->GetNumberOfFiles(); fileIndex < numFiles; ++fileIndex) { std::string filename = std::string(directoryHandler->GetFile(fileIndex)); if (itksys::SystemTools::FileIsDirectory(filename)) continue; if (!DoesFileHaveEnding(filename, ".nrrd")) continue; size_t s = filename.find("_p"); size_t e = filename.find("Fluence", s); std::string sub = filename.substr(s + 2, e - s - 2); std::vector coords = splitString(sub, ","); if (coords.size() != 3) { MITK_ERROR << "Some of the data to read was corrupted or did not match the " << "naming pattern *_pN,N,NFluence*.nrrd"; mitkThrow() << "Some of the data to read was corrupted or did not match the" << " naming pattern *_pN,N,NFluence*.nrrd"; } else { MITK_DEBUG << "Extracted coords: " << coords[0] << "|" << coords[1] << "|" << coords[2] << " from string " << sub; Volume::Pointer nrrdFile = LoadNrrd(foldername + filename, blur); if (doLog10) VolumeManipulator::Log10Image(nrrdFile); resultMap[Position{ coords[0], coords[2] }] = nrrdFile; *progress = *progress + 1; } } return resultMap; } int mitk::pa::IOUtil::GetNumberOfNrrdFilesInDirectory(std::string directory) { return GetListOfAllNrrdFilesInDirectory(directory).size(); } std::vector mitk::pa::IOUtil::GetListOfAllNrrdFilesInDirectory(std::string directory, bool keepFileFormat) { std::vector filenames; itk::Directory::Pointer directoryHandler = itk::Directory::New(); directoryHandler->Load(directory.c_str()); for (unsigned int fileIndex = 0, numFiles = directoryHandler->GetNumberOfFiles(); fileIndex < numFiles; ++fileIndex) { std::string filename = std::string(directoryHandler->GetFile(fileIndex)); if (itksys::SystemTools::FileIsDirectory(filename)) continue; if (!DoesFileHaveEnding(filename, ".nrrd")) continue; if (keepFileFormat) { filenames.push_back(filename); } else { filenames.push_back(filename.substr(0, filename.size() - 5)); } } return filenames; } std::vector mitk::pa::IOUtil::GetAllChildfoldersFromFolder(std::string folderPath) { std::vector returnVector; itksys::Directory directoryHandler; directoryHandler.Load(folderPath.c_str()); for (unsigned int fileIndex = 0, numFiles = directoryHandler.GetNumberOfFiles(); fileIndex < numFiles; ++fileIndex) { std::string foldername = std::string(directoryHandler.GetFile(fileIndex)); std::string filename = folderPath + "/" + foldername; if (itksys::SystemTools::FileIsDirectory(filename)) { if (foldername != std::string(".") && foldername != std::string("..")) { MITK_INFO << filename; returnVector.push_back(filename); } continue; } //If there is a nrrd file in the directory we assume that a bottom level directory was chosen. if (DoesFileHaveEnding(filename, ".nrrd")) { returnVector.clear(); returnVector.push_back(folderPath); return returnVector; } } return returnVector; } mitk::pa::InSilicoTissueVolume::Pointer mitk::pa::IOUtil::LoadInSilicoTissueVolumeFromNrrdFile(std::string nrrdFile) { MITK_INFO << "Initializing ComposedVolume by nrrd..."; auto inputImage = mitk::IOUtil::Load(nrrdFile); auto tissueParameters = TissueGeneratorParameters::New(); unsigned int xDim = inputImage->GetDimensions()[1]; unsigned int yDim = inputImage->GetDimensions()[0]; unsigned int zDim = inputImage->GetDimensions()[2]; tissueParameters->SetXDim(xDim); tissueParameters->SetYDim(yDim); tissueParameters->SetZDim(zDim); double xSpacing = inputImage->GetGeometry(0)->GetSpacing()[1]; double ySpacing = inputImage->GetGeometry(0)->GetSpacing()[0]; double zSpacing = inputImage->GetGeometry(0)->GetSpacing()[2]; if ((xSpacing - ySpacing) > mitk::eps || (xSpacing - zSpacing) > mitk::eps || (ySpacing - zSpacing) > mitk::eps) { throw mitk::Exception("Cannot handle unequal spacing."); } tissueParameters->SetVoxelSpacingInCentimeters(xSpacing); mitk::PropertyList::Pointer propertyList = inputImage->GetPropertyList(); mitk::ImageReadAccessor readAccess0(inputImage, inputImage->GetVolumeData(0)); auto* m_AbsorptionArray = new double[xDim*yDim*zDim]; memcpy(m_AbsorptionArray, readAccess0.GetData(), xDim*yDim*zDim * sizeof(double)); - auto absorptionVolume = Volume::New(m_AbsorptionArray, xDim, yDim, zDim); + auto absorptionVolume = Volume::New(m_AbsorptionArray, xDim, yDim, zDim, xSpacing); mitk::ImageReadAccessor readAccess1(inputImage, inputImage->GetVolumeData(1)); auto* m_ScatteringArray = new double[xDim*yDim*zDim]; memcpy(m_ScatteringArray, readAccess1.GetData(), xDim*yDim*zDim * sizeof(double)); - auto scatteringVolume = Volume::New(m_ScatteringArray, xDim, yDim, zDim); + auto scatteringVolume = Volume::New(m_ScatteringArray, xDim, yDim, zDim, xSpacing); mitk::ImageReadAccessor readAccess2(inputImage, inputImage->GetVolumeData(2)); auto* m_AnisotropyArray = new double[xDim*yDim*zDim]; memcpy(m_AnisotropyArray, readAccess2.GetData(), xDim*yDim*zDim * sizeof(double)); - auto anisotropyVolume = Volume::New(m_AnisotropyArray, xDim, yDim, zDim); + auto anisotropyVolume = Volume::New(m_AnisotropyArray, xDim, yDim, zDim, xSpacing); Volume::Pointer segmentationVolume; if (inputImage->GetDimension() == 4) { mitk::ImageReadAccessor readAccess3(inputImage, inputImage->GetVolumeData(3)); auto* m_SegmentationArray = new double[xDim*yDim*zDim]; memcpy(m_SegmentationArray, readAccess3.GetData(), xDim*yDim*zDim * sizeof(double)); - segmentationVolume = Volume::New(m_SegmentationArray, xDim, yDim, zDim); + segmentationVolume = Volume::New(m_SegmentationArray, xDim, yDim, zDim, xSpacing); } return mitk::pa::InSilicoTissueVolume::New(absorptionVolume, scatteringVolume, anisotropyVolume, segmentationVolume, tissueParameters, propertyList); } mitk::pa::FluenceYOffsetPair::Pointer mitk::pa::IOUtil::LoadFluenceSimulation(std::string fluenceSimulation) { MITK_INFO << "Adding slice..."; - auto inputImage = mitk::IOUtil::Load(fluenceSimulation); - mitk::ImageReadAccessor readAccess0(inputImage, inputImage->GetVolumeData(0)); - - unsigned int xDim = inputImage->GetDimensions()[1]; - unsigned int yDim = inputImage->GetDimensions()[0]; - unsigned int zDim = inputImage->GetDimensions()[2]; - int size = xDim*yDim*zDim; - auto* fluenceArray = new double[size]; - memcpy(fluenceArray, readAccess0.GetData(), size * sizeof(double)); + mitk::Image::Pointer inputImage = mitk::IOUtil::Load(fluenceSimulation); auto yOffsetProperty = inputImage->GetProperty("y-offset"); if (yOffsetProperty.IsNull()) mitkThrow() << "No \"y-offset\" property found in fluence file!"; std::string yOff = yOffsetProperty->GetValueAsString(); MITK_INFO << "Reading y Offset: " << yOff; #ifdef __linux__ std::replace(yOff.begin(), yOff.end(), '.', ','); #endif // __linux__ double yOffset = std::stod(yOff); MITK_INFO << "Converted offset " << yOffset; - return mitk::pa::FluenceYOffsetPair::New(mitk::pa::Volume::New(fluenceArray, xDim, yDim, zDim), yOffset); + return FluenceYOffsetPair::New(Volume::New(inputImage), yOffset); } diff --git a/Modules/PhotoacousticsLib/src/Utils/mitkPATissueGeneratorParameters.cpp b/Modules/PhotoacousticsLib/src/Utils/mitkPATissueGeneratorParameters.cpp index d6e1637a34..bcbeed0f2f 100644 --- a/Modules/PhotoacousticsLib/src/Utils/mitkPATissueGeneratorParameters.cpp +++ b/Modules/PhotoacousticsLib/src/Utils/mitkPATissueGeneratorParameters.cpp @@ -1,79 +1,80 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkPATissueGeneratorParameters.h" mitk::pa::TissueGeneratorParameters::TissueGeneratorParameters() { m_XDim = 50; m_YDim = 50; m_ZDim = 50; m_VoxelSpacingInCentimeters = 1; - m_VolumeSmoothingSigma = 0; - m_DoVolumeSmoothing = false; + m_DoPartialVolume = false; m_UseRngSeed = false; m_RngSeed = 1337L; m_RandomizePhysicalProperties = false; m_RandomizePhysicalPropertiesPercentage = 0; + m_ForceVesselsMoveAlongYDirection = false; - m_BackgroundAbsorption = 0.1; + m_MinBackgroundAbsorption = 0.1; + m_MaxBackgroundAbsorption = 0.1; m_BackgroundScattering = 15; m_BackgroundAnisotropy = 0.9; m_AirAbsorption = 0.0001; m_AirScattering = 1; m_AirAnisotropy = 1; m_AirThicknessInMillimeters = 0; m_SkinAbsorption = 0.1; m_SkinScattering = 15; m_SkinAnisotropy = 0.9; m_SkinThicknessInMillimeters = 0; m_CalculateNewVesselPositionCallback = &VesselMeanderStrategy::CalculateRandomlyDivergingPosition; m_MinNumberOfVessels = 0; m_MaxNumberOfVessels = 0; m_MinVesselBending = 0; m_MaxVesselBending = 0.1; m_MinVesselAbsorption = 1; m_MaxVesselAbsorption = 8; m_MinVesselRadiusInMillimeters = 1; m_MaxVesselRadiusInMillimeters = 3; m_VesselBifurcationFrequency = 25; m_MinVesselScattering = 15; m_MaxVesselScattering = 15; m_MinVesselAnisotropy = 0.9; m_MaxVesselAnisotropy = 0.9; m_MinVesselZOrigin = 10; m_MaxVesselZOrigin = 40; m_MCflag = 1; m_MCLaunchflag = 0; m_MCBoundaryflag = 2; m_MCLaunchPointX = 25; m_MCLaunchPointY = 25; m_MCLaunchPointZ = 2; m_MCFocusPointX = 25; m_MCFocusPointY = 25; m_MCFocusPointZ = 25; m_MCTrajectoryVectorX = 0; m_MCTrajectoryVectorY = 0; m_MCTrajectoryVectorZ = 1; m_MCRadius = 2; m_MCWaist = 4; } mitk::pa::TissueGeneratorParameters::~TissueGeneratorParameters() { } diff --git a/Modules/PhotoacousticsLib/src/Utils/mitkPAVector.cpp b/Modules/PhotoacousticsLib/src/Utils/mitkPAVector.cpp index e621671c53..4c15f75064 100644 --- a/Modules/PhotoacousticsLib/src/Utils/mitkPAVector.cpp +++ b/Modules/PhotoacousticsLib/src/Utils/mitkPAVector.cpp @@ -1,168 +1,182 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkPAVector.h" #include "chrono" #include mitk::pa::Vector::Vector() { m_Vector.Fill(0); } mitk::pa::Vector::~Vector() { m_Vector.Fill(0); } double mitk::pa::Vector::GetNorm() { return m_Vector.GetNorm(); } double mitk::pa::Vector::GetElement(unsigned short index) { return m_Vector.GetElement(index); } void mitk::pa::Vector::SetElement(unsigned short index, double value) { m_Vector.SetElement(index, value); } void mitk::pa::Vector::Normalize() { double norm = m_Vector.GetNorm(); m_Vector.SetElement(0, m_Vector.GetElement(0) / norm); m_Vector.SetElement(1, m_Vector.GetElement(1) / norm); m_Vector.SetElement(2, m_Vector.GetElement(2) / norm); } void mitk::pa::Vector::SetValue(mitk::pa::Vector::Pointer value) { m_Vector.SetElement(0, value->GetElement(0)); m_Vector.SetElement(1, value->GetElement(1)); m_Vector.SetElement(2, value->GetElement(2)); } void mitk::pa::Vector::RandomizeByPercentage(double percentage, double bendingFactor, std::mt19937* rng) { std::uniform_real_distribution<> range(-percentage, percentage); m_Vector.SetElement(0, m_Vector.GetElement(0) + (bendingFactor * range(*rng))); m_Vector.SetElement(1, m_Vector.GetElement(1) + (bendingFactor * range(*rng))); m_Vector.SetElement(2, m_Vector.GetElement(2) + (bendingFactor * range(*rng))); } void mitk::pa::Vector::Randomize(double xLowerLimit, double xUpperLimit, double yLowerLimit, double yUpperLimit, double zLowerLimit, double zUpperLimit, std::mt19937* rng) { std::uniform_real_distribution<> rangeX(xLowerLimit, xUpperLimit); std::uniform_real_distribution<> rangeY(yLowerLimit, yUpperLimit); std::uniform_real_distribution<> rangeZ(zLowerLimit, zUpperLimit); m_Vector.SetElement(0, rangeX(*rng)); m_Vector.SetElement(1, rangeY(*rng)); m_Vector.SetElement(2, rangeZ(*rng)); } void mitk::pa::Vector::Randomize(double xLimit, double yLimit, double zLimit, std::mt19937* rng) { Randomize(0, xLimit, 0, yLimit, 0, zLimit, rng); } void mitk::pa::Vector::Randomize(std::mt19937* rng) { Randomize(-1, 1, -1, 1, -1, 1, rng); } void mitk::pa::Vector::PrintSelf(std::ostream& os, itk::Indent /*indent*/) const { os << "X: " << m_Vector.GetElement(0) << std::endl; os << "Y: " << m_Vector.GetElement(1) << std::endl; os << "Z: " << m_Vector.GetElement(2) << std::endl; os << "Length: " << m_Vector.GetNorm() << std::endl; } void mitk::pa::Vector::Rotate(double thetaChange, double phiChange) { MITK_DEBUG << "Vector before rotation: (" << GetElement(0) << "|" << GetElement(1) << "|" << GetElement(2) << ")"; if (thetaChange == 0 && phiChange == 0) return; double x = GetElement(0); double y = GetElement(1); double z = GetElement(2); double r = sqrt(x*x + y*y + z*z); if (r == 0) return; double theta = acos(z / r); double phi = atan2(y, x); theta += thetaChange; phi += phiChange; SetElement(0, r * sin(theta) * cos(phi)); SetElement(1, r * sin(theta) * sin(phi)); SetElement(2, r * cos(theta)); MITK_DEBUG << "Vector after rotation: (" << GetElement(0) << "|" << GetElement(1) << "|" << GetElement(2) << ")"; } void mitk::pa::Vector::Scale(double factor) { m_Vector.SetElement(0, m_Vector.GetElement(0)*factor); m_Vector.SetElement(1, m_Vector.GetElement(1)*factor); m_Vector.SetElement(2, m_Vector.GetElement(2)*factor); } mitk::pa::Vector::Pointer mitk::pa::Vector::Clone() { auto returnVector = Vector::New(); returnVector->SetElement(0, this->GetElement(0)); returnVector->SetElement(1, this->GetElement(1)); returnVector->SetElement(2, this->GetElement(2)); return returnVector; } +void mitk::pa::Vector::Subtract(Vector::Pointer other) +{ + m_Vector.SetElement(0, m_Vector.GetElement(0) - other->GetElement(0)); + m_Vector.SetElement(1, m_Vector.GetElement(1) - other->GetElement(1)); + m_Vector.SetElement(2, m_Vector.GetElement(2) - other->GetElement(2)); +} + +void mitk::pa::Vector::Add(Vector::Pointer other) +{ + m_Vector.SetElement(0, m_Vector.GetElement(0) + other->GetElement(0)); + m_Vector.SetElement(1, m_Vector.GetElement(1) + other->GetElement(1)); + m_Vector.SetElement(2, m_Vector.GetElement(2) + other->GetElement(2)); +} + bool mitk::pa::Equal(const Vector::Pointer leftHandSide, const Vector::Pointer rightHandSide, double eps, bool verbose) { MITK_INFO(verbose) << "=== mitk::pa::Vector Equal ==="; if (rightHandSide.IsNull() || leftHandSide.IsNull()) { MITK_INFO(verbose) << "Cannot compare nullpointers"; return false; } if (leftHandSide->GetElement(0) - rightHandSide->GetElement(0) > eps) { MITK_INFO(verbose) << "Element[0] not equal"; return false; } if (leftHandSide->GetElement(1) - rightHandSide->GetElement(1) > eps) { MITK_INFO(verbose) << "Element[1] not equal"; return false; } if (leftHandSide->GetElement(2) - rightHandSide->GetElement(2) > eps) { MITK_INFO(verbose) << "Element[2] not equal"; return false; } return true; } diff --git a/Modules/PhotoacousticsLib/src/Utils/mitkPAVesselDrawer.cpp b/Modules/PhotoacousticsLib/src/Utils/mitkPAVesselDrawer.cpp new file mode 100644 index 0000000000..2f9b266fa0 --- /dev/null +++ b/Modules/PhotoacousticsLib/src/Utils/mitkPAVesselDrawer.cpp @@ -0,0 +1,82 @@ +/*=================================================================== + +The Medical Imaging Interaction Toolkit (MITK) + +Copyright (c) German Cancer Research Center, +Division of Medical and Biological Informatics. +All rights reserved. + +This software is distributed WITHOUT ANY WARRANTY; without +even the implied warranty of MERCHANTABILITY or FITNESS FOR +A PARTICULAR PURPOSE. + +See LICENSE.txt or http://www.mitk.org for details. + +===================================================================*/ + +#include "mitkPAVesselDrawer.h" +#include "mitkPAVesselProperties.h" + +mitk::pa::VesselDrawer::VesselDrawer() +{ +} + +mitk::pa::VesselDrawer::~VesselDrawer() +{ +} + +void mitk::pa::VesselDrawer::DrawVesselInVolume( + VesselProperties::Pointer properties, + InSilicoTissueVolume::Pointer volume) +{ + Vector::Pointer stepDirection = properties->GetDirectionVector(); + Vector::Pointer fromPosition = properties->GetPositionVector()->Clone(); + + Vector::Pointer toPosition = properties->GetPositionVector()->Clone(); + Vector::Pointer totalWalkingDistance = stepDirection->Clone(); + totalWalkingDistance->Scale(1.0 / volume->GetSpacing()); + + while (totalWalkingDistance->GetNorm() >= 1) + { + double xPos = fromPosition->GetElement(0); + double yPos = fromPosition->GetElement(1); + double zPos = fromPosition->GetElement(2); + + if (!volume->IsInsideVolume(xPos, yPos, zPos)) + { + properties->SetRadiusInVoxel(0); + return; + } + + double radius = properties->GetRadiusInVoxel(); + double ceiledRadius = ceil(radius); + + for (int x = xPos - ceiledRadius; x <= xPos + ceiledRadius; x += 1) + for (int y = yPos - ceiledRadius; y <= yPos + ceiledRadius; y += 1) + for (int z = zPos - ceiledRadius; z <= zPos + ceiledRadius; z += 1) + { + if (!volume->IsInsideVolume(x, y, z)) + { + continue; + } + double xDiff = x - xPos; + double yDiff = y - yPos; + double zDiff = z - zPos; + double vectorLengthDiff = radius - sqrt(xDiff*xDiff + yDiff*yDiff + zDiff*zDiff); + + if (vectorLengthDiff > 0) + { + volume->SetVolumeValues(x, y, z, + properties->GetAbsorptionCoefficient(), + properties->GetScatteringCoefficient(), + properties->GetAnisotopyCoefficient(), + mitk::pa::InSilicoTissueVolume::SegmentationType::VESSEL); + } + } + + totalWalkingDistance->Subtract(stepDirection); + fromPosition->Add(stepDirection); + } + + properties->SetPositionVector(fromPosition); +} diff --git a/Modules/PhotoacousticsLib/src/Utils/mitkPAVolumeManipulator.cpp b/Modules/PhotoacousticsLib/src/Utils/mitkPAVolumeManipulator.cpp index aa99d1f5f3..c5f728505e 100644 --- a/Modules/PhotoacousticsLib/src/Utils/mitkPAVolumeManipulator.cpp +++ b/Modules/PhotoacousticsLib/src/Utils/mitkPAVolumeManipulator.cpp @@ -1,177 +1,236 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkPAVolumeManipulator.h" #include "mitkPAExceptions.h" +#include "mitkPAInSilicoTissueVolume.h" +#include "itkResampleImageFilter.h" +#include "itkGaussianInterpolateImageFunction.h" + +// Includes for image casting between ITK and MITK +#include +#include + +#include mitk::pa::VolumeManipulator::VolumeManipulator() {} mitk::pa::VolumeManipulator::~VolumeManipulator() {} -void mitk::pa::VolumeManipulator::ThresholdImage(mitk::pa::Volume::Pointer image, double threshold) +void mitk::pa::VolumeManipulator::ThresholdImage(Volume::Pointer image, double threshold) { for (unsigned int z = 0; z < image->GetZDim(); z++) for (unsigned int y = 0; y < image->GetYDim(); y++) for (unsigned int x = 0; x < image->GetXDim(); x++) if (image->GetData(x, y, z) > threshold) image->SetData(1, x, y, z); else image->SetData(0, x, y, z); } -void mitk::pa::VolumeManipulator::MultiplyImage(mitk::pa::Volume::Pointer image, double factor) +void mitk::pa::VolumeManipulator::MultiplyImage(Volume::Pointer image, double factor) { for (unsigned int z = 0; z < image->GetZDim(); z++) for (unsigned int y = 0; y < image->GetYDim(); y++) for (unsigned int x = 0; x < image->GetXDim(); x++) image->SetData(image->GetData(x, y, z)*factor, x, y, z); } -void mitk::pa::VolumeManipulator::Log10Image(mitk::pa::Volume::Pointer image) +void mitk::pa::VolumeManipulator::Log10Image(Volume::Pointer image) { for (unsigned int z = 0; z < image->GetZDim(); z++) for (unsigned int y = 0; y < image->GetYDim(); y++) for (unsigned int x = 0; x < image->GetXDim(); x++) { if (image->GetData(x, y, z) < 0) { MITK_ERROR << "Signal was smaller than 0. There is an error in the input image file."; - throw mitk::pa::InvalidValueException("Signal was smaller than 0. There is an error in the input image file."); + throw InvalidValueException("Signal was smaller than 0. There is an error in the input image file."); } image->SetData(log10(image->GetData(x, y, z)), x, y, z); } } +void mitk::pa::VolumeManipulator::RescaleImage(InSilicoTissueVolume::Pointer image, double ratio) +{ + MITK_INFO << "Rescaling images.."; + double sigma = image->GetSpacing() / (ratio * 2); + image->SetAbsorptionVolume(RescaleImage(image->GetAbsorptionVolume(), ratio, sigma)); + image->SetScatteringVolume(RescaleImage(image->GetScatteringVolume(), ratio, sigma)); + image->SetAnisotropyVolume(RescaleImage(image->GetAnisotropyVolume(), ratio, sigma)); + image->SetSegmentationVolume(RescaleImage(image->GetSegmentationVolume(), ratio, 0)); + MITK_INFO << "Rescaling images..[Done]"; +} + +mitk::pa::Volume::Pointer mitk::pa::VolumeManipulator::RescaleImage(Volume::Pointer image, double ratio, double sigma) +{ + MITK_INFO << "Rescaling image.."; + typedef itk::Image ImageType; + typedef itk::ResampleImageFilter FilterType; + typedef itk::GaussianInterpolateImageFunction InterpolatorType; + + auto input = image->AsMitkImage(); + ImageType::Pointer itkInput = ImageType::New(); + mitk::CastToItkImage(input, itkInput); + + ImageType::SizeType outputSize; + outputSize[0] = input->GetDimensions()[0] * ratio; + outputSize[1] = input->GetDimensions()[1] * ratio; + outputSize[2] = input->GetDimensions()[2] * ratio; + + FilterType::Pointer resampleImageFilter = FilterType::New(); + resampleImageFilter->SetInput(itkInput); + resampleImageFilter->SetSize(outputSize); + if (sigma > mitk::eps) + { + auto interpolator = InterpolatorType::New(); + interpolator->SetSigma(sigma); + resampleImageFilter->SetInterpolator(interpolator); + } + resampleImageFilter->SetOutputSpacing(input->GetGeometry()->GetSpacing()[0] / ratio); + + MITK_INFO << "Update.."; + resampleImageFilter->UpdateLargestPossibleRegion(); + MITK_INFO << "Update..[Done]"; + + ImageType::Pointer output = resampleImageFilter->GetOutput(); + mitk::Image::Pointer mitkOutput = mitk::Image::New(); + + GrabItkImageMemory(output, mitkOutput); + MITK_INFO << "Rescaling image..[Done]"; + return Volume::New(mitkOutput); +} + /** * @brief Fast 3D Gaussian convolution IIR approximation * @param paVolume * @param sigma * @author Pascal Getreuer * * Copyright (c) 2011, Pascal Getreuer * All rights reserved. * * This program is free software: you can redistribute it and/or modify it * under the terms of the simplified BSD license. * * You should have received a copy of these licenses along with this program. * If not, see . */ void mitk::pa::VolumeManipulator::GaussianBlur3D(mitk::pa::Volume::Pointer paVolume, double sigma) { double* volume = paVolume->GetData(); long width = paVolume->GetYDim(); long height = paVolume->GetXDim(); long depth = paVolume->GetZDim(); const long plane = width*height; const long numel = plane*depth; double lambda, dnu; double nu, boundaryscale, postscale; double *ptr; long i, x, y, z; int step; if (sigma <= 0) return; lambda = (sigma*sigma) / (8.0); dnu = (1.0 + 2.0*lambda - sqrt(1.0 + 4.0*lambda)) / (2.0*lambda); nu = dnu; boundaryscale = 1.0 / (1.0 - dnu); postscale = pow(dnu / lambda, 12); /* Filter horizontally along each row */ for (z = 0; z < depth; z++) { for (y = 0; y < height; y++) { for (step = 0; step < 4; step++) { ptr = volume + width*(y + height*z); ptr[0] *= boundaryscale; /* Filter rightwards */ for (x = 1; x < width; x++) { ptr[x] += nu*ptr[x - 1]; } ptr[x = width - 1] *= boundaryscale; /* Filter leftwards */ for (; x > 0; x--) { ptr[x - 1] += nu*ptr[x]; } } } } /* Filter vertically along each column */ for (z = 0; z < depth; z++) { for (x = 0; x < width; x++) { for (step = 0; step < 4; step++) { ptr = volume + x + plane*z; ptr[0] *= boundaryscale; /* Filter downwards */ for (i = width; i < plane; i += width) { ptr[i] += nu*ptr[i - width]; } ptr[i = plane - width] *= boundaryscale; /* Filter upwards */ for (; i > 0; i -= width) { ptr[i - width] += nu*ptr[i]; } } } } /* Filter along z-dimension */ for (y = 0; y < height; y++) { for (x = 0; x < width; x++) { for (step = 0; step < 4; step++) { ptr = volume + x + width*y; ptr[0] *= boundaryscale; for (i = plane; i < numel; i += plane) { ptr[i] += nu*ptr[i - plane]; } ptr[i = numel - plane] *= boundaryscale; for (; i > 0; i -= plane) { ptr[i - plane] += nu*ptr[i]; } } } } for (i = 0; i < numel; i++) { volume[i] *= postscale; } } diff --git a/Modules/PhotoacousticsLib/test/files.cmake b/Modules/PhotoacousticsLib/test/files.cmake index ae1e015083..0796a506a5 100644 --- a/Modules/PhotoacousticsLib/test/files.cmake +++ b/Modules/PhotoacousticsLib/test/files.cmake @@ -1,40 +1,41 @@ set(MODULE_TESTS # IMPORTANT: If you plan to deactivate / comment out a test please write a bug number to the commented out line of code. # # Example: #mitkMyTest #this test is commented out because of bug 12345 # # It is important that the bug is open and that the test will be activated again before the bug is closed. This assures that # no test is forgotten after it was commented out. If there is no bug for your current problem, please add a new one and # mark it as critical. ################## ON THE FENCE TESTS ################################################# # none ################## DISABLED TESTS ##################################################### ################# RUNNING TESTS ####################################################### mitkSlicedVolumeGeneratorTest.cpp mitkPhotoacousticTissueGeneratorTest.cpp mitkPhotoacousticVectorTest.cpp mitkPhotoacoustic3dVolumeTest.cpp mitkPhotoacousticVolumeTest.cpp mitkPhotoacousticVesselTest.cpp mitkPhotoacousticVesselTreeTest.cpp mitkPhotoacousticVesselMeanderStrategyTest.cpp mitkMcxyzXmlTest.cpp mitkPhotoacousticComposedVolumeTest.cpp mitkPhotoacousticNoiseGeneratorTest.cpp mitkPhotoacousticIOTest.cpp mitkMCThreadHandlerTest.cpp mitkSimulationBatchGeneratorTest.cpp mitkPropertyCalculatorTest.cpp + mitkPhotoacousticImageSavingTest.cpp ) set(RESOURCE_FILES pointsource.xml circlesource.xml rectanglesource.xml twopointsources.xml allsources.xml ) diff --git a/Modules/PhotoacousticsLib/test/mitkPhotoacoustic3dVolumeTest.cpp b/Modules/PhotoacousticsLib/test/mitkPhotoacoustic3dVolumeTest.cpp index 215a43d350..8ee9bf73c7 100644 --- a/Modules/PhotoacousticsLib/test/mitkPhotoacoustic3dVolumeTest.cpp +++ b/Modules/PhotoacousticsLib/test/mitkPhotoacoustic3dVolumeTest.cpp @@ -1,224 +1,224 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include #include #include "mitkPAVolume.h" #include #include class mitkPhotoacoustic3dVolumeTestSuite : public mitk::TestFixture { CPPUNIT_TEST_SUITE(mitkPhotoacoustic3dVolumeTestSuite); MITK_TEST(TestCorrectGetDataAndSetDataBehavior); MITK_TEST(TestCallingConstructorWithNullParameter); MITK_TEST(TestCallingConstructorWithCorrectParameters); MITK_TEST(TestModifyImage); MITK_TEST(TestModifyComplexImage); MITK_TEST(TestConvertToMitkImage); MITK_TEST(TestDeepCopy); MITK_TEST(TestCatchException); CPPUNIT_TEST_SUITE_END(); private: mitk::pa::Volume::Pointer m_Photoacoustic3dVolume; public: void setUp() override { } void TestCallingConstructorWithNullParameter() { bool exceptionEncountered = false; try { - m_Photoacoustic3dVolume = mitk::pa::Volume::New(nullptr, 3, 3, 3); + m_Photoacoustic3dVolume = mitk::pa::Volume::New(nullptr, 3, 3, 3, 1); } catch (...) { exceptionEncountered = true; } CPPUNIT_ASSERT(exceptionEncountered); } void TestCallingConstructorWithCorrectParameters() { auto* data = new double[1]; data[0] = 3; - m_Photoacoustic3dVolume = mitk::pa::Volume::New(data, 1, 1, 1); + m_Photoacoustic3dVolume = mitk::pa::Volume::New(data, 1, 1, 1, 1); CPPUNIT_ASSERT(m_Photoacoustic3dVolume->GetData(0, 0, 0) == 3); CPPUNIT_ASSERT(m_Photoacoustic3dVolume->GetXDim() == 1); CPPUNIT_ASSERT(m_Photoacoustic3dVolume->GetYDim() == 1); CPPUNIT_ASSERT(m_Photoacoustic3dVolume->GetZDim() == 1); } void TestModifyImage() { auto* data = new double[1]; data[0] = 3; - m_Photoacoustic3dVolume = mitk::pa::Volume::New(data, 1, 1, 1); + m_Photoacoustic3dVolume = mitk::pa::Volume::New(data, 1, 1, 1, 1); CPPUNIT_ASSERT_MESSAGE(std::to_string(m_Photoacoustic3dVolume->GetData(0, 0, 0)), m_Photoacoustic3dVolume->GetData(0, 0, 0) == 3); m_Photoacoustic3dVolume->SetData(17, 0, 0, 0); CPPUNIT_ASSERT(m_Photoacoustic3dVolume->GetData(0, 0, 0) == 17); } void TestModifyComplexImage() { unsigned int xDim = 4; unsigned int yDim = 7; unsigned int zDim = 12; unsigned int length = xDim * yDim * zDim; auto* data = new double[length]; for (unsigned int i = 0; i < length; i++) data[i] = 5; - m_Photoacoustic3dVolume = mitk::pa::Volume::New(data, xDim, yDim, zDim); + m_Photoacoustic3dVolume = mitk::pa::Volume::New(data, xDim, yDim, zDim, 1); for (unsigned int z = 0; z < zDim; z++) for (unsigned int y = 0; y < yDim; y++) for (unsigned int x = 0; x < xDim; x++) { CPPUNIT_ASSERT(m_Photoacoustic3dVolume->GetData(x, y, z) == 5); m_Photoacoustic3dVolume->SetData((x + y)*(z + 1), x, y, z); CPPUNIT_ASSERT(std::abs(m_Photoacoustic3dVolume->GetData(x, y, z) - (x + y)*(z + 1)) < mitk::eps); } } void TestCorrectGetDataAndSetDataBehavior() { unsigned int xDim = 40; unsigned int yDim = 7; unsigned int zDim = 12; unsigned int length = xDim * yDim * zDim; auto* data = new double[length]; for (unsigned int i = 0; i < length; i++) data[i] = 0; - m_Photoacoustic3dVolume = mitk::pa::Volume::New(data, xDim, yDim, zDim); + m_Photoacoustic3dVolume = mitk::pa::Volume::New(data, xDim, yDim, zDim, 1); for (unsigned int z = 0; z < zDim; z++) for (unsigned int y = 0; y < yDim; y++) for (unsigned int x = 0; x < xDim; x++) { int index = z*xDim*yDim + x*yDim + y; m_Photoacoustic3dVolume->SetData(index, x, y, z); CPPUNIT_ASSERT_MESSAGE(std::to_string(index), m_Photoacoustic3dVolume->GetData(x, y, z) == index); } } void TestConvertToMitkImage() { auto* data = new double[6]; data[0] = 3; data[1] = 3; data[2] = 3; data[3] = 3; data[4] = 3; data[5] = 3; - m_Photoacoustic3dVolume = mitk::pa::Volume::New(data, 1, 2, 3); + m_Photoacoustic3dVolume = mitk::pa::Volume::New(data, 1, 2, 3, 1); CPPUNIT_ASSERT(m_Photoacoustic3dVolume->GetData(0, 0, 0) == 3); CPPUNIT_ASSERT(m_Photoacoustic3dVolume->GetData(0, 0, 1) == 3); CPPUNIT_ASSERT(m_Photoacoustic3dVolume->GetData(0, 0, 2) == 3); CPPUNIT_ASSERT(m_Photoacoustic3dVolume->GetData(0, 1, 0) == 3); CPPUNIT_ASSERT(m_Photoacoustic3dVolume->GetData(0, 1, 1) == 3); CPPUNIT_ASSERT(m_Photoacoustic3dVolume->GetData(0, 1, 2) == 3); m_Photoacoustic3dVolume->SetData(17, 0, 0, 0); m_Photoacoustic3dVolume->SetData(17, 0, 1, 0); m_Photoacoustic3dVolume->SetData(17, 0, 1, 2); CPPUNIT_ASSERT(m_Photoacoustic3dVolume->GetData(0, 0, 0) == 17); CPPUNIT_ASSERT(m_Photoacoustic3dVolume->GetData(0, 0, 1) == 3); CPPUNIT_ASSERT(m_Photoacoustic3dVolume->GetData(0, 0, 2) == 3); CPPUNIT_ASSERT(m_Photoacoustic3dVolume->GetData(0, 1, 0) == 17); CPPUNIT_ASSERT(m_Photoacoustic3dVolume->GetData(0, 1, 1) == 3); CPPUNIT_ASSERT(m_Photoacoustic3dVolume->GetData(0, 1, 2) == 17); mitk::Image::Pointer mitkImage = m_Photoacoustic3dVolume->AsMitkImage(); CPPUNIT_ASSERT(mitkImage->GetDimensions()[0] == 2); CPPUNIT_ASSERT(mitkImage->GetDimensions()[1] == 1); CPPUNIT_ASSERT(mitkImage->GetDimensions()[2] == 3); mitk::ImageReadAccessor readAccess(mitkImage, mitkImage->GetVolumeData()); auto* copyData = (double*)readAccess.GetData(); CPPUNIT_ASSERT_MESSAGE(std::to_string(copyData[0]), copyData[0] == 17); CPPUNIT_ASSERT_MESSAGE(std::to_string(copyData[1]), copyData[1] == 17); CPPUNIT_ASSERT_MESSAGE(std::to_string(copyData[2]), copyData[2] == 3); CPPUNIT_ASSERT_MESSAGE(std::to_string(copyData[3]), copyData[3] == 3); CPPUNIT_ASSERT_MESSAGE(std::to_string(copyData[4]), copyData[4] == 3); CPPUNIT_ASSERT_MESSAGE(std::to_string(copyData[5]), copyData[5] == 17); } void TestDeepCopy() { auto* data = new double[1]; data[0] = 3; - m_Photoacoustic3dVolume = mitk::pa::Volume::New(data, 1, 1, 1); + m_Photoacoustic3dVolume = mitk::pa::Volume::New(data, 1, 1, 1, 1); mitk::pa::Volume::Pointer copiedVolume = m_Photoacoustic3dVolume->DeepCopy(); CPPUNIT_ASSERT(m_Photoacoustic3dVolume->GetXDim() == copiedVolume->GetXDim()); CPPUNIT_ASSERT(m_Photoacoustic3dVolume->GetYDim() == copiedVolume->GetYDim()); CPPUNIT_ASSERT(m_Photoacoustic3dVolume->GetZDim() == copiedVolume->GetZDim()); CPPUNIT_ASSERT(m_Photoacoustic3dVolume->GetData(0, 0, 0) == 3); CPPUNIT_ASSERT(copiedVolume->GetData(0, 0, 0) == 3); m_Photoacoustic3dVolume->SetData(17, 0, 0, 0); CPPUNIT_ASSERT(m_Photoacoustic3dVolume->GetData(0, 0, 0) == 17); CPPUNIT_ASSERT(copiedVolume->GetData(0, 0, 0) == 3); } void AssertIndexException(unsigned int x, unsigned int y, unsigned int z) { bool exceptionCaught = false; try { double thisIsIrrelevant = m_Photoacoustic3dVolume->GetData(x, y, z); thisIsIrrelevant += 1; } catch (...) { exceptionCaught = true; if (exceptionCaught) exceptionCaught = true; } #ifdef _DEBUG CPPUNIT_ASSERT(exceptionCaught); #endif - } + } void TestCatchException() { auto* data = new double[1]; data[0] = 3; - m_Photoacoustic3dVolume = mitk::pa::Volume::New(data, 1, 1, 1); + m_Photoacoustic3dVolume = mitk::pa::Volume::New(data, 1, 1, 1, 1); AssertIndexException(1, 0, 0); AssertIndexException(0, 1, 0); AssertIndexException(0, 0, 1); AssertIndexException(18, 1, 222); } void tearDown() override { m_Photoacoustic3dVolume = nullptr; } - }; +}; MITK_TEST_SUITE_REGISTRATION(mitkPhotoacoustic3dVolume) diff --git a/Modules/PhotoacousticsLib/test/mitkPhotoacousticComposedVolumeTest.cpp b/Modules/PhotoacousticsLib/test/mitkPhotoacousticComposedVolumeTest.cpp index 65cc609884..b2aa4015eb 100644 --- a/Modules/PhotoacousticsLib/test/mitkPhotoacousticComposedVolumeTest.cpp +++ b/Modules/PhotoacousticsLib/test/mitkPhotoacousticComposedVolumeTest.cpp @@ -1,148 +1,149 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include #include #include "mitkPAComposedVolume.h" #include "mitkIOUtil.h" #include "mitkImageReadAccessor.h" #include class mitkPhotoacousticComposedVolumeTestSuite : public mitk::TestFixture { CPPUNIT_TEST_SUITE(mitkPhotoacousticComposedVolumeTestSuite); MITK_TEST(TestCreateAndDestructComposedVolume); MITK_TEST(TestAccessInvalidFluenceComponent); MITK_TEST(TestAccessInvalidFluenceComponentIndex); MITK_TEST(TestAddMultiplePairs); MITK_TEST(TestSortFunctionality); MITK_TEST(TestAccessInvalidFluenceComponentForYOffset); CPPUNIT_TEST_SUITE_END(); private: mitk::pa::ComposedVolume::Pointer m_ComposedVolume; mitk::pa::TissueGeneratorParameters::Pointer m_DefaultParameters; mitk::pa::InSilicoTissueVolume::Pointer m_InSilicoTissueVolume; public: void setUp() override { m_DefaultParameters = mitk::pa::TissueGeneratorParameters::New(); m_DefaultParameters->SetXDim(5); m_DefaultParameters->SetYDim(5); m_DefaultParameters->SetZDim(5); - m_InSilicoTissueVolume = mitk::pa::InSilicoTissueVolume::New(m_DefaultParameters); + auto rng = std::mt19937(); + m_InSilicoTissueVolume = mitk::pa::InSilicoTissueVolume::New(m_DefaultParameters, &rng); m_ComposedVolume = mitk::pa::ComposedVolume::New(m_InSilicoTissueVolume); } mitk::pa::FluenceYOffsetPair::Pointer createFluenceYOffsetPair(double value, double yOffset) { auto* data = new double[125]; for (int i = 0; i < 125; ++i) data[i] = value; - mitk::pa::Volume::Pointer volume = mitk::pa::Volume::New(data, 5, 5, 5); + mitk::pa::Volume::Pointer volume = mitk::pa::Volume::New(data, 5, 5, 5, 1); return mitk::pa::FluenceYOffsetPair::New(volume, yOffset); } void TestCreateAndDestructComposedVolume() { CPPUNIT_ASSERT(m_ComposedVolume->GetNumberOfFluenceComponents() == 0); } void TestAccessInvalidFluenceComponent() { bool caughtException = false; try { m_ComposedVolume->GetFluenceValue(0, 0, 0, 0); } catch (mitk::Exception e) { caughtException = true; } CPPUNIT_ASSERT(caughtException); } void TestAddMultiplePairs() { m_ComposedVolume->AddSlice(createFluenceYOffsetPair(0, 0)); CPPUNIT_ASSERT(m_ComposedVolume->GetNumberOfFluenceComponents() == 1); m_ComposedVolume->AddSlice(createFluenceYOffsetPair(1, 1)); CPPUNIT_ASSERT(m_ComposedVolume->GetNumberOfFluenceComponents() == 2); } void TestSortFunctionality() { m_ComposedVolume->AddSlice(createFluenceYOffsetPair(2, 2)); m_ComposedVolume->AddSlice(createFluenceYOffsetPair(-1, -1)); m_ComposedVolume->AddSlice(createFluenceYOffsetPair(1, 1)); m_ComposedVolume->AddSlice(createFluenceYOffsetPair(0, 0)); m_ComposedVolume->AddSlice(createFluenceYOffsetPair(-2, -2)); CPPUNIT_ASSERT(m_ComposedVolume->GetFluenceValue(0, 0, 2, 0) == 2); CPPUNIT_ASSERT(m_ComposedVolume->GetFluenceValue(1, 0, 2, 0) == -1); CPPUNIT_ASSERT(m_ComposedVolume->GetFluenceValue(2, 0, 2, 0) == 1); CPPUNIT_ASSERT(m_ComposedVolume->GetFluenceValue(3, 0, 2, 0) == 0); CPPUNIT_ASSERT(m_ComposedVolume->GetFluenceValue(4, 0, 2, 0) == -2); m_ComposedVolume->Sort(); CPPUNIT_ASSERT(m_ComposedVolume->GetFluenceValue(0, 0, 2, 0) == -2); CPPUNIT_ASSERT(m_ComposedVolume->GetFluenceValue(1, 0, 2, 0) == -1); CPPUNIT_ASSERT(m_ComposedVolume->GetFluenceValue(2, 0, 2, 0) == 0); CPPUNIT_ASSERT(m_ComposedVolume->GetFluenceValue(3, 0, 2, 0) == 1); CPPUNIT_ASSERT(m_ComposedVolume->GetFluenceValue(4, 0, 2, 0) == 2); } void TestAccessInvalidFluenceComponentIndex() { #ifdef _DEBUG m_ComposedVolume->AddSlice(createFluenceYOffsetPair(0, 0)); bool caughtException = false; try { double unusedValue = m_ComposedVolume->GetFluenceValue(0, 1, 2, 300); unusedValue = 0; } catch (mitk::Exception e) { caughtException = true; } CPPUNIT_ASSERT(caughtException); #endif } void TestAccessInvalidFluenceComponentForYOffset() { bool caughtException = false; try { m_ComposedVolume->GetYOffsetForFluenceComponentInPixels(0); } catch (mitk::Exception e) { caughtException = true; } CPPUNIT_ASSERT(caughtException); } void tearDown() override { m_ComposedVolume = nullptr; } }; MITK_TEST_SUITE_REGISTRATION(mitkPhotoacousticComposedVolume) diff --git a/Modules/PhotoacousticsLib/test/mitkPhotoacousticIOTest.cpp b/Modules/PhotoacousticsLib/test/mitkPhotoacousticIOTest.cpp index 50f7da00cc..ba66cf2099 100644 --- a/Modules/PhotoacousticsLib/test/mitkPhotoacousticIOTest.cpp +++ b/Modules/PhotoacousticsLib/test/mitkPhotoacousticIOTest.cpp @@ -1,187 +1,189 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include #include #include #include #include #include #include #include #include class mitkPhotoacousticIOTestSuite : public mitk::TestFixture { CPPUNIT_TEST_SUITE(mitkPhotoacousticIOTestSuite); MITK_TEST(testLoadInSilicoTissueNrrdFile); MITK_TEST(testLoad3DVolumeNrrdFile); MITK_TEST(testLoad3DVolumeNrrdFileWithBlur); MITK_TEST(testGetNumberOfNrrdFilesInTestDir); MITK_TEST(testGetChildFoldersFromFolder); MITK_TEST(testLoadFCMs); CPPUNIT_TEST_SUITE_END(); private: const std::string TEST_FOLDER_PATH = "testFiles/"; const std::string TEST_IN_SILICO_VOLUME_PATH = "testInSilicoVolume"; const std::string TEST_3D_Volume_PATH = "test3DVolume"; const std::string TEST_FILE_ENDING = ".nrrd"; const std::string TEST_QUALIFIED_FOLDER_PATH = TEST_FOLDER_PATH + TEST_IN_SILICO_VOLUME_PATH + "/"; const std::string FOLDER_FOLDER = "folder/"; const std::string FCM_PATH = TEST_FOLDER_PATH + "fcms/"; const int NUMBER_OF_NRRD_FILES_IN_TEST_DIR = 2; mitk::pa::TissueGeneratorParameters::Pointer m_VolumeProperties; mitk::pa::InSilicoTissueVolume::Pointer m_TestInSilicoVolume; mitk::pa::Volume::Pointer m_Test3DVolume; public: void setUp() override { m_VolumeProperties = createTestVolumeParameters(); - m_TestInSilicoVolume = mitk::pa::InSilicoTissueVolume::New(m_VolumeProperties); + auto rng = std::mt19937(); + m_TestInSilicoVolume = mitk::pa::InSilicoTissueVolume::New(m_VolumeProperties, &rng); m_Test3DVolume = createTest3DVolume(5); itk::FileTools::CreateDirectory(TEST_FOLDER_PATH); itk::FileTools::CreateDirectory(TEST_QUALIFIED_FOLDER_PATH); itk::FileTools::CreateDirectory(TEST_FOLDER_PATH + FOLDER_FOLDER + FOLDER_FOLDER); itk::FileTools::CreateDirectory(FCM_PATH); CPPUNIT_ASSERT(itksys::SystemTools::FileIsDirectory(TEST_FOLDER_PATH)); CPPUNIT_ASSERT(itksys::SystemTools::FileIsDirectory(TEST_QUALIFIED_FOLDER_PATH)); CPPUNIT_ASSERT(itksys::SystemTools::FileIsDirectory(TEST_FOLDER_PATH + FOLDER_FOLDER + FOLDER_FOLDER)); CPPUNIT_ASSERT(itksys::SystemTools::FileIsDirectory(FCM_PATH)); mitk::IOUtil::Save(m_TestInSilicoVolume->ConvertToMitkImage(), TEST_FOLDER_PATH + TEST_IN_SILICO_VOLUME_PATH + TEST_FILE_ENDING); mitk::IOUtil::Save(m_Test3DVolume->AsMitkImage(), TEST_FOLDER_PATH + TEST_3D_Volume_PATH + TEST_FILE_ENDING); auto yo0 = createTest3DVolume(1)->AsMitkImage(); auto yo1 = createTest3DVolume(2)->AsMitkImage(); yo0->GetPropertyList()->SetStringProperty("y-offset", "0"); yo1->GetPropertyList()->SetStringProperty("y-offset", "1"); mitk::CoreServices::GetPropertyPersistence()->AddInfo(mitk::PropertyPersistenceInfo::New("y-offset")); mitk::IOUtil::Save(yo0, TEST_QUALIFIED_FOLDER_PATH + TEST_IN_SILICO_VOLUME_PATH + "_yo0" + TEST_FILE_ENDING); mitk::IOUtil::Save(yo1, TEST_QUALIFIED_FOLDER_PATH + TEST_IN_SILICO_VOLUME_PATH + "_yo1" + TEST_FILE_ENDING); } mitk::pa::Volume::Pointer createTest3DVolume(double value) { unsigned int xDim = 10; unsigned int yDim = 10; unsigned int zDim = 10; unsigned int length = xDim * yDim * zDim; auto* data = new double[length]; for (unsigned int i = 0; i < length; i++) data[i] = value; - return mitk::pa::Volume::New(data, xDim, yDim, zDim); + return mitk::pa::Volume::New(data, xDim, yDim, zDim, 1); } mitk::pa::TissueGeneratorParameters::Pointer createTestVolumeParameters() { auto returnParameters = mitk::pa::TissueGeneratorParameters::New(); returnParameters->SetXDim(10); returnParameters->SetYDim(10); returnParameters->SetZDim(10); - returnParameters->SetBackgroundAbsorption(0); + returnParameters->SetMinBackgroundAbsorption(0); + returnParameters->SetMaxBackgroundAbsorption(0); returnParameters->SetBackgroundScattering(0); returnParameters->SetBackgroundAnisotropy(0); return returnParameters; } void assertEqual(mitk::pa::Volume::Pointer first, mitk::pa::Volume::Pointer second) { CPPUNIT_ASSERT(first->GetXDim() == second->GetXDim()); CPPUNIT_ASSERT(first->GetYDim() == second->GetYDim()); CPPUNIT_ASSERT(first->GetZDim() == second->GetZDim()); for (unsigned int x = 0; x < first->GetXDim(); ++x) for (unsigned int y = 0; y < first->GetYDim(); ++y) for (unsigned int z = 0; z < first->GetZDim(); ++z) { std::string message = "Expected " + std::to_string(first->GetData(x, y, z)) + " but was " + std::to_string(second->GetData(x, y, z)); CPPUNIT_ASSERT_MESSAGE(message, std::abs(first->GetData(x, y, z) - second->GetData(x, y, z)) < 1e-6); } } void testLoadInSilicoTissueNrrdFile() { auto loadedVolume = mitk::pa::IOUtil::LoadInSilicoTissueVolumeFromNrrdFile(TEST_FOLDER_PATH + TEST_IN_SILICO_VOLUME_PATH + TEST_FILE_ENDING); CPPUNIT_ASSERT(loadedVolume->GetTDim() == m_TestInSilicoVolume->GetTDim()); assertEqual(m_TestInSilicoVolume->GetAbsorptionVolume(), loadedVolume->GetAbsorptionVolume()); assertEqual(m_TestInSilicoVolume->GetScatteringVolume(), loadedVolume->GetScatteringVolume()); assertEqual(m_TestInSilicoVolume->GetAnisotropyVolume(), loadedVolume->GetAnisotropyVolume()); } void testLoad3DVolumeNrrdFile() { auto loadedVolume = mitk::pa::IOUtil::LoadNrrd(TEST_FOLDER_PATH + TEST_3D_Volume_PATH + TEST_FILE_ENDING); assertEqual(loadedVolume, m_Test3DVolume); } void testLoad3DVolumeNrrdFileWithBlur() { auto loadedVolume = mitk::pa::IOUtil::LoadNrrd(TEST_FOLDER_PATH + TEST_3D_Volume_PATH + TEST_FILE_ENDING, 1); assertEqual(loadedVolume, m_Test3DVolume); } void testGetNumberOfNrrdFilesInTestDir() { int numberOfFiles = mitk::pa::IOUtil::GetNumberOfNrrdFilesInDirectory(TEST_FOLDER_PATH); CPPUNIT_ASSERT(numberOfFiles == NUMBER_OF_NRRD_FILES_IN_TEST_DIR); } void testGetChildFoldersFromFolder() { std::vector childFolders = mitk::pa::IOUtil::GetAllChildfoldersFromFolder(TEST_FOLDER_PATH); CPPUNIT_ASSERT(childFolders.size() == 1); CPPUNIT_ASSERT(childFolders[0] == TEST_FOLDER_PATH); childFolders = mitk::pa::IOUtil::GetAllChildfoldersFromFolder(TEST_FOLDER_PATH + FOLDER_FOLDER); MITK_INFO << "ChildFolders: " << childFolders.size(); CPPUNIT_ASSERT(childFolders.size() == 1); CPPUNIT_ASSERT(childFolders[0] == TEST_FOLDER_PATH + FOLDER_FOLDER + "/folder"); } void testLoadFCMs() { auto fcm1 = createTest3DVolume(1); auto fcm2 = createTest3DVolume(2); auto fcm3 = createTest3DVolume(3); auto fcm4 = createTest3DVolume(4); mitk::IOUtil::Save(fcm1->AsMitkImage(), FCM_PATH + "fcm1_p0,0,0FluenceContributionMap.nrrd"); mitk::IOUtil::Save(fcm2->AsMitkImage(), FCM_PATH + "fcm1_p0,0,1FluenceContributionMap.nrrd"); mitk::IOUtil::Save(fcm3->AsMitkImage(), FCM_PATH + "fcm1_p1,0,0FluenceContributionMap.nrrd"); mitk::IOUtil::Save(fcm4->AsMitkImage(), FCM_PATH + "fcm1_p1,0,1FluenceContributionMap.nrrd"); int prog = 0; auto map = mitk::pa::IOUtil::LoadFluenceContributionMaps(FCM_PATH, 0, &prog); assertEqual(fcm1, map[mitk::pa::IOUtil::Position{ 0,0 }]); assertEqual(fcm2, map[mitk::pa::IOUtil::Position{ 0,1 }]); assertEqual(fcm3, map[mitk::pa::IOUtil::Position{ 1,0 }]); assertEqual(fcm4, map[mitk::pa::IOUtil::Position{ 1,1 }]); } void tearDown() override { //CPPUNIT_ASSERT_MESSAGE("Resource leak of test files onto hard drive..", itksys::SystemTools::RemoveADirectory(TEST_FOLDER_PATH) == true); } }; MITK_TEST_SUITE_REGISTRATION(mitkPhotoacousticIO) diff --git a/Modules/PhotoacousticsLib/test/mitkPhotoacousticImageSaving.cpp b/Modules/PhotoacousticsLib/test/mitkPhotoacousticImageSaving.cpp new file mode 100644 index 0000000000..5dbf93fa46 --- /dev/null +++ b/Modules/PhotoacousticsLib/test/mitkPhotoacousticImageSaving.cpp @@ -0,0 +1,82 @@ +/*=================================================================== + +The Medical Imaging Interaction Toolkit (MITK) + +Copyright (c) German Cancer Research Center, +Division of Medical and Biological Informatics. +All rights reserved. + +This software is distributed WITHOUT ANY WARRANTY; without +even the implied warranty of MERCHANTABILITY or FITNESS FOR +A PARTICULAR PURPOSE. + +See LICENSE.txt or http://www.mitk.org for details. + +===================================================================*/ + +#include +#include + +#include +#include + +class mitkPhotoacousticImageSavingTestSuite : public mitk::TestFixture +{ + CPPUNIT_TEST_SUITE(mitkPhotoacousticImageSavingTestSuite); + MITK_TEST(testSaveImagePlainMitk); + CPPUNIT_TEST_SUITE_END(); + +private: + +public: + + void setUp() + { + } + + void testSaveImagePlainMitk() + { + unsigned int x = 560; + unsigned int y = 400; + unsigned int z = 720; + unsigned int t = 4; + + unsigned long size = x*y*z*t; + + double* data = new double[size]; + + for (unsigned long i = 0; i < size; i++) + { + data[i] = rand() * 9.7327; + } + + mitk::Image::Pointer image = mitk::Image::New(); + mitk::PixelType TPixel = mitk::MakeScalarPixelType(); + unsigned int* dimensionsOfImage = new unsigned int[4]; + + // Copy dimensions + dimensionsOfImage[0] = 560; + dimensionsOfImage[1] = 400; + dimensionsOfImage[2] = 720; + dimensionsOfImage[3] = 4; + + image->Initialize(TPixel, 4, dimensionsOfImage, 1); + + mitk::Vector3D spacing; + spacing.Fill(0.1); + image->SetSpacing(spacing); + + image->SetImportVolume(data, 0, 0, mitk::Image::CopyMemory); + image->SetImportVolume(data, 1, 0, mitk::Image::CopyMemory); + image->SetImportVolume(data, 2, 0, mitk::Image::CopyMemory); + image->SetImportVolume(data, 3, 0, mitk::Image::CopyMemory); + + mitk::IOUtil::Save(image, "C:/Users/groehl/Desktop/test.nrrd"); + } + + void tearDown() + { + } +}; + +MITK_TEST_SUITE_REGISTRATION(mitkPhotoacousticImageSaving) diff --git a/Modules/PhotoacousticsLib/test/mitkPhotoacousticImageSavingTest.cpp b/Modules/PhotoacousticsLib/test/mitkPhotoacousticImageSavingTest.cpp new file mode 100644 index 0000000000..eb74325e89 --- /dev/null +++ b/Modules/PhotoacousticsLib/test/mitkPhotoacousticImageSavingTest.cpp @@ -0,0 +1,61 @@ +/*=================================================================== + +The Medical Imaging Interaction Toolkit (MITK) + +Copyright (c) German Cancer Research Center, +Division of Medical and Biological Informatics. +All rights reserved. + +This software is distributed WITHOUT ANY WARRANTY; without +even the implied warranty of MERCHANTABILITY or FITNESS FOR +A PARTICULAR PURPOSE. + +See LICENSE.txt or http://www.mitk.org for details. + +===================================================================*/ + +#include +#include + +#include +#include + +class mitkPhotoacousticImageSavingTestSuite : public mitk::TestFixture +{ + CPPUNIT_TEST_SUITE(mitkPhotoacousticImageSavingTestSuite); + MITK_TEST(testSaveImagePlainMitk); + CPPUNIT_TEST_SUITE_END(); + +private: + +public: + + void setUp() + { + } + + void testSaveImagePlainMitk() + { + unsigned int x = pow(2, 30); + unsigned long size = x; + + float* data = new float[size]; + for (unsigned long i = 0; i < size; i++) + data[i] = 0; + + mitk::Image::Pointer image = mitk::Image::New(); + mitk::PixelType TPixel = mitk::MakeScalarPixelType(); + unsigned int* dimensionsOfImage = new unsigned int[1]; + dimensionsOfImage[0] = x; + image->Initialize(TPixel, 1, dimensionsOfImage, 1); + image->SetImportVolume(data, 0, 0, mitk::Image::CopyMemory); + + mitk::IOUtil::Save(image, "C:/Users/groehl/Desktop/test.nrrd"); + } + + void tearDown() + { + } +}; + +MITK_TEST_SUITE_REGISTRATION(mitkPhotoacousticImageSaving) diff --git a/Modules/PhotoacousticsLib/test/mitkPhotoacousticNoiseGeneratorTest.cpp b/Modules/PhotoacousticsLib/test/mitkPhotoacousticNoiseGeneratorTest.cpp index 04a988a528..30a43b9292 100644 --- a/Modules/PhotoacousticsLib/test/mitkPhotoacousticNoiseGeneratorTest.cpp +++ b/Modules/PhotoacousticsLib/test/mitkPhotoacousticNoiseGeneratorTest.cpp @@ -1,71 +1,71 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include #include #include #include "mitkPAVolume.h" #include "mitkPANoiseGenerator.h" class mitkPhotoacousticNoiseGeneratorTestSuite : public mitk::TestFixture { CPPUNIT_TEST_SUITE(mitkPhotoacousticNoiseGeneratorTestSuite); MITK_TEST(testNoiseGenerator); CPPUNIT_TEST_SUITE_END(); private: public: mitk::pa::Volume::Pointer m_Volume; void setUp() override { } void testNoiseGenerator() { int size = 1000 * 100 * 100; auto* volume = new double[size]; for (int i = 0; i < size; i++) { volume[i] = 1; } - m_Volume = mitk::pa::Volume::New(volume, 1000, 100, 100); + m_Volume = mitk::pa::Volume::New(volume, 1000, 100, 100, 1); mitk::pa::NoiseGenerator::ApplyNoiseModel(m_Volume, 0.75, 0.1); int negativecounter = 0; for (int i = 0; i < size; i++) { if (m_Volume->GetData()[i] <= 0) { negativecounter++; } } CPPUNIT_ASSERT_EQUAL_MESSAGE("More than one negative: " + std::to_string(negativecounter) + " (" + std::to_string((((double)negativecounter) / size) * 100) + "%)", negativecounter, 0); } void tearDown() override { } }; MITK_TEST_SUITE_REGISTRATION(mitkPhotoacousticNoiseGenerator) diff --git a/Modules/PhotoacousticsLib/test/mitkPhotoacousticTissueGeneratorTest.cpp b/Modules/PhotoacousticsLib/test/mitkPhotoacousticTissueGeneratorTest.cpp index 8199cdc0bf..0627bfb4b7 100644 --- a/Modules/PhotoacousticsLib/test/mitkPhotoacousticTissueGeneratorTest.cpp +++ b/Modules/PhotoacousticsLib/test/mitkPhotoacousticTissueGeneratorTest.cpp @@ -1,83 +1,85 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include #include #include "mitkPATissueGenerator.h" class mitkPhotoacousticTissueGeneratorTestSuite : public mitk::TestFixture { CPPUNIT_TEST_SUITE(mitkPhotoacousticTissueGeneratorTestSuite); MITK_TEST(testCallWithEmptyParameters); MITK_TEST(testCallWithWorkingParameters); CPPUNIT_TEST_SUITE_END(); private: public: void setUp() override { } mitk::pa::TissueGeneratorParameters::Pointer createRandomTestVolumeParameters() { auto returnParameters = mitk::pa::TissueGeneratorParameters::New(); returnParameters->SetXDim(rand() % 50 + 1); returnParameters->SetYDim(rand() % 50 + 1); returnParameters->SetZDim(rand() % 50 + 1); - returnParameters->SetBackgroundAbsorption(rand() % 100 / 10.0); + double absorb = rand() % 100 / 10.0; + returnParameters->SetMinBackgroundAbsorption(absorb); + returnParameters->SetMaxBackgroundAbsorption(absorb); returnParameters->SetBackgroundScattering(rand() % 100 / 10.0); returnParameters->SetBackgroundAnisotropy(rand() % 100 / 10.0); int min = rand() % 10; returnParameters->SetMinNumberOfVessels(min); returnParameters->SetMaxNumberOfVessels(min + (rand() % 10)); returnParameters->SetCalculateNewVesselPositionCallback( &mitk::pa::VesselMeanderStrategy::CalculateRandomlyDivergingPosition); returnParameters->SetMinVesselZOrigin(rand() % 3 + 1); returnParameters->SetMaxVesselZOrigin(rand() % 3 + 1); int minRad = rand() % 100; returnParameters->SetMinVesselRadiusInMillimeters(minRad); returnParameters->SetMaxVesselRadiusInMillimeters(minRad + (rand() % 100)); returnParameters->SetVoxelSpacingInCentimeters(1); return returnParameters; } void testCallWithEmptyParameters() { auto parameters = mitk::pa::TissueGeneratorParameters::New(); auto volume = mitk::pa::InSilicoTissueGenerator::GenerateInSilicoData(parameters); CPPUNIT_ASSERT(volume.IsNotNull()); } void testCallWithWorkingParameters() { for (int i = 0; i < 20; i++) { auto parameters = createRandomTestVolumeParameters(); auto volume = mitk::pa::InSilicoTissueGenerator::GenerateInSilicoData(parameters); CPPUNIT_ASSERT(volume.IsNotNull()); } } void tearDown() override { } }; MITK_TEST_SUITE_REGISTRATION(mitkPhotoacousticTissueGenerator) diff --git a/Modules/PhotoacousticsLib/test/mitkPhotoacousticVesselTest.cpp b/Modules/PhotoacousticsLib/test/mitkPhotoacousticVesselTest.cpp index 17b06e9e21..c1e3949514 100644 --- a/Modules/PhotoacousticsLib/test/mitkPhotoacousticVesselTest.cpp +++ b/Modules/PhotoacousticsLib/test/mitkPhotoacousticVesselTest.cpp @@ -1,169 +1,242 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include #include #include #include "mitkPAInSilicoTissueVolume.h" #include "mitkPAVector.h" #include "mitkPAVessel.h" #include "mitkIOUtil.h" class mitkPhotoacousticVesselTestSuite : public mitk::TestFixture { CPPUNIT_TEST_SUITE(mitkPhotoacousticVesselTestSuite); MITK_TEST(testEmptyInitializationProperties); MITK_TEST(testWalkInStraightLine); MITK_TEST(testBifurcate); + MITK_TEST(testPartialVolume); CPPUNIT_TEST_SUITE_END(); private: mitk::pa::Vessel::Pointer m_TestVessel; mitk::pa::Vessel::CalculateNewVesselPositionCallback m_StraightLine; mitk::pa::Vessel::CalculateNewVesselPositionCallback m_Diverging; mitk::pa::InSilicoTissueVolume::Pointer m_TestInSilicoVolume; mitk::pa::TissueGeneratorParameters::Pointer m_TestVolumeParameters; public: void setUp() override { auto params = mitk::pa::VesselProperties::New(); m_TestVessel = mitk::pa::Vessel::New(params); m_StraightLine = &mitk::pa::VesselMeanderStrategy::CalculateNewPositionInStraightLine; m_Diverging = &mitk::pa::VesselMeanderStrategy::CalculateRandomlyDivergingPosition; m_TestVolumeParameters = createTestVolumeParameters(); - m_TestInSilicoVolume = mitk::pa::InSilicoTissueVolume::New(m_TestVolumeParameters); + auto rng = std::mt19937(); + m_TestInSilicoVolume = mitk::pa::InSilicoTissueVolume::New(m_TestVolumeParameters, &rng); } mitk::pa::TissueGeneratorParameters::Pointer createTestVolumeParameters() { auto returnParameters = mitk::pa::TissueGeneratorParameters::New(); returnParameters->SetXDim(10); returnParameters->SetYDim(10); returnParameters->SetZDim(10); - returnParameters->SetBackgroundAbsorption(0); + returnParameters->SetMinBackgroundAbsorption(0); + returnParameters->SetMaxBackgroundAbsorption(0); returnParameters->SetBackgroundScattering(0); returnParameters->SetBackgroundAnisotropy(0); return returnParameters; } void testEmptyInitializationProperties() { CPPUNIT_ASSERT(m_TestVessel->CanBifurcate() == false); CPPUNIT_ASSERT(m_TestVessel->IsFinished() == true); } - void testWalkInStraightLine() + void testPartialVolume() { auto testPosition = mitk::pa::Vector::New(); testPosition->SetElement(0, 0); testPosition->SetElement(1, 4); testPosition->SetElement(2, 4); auto testDirection = mitk::pa::Vector::New(); testDirection->SetElement(0, 1); testDirection->SetElement(1, 0); testDirection->SetElement(2, 0); auto params = mitk::pa::VesselProperties::New(); + params->SetDoPartialVolume(true); params->SetRadiusInVoxel(1); params->SetBifurcationFrequency(100); params->SetAbsorptionCoefficient(10); params->SetScatteringCoefficient(10); params->SetAnisotopyCoefficient(10); params->SetPositionVector(testPosition); params->SetDirectionVector(testDirection); m_TestVessel = mitk::pa::Vessel::New(params); CPPUNIT_ASSERT(m_TestVessel->CanBifurcate() == false); CPPUNIT_ASSERT(m_TestVessel->IsFinished() == false); CPPUNIT_ASSERT(std::abs(m_TestInSilicoVolume->GetAbsorptionVolume()->GetData(0, 4, 4)) <= mitk::eps); m_TestVessel->ExpandVessel(m_TestInSilicoVolume, m_StraightLine, 0, nullptr); - CPPUNIT_ASSERT(std::abs(m_TestInSilicoVolume->GetAbsorptionVolume()->GetData(0, 4, 4) - 10) <= mitk::eps); - CPPUNIT_ASSERT(std::abs(m_TestInSilicoVolume->GetAbsorptionVolume()->GetData(0, 5, 4) - 10) <= mitk::eps); - CPPUNIT_ASSERT(std::abs(m_TestInSilicoVolume->GetAbsorptionVolume()->GetData(0, 6, 4)) <= mitk::eps); + CPPUNIT_ASSERT_MESSAGE(std::to_string(m_TestInSilicoVolume->GetAbsorptionVolume()->GetData(0, 4, 4)), + abs(m_TestInSilicoVolume->GetAbsorptionVolume()->GetData(0, 4, 4) - 10) <= mitk::eps); + CPPUNIT_ASSERT_MESSAGE(std::to_string(m_TestInSilicoVolume->GetAbsorptionVolume()->GetData(0, 4, 5)), + abs(m_TestInSilicoVolume->GetAbsorptionVolume()->GetData(0, 4, 5) - 10) <= mitk::eps); + CPPUNIT_ASSERT_MESSAGE(std::to_string(m_TestInSilicoVolume->GetAbsorptionVolume()->GetData(0, 4, 3)), + abs(m_TestInSilicoVolume->GetAbsorptionVolume()->GetData(0, 4, 3) - 10) <= mitk::eps); + CPPUNIT_ASSERT_MESSAGE(std::to_string(m_TestInSilicoVolume->GetAbsorptionVolume()->GetData(0, 5, 4)), + abs(m_TestInSilicoVolume->GetAbsorptionVolume()->GetData(0, 5, 4) - 10) <= mitk::eps); + CPPUNIT_ASSERT_MESSAGE(std::to_string(m_TestInSilicoVolume->GetAbsorptionVolume()->GetData(0, 3, 4)), + abs(m_TestInSilicoVolume->GetAbsorptionVolume()->GetData(0, 3, 4) - 10) <= mitk::eps); + CPPUNIT_ASSERT_MESSAGE(std::to_string(m_TestInSilicoVolume->GetAbsorptionVolume()->GetData(0, 5, 5)), + abs(m_TestInSilicoVolume->GetAbsorptionVolume()->GetData(0, 5, 5) - 5.85786438) <= 1e-6); + CPPUNIT_ASSERT_MESSAGE(std::to_string(m_TestInSilicoVolume->GetAbsorptionVolume()->GetData(0, 3, 3)), + abs(m_TestInSilicoVolume->GetAbsorptionVolume()->GetData(0, 3, 3) - 5.85786438) <= 1e-6); + CPPUNIT_ASSERT_MESSAGE(std::to_string(m_TestInSilicoVolume->GetAbsorptionVolume()->GetData(0, 5, 3)), + abs(m_TestInSilicoVolume->GetAbsorptionVolume()->GetData(0, 5, 3) - 5.85786438) <= 1e-6); + CPPUNIT_ASSERT_MESSAGE(std::to_string(m_TestInSilicoVolume->GetAbsorptionVolume()->GetData(0, 3, 5)), + abs(m_TestInSilicoVolume->GetAbsorptionVolume()->GetData(0, 3, 5) - 5.85786438) <= 1e-6); + } - CPPUNIT_ASSERT(std::abs(m_TestInSilicoVolume->GetAbsorptionVolume()->GetData(0, 4, 4) - 10) <= mitk::eps); - CPPUNIT_ASSERT(std::abs(m_TestInSilicoVolume->GetAbsorptionVolume()->GetData(0, 4, 5) - 10) <= mitk::eps); - CPPUNIT_ASSERT(std::abs(m_TestInSilicoVolume->GetAbsorptionVolume()->GetData(0, 4, 6)) <= mitk::eps); + void testWalkInStraightLine() + { + auto testPosition = mitk::pa::Vector::New(); + testPosition->SetElement(0, 0); + testPosition->SetElement(1, 4); + testPosition->SetElement(2, 4); + auto testDirection = mitk::pa::Vector::New(); + testDirection->SetElement(0, 1); + testDirection->SetElement(1, 0); + testDirection->SetElement(2, 0); + auto params = mitk::pa::VesselProperties::New(); + params->SetDoPartialVolume(false); + params->SetRadiusInVoxel(1); + params->SetBifurcationFrequency(100); + params->SetAbsorptionCoefficient(10); + params->SetScatteringCoefficient(10); + params->SetAnisotopyCoefficient(10); + params->SetPositionVector(testPosition); + params->SetDirectionVector(testDirection); + m_TestVessel = mitk::pa::Vessel::New(params); + + CPPUNIT_ASSERT(m_TestVessel->CanBifurcate() == false); + CPPUNIT_ASSERT(m_TestVessel->IsFinished() == false); - CPPUNIT_ASSERT(std::abs(m_TestInSilicoVolume->GetAbsorptionVolume()->GetData(0, 4, 4) - 10) <= mitk::eps); - CPPUNIT_ASSERT(std::abs(m_TestInSilicoVolume->GetAbsorptionVolume()->GetData(1, 4, 4) - 10) <= mitk::eps); - CPPUNIT_ASSERT(std::abs(m_TestInSilicoVolume->GetAbsorptionVolume()->GetData(2, 4, 4)) <= mitk::eps); + CPPUNIT_ASSERT_MESSAGE(std::to_string(m_TestInSilicoVolume->GetAbsorptionVolume()->GetData(0, 4, 4)), + abs(m_TestInSilicoVolume->GetAbsorptionVolume()->GetData(0, 4, 4)) <= mitk::eps); m_TestVessel->ExpandVessel(m_TestInSilicoVolume, m_StraightLine, 0, nullptr); - CPPUNIT_ASSERT(std::abs(m_TestInSilicoVolume->GetAbsorptionVolume()->GetData(1, 4, 4) - 10) <= mitk::eps); - CPPUNIT_ASSERT(std::abs(m_TestInSilicoVolume->GetAbsorptionVolume()->GetData(1, 5, 4) - 10) <= mitk::eps); - CPPUNIT_ASSERT(std::abs(m_TestInSilicoVolume->GetAbsorptionVolume()->GetData(1, 6, 4)) <= mitk::eps); + mitk::IOUtil::Save(m_TestInSilicoVolume->GetAbsorptionVolume()->AsMitkImage(), "C:/Users/groehl/Desktop/test.nrrd"); + + CPPUNIT_ASSERT_MESSAGE(std::to_string(m_TestInSilicoVolume->GetAbsorptionVolume()->GetData(0, 4, 4)), + abs(m_TestInSilicoVolume->GetAbsorptionVolume()->GetData(0, 4, 4) - 10) <= mitk::eps); + CPPUNIT_ASSERT_MESSAGE(std::to_string(m_TestInSilicoVolume->GetAbsorptionVolume()->GetData(0, 5, 4)), + abs(m_TestInSilicoVolume->GetAbsorptionVolume()->GetData(0, 5, 4)) <= mitk::eps); + CPPUNIT_ASSERT_MESSAGE(std::to_string(m_TestInSilicoVolume->GetAbsorptionVolume()->GetData(0, 6, 4)), + abs(m_TestInSilicoVolume->GetAbsorptionVolume()->GetData(0, 6, 4)) <= mitk::eps); + + CPPUNIT_ASSERT_MESSAGE(std::to_string(m_TestInSilicoVolume->GetAbsorptionVolume()->GetData(0, 4, 4)), + abs(m_TestInSilicoVolume->GetAbsorptionVolume()->GetData(0, 4, 4) - 10) <= mitk::eps); + CPPUNIT_ASSERT_MESSAGE(std::to_string(m_TestInSilicoVolume->GetAbsorptionVolume()->GetData(0, 4, 5)), + abs(m_TestInSilicoVolume->GetAbsorptionVolume()->GetData(0, 4, 5)) <= mitk::eps); + CPPUNIT_ASSERT_MESSAGE(std::to_string(m_TestInSilicoVolume->GetAbsorptionVolume()->GetData(0, 4, 6)), + abs(m_TestInSilicoVolume->GetAbsorptionVolume()->GetData(0, 4, 6)) <= mitk::eps); + + CPPUNIT_ASSERT_MESSAGE(std::to_string(m_TestInSilicoVolume->GetAbsorptionVolume()->GetData(0, 4, 4)), + abs(m_TestInSilicoVolume->GetAbsorptionVolume()->GetData(0, 4, 4) - 10) <= mitk::eps); + CPPUNIT_ASSERT_MESSAGE(std::to_string(m_TestInSilicoVolume->GetAbsorptionVolume()->GetData(1, 4, 4)), + abs(m_TestInSilicoVolume->GetAbsorptionVolume()->GetData(1, 4, 4) - 10) <= mitk::eps); + CPPUNIT_ASSERT_MESSAGE(std::to_string(m_TestInSilicoVolume->GetAbsorptionVolume()->GetData(2, 4, 4)), + abs(m_TestInSilicoVolume->GetAbsorptionVolume()->GetData(2, 4, 4)) <= mitk::eps); - CPPUNIT_ASSERT(std::abs(m_TestInSilicoVolume->GetAbsorptionVolume()->GetData(1, 4, 4) - 10) <= mitk::eps); - CPPUNIT_ASSERT(std::abs(m_TestInSilicoVolume->GetAbsorptionVolume()->GetData(1, 4, 5) - 10) <= mitk::eps); - CPPUNIT_ASSERT(std::abs(m_TestInSilicoVolume->GetAbsorptionVolume()->GetData(1, 4, 6)) <= mitk::eps); + m_TestVessel->ExpandVessel(m_TestInSilicoVolume, m_StraightLine, 0, nullptr); - CPPUNIT_ASSERT(std::abs(m_TestInSilicoVolume->GetAbsorptionVolume()->GetData(1, 4, 4) - 10) <= mitk::eps); - CPPUNIT_ASSERT(std::abs(m_TestInSilicoVolume->GetAbsorptionVolume()->GetData(2, 4, 4) - 10) <= mitk::eps); - CPPUNIT_ASSERT(std::abs(m_TestInSilicoVolume->GetAbsorptionVolume()->GetData(3, 4, 4)) <= mitk::eps); + CPPUNIT_ASSERT_MESSAGE(std::to_string(m_TestInSilicoVolume->GetAbsorptionVolume()->GetData(1, 4, 4)), + abs(m_TestInSilicoVolume->GetAbsorptionVolume()->GetData(1, 4, 4) - 10) <= mitk::eps); + CPPUNIT_ASSERT_MESSAGE(std::to_string(m_TestInSilicoVolume->GetAbsorptionVolume()->GetData(1, 5, 4)), + abs(m_TestInSilicoVolume->GetAbsorptionVolume()->GetData(1, 5, 4)) <= mitk::eps); + CPPUNIT_ASSERT_MESSAGE(std::to_string(m_TestInSilicoVolume->GetAbsorptionVolume()->GetData(1, 6, 4)), + abs(m_TestInSilicoVolume->GetAbsorptionVolume()->GetData(1, 6, 4)) <= mitk::eps); + + CPPUNIT_ASSERT_MESSAGE(std::to_string(m_TestInSilicoVolume->GetAbsorptionVolume()->GetData(1, 4, 4)), + abs(m_TestInSilicoVolume->GetAbsorptionVolume()->GetData(1, 4, 4) - 10) <= mitk::eps); + CPPUNIT_ASSERT_MESSAGE(std::to_string(m_TestInSilicoVolume->GetAbsorptionVolume()->GetData(1, 4, 5)), + abs(m_TestInSilicoVolume->GetAbsorptionVolume()->GetData(1, 4, 5)) <= mitk::eps); + CPPUNIT_ASSERT_MESSAGE(std::to_string(m_TestInSilicoVolume->GetAbsorptionVolume()->GetData(1, 4, 6)), + abs(m_TestInSilicoVolume->GetAbsorptionVolume()->GetData(1, 4, 6)) <= mitk::eps); + + CPPUNIT_ASSERT_MESSAGE(std::to_string(m_TestInSilicoVolume->GetAbsorptionVolume()->GetData(1, 4, 4)), + abs(m_TestInSilicoVolume->GetAbsorptionVolume()->GetData(1, 4, 4) - 10) <= mitk::eps); + CPPUNIT_ASSERT_MESSAGE(std::to_string(m_TestInSilicoVolume->GetAbsorptionVolume()->GetData(2, 4, 4)), + abs(m_TestInSilicoVolume->GetAbsorptionVolume()->GetData(2, 4, 4) - 10) <= mitk::eps); + CPPUNIT_ASSERT_MESSAGE(std::to_string(m_TestInSilicoVolume->GetAbsorptionVolume()->GetData(3, 4, 4)), + abs(m_TestInSilicoVolume->GetAbsorptionVolume()->GetData(3, 4, 4)) <= mitk::eps); } void testBifurcate() { auto testPosition = mitk::pa::Vector::New(); testPosition->SetElement(0, 0); testPosition->SetElement(1, 4); testPosition->SetElement(2, 4); auto testDirection = mitk::pa::Vector::New(); testDirection->SetElement(0, 1); testDirection->SetElement(1, 0); testDirection->SetElement(2, 0); auto params = mitk::pa::VesselProperties::New(); params->SetRadiusInVoxel(1); params->SetBifurcationFrequency(1); params->SetAbsorptionCoefficient(10); params->SetScatteringCoefficient(10); params->SetAnisotopyCoefficient(10); params->SetPositionVector(testPosition); params->SetDirectionVector(testDirection); m_TestVessel = mitk::pa::Vessel::New(params); CPPUNIT_ASSERT(m_TestVessel->CanBifurcate() == false); CPPUNIT_ASSERT(m_TestVessel->IsFinished() == false); CPPUNIT_ASSERT(std::abs(m_TestInSilicoVolume->GetAbsorptionVolume()->GetData(0, 4, 4)) <= mitk::eps); m_TestVessel->ExpandVessel(m_TestInSilicoVolume, m_StraightLine, 0, nullptr); m_TestVessel->ExpandVessel(m_TestInSilicoVolume, m_StraightLine, 0, nullptr); CPPUNIT_ASSERT(m_TestVessel->CanBifurcate() == true); std::mt19937 rng; auto bifurcationVessel = m_TestVessel->Bifurcate(&rng); CPPUNIT_ASSERT(m_TestVessel->CanBifurcate() == false); CPPUNIT_ASSERT(bifurcationVessel->CanBifurcate() == false); } void tearDown() override { } }; MITK_TEST_SUITE_REGISTRATION(mitkPhotoacousticVessel) diff --git a/Modules/PhotoacousticsLib/test/mitkPhotoacousticVesselTreeTest.cpp b/Modules/PhotoacousticsLib/test/mitkPhotoacousticVesselTreeTest.cpp index a78d6a7bb9..1a022017f3 100644 --- a/Modules/PhotoacousticsLib/test/mitkPhotoacousticVesselTreeTest.cpp +++ b/Modules/PhotoacousticsLib/test/mitkPhotoacousticVesselTreeTest.cpp @@ -1,106 +1,108 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include #include #include #include "mitkPAVesselTree.h" #include "mitkPAInSilicoTissueVolume.h" using namespace mitk::pa; class mitkPhotoacousticVesselTreeTestSuite : public mitk::TestFixture { CPPUNIT_TEST_SUITE(mitkPhotoacousticVesselTreeTestSuite); MITK_TEST(testVesselTreeInitialBehavior); MITK_TEST(testCallStepMethod); CPPUNIT_TEST_SUITE_END(); private: public: VesselTree::Pointer m_Tree; VesselProperties::Pointer m_VesselProperties; Vessel::CalculateNewVesselPositionCallback m_StraightLine; InSilicoTissueVolume::Pointer m_TestInSilicoVolume; void setUp() override { m_VesselProperties = VesselProperties::New(); m_Tree = VesselTree::New(m_VesselProperties); m_StraightLine = &VesselMeanderStrategy::CalculateNewPositionInStraightLine; - m_TestInSilicoVolume = InSilicoTissueVolume::New(createTestVolumeParameters()); + auto rng = std::mt19937(); + m_TestInSilicoVolume = InSilicoTissueVolume::New(createTestVolumeParameters(), &rng); } TissueGeneratorParameters::Pointer createTestVolumeParameters() { auto returnParameters = TissueGeneratorParameters::New(); returnParameters->SetXDim(10); returnParameters->SetYDim(10); returnParameters->SetZDim(10); - returnParameters->SetBackgroundAbsorption(0); + returnParameters->SetMinBackgroundAbsorption(0); + returnParameters->SetMaxBackgroundAbsorption(0); returnParameters->SetBackgroundScattering(0); returnParameters->SetBackgroundAnisotropy(0); return returnParameters; } void testVesselTreeInitialBehavior() { CPPUNIT_ASSERT(m_Tree->IsFinished() == true); } void testCallStepMethod() { //really bad test atm.. The only thing that is tested is that the method can be called without a crash. //But hey - it is something :P m_VesselProperties->SetRadiusInVoxel(2); std::mt19937 rng; rng.seed(1); m_Tree = VesselTree::New(m_VesselProperties); m_Tree->Step(m_TestInSilicoVolume, m_StraightLine, 0, &rng); rng.seed(1); auto secondTree = VesselTree::New(m_VesselProperties); secondTree->Step(m_TestInSilicoVolume, m_StraightLine, 0, &rng); CPPUNIT_ASSERT(Equal(m_Tree, secondTree, 1e-6, true)); secondTree->Step(m_TestInSilicoVolume, m_StraightLine, 0, &rng); CPPUNIT_ASSERT(!Equal(m_Tree, secondTree, 1e-6, true)); int i = 0; for (; i < 1000; i++) { secondTree->Step(m_TestInSilicoVolume, m_StraightLine, 0, &rng); if (secondTree->IsFinished()) break; } CPPUNIT_ASSERT(i < 999); } void tearDown() override { } }; MITK_TEST_SUITE_REGISTRATION(mitkPhotoacousticVesselTree) diff --git a/Modules/PhotoacousticsLib/test/mitkPhotoacousticVolumeTest.cpp b/Modules/PhotoacousticsLib/test/mitkPhotoacousticVolumeTest.cpp index 0af0c79d24..b3e874aa1c 100644 --- a/Modules/PhotoacousticsLib/test/mitkPhotoacousticVolumeTest.cpp +++ b/Modules/PhotoacousticsLib/test/mitkPhotoacousticVolumeTest.cpp @@ -1,347 +1,385 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include #include #include #include "mitkPAInSilicoTissueVolume.h" #include "mitkPATissueGeneratorParameters.h" class mitkPhotoacousticVolumeTestSuite : public mitk::TestFixture { CPPUNIT_TEST_SUITE(mitkPhotoacousticVolumeTestSuite); + MITK_TEST(TestUniformDistributionIsUniform); MITK_TEST(TestInitializedTissueContainsOnlyZeros); MITK_TEST(TestConvertedMitkImageContainsOnlyZerosOrAir); MITK_TEST(TestTissueVolumeContainsCorrectAbsorptionNumber); MITK_TEST(TestTissueVolumeContainsCorrectScatteringNumber); MITK_TEST(TestTissueVolumeContainsCorrectAnisotropyNumber); MITK_TEST(testSecondConstructor); MITK_TEST(testCompleteAirVoxelInclusion); MITK_TEST(testHalfAirVoxelInclusion); MITK_TEST(testCompleteAirAndSkinVoxelInclusion); MITK_TEST(testRandomizeCoefficients); CPPUNIT_TEST_SUITE_END(); private: mitk::pa::InSilicoTissueVolume::Pointer m_PhotoacousticVolume; mitk::pa::TissueGeneratorParameters::Pointer m_TissueGeneratorParameters; public: void setUp() override { m_TissueGeneratorParameters = mitk::pa::TissueGeneratorParameters::New(); - m_PhotoacousticVolume = mitk::pa::InSilicoTissueVolume::New(m_TissueGeneratorParameters); + auto rng = std::mt19937(); + m_PhotoacousticVolume = mitk::pa::InSilicoTissueVolume::New(m_TissueGeneratorParameters, &rng); + } + + void TestUniformDistributionIsUniform() + { + + int dims = 30; + m_TissueGeneratorParameters->SetXDim(dims); + m_TissueGeneratorParameters->SetYDim(dims); + m_TissueGeneratorParameters->SetZDim(dims); + m_TissueGeneratorParameters->SetAirThicknessInMillimeters(0); + + m_TissueGeneratorParameters->SetMinBackgroundAbsorption(0.001); + m_TissueGeneratorParameters->SetMaxBackgroundAbsorption(0.2); + + auto rng = std::mt19937(); + m_PhotoacousticVolume = mitk::pa::InSilicoTissueVolume::New(m_TissueGeneratorParameters, &rng); + + for (int x = 0; x < dims; x++) + { + for (int y = 0; y < dims; y++) + { + for (int z = 0; z < dims; z++) + { + CPPUNIT_ASSERT_EQUAL_MESSAGE("Every absorption should be in bounds.", + m_PhotoacousticVolume->GetAbsorptionVolume()->GetData(x, y, z) >= 0.001 && + m_PhotoacousticVolume->GetAbsorptionVolume()->GetData(x, y, z) <= 0.2, true); + } + } + } + } void TestInitializedTissueContainsOnlyZeros() { int dims = 30; m_TissueGeneratorParameters->SetXDim(dims); m_TissueGeneratorParameters->SetYDim(dims); m_TissueGeneratorParameters->SetZDim(dims); m_TissueGeneratorParameters->SetAirThicknessInMillimeters(0); - m_TissueGeneratorParameters->SetBackgroundAbsorption(0); - m_PhotoacousticVolume = mitk::pa::InSilicoTissueVolume::New(m_TissueGeneratorParameters); + m_TissueGeneratorParameters->SetMinBackgroundAbsorption(0); + m_TissueGeneratorParameters->SetMaxBackgroundAbsorption(0); + auto rng = std::mt19937(); + m_PhotoacousticVolume = mitk::pa::InSilicoTissueVolume::New(m_TissueGeneratorParameters, &rng); for (int x = 0; x < dims; x++) { for (int y = 0; y < dims; y++) { for (int z = 0; z < dims; z++) { CPPUNIT_ASSERT_EQUAL_MESSAGE("Every field should be initialized with 0.", std::abs(m_PhotoacousticVolume->GetAbsorptionVolume()->GetData(x, y, z)) < mitk::eps, true); } } } } void TestConvertedMitkImageContainsOnlyZerosOrAir() { int dims = 30; m_TissueGeneratorParameters->SetXDim(dims); m_TissueGeneratorParameters->SetYDim(dims); m_TissueGeneratorParameters->SetZDim(dims); - m_PhotoacousticVolume = mitk::pa::InSilicoTissueVolume::New(m_TissueGeneratorParameters); + auto rng = std::mt19937(); + m_PhotoacousticVolume = mitk::pa::InSilicoTissueVolume::New(m_TissueGeneratorParameters, &rng); mitk::Image::Pointer testImage = m_PhotoacousticVolume->ConvertToMitkImage(); mitk::ImageReadAccessor imgMemAcc(testImage); auto* imagePointer = (double*)imgMemAcc.GetData(); for (int index = 0; index < dims*dims*dims; index++, imagePointer++) { CPPUNIT_ASSERT_EQUAL_MESSAGE("Every voxel in image should be 0.1 or 0.0001.", true, std::abs(*imagePointer - 0.1) <= mitk::eps || std::abs(*imagePointer - 0.0001) <= mitk::eps); } } void TestTissueVolumeContainsCorrectAbsorptionNumber() { int dims = 2; m_TissueGeneratorParameters->SetXDim(dims); m_TissueGeneratorParameters->SetYDim(dims); m_TissueGeneratorParameters->SetZDim(dims); - m_PhotoacousticVolume = mitk::pa::InSilicoTissueVolume::New(m_TissueGeneratorParameters); + auto rng = std::mt19937(); + m_PhotoacousticVolume = mitk::pa::InSilicoTissueVolume::New(m_TissueGeneratorParameters, &rng); m_PhotoacousticVolume->SetVolumeValues(0, 0, 0, 0, 0, 0); m_PhotoacousticVolume->SetVolumeValues(0, 0, 1, 1, 0, 0); m_PhotoacousticVolume->SetVolumeValues(0, 1, 0, 2, 0, 0); m_PhotoacousticVolume->SetVolumeValues(0, 1, 1, 3, 0, 0); m_PhotoacousticVolume->SetVolumeValues(1, 0, 0, 4, 0, 0); m_PhotoacousticVolume->SetVolumeValues(1, 0, 1, 5, 0, 0); m_PhotoacousticVolume->SetVolumeValues(1, 1, 0, 6, 0, 0); m_PhotoacousticVolume->SetVolumeValues(1, 1, 1, 7, 0, 0); CPPUNIT_ASSERT_EQUAL_MESSAGE("Should be correct value", 0.0, m_PhotoacousticVolume->GetAbsorptionVolume()->GetData(0, 0, 0)); CPPUNIT_ASSERT_EQUAL_MESSAGE("Should be correct value", 1.0, m_PhotoacousticVolume->GetAbsorptionVolume()->GetData(0, 0, 1)); CPPUNIT_ASSERT_EQUAL_MESSAGE("Should be correct value", 2.0, m_PhotoacousticVolume->GetAbsorptionVolume()->GetData(0, 1, 0)); CPPUNIT_ASSERT_EQUAL_MESSAGE("Should be correct value", 3.0, m_PhotoacousticVolume->GetAbsorptionVolume()->GetData(0, 1, 1)); CPPUNIT_ASSERT_EQUAL_MESSAGE("Should be correct value", 4.0, m_PhotoacousticVolume->GetAbsorptionVolume()->GetData(1, 0, 0)); CPPUNIT_ASSERT_EQUAL_MESSAGE("Should be correct value", 5.0, m_PhotoacousticVolume->GetAbsorptionVolume()->GetData(1, 0, 1)); CPPUNIT_ASSERT_EQUAL_MESSAGE("Should be correct value", 6.0, m_PhotoacousticVolume->GetAbsorptionVolume()->GetData(1, 1, 0)); CPPUNIT_ASSERT_EQUAL_MESSAGE("Should be correct value", 7.0, m_PhotoacousticVolume->GetAbsorptionVolume()->GetData(1, 1, 1)); } void TestTissueVolumeContainsCorrectScatteringNumber() { int dims = 2; m_TissueGeneratorParameters->SetXDim(dims); m_TissueGeneratorParameters->SetYDim(dims); m_TissueGeneratorParameters->SetZDim(dims); - m_PhotoacousticVolume = mitk::pa::InSilicoTissueVolume::New(m_TissueGeneratorParameters); + auto rng = std::mt19937(); + m_PhotoacousticVolume = mitk::pa::InSilicoTissueVolume::New(m_TissueGeneratorParameters, &rng); m_PhotoacousticVolume->SetVolumeValues(0, 0, 0, 0, 0, 0); m_PhotoacousticVolume->SetVolumeValues(0, 0, 1, 0, 1, 0); m_PhotoacousticVolume->SetVolumeValues(0, 1, 0, 0, 2, 0); m_PhotoacousticVolume->SetVolumeValues(0, 1, 1, 0, 3, 0); m_PhotoacousticVolume->SetVolumeValues(1, 0, 0, 0, 4, 0); m_PhotoacousticVolume->SetVolumeValues(1, 0, 1, 0, 5, 0); m_PhotoacousticVolume->SetVolumeValues(1, 1, 0, 0, 6, 0); m_PhotoacousticVolume->SetVolumeValues(1, 1, 1, 0, 7, 0); CPPUNIT_ASSERT_EQUAL_MESSAGE("Should be correct value", 0.0, m_PhotoacousticVolume->GetScatteringVolume()->GetData(0, 0, 0)); CPPUNIT_ASSERT_EQUAL_MESSAGE("Should be correct value", 1.0, m_PhotoacousticVolume->GetScatteringVolume()->GetData(0, 0, 1)); CPPUNIT_ASSERT_EQUAL_MESSAGE("Should be correct value", 2.0, m_PhotoacousticVolume->GetScatteringVolume()->GetData(0, 1, 0)); CPPUNIT_ASSERT_EQUAL_MESSAGE("Should be correct value", 3.0, m_PhotoacousticVolume->GetScatteringVolume()->GetData(0, 1, 1)); CPPUNIT_ASSERT_EQUAL_MESSAGE("Should be correct value", 4.0, m_PhotoacousticVolume->GetScatteringVolume()->GetData(1, 0, 0)); CPPUNIT_ASSERT_EQUAL_MESSAGE("Should be correct value", 5.0, m_PhotoacousticVolume->GetScatteringVolume()->GetData(1, 0, 1)); CPPUNIT_ASSERT_EQUAL_MESSAGE("Should be correct value", 6.0, m_PhotoacousticVolume->GetScatteringVolume()->GetData(1, 1, 0)); CPPUNIT_ASSERT_EQUAL_MESSAGE("Should be correct value", 7.0, m_PhotoacousticVolume->GetScatteringVolume()->GetData(1, 1, 1)); } void TestTissueVolumeContainsCorrectAnisotropyNumber() { int dims = 2; m_TissueGeneratorParameters->SetXDim(dims); m_TissueGeneratorParameters->SetYDim(dims); m_TissueGeneratorParameters->SetZDim(dims); - m_PhotoacousticVolume = mitk::pa::InSilicoTissueVolume::New(m_TissueGeneratorParameters); + auto rng = std::mt19937(); + m_PhotoacousticVolume = mitk::pa::InSilicoTissueVolume::New(m_TissueGeneratorParameters, &rng); m_PhotoacousticVolume->SetVolumeValues(0, 0, 0, 0, 0, 0); m_PhotoacousticVolume->SetVolumeValues(0, 0, 1, 0, 0, 1); m_PhotoacousticVolume->SetVolumeValues(0, 1, 0, 0, 0, 2); m_PhotoacousticVolume->SetVolumeValues(0, 1, 1, 0, 0, 3); m_PhotoacousticVolume->SetVolumeValues(1, 0, 0, 0, 0, 4); m_PhotoacousticVolume->SetVolumeValues(1, 0, 1, 0, 0, 5); m_PhotoacousticVolume->SetVolumeValues(1, 1, 0, 0, 0, 6); m_PhotoacousticVolume->SetVolumeValues(1, 1, 1, 0, 0, 7); CPPUNIT_ASSERT_EQUAL_MESSAGE("Should be correct value", 0.0, m_PhotoacousticVolume->GetAnisotropyVolume()->GetData(0, 0, 0)); CPPUNIT_ASSERT_EQUAL_MESSAGE("Should be correct value", 1.0, m_PhotoacousticVolume->GetAnisotropyVolume()->GetData(0, 0, 1)); CPPUNIT_ASSERT_EQUAL_MESSAGE("Should be correct value", 2.0, m_PhotoacousticVolume->GetAnisotropyVolume()->GetData(0, 1, 0)); CPPUNIT_ASSERT_EQUAL_MESSAGE("Should be correct value", 3.0, m_PhotoacousticVolume->GetAnisotropyVolume()->GetData(0, 1, 1)); CPPUNIT_ASSERT_EQUAL_MESSAGE("Should be correct value", 4.0, m_PhotoacousticVolume->GetAnisotropyVolume()->GetData(1, 0, 0)); CPPUNIT_ASSERT_EQUAL_MESSAGE("Should be correct value", 5.0, m_PhotoacousticVolume->GetAnisotropyVolume()->GetData(1, 0, 1)); CPPUNIT_ASSERT_EQUAL_MESSAGE("Should be correct value", 6.0, m_PhotoacousticVolume->GetAnisotropyVolume()->GetData(1, 1, 0)); CPPUNIT_ASSERT_EQUAL_MESSAGE("Should be correct value", 7.0, m_PhotoacousticVolume->GetAnisotropyVolume()->GetData(1, 1, 1)); } mitk::pa::Volume::Pointer createTestVolume(double value) { auto* data = new double[27]; for (int i = 0; i < 27; ++i) data[i] = value; - return mitk::pa::Volume::New(data, 3, 3, 3); + return mitk::pa::Volume::New(data, 3, 3, 3, 1); } void assertEqual(mitk::pa::Volume::Pointer first, mitk::pa::Volume::Pointer second) { CPPUNIT_ASSERT(first->GetXDim() == second->GetXDim()); CPPUNIT_ASSERT(first->GetYDim() == second->GetYDim()); CPPUNIT_ASSERT(first->GetZDim() == second->GetZDim()); for (unsigned int x = 0; x < first->GetXDim(); ++x) for (unsigned int y = 0; y < first->GetYDim(); ++y) for (unsigned int z = 0; z < first->GetZDim(); ++z) CPPUNIT_ASSERT(std::abs(first->GetData(x, y, z) - second->GetData(x, y, z)) < mitk::eps); } void testSecondConstructor() { mitk::pa::Volume::Pointer absorption = createTestVolume(1); mitk::pa::Volume::Pointer scattering = createTestVolume(2); mitk::pa::Volume::Pointer anisotropy = createTestVolume(3); mitk::pa::Volume::Pointer segmentation = createTestVolume(4); mitk::PropertyList::Pointer properties = mitk::PropertyList::New(); m_PhotoacousticVolume = mitk::pa::InSilicoTissueVolume::New(absorption, scattering, anisotropy, segmentation, m_TissueGeneratorParameters, properties); assertEqual(m_PhotoacousticVolume->GetAbsorptionVolume(), absorption); assertEqual(m_PhotoacousticVolume->GetScatteringVolume(), scattering); assertEqual(m_PhotoacousticVolume->GetAnisotropyVolume(), anisotropy); assertEqual(m_PhotoacousticVolume->GetSegmentationVolume(), segmentation); } void testCompleteAirVoxelInclusion() { mitk::pa::Volume::Pointer absorption = createTestVolume(1); mitk::pa::Volume::Pointer scattering = createTestVolume(2); mitk::pa::Volume::Pointer anisotropy = createTestVolume(3); mitk::pa::Volume::Pointer segmentation = createTestVolume(4); mitk::PropertyList::Pointer properties = mitk::PropertyList::New(); m_TissueGeneratorParameters->SetXDim(3); m_TissueGeneratorParameters->SetYDim(3); m_TissueGeneratorParameters->SetZDim(3); m_TissueGeneratorParameters->SetAirThicknessInMillimeters(10); m_TissueGeneratorParameters->SetSkinThicknessInMillimeters(0); m_TissueGeneratorParameters->SetAirAbsorption(2); m_TissueGeneratorParameters->SetAirScattering(4); m_TissueGeneratorParameters->SetAirAnisotropy(6); m_PhotoacousticVolume = mitk::pa::InSilicoTissueVolume::New(absorption, scattering, anisotropy, segmentation, m_TissueGeneratorParameters, properties); m_PhotoacousticVolume->FinalizeVolume(); CPPUNIT_ASSERT(std::abs(m_PhotoacousticVolume->GetAbsorptionVolume()->GetData(0, 0, 0) - 2) < mitk::eps); CPPUNIT_ASSERT(std::abs(m_PhotoacousticVolume->GetAbsorptionVolume()->GetData(1, 1, 1) - 1) < mitk::eps); CPPUNIT_ASSERT(std::abs(m_PhotoacousticVolume->GetAbsorptionVolume()->GetData(1, 1, 2) - 1) < mitk::eps); CPPUNIT_ASSERT(std::abs(m_PhotoacousticVolume->GetScatteringVolume()->GetData(0, 0, 0) - 4) < mitk::eps); CPPUNIT_ASSERT(std::abs(m_PhotoacousticVolume->GetScatteringVolume()->GetData(1, 1, 1) - 2) < mitk::eps); CPPUNIT_ASSERT(std::abs(m_PhotoacousticVolume->GetScatteringVolume()->GetData(1, 1, 2) - 2) < mitk::eps); CPPUNIT_ASSERT(std::abs(m_PhotoacousticVolume->GetAnisotropyVolume()->GetData(0, 0, 0) - 6) < mitk::eps); CPPUNIT_ASSERT(std::abs(m_PhotoacousticVolume->GetAnisotropyVolume()->GetData(1, 1, 1) - 3) < mitk::eps); CPPUNIT_ASSERT(std::abs(m_PhotoacousticVolume->GetAnisotropyVolume()->GetData(1, 1, 2) - 3) < mitk::eps); } void testRandomizeCoefficients() { mitk::pa::Volume::Pointer absorption = createTestVolume(1); mitk::pa::Volume::Pointer scattering = createTestVolume(1); mitk::pa::Volume::Pointer anisotropy = createTestVolume(1); mitk::pa::Volume::Pointer segmentation = createTestVolume(4); mitk::PropertyList::Pointer properties = mitk::PropertyList::New(); m_TissueGeneratorParameters->SetXDim(3); m_TissueGeneratorParameters->SetYDim(3); m_TissueGeneratorParameters->SetZDim(3); m_TissueGeneratorParameters->SetRandomizePhysicalProperties(true); m_TissueGeneratorParameters->SetRandomizePhysicalPropertiesPercentage(1); m_TissueGeneratorParameters->SetRngSeed(17); m_TissueGeneratorParameters->SetUseRngSeed(true); m_PhotoacousticVolume = mitk::pa::InSilicoTissueVolume::New(absorption, scattering, anisotropy, segmentation, m_TissueGeneratorParameters, properties); m_PhotoacousticVolume->FinalizeVolume(); CPPUNIT_ASSERT(std::abs(m_PhotoacousticVolume->GetAbsorptionVolume()->GetData(0, 0, 0) - 1) < 0.1); CPPUNIT_ASSERT(std::abs(m_PhotoacousticVolume->GetAbsorptionVolume()->GetData(1, 1, 1) - 1) < 0.1); CPPUNIT_ASSERT(std::abs(m_PhotoacousticVolume->GetAbsorptionVolume()->GetData(1, 1, 2) - 1) < 0.1); CPPUNIT_ASSERT(std::abs(m_PhotoacousticVolume->GetScatteringVolume()->GetData(0, 0, 0) - 1) < 0.1); CPPUNIT_ASSERT(std::abs(m_PhotoacousticVolume->GetScatteringVolume()->GetData(1, 1, 1) - 1) < 0.1); CPPUNIT_ASSERT(std::abs(m_PhotoacousticVolume->GetScatteringVolume()->GetData(1, 1, 2) - 1) < 0.1); CPPUNIT_ASSERT(std::abs(m_PhotoacousticVolume->GetAnisotropyVolume()->GetData(0, 0, 0) - 1) < 0.1); CPPUNIT_ASSERT(std::abs(m_PhotoacousticVolume->GetAnisotropyVolume()->GetData(1, 1, 1) - 1) < 0.1); CPPUNIT_ASSERT(std::abs(m_PhotoacousticVolume->GetAnisotropyVolume()->GetData(1, 1, 2) - 1) < 0.1); } void testCompleteAirAndSkinVoxelInclusion() { mitk::pa::Volume::Pointer absorption = createTestVolume(1); mitk::pa::Volume::Pointer scattering = createTestVolume(2); mitk::pa::Volume::Pointer anisotropy = createTestVolume(3); mitk::pa::Volume::Pointer segmentation = createTestVolume(4); mitk::PropertyList::Pointer properties = mitk::PropertyList::New(); m_TissueGeneratorParameters->SetXDim(3); m_TissueGeneratorParameters->SetYDim(3); m_TissueGeneratorParameters->SetZDim(3); m_TissueGeneratorParameters->SetAirThicknessInMillimeters(10); m_TissueGeneratorParameters->SetSkinThicknessInMillimeters(10); m_TissueGeneratorParameters->SetAirAbsorption(2); m_TissueGeneratorParameters->SetAirScattering(4); m_TissueGeneratorParameters->SetAirAnisotropy(6); m_TissueGeneratorParameters->SetSkinAbsorption(4); m_TissueGeneratorParameters->SetSkinScattering(8); m_TissueGeneratorParameters->SetSkinAnisotropy(12); m_PhotoacousticVolume = mitk::pa::InSilicoTissueVolume::New(absorption, scattering, anisotropy, segmentation, m_TissueGeneratorParameters, properties); m_PhotoacousticVolume->FinalizeVolume(); CPPUNIT_ASSERT(std::abs(m_PhotoacousticVolume->GetAbsorptionVolume()->GetData(0, 0, 0) - 2) < mitk::eps); CPPUNIT_ASSERT(std::abs(m_PhotoacousticVolume->GetAbsorptionVolume()->GetData(1, 1, 1) - 4) < mitk::eps); CPPUNIT_ASSERT(std::abs(m_PhotoacousticVolume->GetAbsorptionVolume()->GetData(1, 1, 2) - 1) < mitk::eps); CPPUNIT_ASSERT(std::abs(m_PhotoacousticVolume->GetScatteringVolume()->GetData(0, 0, 0) - 4) < mitk::eps); CPPUNIT_ASSERT(std::abs(m_PhotoacousticVolume->GetScatteringVolume()->GetData(1, 1, 1) - 8) < mitk::eps); CPPUNIT_ASSERT(std::abs(m_PhotoacousticVolume->GetScatteringVolume()->GetData(1, 1, 2) - 2) < mitk::eps); CPPUNIT_ASSERT(std::abs(m_PhotoacousticVolume->GetAnisotropyVolume()->GetData(0, 0, 0) - 6) < mitk::eps); CPPUNIT_ASSERT(std::abs(m_PhotoacousticVolume->GetAnisotropyVolume()->GetData(1, 1, 1) - 12) < mitk::eps); CPPUNIT_ASSERT(std::abs(m_PhotoacousticVolume->GetAnisotropyVolume()->GetData(1, 1, 2) - 3) < mitk::eps); } void testHalfAirVoxelInclusion() { mitk::pa::Volume::Pointer absorption = createTestVolume(1); mitk::pa::Volume::Pointer scattering = createTestVolume(2); mitk::pa::Volume::Pointer anisotropy = createTestVolume(3); mitk::pa::Volume::Pointer segmentation = createTestVolume(4); mitk::PropertyList::Pointer properties = mitk::PropertyList::New(); m_TissueGeneratorParameters->SetXDim(3); m_TissueGeneratorParameters->SetYDim(3); m_TissueGeneratorParameters->SetZDim(3); m_TissueGeneratorParameters->SetAirThicknessInMillimeters(15); m_TissueGeneratorParameters->SetSkinThicknessInMillimeters(0); m_TissueGeneratorParameters->SetAirAbsorption(2); m_TissueGeneratorParameters->SetAirScattering(4); m_TissueGeneratorParameters->SetAirAnisotropy(6); m_PhotoacousticVolume = mitk::pa::InSilicoTissueVolume::New(absorption, scattering, anisotropy, segmentation, m_TissueGeneratorParameters, properties); m_PhotoacousticVolume->FinalizeVolume(); CPPUNIT_ASSERT(std::abs(m_PhotoacousticVolume->GetAbsorptionVolume()->GetData(0, 0, 0) - 2) < mitk::eps); CPPUNIT_ASSERT(std::abs(m_PhotoacousticVolume->GetAbsorptionVolume()->GetData(1, 1, 1) - 1.5) < mitk::eps); CPPUNIT_ASSERT(std::abs(m_PhotoacousticVolume->GetAbsorptionVolume()->GetData(1, 1, 2) - 1) < mitk::eps); CPPUNIT_ASSERT(std::abs(m_PhotoacousticVolume->GetScatteringVolume()->GetData(0, 0, 0) - 4) < mitk::eps); CPPUNIT_ASSERT(std::abs(m_PhotoacousticVolume->GetScatteringVolume()->GetData(1, 1, 1) - 3) < mitk::eps); CPPUNIT_ASSERT(std::abs(m_PhotoacousticVolume->GetScatteringVolume()->GetData(1, 1, 2) - 2) < mitk::eps); CPPUNIT_ASSERT(std::abs(m_PhotoacousticVolume->GetAnisotropyVolume()->GetData(0, 0, 0) - 6) < mitk::eps); CPPUNIT_ASSERT(std::abs(m_PhotoacousticVolume->GetAnisotropyVolume()->GetData(1, 1, 1) - 4.5) < mitk::eps); CPPUNIT_ASSERT(std::abs(m_PhotoacousticVolume->GetAnisotropyVolume()->GetData(1, 1, 2) - 3) < mitk::eps); } void tearDown() override { m_PhotoacousticVolume = nullptr; } }; MITK_TEST_SUITE_REGISTRATION(mitkPhotoacousticVolume) diff --git a/Modules/PhotoacousticsLib/test/mitkSimulationBatchGeneratorTest.cpp b/Modules/PhotoacousticsLib/test/mitkSimulationBatchGeneratorTest.cpp index 2732c64bd5..10fdbbde73 100644 --- a/Modules/PhotoacousticsLib/test/mitkSimulationBatchGeneratorTest.cpp +++ b/Modules/PhotoacousticsLib/test/mitkSimulationBatchGeneratorTest.cpp @@ -1,90 +1,90 @@ ///*=================================================================== //The Medical Imaging Interaction Toolkit (MITK) //Copyright (c) German Cancer Research Center, //Division of Medical and Biological Informatics. //All rights reserved. //This software is distributed WITHOUT ANY WARRANTY; without //even the implied warranty of MERCHANTABILITY or FITNESS FOR //A PARTICULAR PURPOSE. //See LICENSE.txt or http://www.mitk.org for details. //===================================================================*/ #include #include #include #include #include class mitkSimulationBatchGeneratorTestSuite : public mitk::TestFixture { CPPUNIT_TEST_SUITE(mitkSimulationBatchGeneratorTestSuite); MITK_TEST(testGenerateBatchFileString); MITK_TEST(testGenerateBatchFileAndSaveFile); CPPUNIT_TEST_SUITE_END(); private: const std::string TEST_FOLDER_PATH = "testFiles/"; mitk::pa::SimulationBatchGeneratorParameters::Pointer m_Parameters; mitk::pa::Volume::Pointer m_Test3DVolume; public: void setUp() override { m_Parameters = mitk::pa::SimulationBatchGeneratorParameters::New(); m_Parameters->SetBinaryPath("binary"); m_Parameters->SetNrrdFilePath(TEST_FOLDER_PATH); m_Parameters->SetNumberOfPhotons(100); m_Parameters->SetTissueName("tissueName"); m_Parameters->SetVolumeIndex(0); m_Parameters->SetYOffsetLowerThresholdInCentimeters(-1); m_Parameters->SetYOffsetUpperThresholdInCentimeters(1); m_Parameters->SetYOffsetStepInCentimeters(0.5); m_Test3DVolume = createTest3DVolume(5); itk::FileTools::CreateDirectory(TEST_FOLDER_PATH); CPPUNIT_ASSERT(itksys::SystemTools::FileIsDirectory(TEST_FOLDER_PATH)); } mitk::pa::Volume::Pointer createTest3DVolume(double value) { unsigned int xDim = 10; unsigned int yDim = 10; unsigned int zDim = 10; unsigned int length = xDim * yDim * zDim; auto* data = new double[length]; for (unsigned int i = 0; i < length; i++) data[i] = value; - return mitk::pa::Volume::New(data, xDim, yDim, zDim); + return mitk::pa::Volume::New(data, xDim, yDim, zDim, 1); } void testGenerateBatchFileString() { std::string batchGenerationString = mitk::pa::SimulationBatchGenerator::CreateBatchSimulationString(m_Parameters); CPPUNIT_ASSERT(!batchGenerationString.empty()); } void testGenerateBatchFileAndSaveFile() { mitk::pa::SimulationBatchGenerator::WriteBatchFileAndSaveTissueVolume(m_Parameters, m_Test3DVolume->AsMitkImage()); CPPUNIT_ASSERT(itksys::SystemTools::FileExists(TEST_FOLDER_PATH + m_Parameters->GetTissueName() + "000.nrrd")); CPPUNIT_ASSERT(itksys::SystemTools::FileExists(TEST_FOLDER_PATH + "simulate_all.sh") || itksys::SystemTools::FileExists(TEST_FOLDER_PATH + "simulate_all.bat")); CPPUNIT_ASSERT(itksys::SystemTools::FileExists(TEST_FOLDER_PATH + m_Parameters->GetTissueName() + "000") && itksys::SystemTools::FileIsDirectory(TEST_FOLDER_PATH + m_Parameters->GetTissueName() + "000")); } void tearDown() override { m_Parameters = nullptr; CPPUNIT_ASSERT_MESSAGE("Resource leak of test files onto hard drive..", itksys::SystemTools::RemoveADirectory(TEST_FOLDER_PATH) == true); } }; MITK_TEST_SUITE_REGISTRATION(mitkSimulationBatchGenerator) diff --git a/Modules/PhotoacousticsLib/test/mitkSlicedVolumeGeneratorTest.cpp b/Modules/PhotoacousticsLib/test/mitkSlicedVolumeGeneratorTest.cpp index c4bfc1fa09..aab23e0aa2 100644 --- a/Modules/PhotoacousticsLib/test/mitkSlicedVolumeGeneratorTest.cpp +++ b/Modules/PhotoacousticsLib/test/mitkSlicedVolumeGeneratorTest.cpp @@ -1,187 +1,188 @@ ///*=================================================================== //The Medical Imaging Interaction Toolkit (MITK) //Copyright (c) German Cancer Research Center, //Division of Medical and Biological Informatics. //All rights reserved. //This software is distributed WITHOUT ANY WARRANTY; without //even the implied warranty of MERCHANTABILITY or FITNESS FOR //A PARTICULAR PURPOSE. //See LICENSE.txt or http://www.mitk.org for details. //===================================================================*/ #include #include #include class mitkSlicedVolumeGeneratorTestSuite : public mitk::TestFixture { CPPUNIT_TEST_SUITE(mitkSlicedVolumeGeneratorTestSuite); MITK_TEST(testConstructorDestructor); MITK_TEST(testGetSlicedFluenceVolume); MITK_TEST(testGetSlicedFluenceVolumeInverse); MITK_TEST(testGetSlicedFluenceVolumeWithPrecorrection); MITK_TEST(testGetSlicedFluenceVolumeWithPrecorrectionInverse); MITK_TEST(testGetSlicedSignalVolume); MITK_TEST(testGetSlicedAbsorptionVolume); CPPUNIT_TEST_SUITE_END(); private: mitk::pa::ComposedVolume::Pointer m_ComposedVolume; mitk::pa::TissueGeneratorParameters::Pointer m_DefaultParameters; mitk::pa::InSilicoTissueVolume::Pointer m_InSilicoTissueVolume; mitk::pa::SlicedVolumeGenerator::Pointer m_SlicedVolumeGenerator; mitk::pa::Volume::Pointer m_PrecorrectionVolume; public: void setUp() override { m_SlicedVolumeGenerator = nullptr; m_DefaultParameters = mitk::pa::TissueGeneratorParameters::New(); m_DefaultParameters->SetXDim(3); m_DefaultParameters->SetYDim(3); m_DefaultParameters->SetZDim(3); - m_InSilicoTissueVolume = mitk::pa::InSilicoTissueVolume::New(m_DefaultParameters); + auto rng = std::mt19937(); + m_InSilicoTissueVolume = mitk::pa::InSilicoTissueVolume::New(m_DefaultParameters, &rng); m_ComposedVolume = mitk::pa::ComposedVolume::New(m_InSilicoTissueVolume); m_ComposedVolume->AddSlice(CreateValidationPair(-1, 1)); m_ComposedVolume->AddSlice(CreateValidationPair(0, 3)); m_ComposedVolume->AddSlice(CreateValidationPair(1, 6)); m_PrecorrectionVolume = CreatePrecorrectionVolume(); } mitk::pa::Volume::Pointer CreatePrecorrectionVolume() { auto* data = new double[27]; for (int i = 0; i < 27; ++i) data[i] = 0.5; - return mitk::pa::Volume::New(data, 3, 3, 3); + return mitk::pa::Volume::New(data, 3, 3, 3, 1); } void FillYSliceWith(mitk::pa::Volume::Pointer fluenceVolume, double ySlice, double value) { for (unsigned int x = 0; x < fluenceVolume->GetXDim(); ++x) for (unsigned int z = 0; z < fluenceVolume->GetZDim(); ++z) { fluenceVolume->SetData(value, x, ySlice, z); } } mitk::pa::FluenceYOffsetPair::Pointer CreateValidationPair(double yOffset, int start) { auto* data = new double[27]; - mitk::pa::Volume::Pointer fluenceVolume = mitk::pa::Volume::New(data, 3, 3, 3); + mitk::pa::Volume::Pointer fluenceVolume = mitk::pa::Volume::New(data, 3, 3, 3, 1); FillYSliceWith(fluenceVolume, 0, start + 0); FillYSliceWith(fluenceVolume, 1, start + 1); FillYSliceWith(fluenceVolume, 2, start + 2); return mitk::pa::FluenceYOffsetPair::New(fluenceVolume, yOffset); } void AssertYSliceValue(mitk::pa::Volume::Pointer fluenceVolume, double ySlice, double value) { for (unsigned int x = 0; x < fluenceVolume->GetXDim(); ++x) for (unsigned int z = 0; z < fluenceVolume->GetZDim(); ++z) { std::string msg = "Expected: " + std::to_string(value) + " actual: " + std::to_string(fluenceVolume->GetData(x, ySlice, z)); CPPUNIT_ASSERT_MESSAGE(msg, std::abs(fluenceVolume->GetData(x, ySlice, z) - value) < mitk::eps); } } void testConstructorDestructor() { m_SlicedVolumeGenerator = mitk::pa::SlicedVolumeGenerator::New(0, false, nullptr, false); CPPUNIT_ASSERT(m_SlicedVolumeGenerator.IsNotNull()); } void testGetSlicedFluenceVolume() { m_SlicedVolumeGenerator = mitk::pa::SlicedVolumeGenerator::New(1, false, nullptr, false); mitk::pa::Volume::Pointer slicedFluence = m_SlicedVolumeGenerator->GetSlicedFluenceImageFromComposedVolume(m_ComposedVolume); CPPUNIT_ASSERT(slicedFluence->GetXDim() == 3); CPPUNIT_ASSERT(slicedFluence->GetYDim() == 3); CPPUNIT_ASSERT(slicedFluence->GetZDim() == 3); AssertYSliceValue(slicedFluence, 0, 1); AssertYSliceValue(slicedFluence, 1, 4); AssertYSliceValue(slicedFluence, 2, 8); } void testGetSlicedFluenceVolumeInverse() { m_SlicedVolumeGenerator = mitk::pa::SlicedVolumeGenerator::New(1, false, nullptr, true); mitk::pa::Volume::Pointer slicedFluence = m_SlicedVolumeGenerator->GetSlicedFluenceImageFromComposedVolume(m_ComposedVolume); CPPUNIT_ASSERT(slicedFluence->GetXDim() == 3); CPPUNIT_ASSERT(slicedFluence->GetYDim() == 3); CPPUNIT_ASSERT(slicedFluence->GetZDim() == 3); AssertYSliceValue(slicedFluence, 0, 1); AssertYSliceValue(slicedFluence, 1, 1.0 / 4.0); AssertYSliceValue(slicedFluence, 2, 1.0 / 8.0); } void testGetSlicedFluenceVolumeWithPrecorrection() { m_SlicedVolumeGenerator = mitk::pa::SlicedVolumeGenerator::New(1, true, m_PrecorrectionVolume, false); mitk::pa::Volume::Pointer slicedFluence = m_SlicedVolumeGenerator->GetSlicedFluenceImageFromComposedVolume(m_ComposedVolume); CPPUNIT_ASSERT(slicedFluence->GetXDim() == 3); CPPUNIT_ASSERT(slicedFluence->GetYDim() == 3); CPPUNIT_ASSERT(slicedFluence->GetZDim() == 3); AssertYSliceValue(slicedFluence, 0, 2); AssertYSliceValue(slicedFluence, 1, 8); AssertYSliceValue(slicedFluence, 2, 16); } void testGetSlicedFluenceVolumeWithPrecorrectionInverse() { m_SlicedVolumeGenerator = mitk::pa::SlicedVolumeGenerator::New(1, true, m_PrecorrectionVolume, true); mitk::pa::Volume::Pointer slicedFluence = m_SlicedVolumeGenerator->GetSlicedFluenceImageFromComposedVolume(m_ComposedVolume); CPPUNIT_ASSERT(slicedFluence->GetXDim() == 3); CPPUNIT_ASSERT(slicedFluence->GetYDim() == 3); CPPUNIT_ASSERT(slicedFluence->GetZDim() == 3); AssertYSliceValue(slicedFluence, 0, 1.0 / 2); AssertYSliceValue(slicedFluence, 1, 1.0 / 8); AssertYSliceValue(slicedFluence, 2, 1.0 / 16); } void testGetSlicedSignalVolume() { m_SlicedVolumeGenerator = mitk::pa::SlicedVolumeGenerator::New(1, false, nullptr, false); mitk::pa::Volume::Pointer slicedFluence = m_SlicedVolumeGenerator->GetSlicedSignalImageFromComposedVolume(m_ComposedVolume); CPPUNIT_ASSERT(slicedFluence->GetXDim() == 3); CPPUNIT_ASSERT(slicedFluence->GetYDim() == 3); CPPUNIT_ASSERT(slicedFluence->GetZDim() == 3); - AssertYSliceValue(slicedFluence, 0, 1 * m_DefaultParameters->GetBackgroundAbsorption()); - AssertYSliceValue(slicedFluence, 1, 4 * m_DefaultParameters->GetBackgroundAbsorption()); - AssertYSliceValue(slicedFluence, 2, 8 * m_DefaultParameters->GetBackgroundAbsorption()); + AssertYSliceValue(slicedFluence, 0, 1 * m_DefaultParameters->GetMinBackgroundAbsorption()); + AssertYSliceValue(slicedFluence, 1, 4 * m_DefaultParameters->GetMinBackgroundAbsorption()); + AssertYSliceValue(slicedFluence, 2, 8 * m_DefaultParameters->GetMinBackgroundAbsorption()); } void testGetSlicedAbsorptionVolume() { m_SlicedVolumeGenerator = mitk::pa::SlicedVolumeGenerator::New(1, false, nullptr, false); mitk::pa::Volume::Pointer slicedFluence = m_SlicedVolumeGenerator->GetSlicedGroundTruthImageFromComposedVolume(m_ComposedVolume); CPPUNIT_ASSERT(slicedFluence->GetXDim() == 3); CPPUNIT_ASSERT(slicedFluence->GetYDim() == 3); CPPUNIT_ASSERT(slicedFluence->GetZDim() == 3); - AssertYSliceValue(slicedFluence, 0, m_DefaultParameters->GetBackgroundAbsorption()); - AssertYSliceValue(slicedFluence, 1, m_DefaultParameters->GetBackgroundAbsorption()); - AssertYSliceValue(slicedFluence, 2, m_DefaultParameters->GetBackgroundAbsorption()); + AssertYSliceValue(slicedFluence, 0, m_DefaultParameters->GetMinBackgroundAbsorption()); + AssertYSliceValue(slicedFluence, 1, m_DefaultParameters->GetMinBackgroundAbsorption()); + AssertYSliceValue(slicedFluence, 2, m_DefaultParameters->GetMinBackgroundAbsorption()); } void tearDown() override { m_SlicedVolumeGenerator = nullptr; } }; MITK_TEST_SUITE_REGISTRATION(mitkSlicedVolumeGenerator) diff --git a/Plugins/org.mitk.gui.qt.photoacoustics.imageprocessing/src/internal/PAImageProcessing.cpp b/Plugins/org.mitk.gui.qt.photoacoustics.imageprocessing/src/internal/PAImageProcessing.cpp index 36f37eadde..511591404d 100644 --- a/Plugins/org.mitk.gui.qt.photoacoustics.imageprocessing/src/internal/PAImageProcessing.cpp +++ b/Plugins/org.mitk.gui.qt.photoacoustics.imageprocessing/src/internal/PAImageProcessing.cpp @@ -1,1166 +1,1011 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ - // Blueberry #include #include // Qmitk #include "PAImageProcessing.h" // Qt #include #include #include #include //mitk image #include -#include "mitkPhotoacousticImage.h" -#include "mitkPhotoacousticBeamformingFilter.h" +#include "mitkPhotoacousticFilterService.h" +#include "mitkCastToFloatImageFilter.h" +#include "mitkBeamformingFilter.h" //other #include #include #include const std::string PAImageProcessing::VIEW_ID = "org.mitk.views.paimageprocessing"; -PAImageProcessing::PAImageProcessing() : m_ResampleSpacing(0), m_UseLogfilter(false), m_FilterBank(mitk::PhotoacousticImage::New()) +PAImageProcessing::PAImageProcessing() : m_ResampleSpacing(0), m_UseLogfilter(false), m_FilterBank(mitk::PhotoacousticFilterService::New()) { qRegisterMetaType(); qRegisterMetaType(); } void PAImageProcessing::SetFocus() { m_Controls.buttonApplyBModeFilter->setFocus(); } void PAImageProcessing::CreateQtPartControl(QWidget *parent) { // create GUI widgets from the Qt Designer's .ui file m_Controls.setupUi(parent); connect(m_Controls.buttonApplyBModeFilter, SIGNAL(clicked()), this, SLOT(StartBmodeThread())); connect(m_Controls.DoResampling, SIGNAL(clicked()), this, SLOT(UseResampling())); connect(m_Controls.Logfilter, SIGNAL(clicked()), this, SLOT(UseLogfilter())); connect(m_Controls.ResamplingValue, SIGNAL(valueChanged(double)), this, SLOT(SetResampling())); connect(m_Controls.buttonApplyBeamforming, SIGNAL(clicked()), this, SLOT(StartBeamformingThread())); connect(m_Controls.buttonApplyCropFilter, SIGNAL(clicked()), this, SLOT(StartCropThread())); connect(m_Controls.buttonApplyBandpass, SIGNAL(clicked()), this, SLOT(StartBandpassThread())); connect(m_Controls.UseImageSpacing, SIGNAL(clicked()), this, SLOT(UseImageSpacing())); connect(m_Controls.ScanDepth, SIGNAL(valueChanged(double)), this, SLOT(UpdateImageInfo())); connect(m_Controls.SpeedOfSound, SIGNAL(valueChanged(double)), this, SLOT(UpdateImageInfo())); connect(m_Controls.SpeedOfSound, SIGNAL(valueChanged(double)), this, SLOT(ChangedSOSBeamforming())); connect(m_Controls.BPSpeedOfSound, SIGNAL(valueChanged(double)), this, SLOT(ChangedSOSBandpass())); connect(m_Controls.Samples, SIGNAL(valueChanged(int)), this, SLOT(UpdateImageInfo())); connect(m_Controls.UseImageSpacing, SIGNAL(clicked()), this, SLOT(UpdateImageInfo())); connect(m_Controls.boundLow, SIGNAL(valueChanged(int)), this, SLOT(LowerSliceBoundChanged())); connect(m_Controls.boundHigh, SIGNAL(valueChanged(int)), this, SLOT(UpperSliceBoundChanged())); connect(m_Controls.Partial, SIGNAL(clicked()), this, SLOT(SliceBoundsEnabled())); connect(m_Controls.BatchProcessing, SIGNAL(clicked()), this, SLOT(BatchProcessing())); connect(m_Controls.StepBeamforming, SIGNAL(clicked()), this, SLOT(UpdateSaveBoxes())); connect(m_Controls.StepCropping, SIGNAL(clicked()), this, SLOT(UpdateSaveBoxes())); connect(m_Controls.StepBandpass, SIGNAL(clicked()), this, SLOT(UpdateSaveBoxes())); connect(m_Controls.StepBMode, SIGNAL(clicked()), this, SLOT(UpdateSaveBoxes())); UpdateSaveBoxes(); m_Controls.DoResampling->setChecked(false); m_Controls.ResamplingValue->setEnabled(false); m_Controls.progressBar->setMinimum(0); m_Controls.progressBar->setMaximum(100); m_Controls.progressBar->setVisible(false); m_Controls.UseImageSpacing->setToolTip("Image spacing of y-Axis must be in us, x-Axis in mm."); m_Controls.UseImageSpacing->setToolTipDuration(5000); m_Controls.ProgressInfo->setVisible(false); m_Controls.UseBP->hide(); m_Controls.UseGPUBmode->hide(); - #ifndef PHOTOACOUSTICS_USE_GPU - m_Controls.UseGPUBf->setEnabled(false); - m_Controls.UseGPUBf->setChecked(false); - m_Controls.UseGPUBmode->setEnabled(false); - m_Controls.UseGPUBmode->setChecked(false); - #endif - +#ifndef PHOTOACOUSTICS_USE_GPU + m_Controls.UseGPUBf->setEnabled(false); + m_Controls.UseGPUBf->setChecked(false); + m_Controls.UseGPUBmode->setEnabled(false); + m_Controls.UseGPUBmode->setChecked(false); +#endif + UseImageSpacing(); } void PAImageProcessing::ChangedSOSBandpass() { m_Controls.SpeedOfSound->setValue(m_Controls.BPSpeedOfSound->value()); } void PAImageProcessing::ChangedSOSBeamforming() { m_Controls.BPSpeedOfSound->setValue(m_Controls.SpeedOfSound->value()); } std::vector splitpath( const std::string& str , const std::set delimiters) { std::vector result; char const* pch = str.c_str(); char const* start = pch; for (; *pch; ++pch) { if (delimiters.find(*pch) != delimiters.end()) { if (start != pch) { std::string str(start, pch); result.push_back(str); } else { result.push_back(""); } start = pch + 1; } } result.push_back(start); return result; } void PAImageProcessing::UpdateSaveBoxes() { if (m_Controls.StepBeamforming->isChecked()) m_Controls.SaveBeamforming->setEnabled(true); else m_Controls.SaveBeamforming->setEnabled(false); if (m_Controls.StepCropping->isChecked()) m_Controls.SaveCropping->setEnabled(true); else m_Controls.SaveCropping->setEnabled(false); if (m_Controls.StepBandpass->isChecked()) m_Controls.SaveBandpass->setEnabled(true); else m_Controls.SaveBandpass->setEnabled(false); if (m_Controls.StepBMode->isChecked()) m_Controls.SaveBMode->setEnabled(true); else m_Controls.SaveBMode->setEnabled(false); } void PAImageProcessing::BatchProcessing() { QFileDialog LoadDialog(nullptr, "Select Files to be processed"); LoadDialog.setFileMode(QFileDialog::FileMode::ExistingFiles); LoadDialog.setNameFilter(tr("Images (*.nrrd)")); LoadDialog.setViewMode(QFileDialog::Detail); QStringList fileNames; if (LoadDialog.exec()) fileNames = LoadDialog.selectedFiles(); QString saveDir = QFileDialog::getExistingDirectory(nullptr, tr("Select Directory To Save To"), "", QFileDialog::ShowDirsOnly | QFileDialog::DontResolveSymlinks); DisableControls(); - std::set delims{'/'}; + std::set delims{ '/' }; bool doSteps[] = { m_Controls.StepBeamforming->isChecked(), m_Controls.StepCropping->isChecked() , m_Controls.StepBandpass->isChecked(), m_Controls.StepBMode->isChecked() }; bool saveSteps[] = { m_Controls.SaveBeamforming->isChecked(), m_Controls.SaveCropping->isChecked() , m_Controls.SaveBandpass->isChecked(), m_Controls.SaveBMode->isChecked() }; for (int fileNumber = 0; fileNumber < fileNames.size(); ++fileNumber) { m_Controls.progressBar->setValue(0); m_Controls.progressBar->setVisible(true); m_Controls.ProgressInfo->setVisible(true); m_Controls.ProgressInfo->setText("loading file"); QString filename = fileNames.at(fileNumber); auto split = splitpath(filename.toStdString(), delims); - std::string imageName = split.at(split.size()-1); + std::string imageName = split.at(split.size() - 1); // remove ".nrrd" - imageName = imageName.substr(0, imageName.size()-5); + imageName = imageName.substr(0, imageName.size() - 5); mitk::Image::Pointer image = mitk::IOUtil::Load(filename.toStdString().c_str()); + auto BFconfig = CreateBeamformingSettings(image); - UpdateBFSettings(image); // Beamforming if (doSteps[0]) { std::function progressHandle = [this](int progress, std::string progressInfo) { this->UpdateProgress(progress, progressInfo); }; m_Controls.progressBar->setValue(100); - std::string errorMessage = ""; - image = m_FilterBank->ApplyBeamforming(image, BFconfig, errorMessage, progressHandle); + image = m_FilterBank->ApplyBeamforming(image, BFconfig, progressHandle); if (saveSteps[0]) { std::string saveFileName = saveDir.toStdString() + "/" + imageName + " beamformed" + ".nrrd"; mitk::IOUtil::Save(image, saveFileName); } } // Cropping if (doSteps[1]) { m_Controls.ProgressInfo->setText("cropping image"); - image = m_FilterBank->ApplyCropping(image, m_Controls.CutoffAbove->value(), m_Controls.CutoffBelow->value(), 0, 0, 0, image->GetDimension(2) - 1); + image = m_FilterBank->ApplyCropping(image, m_Controls.CutoffAbove->value(), m_Controls.CutoffBelow->value(), 0, 0, 0, 0); if (saveSteps[1]) { std::string saveFileName = saveDir.toStdString() + "/" + imageName + " cropped" + ".nrrd"; mitk::IOUtil::Save(image, saveFileName); } } // Bandpass if (doSteps[2]) { m_Controls.ProgressInfo->setText("applying bandpass"); float recordTime = image->GetDimension(1)*image->GetGeometry()->GetSpacing()[1] / 1000 / m_Controls.BPSpeedOfSound->value(); // add a safeguard so the program does not chrash when applying a Bandpass that reaches out of the bounds of the image float maxFrequency = 1 / (recordTime / image->GetDimension(1)) * image->GetDimension(1) / 2 / 2 / 1000; float BPHighPass = 1000000 * m_Controls.BPhigh->value(); // [Hz] float BPLowPass = maxFrequency - 1000000 * m_Controls.BPlow->value(); // [Hz] if (BPLowPass > maxFrequency && m_Controls.UseBP->isChecked()) { QMessageBox Msgbox; Msgbox.setText("LowPass too low, disabled it."); Msgbox.exec(); BPLowPass = 0; } if (BPLowPass < 0 && m_Controls.UseBP->isChecked()) { QMessageBox Msgbox; Msgbox.setText("LowPass too high, disabled it."); Msgbox.exec(); BPLowPass = 0; } if (BPHighPass > maxFrequency && m_Controls.UseBP->isChecked()) { QMessageBox Msgbox; Msgbox.setText("HighPass too high, disabled it."); Msgbox.exec(); BPHighPass = 0; } - if (BPHighPass > maxFrequency - BFconfig.BPLowPass) + if (BPHighPass > maxFrequency - BPLowPass) { QMessageBox Msgbox; Msgbox.setText("HighPass higher than LowPass, disabled both."); Msgbox.exec(); BPHighPass = 0; BPLowPass = 0; } - image = m_FilterBank->BandpassFilter(image, recordTime, BPHighPass, BPLowPass, - m_Controls.BPFalloffHigh->value(), - m_Controls.BPFalloffLow->value()); + image = m_FilterBank->ApplyBandpassFilter(image, BPHighPass, BPLowPass, + m_Controls.BPFalloffHigh->value(), + m_Controls.BPFalloffLow->value()); if (saveSteps[2]) { std::string saveFileName = saveDir.toStdString() + "/" + imageName + " bandpassed" + ".nrrd"; mitk::IOUtil::Save(image, saveFileName); } } - // Bmode + // Bmode if (doSteps[3]) { m_Controls.ProgressInfo->setText("applying bmode filter"); - bool useGPU = m_Controls.UseGPUBmode->isChecked(); - + if (m_Controls.BModeMethod->currentText() == "Absolute Filter") - image = m_FilterBank->ApplyBmodeFilter(image, mitk::PhotoacousticImage::BModeMethod::Abs, useGPU, m_UseLogfilter, m_ResampleSpacing); + image = m_FilterBank->ApplyBmodeFilter(image, mitk::PhotoacousticFilterService::BModeMethod::Abs, m_UseLogfilter); else if (m_Controls.BModeMethod->currentText() == "Envelope Detection") - image = m_FilterBank->ApplyBmodeFilter(image, mitk::PhotoacousticImage::BModeMethod::EnvelopeDetection, useGPU, m_UseLogfilter, m_ResampleSpacing); - + image = m_FilterBank->ApplyBmodeFilter(image, mitk::PhotoacousticFilterService::BModeMethod::EnvelopeDetection, m_UseLogfilter); + + double desiredSpacing[2]{ image->GetGeometry()->GetSpacing()[0], m_ResampleSpacing }; + + image = m_FilterBank->ApplyResampling(image, desiredSpacing); + if (saveSteps[3]) { std::string saveFileName = saveDir.toStdString() + "/" + imageName + " bmode" + ".nrrd"; mitk::IOUtil::Save(image, saveFileName); } } m_Controls.progressBar->setVisible(false); } EnableControls(); } void PAImageProcessing::StartBeamformingThread() { QList nodes = this->GetDataManagerSelection(); if (nodes.empty()) return; mitk::DataStorage::Pointer storage = this->GetDataStorage(); mitk::DataNode::Pointer node = nodes.front(); if (!node) { // Nothing selected. Inform the user and return QMessageBox::information(NULL, "Template", "Please load and select an image before starting image processing."); return; } mitk::BaseData* data = node->GetData(); if (data) { // test if this data item is an image or not (could also be a surface or something totally different) mitk::Image* image = dynamic_cast(data); if (image) { - UpdateBFSettings(image); + auto BFconfig = CreateBeamformingSettings(image); std::stringstream message; std::string name; message << "Performing beamforming for image "; if (node->GetName(name)) { // a property called "name" was found for this DataNode message << "'" << name << "'"; m_OldNodeName = name; } else m_OldNodeName = " "; message << "."; MITK_INFO << message.str(); m_Controls.progressBar->setValue(0); m_Controls.progressBar->setVisible(true); m_Controls.ProgressInfo->setVisible(true); m_Controls.ProgressInfo->setText("started"); - m_Controls.buttonApplyBeamforming->setText("working..."); DisableControls(); BeamformingThread *thread = new BeamformingThread(); - connect(thread, &BeamformingThread::result, this, &PAImageProcessing::HandleBeamformingResults); + connect(thread, &BeamformingThread::result, this, &PAImageProcessing::HandleResults); connect(thread, &BeamformingThread::updateProgress, this, &PAImageProcessing::UpdateProgress); - connect(thread, &BeamformingThread::message, this, &PAImageProcessing::PAMessageBox); connect(thread, &BeamformingThread::finished, thread, &QObject::deleteLater); thread->setConfig(BFconfig); thread->setInputImage(image); thread->setFilterBank(m_FilterBank); MITK_INFO << "Started new thread for Beamforming"; thread->start(); } } } -void PAImageProcessing::HandleBeamformingResults(mitk::Image::Pointer image) +void PAImageProcessing::HandleResults(mitk::Image::Pointer image, std::string nameExtension) { + MITK_INFO << "Handling results..."; auto newNode = mitk::DataNode::New(); newNode->SetData(image); - // name the new Data node - std::stringstream newNodeName; - - newNodeName << m_OldNodeName << " "; - - if (BFconfig.Algorithm == mitk::BeamformingSettings::BeamformingAlgorithm::DAS) - newNodeName << "DAS bf, "; - else if (BFconfig.Algorithm == mitk::BeamformingSettings::BeamformingAlgorithm::DMAS) - newNodeName << "DMAS bf, "; - - if (BFconfig.DelayCalculationMethod == mitk::BeamformingSettings::DelayCalc::QuadApprox) - newNodeName << "q. delay"; - if (BFconfig.DelayCalculationMethod == mitk::BeamformingSettings::DelayCalc::Spherical) - newNodeName << "s. delay"; - - newNode->SetName(newNodeName.str()); + newNode->SetName(m_OldNodeName + nameExtension); // update level window for the current dynamic range mitk::LevelWindow levelWindow; newNode->GetLevelWindow(levelWindow); levelWindow.SetAuto(image, true, true); newNode->SetLevelWindow(levelWindow); // add new node to data storage this->GetDataStorage()->Add(newNode); // disable progress bar m_Controls.progressBar->setVisible(false); m_Controls.ProgressInfo->setVisible(false); - m_Controls.buttonApplyBeamforming->setText("Apply Beamforming"); EnableControls(); // update rendering mitk::RenderingManager::GetInstance()->InitializeViews(image->GetGeometry(), mitk::RenderingManager::REQUEST_UPDATE_ALL, true); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); + MITK_INFO << "Handling results...[Done]"; } void PAImageProcessing::StartBmodeThread() { QList nodes = this->GetDataManagerSelection(); if (nodes.empty()) return; mitk::DataStorage::Pointer storage = this->GetDataStorage(); mitk::DataNode::Pointer node = nodes.front(); if (!node) { // Nothing selected. Inform the user and return QMessageBox::information(NULL, "Template", "Please load and select an image before starting image processing."); return; } mitk::BaseData* data = node->GetData(); if (data) { // test if this data item is an image or not (could also be a surface or something totally different) mitk::Image* image = dynamic_cast(data); if (image) { - UpdateBFSettings(image); std::stringstream message; std::string name; message << "Performing image processing for image "; if (node->GetName(name)) { // a property called "name" was found for this DataNode message << "'" << name << "'"; m_OldNodeName = name; } else m_OldNodeName = " "; message << "."; MITK_INFO << message.str(); - m_Controls.buttonApplyBModeFilter->setText("working..."); DisableControls(); BmodeThread *thread = new BmodeThread(); - connect(thread, &BmodeThread::result, this, &PAImageProcessing::HandleBmodeResults); + connect(thread, &BmodeThread::result, this, &PAImageProcessing::HandleResults); connect(thread, &BmodeThread::finished, thread, &QObject::deleteLater); bool useGPU = m_Controls.UseGPUBmode->isChecked(); - if(m_Controls.BModeMethod->currentText() == "Absolute Filter") - thread->setConfig(m_UseLogfilter, m_ResampleSpacing, mitk::PhotoacousticImage::BModeMethod::Abs, useGPU); - else if(m_Controls.BModeMethod->currentText() == "Envelope Detection") - thread->setConfig(m_UseLogfilter, m_ResampleSpacing, mitk::PhotoacousticImage::BModeMethod::EnvelopeDetection, useGPU); + if (m_Controls.BModeMethod->currentText() == "Absolute Filter") + thread->setConfig(m_UseLogfilter, m_ResampleSpacing, mitk::PhotoacousticFilterService::BModeMethod::Abs, useGPU); + else if (m_Controls.BModeMethod->currentText() == "Envelope Detection") + thread->setConfig(m_UseLogfilter, m_ResampleSpacing, mitk::PhotoacousticFilterService::BModeMethod::EnvelopeDetection, useGPU); thread->setInputImage(image); thread->setFilterBank(m_FilterBank); MITK_INFO << "Started new thread for Image Processing"; thread->start(); } } } -void PAImageProcessing::HandleBmodeResults(mitk::Image::Pointer image) -{ - auto newNode = mitk::DataNode::New(); - newNode->SetData(image); - - // name the new Data node - std::stringstream newNodeName; - newNodeName << m_OldNodeName << " "; - newNodeName << "B-Mode"; - - newNode->SetName(newNodeName.str()); - - // update level window for the current dynamic range - mitk::LevelWindow levelWindow; - newNode->GetLevelWindow(levelWindow); - auto data = newNode->GetData(); - levelWindow.SetAuto(dynamic_cast(data), true, true); - newNode->SetLevelWindow(levelWindow); - - // add new node to data storage - this->GetDataStorage()->Add(newNode); - - // disable progress bar - m_Controls.progressBar->setVisible(false); - m_Controls.buttonApplyBModeFilter->setText("Apply B-mode Filter"); - EnableControls(); - - // update rendering - mitk::RenderingManager::GetInstance()->InitializeViews( - dynamic_cast(data)->GetGeometry(), mitk::RenderingManager::REQUEST_UPDATE_ALL, true); - mitk::RenderingManager::GetInstance()->RequestUpdateAll(); -} - void PAImageProcessing::StartCropThread() { QList nodes = this->GetDataManagerSelection(); if (nodes.empty()) return; mitk::DataStorage::Pointer storage = this->GetDataStorage(); mitk::DataNode::Pointer node = nodes.front(); if (!node) { // Nothing selected. Inform the user and return QMessageBox::information(NULL, "Template", "Please load and select an image before starting image cropping."); return; } mitk::BaseData* data = node->GetData(); if (data) { // test if this data item is an image or not (could also be a surface or something totally different) mitk::Image* image = dynamic_cast(data); if (image) { - UpdateBFSettings(image); std::stringstream message; std::string name; message << "Performing image cropping for image "; if (node->GetName(name)) { // a property called "name" was found for this DataNode message << "'" << name << "'"; m_OldNodeName = name; } else m_OldNodeName = " "; message << "."; MITK_INFO << message.str(); - m_Controls.buttonApplyCropFilter->setText("working..."); DisableControls(); CropThread *thread = new CropThread(); - connect(thread, &CropThread::result, this, &PAImageProcessing::HandleCropResults); + connect(thread, &CropThread::result, this, &PAImageProcessing::HandleResults); connect(thread, &CropThread::finished, thread, &QObject::deleteLater); - thread->setConfig(m_Controls.CutoffAbove->value(), m_Controls.CutoffBelow->value(), 0, image->GetDimension(2) - 1); + if(m_Controls.Partial->isChecked()) + thread->setConfig(m_Controls.CutoffAbove->value(), m_Controls.CutoffBelow->value(), m_Controls.boundLow->value(), m_Controls.boundHigh->value()); + else + thread->setConfig(m_Controls.CutoffAbove->value(), m_Controls.CutoffBelow->value(), 0, image->GetDimension(2) - 1); + thread->setInputImage(image); thread->setFilterBank(m_FilterBank); MITK_INFO << "Started new thread for Image Cropping"; thread->start(); } } } -void PAImageProcessing::HandleCropResults(mitk::Image::Pointer image) -{ - auto newNode = mitk::DataNode::New(); - newNode->SetData(image); - - // name the new Data node - std::stringstream newNodeName; - newNodeName << m_OldNodeName << " "; - newNodeName << "Cropped"; - - newNode->SetName(newNodeName.str()); - - // update level window for the current dynamic range - mitk::LevelWindow levelWindow; - newNode->GetLevelWindow(levelWindow); - auto data = newNode->GetData(); - levelWindow.SetAuto(dynamic_cast(data), true, true); - newNode->SetLevelWindow(levelWindow); - - // add new node to data storage - this->GetDataStorage()->Add(newNode); - - m_Controls.buttonApplyCropFilter->setText("Apply Crop Filter"); - EnableControls(); - - // update rendering - mitk::RenderingManager::GetInstance()->InitializeViews( - dynamic_cast(data)->GetGeometry(), mitk::RenderingManager::REQUEST_UPDATE_ALL, true); - mitk::RenderingManager::GetInstance()->RequestUpdateAll(); -} - void PAImageProcessing::StartBandpassThread() { QList nodes = this->GetDataManagerSelection(); if (nodes.empty()) return; mitk::DataStorage::Pointer storage = this->GetDataStorage(); mitk::DataNode::Pointer node = nodes.front(); if (!node) { // Nothing selected. Inform the user and return - QMessageBox::information(NULL, "Template", "Please load and select an image before starting image cropping."); + QMessageBox::information(NULL, "Template", "Please load and select an image before applying a bandpass filter."); return; } mitk::BaseData* data = node->GetData(); if (data) { // test if this data item is an image or not (could also be a surface or something totally different) mitk::Image* image = dynamic_cast(data); if (image) { - UpdateBFSettings(image); std::stringstream message; std::string name; message << "Performing Bandpass filter on image "; if (node->GetName(name)) { // a property called "name" was found for this DataNode message << "'" << name << "'"; m_OldNodeName = name; } else m_OldNodeName = " "; message << "."; MITK_INFO << message.str(); - m_Controls.buttonApplyBandpass->setText("working..."); DisableControls(); BandpassThread *thread = new BandpassThread(); - connect(thread, &BandpassThread::result, this, &PAImageProcessing::HandleBandpassResults); + connect(thread, &BandpassThread::result, this, &PAImageProcessing::HandleResults); connect(thread, &BandpassThread::finished, thread, &QObject::deleteLater); - float recordTime = image->GetDimension(1)*image->GetGeometry()->GetSpacing()[1] / 1000 / m_Controls.BPSpeedOfSound->value(); - - // add a safeguard so the program does not chrash when applying a Bandpass that reaches out of the bounds of the image - float maxFrequency = 1 / (recordTime / image->GetDimension(1)) * image->GetDimension(1) / 2 / 2 / 1000; - float BPHighPass = 1000000 * m_Controls.BPhigh->value(); // [Hz] - float BPLowPass = maxFrequency - 1000000 * m_Controls.BPlow->value(); // [Hz] - - if (BPLowPass > maxFrequency && m_Controls.UseBP->isChecked()) - { - QMessageBox Msgbox; - Msgbox.setText("LowPass too low, disabled it."); - Msgbox.exec(); - - BPLowPass = 0; - } - if (BPLowPass < 0 && m_Controls.UseBP->isChecked()) - { - QMessageBox Msgbox; - Msgbox.setText("LowPass too high, disabled it."); - Msgbox.exec(); - - BPLowPass = 0; - } - if (BPHighPass > maxFrequency && m_Controls.UseBP->isChecked()) - { - QMessageBox Msgbox; - Msgbox.setText("HighPass too high, disabled it."); - Msgbox.exec(); - - BPHighPass = 0; - } - if (BPHighPass > maxFrequency - BFconfig.BPLowPass) - { - QMessageBox Msgbox; - Msgbox.setText("HighPass higher than LowPass, disabled both."); - Msgbox.exec(); + float BPHighPass = 1000000.0f * m_Controls.BPhigh->value(); // [Now in Hz] + float BPLowPass = 1000000.0f * m_Controls.BPlow->value(); // [Now in Hz] - BPHighPass = 0; - BPLowPass = 0; - } - - thread->setConfig(BPHighPass, BPLowPass, m_Controls.BPFalloffLow->value(), m_Controls.BPFalloffHigh->value(), recordTime); + thread->setConfig(BPHighPass, BPLowPass, m_Controls.BPFalloffLow->value(), m_Controls.BPFalloffHigh->value(), 0); thread->setInputImage(image); thread->setFilterBank(m_FilterBank); MITK_INFO << "Started new thread for Bandpass filter"; thread->start(); } } } -void PAImageProcessing::HandleBandpassResults(mitk::Image::Pointer image) -{ - auto newNode = mitk::DataNode::New(); - newNode->SetData(image); - - // name the new Data node - std::stringstream newNodeName; - newNodeName << m_OldNodeName << " "; - newNodeName << "Bandpassed"; - - newNode->SetName(newNodeName.str()); - - // update level window for the current dynamic range - mitk::LevelWindow levelWindow; - newNode->GetLevelWindow(levelWindow); - auto data = newNode->GetData(); - levelWindow.SetAuto(dynamic_cast(data), true, true); - newNode->SetLevelWindow(levelWindow); - - // add new node to data storage - this->GetDataStorage()->Add(newNode); - - m_Controls.buttonApplyBandpass->setText("Apply Bandpass"); - EnableControls(); - - // update rendering - mitk::RenderingManager::GetInstance()->InitializeViews( - dynamic_cast(data)->GetGeometry(), mitk::RenderingManager::REQUEST_UPDATE_ALL, true); - mitk::RenderingManager::GetInstance()->RequestUpdateAll(); -} - void PAImageProcessing::SliceBoundsEnabled() { if (!m_Controls.Partial->isChecked()) { m_Controls.boundLow->setEnabled(false); m_Controls.boundHigh->setEnabled(false); return; } else { m_Controls.boundLow->setEnabled(true); m_Controls.boundHigh->setEnabled(true); } } void PAImageProcessing::UpperSliceBoundChanged() { - if(m_Controls.boundLow->value() > m_Controls.boundHigh->value()) + if (m_Controls.boundLow->value() > m_Controls.boundHigh->value()) { m_Controls.boundLow->setValue(m_Controls.boundHigh->value()); } } void PAImageProcessing::LowerSliceBoundChanged() { if (m_Controls.boundLow->value() > m_Controls.boundHigh->value()) { m_Controls.boundHigh->setValue(m_Controls.boundLow->value()); } } void PAImageProcessing::UpdateProgress(int progress, std::string progressInfo) { if (progress < 100) m_Controls.progressBar->setValue(progress); else m_Controls.progressBar->setValue(100); m_Controls.ProgressInfo->setText(progressInfo.c_str()); qApp->processEvents(); } void PAImageProcessing::PAMessageBox(std::string message) { if (0 != message.compare("noMessage")) { QMessageBox msgBox; msgBox.setText(message.c_str()); msgBox.exec(); } } void PAImageProcessing::UpdateImageInfo() { QList nodes = this->GetDataManagerSelection(); if (nodes.empty()) return; mitk::DataNode::Pointer node = nodes.front(); if (!node) { // Nothing selected return; } mitk::BaseData* data = node->GetData(); if (data) { // test if this data item is an image or not (could also be a surface or something totally different) mitk::Image* image = dynamic_cast(data); if (image) { // beamforming configs if (m_Controls.UseImageSpacing->isChecked()) { m_Controls.ElementCount->setValue(image->GetDimension(0)); m_Controls.Pitch->setValue(image->GetGeometry()->GetSpacing()[0]); } m_Controls.boundLow->setMaximum(image->GetDimension(2) - 1); m_Controls.boundHigh->setMaximum(image->GetDimension(2) - 1); - UpdateBFSettings(image); - - m_Controls.CutoffBeforeBF->setValue(0.000001 / BFconfig.TimeSpacing); // 1us standard offset for our transducer - + float speedOfSound = m_Controls.SpeedOfSound->value(); // [m/s] std::stringstream frequency; - float maxFrequency = (1 / BFconfig.TimeSpacing) * image->GetDimension(1) / 2 / 2 / 1000; + float timeSpacing; + if (m_Controls.UseImageSpacing->isChecked()) + { + timeSpacing = image->GetGeometry()->GetSpacing()[1] / 1000000.0f; + MITK_INFO << "Calculated Scan Depth of " << (image->GetDimension(1)*image->GetGeometry()->GetSpacing()[1] / + 1000000) * speedOfSound * 100 / 2 << "cm"; + } + else + { + timeSpacing = (2 * m_Controls.ScanDepth->value() / 1000 / speedOfSound) / image->GetDimension(1); + } + float maxFrequency = (1 / timeSpacing) * image->GetDimension(1) / 2 / 2 / 1000; frequency << maxFrequency / 1000000; //[MHz] frequency << "MHz"; m_Controls.BPhigh->setMaximum(maxFrequency / 1000000); m_Controls.BPlow->setMaximum(maxFrequency / 1000000); frequency << " is the maximal allowed frequency for the selected image."; m_Controls.BPhigh->setToolTip(frequency.str().c_str()); m_Controls.BPlow->setToolTip(frequency.str().c_str()); m_Controls.BPhigh->setToolTipDuration(5000); m_Controls.BPlow->setToolTipDuration(5000); } } } -void PAImageProcessing::OnSelectionChanged( berry::IWorkbenchPart::Pointer /*source*/, - const QList& nodes ) +void PAImageProcessing::OnSelectionChanged(berry::IWorkbenchPart::Pointer /*source*/, + const QList& nodes) { // iterate all selected objects, adjust warning visibility - foreach( mitk::DataNode::Pointer node, nodes ) + foreach(mitk::DataNode::Pointer node, nodes) { - if( node.IsNotNull() && dynamic_cast(node->GetData()) ) + if (node.IsNotNull() && dynamic_cast(node->GetData())) { - m_Controls.labelWarning->setVisible( false ); - m_Controls.buttonApplyBModeFilter->setEnabled( true ); + m_Controls.labelWarning->setVisible(false); + m_Controls.buttonApplyBModeFilter->setEnabled(true); m_Controls.labelWarning2->setVisible(false); m_Controls.buttonApplyCropFilter->setEnabled(true); m_Controls.labelWarning3->setVisible(false); m_Controls.buttonApplyBandpass->setEnabled(true); m_Controls.labelWarning4->setVisible(false); m_Controls.buttonApplyBeamforming->setEnabled(true); UpdateImageInfo(); return; } } - m_Controls.labelWarning->setVisible( true ); - m_Controls.buttonApplyBModeFilter->setEnabled( false ); + m_Controls.labelWarning->setVisible(true); + m_Controls.buttonApplyBModeFilter->setEnabled(false); m_Controls.labelWarning2->setVisible(true); m_Controls.buttonApplyCropFilter->setEnabled(false); m_Controls.labelWarning3->setVisible(true); m_Controls.buttonApplyBandpass->setEnabled(false); m_Controls.labelWarning4->setVisible(true); m_Controls.buttonApplyBeamforming->setEnabled(false); } void PAImageProcessing::UseResampling() { if (m_Controls.DoResampling->isChecked()) { m_Controls.ResamplingValue->setEnabled(true); m_ResampleSpacing = m_Controls.ResamplingValue->value(); } else { m_Controls.ResamplingValue->setEnabled(false); m_ResampleSpacing = 0; } } void PAImageProcessing::UseLogfilter() { m_UseLogfilter = m_Controls.Logfilter->isChecked(); } void PAImageProcessing::SetResampling() { m_ResampleSpacing = m_Controls.ResamplingValue->value(); } -void PAImageProcessing::UpdateBFSettings(mitk::Image::Pointer image) +mitk::BeamformingSettings::Pointer PAImageProcessing::CreateBeamformingSettings(mitk::Image::Pointer image) { + mitk::BeamformingSettings::BeamformingAlgorithm algorithm; if ("DAS" == m_Controls.BFAlgorithm->currentText()) - BFconfig.Algorithm = mitk::BeamformingSettings::BeamformingAlgorithm::DAS; + algorithm = mitk::BeamformingSettings::BeamformingAlgorithm::DAS; else if ("DMAS" == m_Controls.BFAlgorithm->currentText()) - BFconfig.Algorithm = mitk::BeamformingSettings::BeamformingAlgorithm::DMAS; + algorithm = mitk::BeamformingSettings::BeamformingAlgorithm::DMAS; else if ("sDMAS" == m_Controls.BFAlgorithm->currentText()) - BFconfig.Algorithm = mitk::BeamformingSettings::BeamformingAlgorithm::sDMAS; + algorithm = mitk::BeamformingSettings::BeamformingAlgorithm::sDMAS; - if ("Quad. Approx." == m_Controls.DelayCalculation->currentText()) - { - BFconfig.DelayCalculationMethod = mitk::BeamformingSettings::DelayCalc::QuadApprox; - } - else if ("Spherical Wave" == m_Controls.DelayCalculation->currentText()) - { - BFconfig.DelayCalculationMethod = mitk::BeamformingSettings::DelayCalc::Spherical; - } + mitk::BeamformingSettings::DelayCalc delay = mitk::BeamformingSettings::DelayCalc::Spherical; + mitk::BeamformingSettings::Apodization apod; if ("Von Hann" == m_Controls.Apodization->currentText()) { - BFconfig.Apod = mitk::BeamformingSettings::Apodization::Hann; + apod = mitk::BeamformingSettings::Apodization::Hann; } else if ("Hamming" == m_Controls.Apodization->currentText()) { - BFconfig.Apod = mitk::BeamformingSettings::Apodization::Hamm; + apod = mitk::BeamformingSettings::Apodization::Hamm; } else if ("Box" == m_Controls.Apodization->currentText()) { - BFconfig.Apod = mitk::BeamformingSettings::Apodization::Box; + apod = mitk::BeamformingSettings::Apodization::Box; } - BFconfig.Pitch = m_Controls.Pitch->value() / 1000; // [m] - BFconfig.SpeedOfSound = m_Controls.SpeedOfSound->value(); // [m/s] - BFconfig.SamplesPerLine = m_Controls.Samples->value(); - BFconfig.ReconstructionLines = m_Controls.Lines->value(); - BFconfig.TransducerElements = m_Controls.ElementCount->value(); - BFconfig.apodizationArraySize = m_Controls.Lines->value(); - BFconfig.Angle = m_Controls.Angle->value(); // [deg] - BFconfig.UseBP = m_Controls.UseBP->isChecked(); - BFconfig.UseGPU = m_Controls.UseGPUBf->isChecked(); - BFconfig.upperCutoff = m_Controls.CutoffBeforeBF->value(); + float pitchInMeters = m_Controls.Pitch->value() / 1000; // [m] + float speedOfSound = m_Controls.SpeedOfSound->value(); // [m/s] + unsigned int samplesPerLine = m_Controls.Samples->value(); + unsigned int reconstructionLines = m_Controls.Lines->value(); + unsigned int apodizatonArraySize = m_Controls.Lines->value(); + float angle = m_Controls.Angle->value(); // [deg] + bool useGPU = m_Controls.UseGPUBf->isChecked(); + float timeSpacing; if (m_Controls.UseImageSpacing->isChecked()) { - BFconfig.RecordTime = image->GetDimension(1)*image->GetGeometry()->GetSpacing()[1] / 1000000; // [s] - BFconfig.TimeSpacing = image->GetGeometry()->GetSpacing()[1] / 1000000; - MITK_INFO << "Calculated Scan Depth of " << BFconfig.RecordTime * BFconfig.SpeedOfSound * 100 / 2 << "cm"; + timeSpacing = image->GetGeometry()->GetSpacing()[1] / 1000000.0f; + MITK_INFO << "Calculated Scan Depth of " << (image->GetDimension(1)*image->GetGeometry()->GetSpacing()[1] / + 1000000) * speedOfSound * 100 / 2 << "cm"; } else { - BFconfig.RecordTime = 2 * m_Controls.ScanDepth->value() / 1000 / BFconfig.SpeedOfSound; // [s] - BFconfig.TimeSpacing = BFconfig.RecordTime / image->GetDimension(1); + timeSpacing = (2 * m_Controls.ScanDepth->value() / 1000 / speedOfSound) / image->GetDimension(1); } + bool isPAImage; if ("US Image" == m_Controls.ImageType->currentText()) { - BFconfig.isPhotoacousticImage = false; + isPAImage = false; } else if ("PA Image" == m_Controls.ImageType->currentText()) { - BFconfig.isPhotoacousticImage = true; + isPAImage = true; } - BFconfig.partial = m_Controls.Partial->isChecked(); - BFconfig.CropBounds[0] = m_Controls.boundLow->value(); - BFconfig.CropBounds[1] = m_Controls.boundHigh->value(); + float reconstructionDepth = m_Controls.ReconstructionDepth->value() / 1000.f; // [m] + + return mitk::BeamformingSettings::New(pitchInMeters, + speedOfSound, timeSpacing, angle, isPAImage, samplesPerLine, reconstructionLines, + image->GetDimensions(), reconstructionDepth, useGPU, 16, delay, apod, + apodizatonArraySize, algorithm); } void PAImageProcessing::EnableControls() { m_Controls.BatchProcessing->setEnabled(true); m_Controls.StepBeamforming->setEnabled(true); m_Controls.StepBandpass->setEnabled(true); m_Controls.StepCropping->setEnabled(true); m_Controls.StepBMode->setEnabled(true); UpdateSaveBoxes(); m_Controls.DoResampling->setEnabled(true); UseResampling(); m_Controls.Logfilter->setEnabled(true); m_Controls.BModeMethod->setEnabled(true); m_Controls.buttonApplyBModeFilter->setEnabled(true); m_Controls.CutoffAbove->setEnabled(true); m_Controls.CutoffBelow->setEnabled(true); - m_Controls.CutoffBeforeBF->setEnabled(true); m_Controls.buttonApplyCropFilter->setEnabled(true); m_Controls.BPSpeedOfSound->setEnabled(true); m_Controls.buttonApplyBandpass->setEnabled(true); m_Controls.Partial->setEnabled(true); m_Controls.boundHigh->setEnabled(true); m_Controls.boundLow->setEnabled(true); m_Controls.BFAlgorithm->setEnabled(true); - m_Controls.DelayCalculation->setEnabled(true); + m_Controls.ReconstructionDepth->setEnabled(true); m_Controls.ImageType->setEnabled(true); m_Controls.Apodization->setEnabled(true); m_Controls.UseBP->setEnabled(true); - #ifdef PHOTOACOUSTICS_USE_GPU - m_Controls.UseGPUBf->setEnabled(true); - m_Controls.UseGPUBmode->setEnabled(true); - #endif +#ifdef PHOTOACOUSTICS_USE_GPU + m_Controls.UseGPUBf->setEnabled(true); + m_Controls.UseGPUBmode->setEnabled(true); +#endif m_Controls.BPhigh->setEnabled(true); m_Controls.BPlow->setEnabled(true); m_Controls.BPFalloffLow->setEnabled(true); m_Controls.BPFalloffHigh->setEnabled(true); m_Controls.UseImageSpacing->setEnabled(true); UseImageSpacing(); m_Controls.Pitch->setEnabled(true); m_Controls.ElementCount->setEnabled(true); m_Controls.SpeedOfSound->setEnabled(true); m_Controls.Samples->setEnabled(true); m_Controls.Lines->setEnabled(true); m_Controls.Angle->setEnabled(true); m_Controls.buttonApplyBeamforming->setEnabled(true); } void PAImageProcessing::DisableControls() { m_Controls.BatchProcessing->setEnabled(false); m_Controls.StepBeamforming->setEnabled(false); m_Controls.StepBandpass->setEnabled(false); m_Controls.StepCropping->setEnabled(false); m_Controls.StepBMode->setEnabled(false); m_Controls.SaveBeamforming->setEnabled(false); m_Controls.SaveBandpass->setEnabled(false); m_Controls.SaveCropping->setEnabled(false); m_Controls.SaveBMode->setEnabled(false); m_Controls.DoResampling->setEnabled(false); m_Controls.ResamplingValue->setEnabled(false); m_Controls.Logfilter->setEnabled(false); m_Controls.BModeMethod->setEnabled(false); m_Controls.buttonApplyBModeFilter->setEnabled(false); m_Controls.CutoffAbove->setEnabled(false); m_Controls.CutoffBelow->setEnabled(false); - m_Controls.CutoffBeforeBF->setEnabled(false); m_Controls.buttonApplyCropFilter->setEnabled(false); m_Controls.BPSpeedOfSound->setEnabled(false); m_Controls.buttonApplyBandpass->setEnabled(false); m_Controls.Partial->setEnabled(false); m_Controls.boundHigh->setEnabled(false); m_Controls.boundLow->setEnabled(false); m_Controls.BFAlgorithm->setEnabled(false); - m_Controls.DelayCalculation->setEnabled(false); + m_Controls.ReconstructionDepth->setEnabled(false); m_Controls.ImageType->setEnabled(false); m_Controls.Apodization->setEnabled(false); m_Controls.UseBP->setEnabled(false); - #ifdef PHOTOACOUSTICS_USE_GPU - m_Controls.UseGPUBf->setEnabled(false); - m_Controls.UseGPUBmode->setEnabled(false); - #endif +#ifdef PHOTOACOUSTICS_USE_GPU + m_Controls.UseGPUBf->setEnabled(false); + m_Controls.UseGPUBmode->setEnabled(false); +#endif m_Controls.BPhigh->setEnabled(false); m_Controls.BPlow->setEnabled(false); m_Controls.BPFalloffLow->setEnabled(false); m_Controls.BPFalloffHigh->setEnabled(false); m_Controls.UseImageSpacing->setEnabled(false); m_Controls.ScanDepth->setEnabled(false); m_Controls.Pitch->setEnabled(false); m_Controls.ElementCount->setEnabled(false); m_Controls.SpeedOfSound->setEnabled(false); m_Controls.Samples->setEnabled(false); m_Controls.Lines->setEnabled(false); m_Controls.Angle->setEnabled(false); m_Controls.buttonApplyBeamforming->setEnabled(false); } void PAImageProcessing::UseImageSpacing() { if (m_Controls.UseImageSpacing->isChecked()) { m_Controls.ScanDepth->setDisabled(true); } else { m_Controls.ScanDepth->setEnabled(true); } } #include void BeamformingThread::run() { mitk::Image::Pointer resultImage = mitk::Image::New(); mitk::Image::Pointer resultImageBuffer; - std::string errorMessage = ""; std::function progressHandle = [this](int progress, std::string progressInfo) { - emit updateProgress(progress, progressInfo); - }; - - resultImageBuffer = m_FilterBank->ApplyBeamforming(m_InputImage, m_BFconfig, errorMessage, progressHandle); - mitk::ImageReadAccessor copy(resultImageBuffer); - - resultImage->Initialize(resultImageBuffer); - resultImage->SetSpacing(resultImageBuffer->GetGeometry()->GetSpacing()); - resultImage->SetImportVolume(const_cast(copy.GetData()), 0, 0, mitk::Image::CopyMemory); - - emit result(resultImage); - emit message(errorMessage); + emit updateProgress(progress, progressInfo); + }; + resultImageBuffer = m_FilterBank->ApplyBeamforming(m_InputImage, m_BFconfig, progressHandle); + emit result(resultImageBuffer, "_bf"); } -void BeamformingThread::setConfig(mitk::BeamformingSettings BFconfig) +void BeamformingThread::setConfig(mitk::BeamformingSettings::Pointer BFconfig) { m_BFconfig = BFconfig; } void BeamformingThread::setInputImage(mitk::Image::Pointer image) { m_InputImage = image; } void BmodeThread::run() { - mitk::Image::Pointer resultImage; + mitk::Image::Pointer resultImage = m_FilterBank->ApplyBmodeFilter(m_InputImage, + m_Method, m_UseLogfilter); - resultImage = m_FilterBank->ApplyBmodeFilter(m_InputImage, m_Method, m_UseGPU, m_UseLogfilter, m_ResampleSpacing); - - emit result(resultImage); + if (m_ResampleSpacing != 0) + { + double desiredSpacing[2]{ m_InputImage->GetGeometry()->GetSpacing()[0], m_ResampleSpacing }; + resultImage = m_FilterBank->ApplyResampling(resultImage, desiredSpacing); + } + emit result(resultImage, "_bmode"); } -void BmodeThread::setConfig(bool useLogfilter, double resampleSpacing, mitk::PhotoacousticImage::BModeMethod method, bool useGPU) +void BmodeThread::setConfig(bool useLogfilter, double resampleSpacing, mitk::PhotoacousticFilterService::BModeMethod method, bool useGPU) { m_UseLogfilter = useLogfilter; m_ResampleSpacing = resampleSpacing; m_Method = method; m_UseGPU = useGPU; } void BmodeThread::setInputImage(mitk::Image::Pointer image) { m_InputImage = image; } void CropThread::run() { mitk::Image::Pointer resultImage; - resultImage = m_FilterBank->ApplyCropping(m_InputImage, m_CutAbove, m_CutBelow, 0, 0, m_CutSliceFirst, m_CutSliceLast); - - emit result(resultImage); + resultImage = m_FilterBank->ApplyCropping(m_InputImage, m_CutAbove, m_CutBelow, 0, 0, m_CutSliceFirst, (m_InputImage->GetDimension(2) - 1) - m_CutSliceLast); + emit result(resultImage, "_cropped"); } void CropThread::setConfig(unsigned int CutAbove, unsigned int CutBelow, unsigned int CutSliceFirst, unsigned int CutSliceLast) { m_CutAbove = CutAbove; m_CutBelow = CutBelow; m_CutSliceLast = CutSliceLast; m_CutSliceFirst = CutSliceFirst; } void CropThread::setInputImage(mitk::Image::Pointer image) { m_InputImage = image; } void BandpassThread::run() { - mitk::Image::Pointer resultImage; - - resultImage = m_FilterBank->BandpassFilter(m_InputImage, m_RecordTime, m_BPHighPass, m_BPLowPass, m_TukeyAlphaHighPass, m_TukeyAlphaLowPass); - emit result(resultImage); + mitk::Image::Pointer resultImage = m_FilterBank->ApplyBandpassFilter(m_InputImage, m_BPHighPass, m_BPLowPass, m_TukeyAlphaHighPass, m_TukeyAlphaLowPass); + emit result(resultImage, "_bandpassed"); } void BandpassThread::setConfig(float BPHighPass, float BPLowPass, float TukeyAlphaHighPass, float TukeyAlphaLowPass, float recordTime) { m_BPHighPass = BPHighPass; m_BPLowPass = BPLowPass; m_TukeyAlphaHighPass = TukeyAlphaHighPass; m_TukeyAlphaLowPass = TukeyAlphaLowPass; m_RecordTime = recordTime; } void BandpassThread::setInputImage(mitk::Image::Pointer image) { m_InputImage = image; } diff --git a/Plugins/org.mitk.gui.qt.photoacoustics.imageprocessing/src/internal/PAImageProcessing.h b/Plugins/org.mitk.gui.qt.photoacoustics.imageprocessing/src/internal/PAImageProcessing.h index 8bb5eb860e..c111aa71df 100644 --- a/Plugins/org.mitk.gui.qt.photoacoustics.imageprocessing/src/internal/PAImageProcessing.h +++ b/Plugins/org.mitk.gui.qt.photoacoustics.imageprocessing/src/internal/PAImageProcessing.h @@ -1,255 +1,237 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef PAImageProcessing_h #define PAImageProcessing_h -#include +#include #include #include #include #include "ui_PAImageProcessingControls.h" -#include "mitkPhotoacousticBeamformingFilter.h" -#include "mitkPhotoacousticBeamformingSettings.h" +#include "mitkBeamformingFilter.h" +#include "mitkBeamformingSettings.h" Q_DECLARE_METATYPE(mitk::Image::Pointer) Q_DECLARE_METATYPE(std::string) /*! * \brief Plugin implementing an interface for the Photoacoustic Algorithms Module * * Beamforming, Image processing as B-Mode filtering, cropping, resampling, as well as batch processing can be performed using this plugin. */ class PAImageProcessing : public QmitkAbstractView { // this is needed for all Qt objects that should have a Qt meta-object // (everything that derives from QObject and wants to have signal/slots) Q_OBJECT - public: +public: - static const std::string VIEW_ID; + static const std::string VIEW_ID; - PAImageProcessing(); + PAImageProcessing(); protected slots: - void UpperSliceBoundChanged(); - void LowerSliceBoundChanged(); - void SliceBoundsEnabled(); - - void UseResampling(); - void UseLogfilter(); - void SetResampling(); - void UseImageSpacing(); - void UpdateImageInfo(); - - /** \brief Method called when the beamforming thread finishes; - * it adds the image to a new data node and registers it to the worbench's data storage - */ - void HandleBeamformingResults(mitk::Image::Pointer image); - /** \brief Beamforming is being performed in a separate thread to keep the workbench from freezing. - */ - void StartBeamformingThread(); - - /** \brief Method called when the B-mode filter thread finishes; - * it adds the image to a new data node and registers it to the worbench's data storage - */ - void HandleBmodeResults(mitk::Image::Pointer image); - /** \brief B-mode filtering is being performed in a separate thread to keep the workbench from freezing. - */ - void StartBmodeThread(); - - /** \brief Method called when the Cropping thread finishes; - * it adds the image to a new data node and registers it to the worbench's data storage - */ - void HandleCropResults(mitk::Image::Pointer image); - /** \brief Cropping is being performed in a separate thread to keep the workbench from freezing. - */ - void StartCropThread(); - - /** \brief Method called when the bandpass thread finishes; - * it adds the image to a new data node and registers it to the worbench's data storage - */ - void HandleBandpassResults(mitk::Image::Pointer image); - /** \brief Bandpassing is being performed in a separate thread to keep the workbench from freezing. - */ - void StartBandpassThread(); - - void UpdateProgress(int progress, std::string progressInfo); - void PAMessageBox(std::string message); - - void BatchProcessing(); - void UpdateSaveBoxes(); - - void ChangedSOSBandpass(); - void ChangedSOSBeamforming(); - - protected: - virtual void CreateQtPartControl(QWidget *parent) override; - - virtual void SetFocus() override; - - /** \brief called by QmitkFunctionality when DataManager's selection has changed. - * On a change some parameters are internally updated to calculate bounds for GUI elements as the slice selector for beamforming or - * the bandpass filter settings. - */ - virtual void OnSelectionChanged( berry::IWorkbenchPart::Pointer source, - const QList& nodes ) override; - - /** \brief Instance of the GUI controls - */ - Ui::PAImageProcessingControls m_Controls; - - float m_ResampleSpacing; - bool m_UseLogfilter; - std::string m_OldNodeName; - - /** \brief The settings set which is used for beamforming, updated through this class. - */ - mitk::BeamformingSettings BFconfig; - - /** \brief Method for updating the BFconfig by using a selected image and the GUI configuration. - */ - void UpdateBFSettings(mitk::Image::Pointer image); - - void EnableControls(); - void DisableControls(); - - /** \brief Class through which the filters are called. - */ - mitk::PhotoacousticImage::Pointer m_FilterBank; + void UpperSliceBoundChanged(); + void LowerSliceBoundChanged(); + void SliceBoundsEnabled(); + + void UseResampling(); + void UseLogfilter(); + void SetResampling(); + void UseImageSpacing(); + void UpdateImageInfo(); + + /** \brief Beamforming is being performed in a separate thread to keep the workbench from freezing. + */ + void StartBeamformingThread(); + + /** \brief B-mode filtering is being performed in a separate thread to keep the workbench from freezing. + */ + void StartBmodeThread(); + + /** \brief Cropping is being performed in a separate thread to keep the workbench from freezing. + */ + void StartCropThread(); + + /** \brief Method called when the bandpass thread finishes; + * it adds the image to a new data node and registers it to the worbench's data storage + */ + void HandleResults(mitk::Image::Pointer image, std::string nameExtension); + + /** \brief Bandpassing is being performed in a separate thread to keep the workbench from freezing. + */ + void StartBandpassThread(); + + void UpdateProgress(int progress, std::string progressInfo); + void PAMessageBox(std::string message); + + void BatchProcessing(); + void UpdateSaveBoxes(); + + void ChangedSOSBandpass(); + void ChangedSOSBeamforming(); + +protected: + virtual void CreateQtPartControl(QWidget *parent) override; + + virtual void SetFocus() override; + + /** \brief called by QmitkFunctionality when DataManager's selection has changed. + * On a change some parameters are internally updated to calculate bounds for GUI elements as the slice selector for beamforming or + * the bandpass filter settings. + */ + virtual void OnSelectionChanged(berry::IWorkbenchPart::Pointer source, + const QList& nodes) override; + + /** \brief Instance of the GUI controls + */ + Ui::PAImageProcessingControls m_Controls; + + float m_ResampleSpacing; + bool m_UseLogfilter; + std::string m_OldNodeName; + + /** \brief Method for updating the BFconfig by using a selected image and the GUI configuration. + */ + mitk::BeamformingSettings::Pointer CreateBeamformingSettings(mitk::Image::Pointer image); + + void EnableControls(); + void DisableControls(); + + /** \brief Class through which the filters are called. + */ + mitk::PhotoacousticFilterService::Pointer m_FilterBank; }; class BeamformingThread : public QThread { Q_OBJECT void run() Q_DECL_OVERRIDE; - signals: - void result(mitk::Image::Pointer); - void updateProgress(int, std::string); - void message(std::string); - - public: - void setConfig(mitk::BeamformingSettings BFconfig); - void setInputImage(mitk::Image::Pointer image); - void setFilterBank(mitk::PhotoacousticImage::Pointer filterBank) - { - m_FilterBank = filterBank; - } +signals: + void result(mitk::Image::Pointer, std::string nameExtension); + void updateProgress(int, std::string); + void message(std::string); +public: + void setConfig(mitk::BeamformingSettings::Pointer BFconfig); + void setInputImage(mitk::Image::Pointer image); + void setFilterBank(mitk::PhotoacousticFilterService::Pointer filterBank) + { + m_FilterBank = filterBank; + } - protected: - mitk::BeamformingSettings m_BFconfig; - mitk::Image::Pointer m_InputImage; - int m_Cutoff; +protected: + mitk::BeamformingSettings::Pointer m_BFconfig; + mitk::Image::Pointer m_InputImage; + int m_Cutoff; - mitk::PhotoacousticImage::Pointer m_FilterBank; + mitk::PhotoacousticFilterService::Pointer m_FilterBank; }; class BmodeThread : public QThread { Q_OBJECT void run() Q_DECL_OVERRIDE; - signals: - void result(mitk::Image::Pointer); - - public: - enum BModeMethod { ShapeDetection, Abs }; +signals: + void result(mitk::Image::Pointer, std::string nameExtension); - void setConfig(bool useLogfilter, double resampleSpacing, mitk::PhotoacousticImage::BModeMethod method, bool useGPU); - void setInputImage(mitk::Image::Pointer image); - void setFilterBank(mitk::PhotoacousticImage::Pointer filterBank) - { - m_FilterBank = filterBank; - } +public: + enum BModeMethod { ShapeDetection, Abs }; + void setConfig(bool useLogfilter, double resampleSpacing, mitk::PhotoacousticFilterService::BModeMethod method, bool useGPU); + void setInputImage(mitk::Image::Pointer image); + void setFilterBank(mitk::PhotoacousticFilterService::Pointer filterBank) + { + m_FilterBank = filterBank; + } - protected: - mitk::Image::Pointer m_InputImage; +protected: + mitk::Image::Pointer m_InputImage; - mitk::PhotoacousticImage::BModeMethod m_Method; - bool m_UseLogfilter; - double m_ResampleSpacing; - bool m_UseGPU; + mitk::PhotoacousticFilterService::BModeMethod m_Method; + bool m_UseLogfilter; + double m_ResampleSpacing; + bool m_UseGPU; - mitk::PhotoacousticImage::Pointer m_FilterBank; + mitk::PhotoacousticFilterService::Pointer m_FilterBank; }; class CropThread : public QThread { Q_OBJECT void run() Q_DECL_OVERRIDE; signals: - void result(mitk::Image::Pointer); + void result(mitk::Image::Pointer, std::string nameExtension); public: void setConfig(unsigned int CutAbove, unsigned int CutBelow, unsigned int CutSliceFirst, unsigned int CutSliceLast); void setInputImage(mitk::Image::Pointer image); - void setFilterBank(mitk::PhotoacousticImage::Pointer filterBank) + void setFilterBank(mitk::PhotoacousticFilterService::Pointer filterBank) { m_FilterBank = filterBank; } protected: mitk::Image::Pointer m_InputImage; unsigned int m_CutAbove; unsigned int m_CutBelow; unsigned int m_CutSliceLast; unsigned int m_CutSliceFirst; - mitk::PhotoacousticImage::Pointer m_FilterBank; + mitk::PhotoacousticFilterService::Pointer m_FilterBank; }; - class BandpassThread : public QThread { Q_OBJECT void run() Q_DECL_OVERRIDE; signals: - void result(mitk::Image::Pointer); + void result(mitk::Image::Pointer, std::string nameExtension); public: void setConfig(float BPHighPass, float BPLowPass, float TukeyAlphaHighPass, float TukeyAlphaLowPass, float recordTime); void setInputImage(mitk::Image::Pointer image); - void setFilterBank(mitk::PhotoacousticImage::Pointer filterBank) + void setFilterBank(mitk::PhotoacousticFilterService::Pointer filterBank) { m_FilterBank = filterBank; } protected: mitk::Image::Pointer m_InputImage; float m_BPHighPass; float m_BPLowPass; float m_TukeyAlphaHighPass; float m_TukeyAlphaLowPass; float m_RecordTime; - mitk::PhotoacousticImage::Pointer m_FilterBank; + mitk::PhotoacousticFilterService::Pointer m_FilterBank; }; #endif // PAImageProcessing_h diff --git a/Plugins/org.mitk.gui.qt.photoacoustics.imageprocessing/src/internal/PAImageProcessingControls.ui b/Plugins/org.mitk.gui.qt.photoacoustics.imageprocessing/src/internal/PAImageProcessingControls.ui index 2cf6316fd3..d4b2d03e51 100644 --- a/Plugins/org.mitk.gui.qt.photoacoustics.imageprocessing/src/internal/PAImageProcessingControls.ui +++ b/Plugins/org.mitk.gui.qt.photoacoustics.imageprocessing/src/internal/PAImageProcessingControls.ui @@ -1,1027 +1,998 @@ PAImageProcessingControls 0 0 385 1278 0 0 QmitkTemplate <html><head/><body><p><span style=" font-weight:600;">Batch Processing</span></p></body></html> Start Batch Processing Bandpass true Crop true Save false Save false Save true Beamform true BMode true Save true <html><head/><body><p><span style=" font-weight:600;">B-mode Filter Settings</span></p></body></html> - Envelope Detection + Absolute Filter Absolute Filter Envelope Detection Do Resampling true 0 0 13 0 11 3 0.010000000000000 1.000000000000000 0.010000000000000 0.150000000000000 [mm] Resampled Depth Spacing Logarithmic Compression Use GPU QLabel { color: rgb(255, 0, 0) } <html><head/><body><p align="center"><span style=" font-size:10pt; font-weight:600;">Please select an image!</span></p></body></html> 0 0 Do image processing Apply B-mode Filter Qt::Horizontal <html><head/><body><p><span style=" font-weight:600;">Bandpass Filter Settings</span></p></body></html> QLayout::SetDefaultConstraint 0 0 0 3 0.010000000000000 200.000000000000000 + + 0.100000000000000 + 1.000000000000000 [MHz] f High Pass [MHz] f Low Pass 0 0 3 200.000000000000000 + + 0.100000000000000 + 1 200.000000000000000 3000.000000000000000 5.000000000000000 1540.000000000000000 [m/s] Speed of Sound 1.000000000000000 0.100000000000000 0.500000000000000 Tukey Window α High Pass 2 1.000000000000000 0.100000000000000 0.000000000000000 Tukey Window α Low Pass <html><head/><body><p align="center"><span style=" font-size:10pt; font-weight:600; color:#ff0000;">Please select an image!</span></p></body></html> Apply Bandpass Qt::Horizontal <html><head/><body><p><span style=" font-weight:600;">Crop Filter Settings</span></p></body></html> 99999 300 Cut Top Cut Bottom 99999 5 800 + + + + false + + + 99999 + + + + + + + Maximal beamformed slice + + + max + + + + + + + false + + + 99999 + + + 10 + + + + + + + minimal beamformed slice + + + min + + + + + + + select slices + + + <html><head/><body><p align="center"><span style=" font-size:10pt; font-weight:600; color:#ff0000;">Please select an image!</span></p></body></html> Apply Crop Filer Qt::Horizontal <html><head/><body><p><span style=" font-weight:600;">Beamforming Filter Settings</span></p></body></html> 5 2 - - - - Delay Calculation + + + + + 0 + 0 + + + + 1 + + + 200.000000000000000 + + + 3000.000000000000000 + + + 5.000000000000000 + + + 1540.000000000000000 - + Auto Get Depth true - + Apply Beamforming - + - + Beamforming Method - + [mm] Scan Depth - + 0 0 3 0.010000000000000 9.000000000000000 0.050000000000000 0.300000000000000 - + Transducer Elements - + 0 0 4 300.000000000000000 0.100000000000000 50.000000000000000 - + [mm] Transducer Pitch - + 0 0 64 1024 128 128 - + 0 0 256 16384 256 2048 - + 0 0 64 2048 128 256 - + Samples - + Reconstruction Lines - + true 0 0 100 0 - - - - - 0 - 0 - - - - 900 - - - 10 - - - 0 - - - - + 0 0 DAS DMAS sDMAS - - - - - 0 - 0 - - - - Spherical Wave - - - - Quad. Approx. - - - - - Spherical Wave - - - - - + 0 0 PA Image US Image - + Image Type - + 0 0 Von Hann Hamming Box - + Apodization - - - - - 0 - 0 - - - - 1 - - - 200.000000000000000 - - - 3000.000000000000000 - - - 5.000000000000000 - - - 1540.000000000000000 - - - - + [m/s] Speed of Sound - - - - - - false - - - 99999 - - - - - - - minimal beamformed slice - - - min - - - - + + - - - - - - false - - - 99999 - - - 10 - - - - - - - Maximal beamformed slice - - - max - - - - + + - + - - - - select slices - - - - + Compute On GPU true - + true Auto Use Bandpass - + 0 0 1 1.000000000000000 180.000000000000000 27.000000000000000 - + [°] Element Angle - - + + - Cutoff Upper Voxels + <html><head/><body><p align="center"><span style=" font-size:10pt; font-weight:600; color:#ff0000;">Please select an image!</span></p></body></html> - - + + - <html><head/><body><p align="center"><span style=" font-size:10pt; font-weight:600; color:#ff0000;">Please select an image!</span></p></body></html> + [mm] Reconstruction Depth + + + + + + + 4 + + + 300.000000000000000 + + + 0.100000000000000 + + + 10.000000000000000 Qt::Vertical 20 40 diff --git a/Plugins/org.mitk.gui.qt.photoacoustics.simulation/src/internal/PASimulator.cpp b/Plugins/org.mitk.gui.qt.photoacoustics.simulation/src/internal/PASimulator.cpp index e1b432613a..20109b4428 100644 --- a/Plugins/org.mitk.gui.qt.photoacoustics.simulation/src/internal/PASimulator.cpp +++ b/Plugins/org.mitk.gui.qt.photoacoustics.simulation/src/internal/PASimulator.cpp @@ -1,271 +1,256 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ // Blueberry #include #include // Qmitk #include "PASimulator.h" // Qt #include #include #include // mitk #include #include #include #include #include const std::string PASimulator::VIEW_ID = "org.mitk.views.pasimulator"; void PASimulator::SetFocus() { m_Controls.pushButtonShowRandomTissue->setFocus(); } void PASimulator::CreateQtPartControl(QWidget *parent) { m_Controls.setupUi(parent); connect(m_Controls.pushButtonShowRandomTissue, SIGNAL(clicked()), this, SLOT(DoImageProcessing())); - connect(m_Controls.checkBoxGauss, SIGNAL(stateChanged(int)), this, SLOT(ClickedGaussBox())); connect(m_Controls.pushButtonOpenPath, SIGNAL(clicked()), this, SLOT(OpenFolder())); connect(m_Controls.pushButtonOpenBinary, SIGNAL(clicked()), this, SLOT(OpenBinary())); connect(m_Controls.checkBoxGenerateBatch, SIGNAL(clicked()), this, SLOT(UpdateVisibilityOfBatchCreation())); connect(m_Controls.pushButtonAjustWavelength, SIGNAL(clicked()), this, SLOT(UpdateParametersAccordingToWavelength())); connect(m_Controls.checkBoxRngSeed, SIGNAL(clicked()), this, SLOT(ClickedCheckboxFixedSeed())); connect(m_Controls.checkBoxRandomizeParameters, SIGNAL(clicked()), this, SLOT(ClickedRandomizePhysicalParameters())); - m_Controls.spinboxSigma->setEnabled(false); - m_Controls.labelSigma->setEnabled(false); - - std::string home_env = std::string(std::getenv("HOME")); - if (home_env.empty()) + auto home = std::getenv("HOME"); + std::string home_env = ""; + if (home != nullptr) { - home_env = std::string(std::getenv("HOMEPATH")); + home_env = std::string(home); } - if (home_env.empty()) + else { - home_env = ""; + home = std::getenv("HOMEPATH"); + if (home != nullptr) + { + home_env = std::string(home); + } } m_Controls.label_NrrdFilePath->setText(home_env.c_str()); m_PhotoacousticPropertyCalculator = mitk::pa::PropertyCalculator::New(); UpdateVisibilityOfBatchCreation(); ClickedRandomizePhysicalParameters(); ClickedCheckboxFixedSeed(); - ClickedGaussBox(); } void PASimulator::ClickedRandomizePhysicalParameters() { m_Controls.spinboxRandomizeParameters->setEnabled(m_Controls.checkBoxRandomizeParameters->isChecked()); } void PASimulator::ClickedCheckboxFixedSeed() { m_Controls.spinBoxRngSeed->setEnabled(m_Controls.checkBoxRngSeed->isChecked()); } void PASimulator::UpdateParametersAccordingToWavelength() { int wavelength = m_Controls.spinboxWavelength->value(); double bloodOxygenation = m_Controls.spinboxBloodOxygenSaturation->value() / 100; auto result = m_PhotoacousticPropertyCalculator->CalculatePropertyForSpecificWavelength( mitk::pa::PropertyCalculator::TissueType::BLOOD, wavelength, bloodOxygenation); m_Controls.spinboxMaxAbsorption->setValue(result.mua); m_Controls.spinboxMinAbsorption->setValue(result.mua); m_Controls.spinboxBloodVesselScatteringMinimum->setValue(result.mus); m_Controls.spinboxBloodVesselScatteringMaximum->setValue(result.mus); m_Controls.spinboxBloodVesselAnisotropyMinimum->setValue(result.g); m_Controls.spinboxBloodVesselAnisotropyMaximum->setValue(result.g); result = m_PhotoacousticPropertyCalculator->CalculatePropertyForSpecificWavelength( mitk::pa::PropertyCalculator::TissueType::EPIDERMIS, wavelength, bloodOxygenation); m_Controls.spinboxSkinAbsorption->setValue(result.mua); m_Controls.spinboxSkinScattering->setValue(result.mus); m_Controls.spinboxSkinAnisotropy->setValue(result.g); result = m_PhotoacousticPropertyCalculator->CalculatePropertyForSpecificWavelength( mitk::pa::PropertyCalculator::TissueType::STANDARD_TISSUE, wavelength, bloodOxygenation); m_Controls.spinboxBackgroundAbsorption->setValue(result.mua); m_Controls.spinboxBackgroundScattering->setValue(result.mus); m_Controls.spinboxBackgroundAnisotropy->setValue(result.g); } void PASimulator::UpdateVisibilityOfBatchCreation() { m_Controls.widgetBatchFile->setVisible(m_Controls.checkBoxGenerateBatch->isChecked()); } mitk::pa::TissueGeneratorParameters::Pointer PASimulator::GetParametersFromUIInput() { auto parameters = mitk::pa::TissueGeneratorParameters::New(); // Getting settings from UI // General settings parameters->SetXDim(m_Controls.spinboxXDim->value()); parameters->SetYDim(m_Controls.spinboxYDim->value()); parameters->SetZDim(m_Controls.spinboxZDim->value()); - parameters->SetDoVolumeSmoothing(m_Controls.checkBoxGauss->isChecked()); - if (parameters->GetDoVolumeSmoothing()) - parameters->SetVolumeSmoothingSigma(m_Controls.spinboxSigma->value()); + parameters->SetDoPartialVolume(m_Controls.checkBoxPartialVolume->isChecked()); parameters->SetRandomizePhysicalProperties(m_Controls.checkBoxRandomizeParameters->isChecked()); parameters->SetRandomizePhysicalPropertiesPercentage(m_Controls.spinboxRandomizeParameters->value()); parameters->SetVoxelSpacingInCentimeters(m_Controls.spinboxSpacing->value()); parameters->SetUseRngSeed(m_Controls.checkBoxRngSeed->isChecked()); parameters->SetRngSeed(m_Controls.spinBoxRngSeed->value()); // Monte Carlo simulation parameters parameters->SetMCflag(m_Controls.spinboxMcFlag->value()); parameters->SetMCLaunchflag(m_Controls.spinboxLaunchFlag->value()); parameters->SetMCBoundaryflag(m_Controls.spinboxboundaryFlag->value()); parameters->SetMCLaunchPointX(m_Controls.spinboxLaunchpointX->value()); parameters->SetMCLaunchPointY(m_Controls.spinboxLaunchpointY->value()); parameters->SetMCLaunchPointZ(m_Controls.spinboxLaunchpointZ->value()); parameters->SetMCFocusPointX(m_Controls.spinboxFocuspointX->value()); parameters->SetMCFocusPointY(m_Controls.spinboxFocuspointY->value()); parameters->SetMCFocusPointZ(m_Controls.spinboxFocuspointZ->value()); parameters->SetMCTrajectoryVectorX(m_Controls.spinboxTrajectoryVectorX->value()); parameters->SetMCTrajectoryVectorY(m_Controls.spinboxTrajectoryVectorY->value()); parameters->SetMCTrajectoryVectorZ(m_Controls.spinboxTrajectoryVectorZ->value()); parameters->SetMCRadius(m_Controls.spinboxRadius->value()); parameters->SetMCWaist(m_Controls.spinboxWaist->value()); // Vessel settings parameters->SetMaxVesselAbsorption(m_Controls.spinboxMaxAbsorption->value()); parameters->SetMinVesselAbsorption(m_Controls.spinboxMinAbsorption->value()); parameters->SetMaxVesselBending(m_Controls.spinboxMaxBending->value()); parameters->SetMinVesselBending(m_Controls.spinboxMinBending->value()); parameters->SetMaxVesselRadiusInMillimeters(m_Controls.spinboxMaxDiameter->value()); parameters->SetMinVesselRadiusInMillimeters(m_Controls.spinboxMinDiameter->value()); parameters->SetMaxNumberOfVessels(m_Controls.spinboxMaxVessels->value()); parameters->SetMinNumberOfVessels(m_Controls.spinboxMinVessels->value()); parameters->SetMinVesselScattering(m_Controls.spinboxBloodVesselScatteringMinimum->value()); parameters->SetMaxVesselScattering(m_Controls.spinboxBloodVesselScatteringMaximum->value()); parameters->SetMinVesselAnisotropy(m_Controls.spinboxBloodVesselAnisotropyMinimum->value()); parameters->SetMaxVesselAnisotropy(m_Controls.spinboxBloodVesselAnisotropyMaximum->value()); parameters->SetVesselBifurcationFrequency(m_Controls.spinboxBifurcationFrequency->value()); parameters->SetMinVesselZOrigin(m_Controls.spinboxMinSpawnDepth->value()); parameters->SetMaxVesselZOrigin(m_Controls.spinboxMaxSpawnDepth->value()); // Background tissue settings - parameters->SetBackgroundAbsorption(m_Controls.spinboxBackgroundAbsorption->value()); + parameters->SetMinBackgroundAbsorption(m_Controls.spinboxBackgroundAbsorption->value()); + parameters->SetMaxBackgroundAbsorption(m_Controls.spinboxBackgroundAbsorption->value()); parameters->SetBackgroundScattering(m_Controls.spinboxBackgroundScattering->value()); parameters->SetBackgroundAnisotropy(m_Controls.spinboxBackgroundAnisotropy->value()); // Air settings parameters->SetAirThicknessInMillimeters(m_Controls.spinboxAirThickness->value()); //Skin tissue settings parameters->SetSkinThicknessInMillimeters(m_Controls.spinboxSkinThickness->value()); parameters->SetSkinAbsorption(m_Controls.spinboxSkinAbsorption->value()); parameters->SetSkinScattering(m_Controls.spinboxSkinScattering->value()); parameters->SetSkinAnisotropy(m_Controls.spinboxSkinAnisotropy->value()); parameters->SetCalculateNewVesselPositionCallback(&mitk::pa::VesselMeanderStrategy::CalculateRandomlyDivergingPosition); return parameters; } void PASimulator::DoImageProcessing() { int numberOfVolumes = 1; if (m_Controls.checkBoxGenerateBatch->isChecked()) { if (m_Controls.labelBinarypath->text().isNull() || m_Controls.labelBinarypath->text().isEmpty()) { QMessageBox::warning(nullptr, QString("Warning"), QString("You need to specify the binary first!")); return; } numberOfVolumes = m_Controls.spinboxNumberVolumes->value(); if (numberOfVolumes < 1) { QMessageBox::warning(nullptr, QString("Warning"), QString("You need to create at least one volume!")); return; } } auto tissueParameters = GetParametersFromUIInput(); for (int volumeIndex = 0; volumeIndex < numberOfVolumes; volumeIndex++) { mitk::pa::InSilicoTissueVolume::Pointer volume = mitk::pa::InSilicoTissueGenerator::GenerateInSilicoData(tissueParameters); mitk::Image::Pointer tissueVolume = volume->ConvertToMitkImage(); if (m_Controls.checkBoxGenerateBatch->isChecked()) { std::string nrrdFilePath = m_Controls.label_NrrdFilePath->text().toStdString(); std::string tissueName = m_Controls.lineEditTissueName->text().toStdString(); std::string binaryPath = m_Controls.labelBinarypath->text().toStdString(); long numberOfPhotons = m_Controls.spinboxNumberPhotons->value() * 1000L; auto batchParameters = mitk::pa::SimulationBatchGeneratorParameters::New(); batchParameters->SetBinaryPath(binaryPath); batchParameters->SetNrrdFilePath(nrrdFilePath); batchParameters->SetNumberOfPhotons(numberOfPhotons); batchParameters->SetTissueName(tissueName); batchParameters->SetVolumeIndex(volumeIndex); batchParameters->SetYOffsetLowerThresholdInCentimeters(m_Controls.spinboxFromValue->value()); batchParameters->SetYOffsetUpperThresholdInCentimeters(m_Controls.spinboxToValue->value()); batchParameters->SetYOffsetStepInCentimeters(m_Controls.spinboxStepValue->value()); mitk::pa::SimulationBatchGenerator::WriteBatchFileAndSaveTissueVolume(batchParameters, tissueVolume); } else { mitk::DataNode::Pointer dataNode = mitk::DataNode::New(); dataNode->SetData(tissueVolume); dataNode->SetName(m_Controls.lineEditTissueName->text().toStdString()); this->GetDataStorage()->Add(dataNode); mitk::RenderingManager::GetInstance()->InitializeViewsByBoundingObjects(this->GetDataStorage()); } } } -void PASimulator::ClickedGaussBox() -{ - if (m_Controls.checkBoxGauss->isChecked()) - { - m_Controls.spinboxSigma->setEnabled(true); - m_Controls.labelSigma->setEnabled(true); - } - else - { - m_Controls.spinboxSigma->setEnabled(false); - m_Controls.labelSigma->setEnabled(false); - } -} - void PASimulator::OpenFolder() { m_Controls.label_NrrdFilePath->setText(QFileDialog::getExistingDirectory().append("/")); } void PASimulator::OpenBinary() { m_Controls.labelBinarypath->setText(QFileDialog::getOpenFileName()); } diff --git a/Plugins/org.mitk.gui.qt.photoacoustics.simulation/src/internal/PASimulator.h b/Plugins/org.mitk.gui.qt.photoacoustics.simulation/src/internal/PASimulator.h index b002562b30..d6f8492faa 100644 --- a/Plugins/org.mitk.gui.qt.photoacoustics.simulation/src/internal/PASimulator.h +++ b/Plugins/org.mitk.gui.qt.photoacoustics.simulation/src/internal/PASimulator.h @@ -1,77 +1,76 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef PASimulator_h #define PASimulator_h #include #include #include "ui_PASimulatorControls.h" #include "mitkPATissueGenerator.h" #include "mitkPATissueGeneratorParameters.h" #include "mitkPAInSilicoTissueVolume.h" #include "mitkPAPropertyCalculator.h" #include "mitkPASimulationBatchGenerator.h" /** \brief PASimulator \warning This class is not yet documented. Use "git blame" and ask the author to provide basic documentation. \sa QmitkAbstractView \ingroup ${plugin_target}_internal */ class PASimulator : public QmitkAbstractView { // this is needed for all Qt objects that should have a Qt meta-object // (everything that derives from QObject and wants to have signal/slots) Q_OBJECT public: static const std::string VIEW_ID; protected slots: /// \brief Called when the user clicks the GUI button void DoImageProcessing(); - void ClickedGaussBox(); void ClickedCheckboxFixedSeed(); void ClickedRandomizePhysicalParameters(); void OpenFolder(); void OpenBinary(); void UpdateVisibilityOfBatchCreation(); void UpdateParametersAccordingToWavelength(); protected: virtual void CreateQtPartControl(QWidget *parent) override; virtual void SetFocus() override; Ui::PASimulatorControls m_Controls; mitk::pa::PropertyCalculator::Pointer m_PhotoacousticPropertyCalculator; private: mitk::pa::TissueGeneratorParameters::Pointer GetParametersFromUIInput(); }; #endif // PASimulator_h diff --git a/Plugins/org.mitk.gui.qt.photoacoustics.simulation/src/internal/PASimulatorControls.ui b/Plugins/org.mitk.gui.qt.photoacoustics.simulation/src/internal/PASimulatorControls.ui index 8e0876504b..4187e1e967 100644 --- a/Plugins/org.mitk.gui.qt.photoacoustics.simulation/src/internal/PASimulatorControls.ui +++ b/Plugins/org.mitk.gui.qt.photoacoustics.simulation/src/internal/PASimulatorControls.ui @@ -1,4793 +1,4728 @@ PASimulatorControls 0 0 437 655 0 0 Ubuntu Qt::NoContextMenu QmitkTemplate :/org.mitk.gui.qt.photoacousticsimulation/resources/icon.xpm:/org.mitk.gui.qt.photoacousticsimulation/resources/icon.xpm 0 0 415 600 Ubuntu 0 Generator 10 10 391 581 0 0 391 581 0 0 0 0 75 true Volume parameters 0 0 0 25 16777215 25 Tissue name: 0 0 0 25 16777215 25 Size x: 0 0 0 25 16777215 25 50 false Spacing: 0 0 0 25 16777215 25 PhotoacousticTissue 0 0 0 25 16777215 25 1 9999 - 140 + 35 0 0 0 25 16777215 25 y: 0 0 0 25 16777215 25 9999 - 200 + 50 0 0 0 25 16777215 25 z: 0 0 0 25 16777215 25 9999 - 200 + 50 0 0 0 25 16777215 25 voxels Qt::Horizontal 40 20 0 0 0 25 16777215 25 - 2 + 5 0.010000000000000 - 0.030000000000000 + 0.120000000000000 0 0 0 25 16777215 25 cm Qt::Horizontal 40 20 0 0 0 25 16777215 25 Randomize: - + 0 0 0 25 16777215 25 - Partial volume effects: + Custom seed: - + 0 0 0 25 16777215 25 - Custom seed: + Partial volume effects: + + + + + + + Generate batch file output: 0 0 0 25 16777215 25 false - + true 0 0 0 25 16777215 25 - false + true - + true 0 0 0 25 16777215 25 + + true + + + + + + + + + + false + 0 0 0 25 16777215 25 sigma: 0 0 0 25 16777215 25 0 100.000000000000000 0.010000000000000 2.000000000000000 0 0 0 25 16777215 25 % Qt::Horizontal 40 20 - - - - - - 0 - 0 - - - - - 0 - 25 - - - - - 16777215 - 25 - - - - sigma: - - - + - + 0 0 0 25 16777215 25 - 10.000000000000000 - - - 0.100000000000000 + 999999999 - 1.000000000000000 + 1337 - - - - 0 - 0 - - - - - 0 - 25 - - - - - 16777215 - 25 - - - - voxels - - - - - + Qt::Horizontal 40 20 - + - - - - 0 - 0 - - - - - 0 - 25 - + + + Qt::Horizontal - + - 16777215 - 25 + 40 + 20 - - 999999999 - - - 170704057 - - + + + + + - + Qt::Horizontal 40 20 - - - - - - Generate batch file output: - - - - - - - - - - false - - - - - Number of volumes to generate: 1 9999999 1 Qt::Horizontal 40 20 Save generated tissue path: 0 0 50 0 50 16777215 open Path to MitkMcxyz binary: 0 0 50 0 50 16777215 open From (cm): 0 0 0 25 16777215 25 -100000.000000000000000 100000.000000000000000 0.100000000000000 -1.800000000000000 To (cm): 0 0 0 25 16777215 25 -10000.000000000000000 10000.000000000000000 0.100000000000000 1.800000000000000 Step: 0 0 0 25 16777215 25 -10000.000000000000000 10000.000000000000000 0.010000000000000 0.120000000000000 Number of Photons (x1000): 0 999999999 1 100000 0 0 Generate Tissue Qt::Vertical 20 40 Tissue 10 10 391 581 0 0 391 581 0 0 0 0 0 0 0 25 16777215 25 11 75 false true false Air Parameters 0 0 0 25 16777215 25 50 false Thickness: 0 0 0 10 16777215 10 11 75 true 0 0 0 25 16777215 25 75 false true false Background Parameters 0 0 0 25 16777215 25 Absorption coefficient: 0 0 0 25 16777215 25 Scattering coefficient: 0 0 0 25 16777215 25 Anisotropy facor: 0 0 0 10 16777215 10 11 75 true 0 0 0 25 16777215 25 75 false true false Skin Parameters 0 0 0 25 16777215 25 Thickness: 0 0 0 25 16777215 25 Absorption coefficient: 0 0 0 25 16777215 25 Scattering coefficient: 0 0 0 25 16777215 25 Anisotropy facor: 0 0 0 10 16777215 10 11 75 true 0 0 0 25 16777215 25 11 75 true 0 0 0 25 16777215 25 999.990000000000009 0.100000000000000 12.000000000000000 0 0 0 25 16777215 25 mm Qt::Horizontal 40 20 0 0 0 10 16777215 10 11 75 true 0 0 0 25 16777215 25 75 true 0 0 0 25 16777215 25 5 0.010000000000000 0.100000000000000 0.100000000000000 0 0 0 25 16777215 25 per cm Qt::Horizontal 40 20 0 0 0 25 16777215 25 5 0.010000000000000 1000.000000000000000 0.500000000000000 15.000000000000000 0 0 0 25 16777215 25 per cm Qt::Horizontal 40 20 0 0 0 25 16777215 25 5 0.010000000000000 1.000000000000000 0.010000000000000 0.900000000000000 Qt::Horizontal 40 20 0 0 0 10 16777215 10 11 75 true 0 0 0 25 16777215 25 75 true 0 0 0 25 16777215 25 0.100000000000000 0.000000000000000 0 0 0 25 16777215 25 mm Qt::Horizontal 40 20 0 0 0 25 16777215 25 5 0.010000000000000 0.100000000000000 3.000000000000000 0 0 0 25 16777215 25 per cm Qt::Horizontal 40 20 0 0 0 25 16777215 25 5 0.010000000000000 1000.000000000000000 0.500000000000000 20.000000000000000 0 0 0 25 16777215 25 per cm Qt::Horizontal 40 20 0 0 0 25 16777215 25 5 0.010000000000000 1.000000000000000 0.010000000000000 0.900000000000000 Qt::Horizontal 40 20 0 0 0 10 16777215 10 11 75 true Qt::Vertical 20 40 Vessels 10 10 391 581 0 0 391 581 0 0 0 0 75 true Vessel Parameters 0 25 16777215 25 The number of bloos vessels to generate Count: Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter 0 25 16777215 25 The radius of the blood vessels in mm Radius: 0 25 16777215 25 the absorption coefficient refers to the number of absorption events per centimeter. Absorption: 0 25 16777215 25 The reduced scattering coefficient. It refers to the amount of scattering events per centimeter. Scattering: 0 25 16777215 25 The anisotropy factor is the probability of a photon to not change its direction after a scattering event. Anisotropy factor: 0 25 16777215 25 The bifurcation frequency determines how often the vessel should bifurcate. The vessel will bifurcate after meandering a mean of the specified amount of voxels. When given a value of 0, the vessel will never bifurcate. Bifurcation frequency: 0 25 16777215 25 The curvedness it a setting to determine how much the vessel is allowed to bend during creation. A value of 0 refers to no bending at all and a value of 5 is the maximum. Curvedness: 0 25 16777215 25 The spawn depth defines the depth range in which the vessels enter the volume. Spawn depth: 0 25 16777215 25 The minimum number of blood vessels 0 1 0 25 16777215 25 to 0 25 16777215 25 The maximum number of blood vessels 1 0 25 16777215 25 Vessels Qt::Horizontal QSizePolicy::Expanding 60 20 0 25 16777215 25 The minimum radius 5 2.250000000000000 0 25 16777215 25 to 0 25 16777215 25 The maximum radius 5 4.050000000000000 0 25 16777215 25 mm Qt::Horizontal QSizePolicy::Expanding 60 20 0 25 16777215 25 The minimum absorption coefficient 5 0.010000000000000 2.000000000000000 0 25 16777215 25 to 0 25 16777215 25 The maximum absorption coefficient 5 0.010000000000000 8.000000000000000 0 25 16777215 25 per cm Qt::Horizontal QSizePolicy::Expanding 60 20 0 25 16777215 25 The minimum scattering 5 0.010000000000000 999.000000000000000 1.000000000000000 15.000000000000000 0 25 16777215 25 to 0 25 16777215 25 The minimum scattering 5 0.010000000000000 999.000000000000000 1.000000000000000 15.000000000000000 0 25 16777215 25 per cm Qt::Horizontal QSizePolicy::Expanding 60 20 0 25 16777215 25 The minimum anisotropy factor 5 0.010000000000000 1.000000000000000 0.100000000000000 0.900000000000000 0 25 16777215 25 to 0 25 16777215 25 The maximum anisotropy factor 5 0.010000000000000 1.000000000000000 0.100000000000000 0.900000000000000 Qt::Horizontal QSizePolicy::Expanding 60 20 0 25 16777215 25 The bifurcation frequency determines how often the vessel should bifurcate. The vessel will bifurcate after meandering a mean of the specified amount of voxels. When given a value of 0, the vessel will never bifurcate. 0 1.000000000000000 999999999.000000000000000 5.000000000000000 50.000000000000000 0 25 16777215 25 voxels Qt::Horizontal QSizePolicy::Expanding 60 20 0 25 16777215 25 The minimal curvedness of the vessel. A value of 0 refers to no bending at all and a value of 5 is the maximum. 5.000000000000000 0.000000000000000 0 25 16777215 25 to 0 25 16777215 25 The maximal curvedness of the vessel. A value of 0 refers to no bending at all and a value of 5 is the maximum. 5.000000000000000 0.010000000000000 0.200000000000000 0 25 16777215 25 A.U. Qt::Horizontal QSizePolicy::Expanding 60 20 0 25 16777215 25 The minimum spawn depth 5 0.010000000000000 2.200000000000000 0 25 16777215 25 to 0 25 16777215 25 the maximum spawn depth 5 0.010000000000000 4.200000000000000 0 25 16777215 25 cm Qt::Horizontal QSizePolicy::Expanding 60 20 Qt::Vertical 20 40 Monte Carlo 10 10 391 581 0 0 391 581 0 0 0 0 75 true Monte Carlo Parameters 0 25 16777215 25 50 false General: 0 0 0 25 16777215 25 Mcflag: 0 0 0 25 16777215 25 Launchflag: 0 0 0 25 16777215 25 Boundaryflag: 0 0 0 25 16777215 25 1 4 Qt::Horizontal 40 20 0 0 0 25 16777215 25 0 0 Qt::Horizontal 40 20 0 0 0 25 16777215 25 1 2 Qt::Horizontal 40 20 0 25 16777215 25 50 false Initial launch point: 0 25 16777215 25 x 0 25 16777215 25 4 1000000.000000000000000 0.000000000000000 0 25 16777215 25 y 0 25 16777215 25 4 1000000.000000000000000 0.000000000000000 0 25 16777215 25 z 0 25 16777215 25 4 1000000.000000000000000 0.000000000000000 Qt::Horizontal 40 20 0 25 16777215 25 50 false Focus point: 0 25 16777215 25 x 0 25 16777215 25 4 1000000.000000000000000 0.000000000000000 0 25 16777215 25 y 0 25 16777215 25 4 1000000.000000000000000 0.000000000000000 0 25 16777215 25 z 0 25 16777215 25 4 1000000.000000000000000 0.000000000000000 Qt::Horizontal 40 20 0 25 16777215 25 50 false Trajectory vector: 0 25 16777215 25 x 0 25 16777215 25 4 1000000.000000000000000 0.000000000000000 0 25 16777215 25 y 0 25 16777215 25 4 1000000.000000000000000 0.342000000000000 0 25 16777215 25 z 0 25 16777215 25 4 1000000.000000000000000 0.939700000000000 Qt::Horizontal 40 20 radius: waist: 4 1000.000000000000000 0.500000000000000 Qt::Horizontal 40 20 4 1000.000000000000000 0.010000000000000 Qt::Horizontal 40 20 Qt::Vertical 20 40 Wavelength 10 10 391 581 0 0 391 581 Qt::NoContextMenu 0 0 0 0 75 true Adjust physical properties by wavelength false 16777215 250 <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Ubuntu'; font-size:7.8pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:11pt;">This widget enables the adjustment of the physical properties of the tissue according to a selected wavelength of the light and the oxygen saturation of the blood.</span></p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:11pt;"><br /></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:11pt;">The algorithm and measurements were provided by the publication </span><span style=" font-size:11pt; font-weight:600;">Optical properties of biological tissues: a review </span><span style=" font-size:11pt;">by Steve L. Jacques.</span></p></body></html> 0 0 0 25 16777215 25 Wavelength: 0 0 0 25 16777215 25 Vessel oxygen saturation: 0 0 0 25 16777215 25 0 300.000000000000000 1000.000000000000000 650.000000000000000 Qt::Horizontal 40 20 0 0 0 25 16777215 25 0 100.000000000000000 75.000000000000000 0 0 0 25 16777215 25 % Qt::Horizontal 40 20 Adjust tissue properties Qt::Vertical 20 40