diff --git a/Modules/PhotoacousticsAlgorithms/MitkPABeamformingTool/PABeamformingTool.cpp b/Modules/PhotoacousticsAlgorithms/MitkPABeamformingTool/PABeamformingTool.cpp index f030cc339e..fd029145e8 100644 --- a/Modules/PhotoacousticsAlgorithms/MitkPABeamformingTool/PABeamformingTool.cpp +++ b/Modules/PhotoacousticsAlgorithms/MitkPABeamformingTool/PABeamformingTool.cpp @@ -1,233 +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 #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.cutoff, false, (unsigned int*) nullptr, input.inputImage->GetDimensions(), false, mitk::BeamformingSettings::DelayCalc::Spherical, mitk::BeamformingSettings::Apodization::Box, input.inputImage->GetDimension(0), input.algorithm, false, 0.0f, 0.0f); 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::Abs, false, false, 0.3); + auto output2 = m_BeamformingService->ApplyBmodeFilter(output, mitk::PhotoacousticFilterService::EnvelopeDetection, false, 0.3); 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/include/OpenCLFilter/mitkPhotoacousticBModeFilter.h b/Modules/PhotoacousticsAlgorithms/include/OpenCLFilter/mitkPhotoacousticBModeFilter.h index aed8d626fe..07413ed8e2 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) + 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) + /** \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; } 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/mitkPhotoacousticFilterService.h b/Modules/PhotoacousticsAlgorithms/include/mitkPhotoacousticFilterService.h index 0c017de1ff..d64ff8f64d 100644 --- a/Modules/PhotoacousticsAlgorithms/include/mitkPhotoacousticFilterService.h +++ b/Modules/PhotoacousticsAlgorithms/include/mitkPhotoacousticFilterService.h @@ -1,131 +1,132 @@ /*=================================================================== 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 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); + mitk::Image::Pointer ApplyBmodeFilter(mitk::Image::Pointer inputImage, + BModeMethod method = BModeMethod::Abs, + bool UseLogFilter = false, + float resampleSpacing = 0.15); /** \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 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 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 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 BandpassFilter(mitk::Image::Pointer data, float recordTime, 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; /** \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 /* mitkPhotoacousticFilterService_H_HEADER_INCLUDED */ diff --git a/Modules/PhotoacousticsAlgorithms/source/OpenCLFilter/mitkPhotoacousticBModeFilter.cpp b/Modules/PhotoacousticsAlgorithms/source/OpenCLFilter/mitkPhotoacousticBModeFilter.cpp index 65164364df..b2267d3a97 100644 --- a/Modules/PhotoacousticsAlgorithms/source/OpenCLFilter/mitkPhotoacousticBModeFilter.cpp +++ b/Modules/PhotoacousticsAlgorithms/source/OpenCLFilter/mitkPhotoacousticBModeFilter.cpp @@ -1,222 +1,94 @@ /*=================================================================== 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::CopyMemory); - delete[] pData; - } - - 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()); 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->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); 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::CopyMemory); delete[] OutputData; m_TimeOfHeaderInitialization.Modified(); -} \ No newline at end of file +} diff --git a/Modules/PhotoacousticsAlgorithms/source/utils/mitkPhotoacousticFilterService.cpp b/Modules/PhotoacousticsAlgorithms/source/utils/mitkPhotoacousticFilterService.cpp index ed8914d7c4..3fa2126eb8 100644 --- a/Modules/PhotoacousticsAlgorithms/source/utils/mitkPhotoacousticFilterService.cpp +++ b/Modules/PhotoacousticsAlgorithms/source/utils/mitkPhotoacousticFilterService.cpp @@ -1,443 +1,400 @@ /*=================================================================== 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 "../ITKFilter/ITKUltrasound/itkBModeImageFilter.h" #include "../ITKFilter/itkPhotoacousticBModeImageFilter.h" #include "mitkImageCast.h" #include "mitkITKImageImport.h" #include "mitkBeamformingFilter.h" #include #include #include #include "./OpenCLFilter/mitkPhotoacousticBModeFilter.h" #include "mitkConvert2Dto3DImageFilter.h" #include // 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::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 UseGPU, bool UseLogFilter, float resampleSpacing) +mitk::Image::Pointer mitk::PhotoacousticFilterService::ApplyBmodeFilter(mitk::Image::Pointer inputImage, BModeMethod method, 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 (inputImage.IsNull() || !(inputImage->GetPixelType().GetTypeAsString() == "scalar (float)" || inputImage->GetPixelType().GetTypeAsString() == " (float)")) { MITK_ERROR << "BMode Filter can only handle float image types."; mitkThrow() << "BMode Filter can only handle float image types."; } + mitk::Image::Pointer out; + if (method == BModeMethod::Abs) { - mitk::Image::Pointer input = inputImage; - mitk::Image::Pointer out; - - 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 = itkFloatImageType::New(); - 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()); + PhotoacousticBModeFilter::Pointer filter = PhotoacousticBModeFilter::New(); + filter->SetParameters(UseLogFilter); + filter->SetInput(inputImage); + filter->Update(); + out = filter->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); - } + out = mitk::GrabItkImageMemory(bmode); + } + + if (resampleSpacing == 0) + return out; + + typedef itk::ResampleImageFilter < itkFloatImageType, itkFloatImageType > ResampleImageFilter; + ResampleImageFilter::Pointer resampleImageFilter = ResampleImageFilter::New(); - itkFloatImageType::SpacingType outputSpacing; - itkFloatImageType::SizeType inputSize = itkImage->GetLargestPossibleRegion().GetSize(); - itkFloatImageType::SizeType outputSize = inputSize; + itkFloatImageType::Pointer itkImage = itkFloatImageType::New(); + 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]; + outputSpacing[0] = itkImage->GetSpacing()[0]; + outputSpacing[1] = resampleSpacing; + outputSpacing[2] = itkImage->GetSpacing()[2]; - outputSize[1] = inputSize[1] * itkImage->GetSpacing()[1] / outputSpacing[1]; + outputSize[1] = inputSize[1] * itkImage->GetSpacing()[1] / outputSpacing[1]; - resampleImageFilter->SetInput(bmode); - resampleImageFilter->SetSize(outputSize); - resampleImageFilter->SetOutputSpacing(outputSpacing); - resampleImageFilter->SetTransform(TransformType::New()); + typedef itk::IdentityTransform TransformType; + resampleImageFilter->SetInput(itkImage); + resampleImageFilter->SetSize(outputSize); + resampleImageFilter->SetOutputSpacing(outputSpacing); + resampleImageFilter->SetTransform(TransformType::New()); - resampleImageFilter->UpdateLargestPossibleRegion(); - return mitk::GrabItkImageMemory(resampleImageFilter->GetOutput()); - } - return nullptr; + resampleImageFilter->UpdateLargestPossibleRegion(); + return mitk::GrabItkImageMemory(resampleImageFilter->GetOutput()); } mitk::Image::Pointer mitk::PhotoacousticFilterService::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::PhotoacousticFilterService::ApplyCropping(mitk::Image::Pointer inputImage, int above, int below, int right, int left, int zStart, int zEnd) { mitk::CropImageFilter::Pointer cropImageFilter = mitk::CropImageFilter::New(); cropImageFilter->SetInput(inputImage); cropImageFilter->SetXPixelsCropStart(left); cropImageFilter->SetXPixelsCropEnd(right); cropImageFilter->SetYPixelsCropStart(above); cropImageFilter->SetYPixelsCropEnd(below); cropImageFilter->SetZPixelsCropStart(zStart); cropImageFilter->SetZPixelsCropEnd(zEnd); cropImageFilter->Update(); return cropImageFilter->GetOutput(); } 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; } // perform the beamforming m_BeamformingFilter = mitk::BeamformingFilter::New(config); m_BeamformingFilter->SetInput(processedImage); m_BeamformingFilter->SetProgressHandle(progressHandle); m_BeamformingFilter->UpdateLargestPossibleRegion(); processedImage = m_BeamformingFilter->GetOutput(); return processedImage; } mitk::Image::Pointer mitk::PhotoacousticFilterService::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::PhotoacousticFilterService::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/Plugins/org.mitk.gui.qt.photoacoustics.imageprocessing/src/internal/PAImageProcessing.cpp b/Plugins/org.mitk.gui.qt.photoacoustics.imageprocessing/src/internal/PAImageProcessing.cpp index 928f5b24ef..7de530ee1a 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,1181 +1,1057 @@ /*=================================================================== 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 "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::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 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{ '/' }; 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); // remove ".nrrd" imageName = imageName.substr(0, imageName.size() - 5); mitk::Image::Pointer image = mitk::IOUtil::Load(filename.toStdString().c_str()); auto BFconfig = CreateBeamformingSettings(image); // Beamforming if (doSteps[0]) { std::function progressHandle = [this](int progress, std::string progressInfo) { this->UpdateProgress(progress, progressInfo); }; m_Controls.progressBar->setValue(100); 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, 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 - 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()); if (saveSteps[2]) { std::string saveFileName = saveDir.toStdString() + "/" + imageName + " bandpassed" + ".nrrd"; mitk::IOUtil::Save(image, saveFileName); } } // 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::PhotoacousticFilterService::BModeMethod::Abs, useGPU, m_UseLogfilter, m_ResampleSpacing); + image = m_FilterBank->ApplyBmodeFilter(image, mitk::PhotoacousticFilterService::BModeMethod::Abs, m_UseLogfilter, m_ResampleSpacing); else if (m_Controls.BModeMethod->currentText() == "Envelope Detection") - image = m_FilterBank->ApplyBmodeFilter(image, mitk::PhotoacousticFilterService::BModeMethod::EnvelopeDetection, useGPU, m_UseLogfilter, m_ResampleSpacing); + image = m_FilterBank->ApplyBmodeFilter(image, mitk::PhotoacousticFilterService::BModeMethod::EnvelopeDetection, m_UseLogfilter, m_ResampleSpacing); 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) { 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::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 << "Handle Beamforming results"; + MITK_INFO << "Handling results..."; auto newNode = mitk::DataNode::New(); newNode->SetData(image); - // name the new Data node - std::stringstream newNodeName; - - newNodeName << m_OldNodeName << " "; - - auto BFconfig = CreateBeamformingSettings(image); - - if (BFconfig->GetAlgorithm() == mitk::BeamformingSettings::BeamformingAlgorithm::DAS) - newNodeName << "DAS bf, "; - else if (BFconfig->GetAlgorithm() == mitk::BeamformingSettings::BeamformingAlgorithm::DMAS) - newNodeName << "DMAS bf, "; - - if (BFconfig->GetDelayCalculationMethod() == mitk::BeamformingSettings::DelayCalc::QuadApprox) - newNodeName << "q. delay"; - if (BFconfig->GetDelayCalculationMethod() == 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) { 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::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) { 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); 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."); 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) { 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 - BPLowPass) { QMessageBox Msgbox; Msgbox.setText("HighPass higher than LowPass, disabled both."); Msgbox.exec(); BPHighPass = 0; BPLowPass = 0; } thread->setConfig(BPHighPass, BPLowPass, m_Controls.BPFalloffLow->value(), m_Controls.BPFalloffHigh->value(), recordTime); 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()) { 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); float speedOfSound = m_Controls.SpeedOfSound->value(); // [m/s] std::stringstream frequency; 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) { // iterate all selected objects, adjust warning visibility foreach(mitk::DataNode::Pointer node, nodes) { if (node.IsNotNull() && dynamic_cast(node->GetData())) { 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.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(); } mitk::BeamformingSettings::Pointer PAImageProcessing::CreateBeamformingSettings(mitk::Image::Pointer image) { mitk::BeamformingSettings::BeamformingAlgorithm algorithm; if ("DAS" == m_Controls.BFAlgorithm->currentText()) algorithm = mitk::BeamformingSettings::BeamformingAlgorithm::DAS; else if ("DMAS" == m_Controls.BFAlgorithm->currentText()) algorithm = mitk::BeamformingSettings::BeamformingAlgorithm::DMAS; else if ("sDMAS" == m_Controls.BFAlgorithm->currentText()) algorithm = mitk::BeamformingSettings::BeamformingAlgorithm::sDMAS; mitk::BeamformingSettings::DelayCalc delay; if ("Quad. Approx." == m_Controls.DelayCalculation->currentText()) { delay = mitk::BeamformingSettings::DelayCalc::QuadApprox; } else if ("Spherical Wave" == m_Controls.DelayCalculation->currentText()) { delay = mitk::BeamformingSettings::DelayCalc::Spherical; } mitk::BeamformingSettings::Apodization apod; if ("Von Hann" == m_Controls.Apodization->currentText()) { apod = mitk::BeamformingSettings::Apodization::Hann; } else if ("Hamming" == m_Controls.Apodization->currentText()) { apod = mitk::BeamformingSettings::Apodization::Hamm; } else if ("Box" == m_Controls.Apodization->currentText()) { apod = mitk::BeamformingSettings::Apodization::Box; } 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 useBandpass = m_Controls.UseBP->isChecked(); bool useGPU = m_Controls.UseGPUBf->isChecked(); 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); } bool isPAImage; if ("US Image" == m_Controls.ImageType->currentText()) { isPAImage = false; } else if ("PA Image" == m_Controls.ImageType->currentText()) { isPAImage = true; } bool partial = m_Controls.Partial->isChecked(); unsigned int cropBoundLow = m_Controls.boundLow->value(); unsigned int cropBoundHigh = m_Controls.boundHigh->value(); unsigned int* cropBounds = new unsigned int[2]{ cropBoundLow, cropBoundHigh }; return mitk::BeamformingSettings::New(pitchInMeters, speedOfSound, timeSpacing, angle, isPAImage, samplesPerLine, reconstructionLines, 0, partial, cropBounds, image->GetDimensions(), useGPU, delay, apod, apodizatonArraySize, algorithm, useBandpass, 50, 50); } 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.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.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 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.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.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 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::function progressHandle = [this](int progress, std::string progressInfo) { emit updateProgress(progress, progressInfo); }; mitk::CastToFloatImageFilter::Pointer castToFloatImageFilter = mitk::CastToFloatImageFilter::New(); castToFloatImageFilter->SetInput(m_InputImage); castToFloatImageFilter->Update(); m_InputImage = castToFloatImageFilter->GetOutput(); mitk::IOUtil::Save(m_InputImage, "G:/castedImage.nrrd"); resultImageBuffer = m_FilterBank->ApplyBeamforming(m_InputImage, m_BFconfig, 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 result(resultImageBuffer, "_bf"); } 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; - - resultImage = m_FilterBank->ApplyBmodeFilter(m_InputImage, m_Method, m_UseGPU, m_UseLogfilter, m_ResampleSpacing); + mitk::Image::Pointer resultImage = m_FilterBank->ApplyBmodeFilter(m_InputImage, + m_Method, m_UseLogfilter, m_ResampleSpacing); - emit result(resultImage); + emit result(resultImage, "_bmode"); } 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, 0, 0); - emit result(resultImage); + 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); + 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 4894bdf768..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,248 +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 "ui_PAImageProcessingControls.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: static const std::string VIEW_ID; 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); + 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 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::Pointer m_BFconfig; mitk::Image::Pointer m_InputImage; int m_Cutoff; mitk::PhotoacousticFilterService::Pointer m_FilterBank; }; class BmodeThread : public QThread { Q_OBJECT void run() Q_DECL_OVERRIDE; signals: - void result(mitk::Image::Pointer); + void result(mitk::Image::Pointer, std::string nameExtension); 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; mitk::PhotoacousticFilterService::BModeMethod m_Method; bool m_UseLogfilter; double m_ResampleSpacing; bool m_UseGPU; 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::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::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::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::PhotoacousticFilterService::Pointer m_FilterBank; }; #endif // PAImageProcessing_h