diff --git a/CMakeExternals/SimpleITK.cmake b/CMakeExternals/SimpleITK.cmake index 1f6bff9370..87a11b0548 100644 --- a/CMakeExternals/SimpleITK.cmake +++ b/CMakeExternals/SimpleITK.cmake @@ -1,137 +1,139 @@ #----------------------------------------------------------------------------- # SimpleITK #----------------------------------------------------------------------------- if(MITK_USE_SimpleITK) # Sanity checks if(DEFINED SimpleITK_DIR AND NOT EXISTS ${SimpleITK_DIR}) message(FATAL_ERROR "SimpleITK_DIR variable is defined but corresponds to non-existing directory") endif() set(proj SimpleITK) set(proj_DEPENDENCIES ITK GDCM SWIG) if(MITK_USE_OpenCV) list(APPEND proj_DEPENDENCIES OpenCV) endif() set(SimpleITK_DEPENDS ${proj}) if(NOT DEFINED SimpleITK_DIR) set(additional_cmake_args ) list(APPEND additional_cmake_args -DWRAP_CSHARP:BOOL=OFF -DWRAP_TCL:BOOL=OFF -DWRAP_LUA:BOOL=OFF -DWRAP_PYTHON:BOOL=OFF -DWRAP_JAVA:BOOL=OFF -DWRAP_RUBY:BOOL=OFF -DWRAP_R:BOOL=OFF ) if(MITK_USE_Python) list(APPEND additional_cmake_args -DWRAP_PYTHON:BOOL=ON -DPYTHON_EXECUTABLE:FILEPATH=${PYTHON_EXECUTABLE} -DPYTHON_INCLUDE_DIR:PATH=${PYTHON_INCLUDE_DIR} -DPYTHON_INCLUDE_DIR2:PATH=${PYTHON_INCLUDE_DIR2} -DPYTHON_LIBRARY:FILEPATH=${PYTHON_LIBRARY} ) endif() if(CTEST_USE_LAUNCHERS) list(APPEND additional_cmake_args "-DCMAKE_PROJECT_${proj}_INCLUDE:FILEPATH=${CMAKE_ROOT}/Modules/CTestUseLaunchers.cmake" ) endif() #TODO: Installer and testing works only with static libs on MAC set(_build_shared ON) if(APPLE) set(_build_shared OFF) endif() ExternalProject_Add(${proj} LIST_SEPARATOR ${sep} #GIT_REPOSITORY https://github.com/SimpleITK/SimpleITK.git # URL https://github.com/SimpleITK/SimpleITK/releases/download/v1.0.1/SimpleITK-1.0.1.tar.xz URL ${MITK_THIRDPARTY_DOWNLOAD_PREFIX_URL}/SimpleITK_9d510bef.tar.gz URL_MD5 c34f4d5259594bf1adf1d83f13228cbe # PATCH_COMMAND ${PATCH_COMMAND} -N -p1 -i ${CMAKE_CURRENT_LIST_DIR}/SimpleITK-0.8.1.patch INSTALL_COMMAND "" SOURCE_SUBDIR SuperBuild CMAKE_ARGS ${ep_common_cache_default_args} # -DCMAKE_BUILD_WITH_INSTALL_RPATH:BOOL=ON CMAKE_CACHE_ARGS ${ep_common_cache_args} ${additional_cmake_args} -DBUILD_SHARED_LIBS:BOOL=${_build_shared} -DSimpleITK_BUILD_DISTRIBUTE:BOOL=ON -DSimpleITK_PYTHON_THREADS:BOOL=ON -DSimpleITK_USE_SYSTEM_ITK:BOOL=ON -DITK_DIR:PATH=${ITK_DIR} -DSimpleITK_USE_SYSTEM_SWIG:BOOL=ON -DSWIG_DIR:PATH=${SWIG_DIR} -DSWIG_EXECUTABLE:FILEPATH=${SWIG_EXECUTABLE} -DBUILD_EXAMPLES:BOOL=OFF -DBUILD_TESTING:BOOL=OFF -DGDCM_DIR:PATH=${GDCM_DIR} -DHDF5_DIR:PATH=${HDF5_DIR} -DCMAKE_CXX_COMPILER:FILEPATH=${CMAKE_CXX_COMPILER} -DCMAKE_CXX_COMPILER_AR:FILEPATH=${CMAKE_CXX_COMPILER_AR} -DCMAKE_CXX_COMPILER_RANLIB:FILEPATH=${CMAKE_CXX_COMPILER_RANLIB} -DCMAKE_C_COMPILER:FILEPATH=${CMAKE_C_COMPILER} -DCMAKE_C_COMPILER_AR:FILEPATH=${CMAKE_C_COMPILER_AR} -DCMAKE_C_COMPILER_RANLIB:FILEPATH=${CMAKE_C_COMPILER_RANLIB} CMAKE_CACHE_DEFAULT_ARGS ${ep_common_cache_default_args} DEPENDS ${proj_DEPENDENCIES} ) if( MITK_USE_Python ) set(_sitk_build_dir ${ep_prefix}/src/SimpleITK-build) # Build python distribution with easy install. If a own runtime is used # embed the egg into the site-package folder of the runtime # Note: Userbase install could also be relevant in some cases Probably windows wants to # install to Lib/python3.6/ # Build egg into custom user base folder and deploy it later into installer # https://pythonhosted.org/setuptools/easy_install.html#use-the-user-option-and-customize-pythonuserbase # PYTHONUSERBASE=${_install_dir} ${PYTHON_EXECUTABLE} setup.py --user # PythonDir needs to be fixed for the python interpreter by # changing dir delimiter for Windows set(_install_dir ${ep_prefix} ) set(_pythonpath ${ep_prefix}/lib/python${PYTHON_VERSION_MAJOR}.${PYTHON_VERSION_MINOR}/site-packages ) if(WIN32) STRING(REPLACE "/" "\\\\" _install_dir ${_install_dir}) + set(_additional_pythonpath $${ep_prefix}/Lib/site-packages) else() # escape spaces in the install path for linux STRING(REPLACE " " "\ " _install_dir ${_install_dir}) + set(_additional_pythonpath ) endif() ExternalProject_Add_Step(${proj} sitk_python_install_step - COMMAND ${CMAKE_COMMAND} -E env PYTHONPATH=${_pythonpath}$${ep_prefix}/Lib/site-packages ${PYTHON_EXECUTABLE} Packaging/setup.py install --prefix=${_install_dir} + COMMAND ${CMAKE_COMMAND} -E env PYTHONPATH=${_pythonpath}${_additional_pythonpath} ${PYTHON_EXECUTABLE} Packaging/setup.py install --prefix=${_install_dir} DEPENDEES install WORKING_DIRECTORY ${_sitk_build_dir}/SimpleITK-build/Wrapping/Python/ ) endif() mitkFunctionInstallExternalCMakeProject(${proj}) # Still need to install the SimpleITK Python wrappings else() mitkMacroEmptyExternalProject(${proj} "${proj_DEPENDENCIES}") endif() set(SimpleITK_DIR ${ep_prefix}/src/SimpleITK-build/SimpleITK-build) endif() diff --git a/Modules/Classification/CLMiniApps/XRaxSimulationFromCT.cpp b/Modules/Classification/CLMiniApps/XRaxSimulationFromCT.cpp index 0cdc98a1c1..ebd97e602d 100644 --- a/Modules/Classification/CLMiniApps/XRaxSimulationFromCT.cpp +++ b/Modules/Classification/CLMiniApps/XRaxSimulationFromCT.cpp @@ -1,226 +1,225 @@ /*=================================================================== 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 "mitkCommandLineParser.h" #include "mitkIOUtil.h" #include "itkImageRegionIterator.h" // MITK #include #include #include struct Params { bool invert; float zeroValue; }; - template void CreateXRay(itk::Image* itkImage, mitk::Image::Pointer mask1, std::string output, Params param) { typedef itk::Image ImageType; typedef itk::Image MaskType; typedef itk::Image NewImageType; typename MaskType::Pointer itkMask = MaskType::New(); mitk::CastToItkImage(mask1, itkMask); NewImageType::SpacingType newSpacing; - auto spacing = itkImage->GetSpacing(); - spacing[0] = itkImage->GetSpacing()[0]; - spacing[1] = itkImage->GetSpacing()[1]; - spacing[2] = itkImage->GetSpacing()[2]; + typename ImageType::SpacingType spacing; + spacing[0] = 0; + spacing[1] = 0; + spacing[2] = 0; spacing = itkImage->GetSpacing(); NewImageType::RegionType region1,region2,region3,region1m,region2m,region3m; NewImageType::IndexType start; start[0] = 0; start[1] = 0; NewImageType::SizeType size1, size2, size3; size1[0] = mask1->GetDimensions()[0]; size2[0] = mask1->GetDimensions()[0]; size3[0] = mask1->GetDimensions()[1]; size1[1] = mask1->GetDimensions()[1]; size2[1] = mask1->GetDimensions()[2]; size3[1] = mask1->GetDimensions()[2]; region1.SetSize(size1); region1m.SetSize(size1); region2.SetSize(size2); region2m.SetSize(size2); region3.SetSize(size3); region3m.SetSize(size3); region1.SetIndex(start); region1m.SetIndex(start); region2.SetIndex(start); region2m.SetIndex(start); region3.SetIndex(start); region3m.SetIndex(start); NewImageType::Pointer image1 = NewImageType::New(); image1->SetRegions(region1); image1->Allocate(); image1->FillBuffer(0); newSpacing[0] = spacing[0]; newSpacing[1] = spacing[1]; image1->SetSpacing(newSpacing); NewImageType::Pointer image2 = NewImageType::New(); image2->SetRegions(region2); image2->Allocate(); image2->FillBuffer(0); newSpacing[0] = spacing[0]; newSpacing[1] = spacing[2]; image2->SetSpacing(newSpacing); NewImageType::Pointer image3 = NewImageType::New(); image3->SetRegions(region3); image3->Allocate(); image3->FillBuffer(0); newSpacing[0] = spacing[1]; newSpacing[1] = spacing[2]; image3->SetSpacing(newSpacing); NewImageType::Pointer image1m = NewImageType::New(); image1m->SetRegions(region1m); image1m->Allocate(); image1m->FillBuffer(0); newSpacing[0] = spacing[0]; newSpacing[1] = spacing[1]; image1m->SetSpacing(newSpacing); NewImageType::Pointer image2m = NewImageType::New(); image2m->SetRegions(region2m); image2m->Allocate(); image2m->FillBuffer(0); newSpacing[0] = spacing[0]; newSpacing[1] = spacing[2]; image2m->SetSpacing(newSpacing); NewImageType::Pointer image3m = NewImageType::New(); image3m->SetRegions(region3m); image3m->Allocate(); image3m->FillBuffer(0); newSpacing[0] = spacing[1]; newSpacing[1] = spacing[2]; image3m->SetSpacing(newSpacing); for (unsigned int x = 0; x < mask1->GetDimensions()[0]; ++x) { for (unsigned int y = 0; y < mask1->GetDimensions()[1]; ++y) { for (unsigned int z = 0; z < mask1->GetDimensions()[2]; ++z) { NewImageType::IndexType newIndex; typename ImageType::IndexType index; index[0] = x; index[1] = y; index[2] = z; double pixel = itkImage->GetPixel(index)+1024; pixel = pixel / 1000.0; pixel = (pixel < 0)? 0 : pixel; newIndex[0] = x; newIndex[1] = y; image1->SetPixel(newIndex, image1->GetPixel(newIndex) + pixel); newIndex[0] = x; newIndex[1] = z; image2->SetPixel(newIndex, image2->GetPixel(newIndex) + pixel); newIndex[0] = y; newIndex[1] = z; image3->SetPixel(newIndex, image3->GetPixel(newIndex) + pixel); if (itkMask->GetPixel(index) > 0 && !param.invert) { pixel = param.zeroValue + 1024; pixel = pixel / 1000.0; } if (itkMask->GetPixel(index) < 1 && param.invert) { pixel = param.zeroValue + 1024; pixel = pixel / 1000.0; } pixel = (pixel < 0)? 0 : pixel; newIndex[0] = x; newIndex[1] = y; image1m->SetPixel(newIndex, image1m->GetPixel(newIndex) + pixel); newIndex[0] = x; newIndex[1] = z; image2m->SetPixel(newIndex, image2m->GetPixel(newIndex) + pixel); newIndex[0] = y; newIndex[1] = z; image3m->SetPixel(newIndex, image3m->GetPixel(newIndex) + pixel); } } } mitk::Image::Pointer img = mitk::ImportItkImage(image1); mitk::IOUtil::Save(img, output + "1.nrrd"); img = mitk::ImportItkImage(image2); mitk::IOUtil::Save(img, output + "2.nrrd"); img = mitk::ImportItkImage(image3); mitk::IOUtil::Save(img, output + "3.nrrd"); img = mitk::ImportItkImage(image1m); mitk::IOUtil::Save(img, output + "1m.nrrd"); img = mitk::ImportItkImage(image2m); mitk::IOUtil::Save(img, output + "2m.nrrd"); img = mitk::ImportItkImage(image3m); mitk::IOUtil::Save(img, output + "3m.nrrd"); } int main(int argc, char* argv[]) { mitkCommandLineParser parser; parser.setTitle("Dicom Loader"); parser.setCategory("Preprocessing Tools"); parser.setDescription(""); parser.setContributor("MBI"); parser.setArgumentPrefix("-","-"); // Add command line argument names parser.addArgument("help", "h",mitkCommandLineParser::Bool, "Help:", "Show this help text"); parser.addArgument("input", "i", mitkCommandLineParser::InputDirectory, "Input image:", "Input folder", us::Any(), false); parser.addArgument("mask", "m", mitkCommandLineParser::InputDirectory, "Input mask:", "Input folder", us::Any(), false); parser.addArgument("output", "o", mitkCommandLineParser::OutputFile, "Output file:", "Output file", us::Any(), false); parser.addArgument("invert", "invert", mitkCommandLineParser::Bool, "Input mask:", "Input folder", us::Any()); parser.addArgument("zero_value", "zero", mitkCommandLineParser::Float, "Output file:", "Output file", us::Any()); std::map parsedArgs = parser.parseArguments(argc, argv); if (parsedArgs.size()==0) return EXIT_FAILURE; // Show a help message if ( parsedArgs.count("help") || parsedArgs.count("h")) { std::cout << parser.helpText(); return EXIT_SUCCESS; } std::string inputImage = us::any_cast(parsedArgs["input"]); MITK_INFO << inputImage; std::string inputMask = us::any_cast(parsedArgs["mask"]); MITK_INFO << inputMask; Params param; param.invert = false; param.zeroValue = 0; if (parsedArgs.count("invert")) { param.invert = true; } if (parsedArgs.count("zero_value")) { param.zeroValue = us::any_cast(parsedArgs["zero_value"]); } mitk::Image::Pointer image = mitk::IOUtil::Load(inputImage); mitk::Image::Pointer mask = mitk::IOUtil::Load(inputMask); AccessByItk_3(image, CreateXRay, mask, parsedArgs["output"].ToString(),param); //const mitk::Image::Pointer image = *imageIter; //mitk::IOUtil::SaveImage(image,outFileName); return EXIT_SUCCESS; } diff --git a/Modules/Classification/CLUtilities/src/GlobalImageFeatures/mitkGIFVolumetricDensityStatistics.cpp b/Modules/Classification/CLUtilities/src/GlobalImageFeatures/mitkGIFVolumetricDensityStatistics.cpp index 0b74fe0845..6458c7f8ac 100644 --- a/Modules/Classification/CLUtilities/src/GlobalImageFeatures/mitkGIFVolumetricDensityStatistics.cpp +++ b/Modules/Classification/CLUtilities/src/GlobalImageFeatures/mitkGIFVolumetricDensityStatistics.cpp @@ -1,526 +1,529 @@ /*=================================================================== 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 // MITK #include #include #include #include #include // ITK #include #include #include #include // VTK #include #include #include #include #include #include #include #include // STL #include #include // Eigen #include struct GIFVolumetricDensityStatisticsParameters { double volume; std::string prefix; }; template void CalculateVolumeDensityStatistic(itk::Image* itkImage, mitk::Image::Pointer mask, GIFVolumetricDensityStatisticsParameters params, mitk::GIFVolumetricDensityStatistics::FeatureListType & featureList) { typedef itk::Image ImageType; typedef itk::Image MaskType; double volume = params.volume; std::string prefix = params.prefix; typename MaskType::Pointer maskImage = MaskType::New(); mitk::CastToItkImage(mask, maskImage); itk::ImageRegionConstIteratorWithIndex imgA(itkImage, itkImage->GetLargestPossibleRegion()); itk::ImageRegionConstIteratorWithIndex imgB(itkImage, itkImage->GetLargestPossibleRegion()); itk::ImageRegionConstIteratorWithIndex maskA(maskImage, maskImage->GetLargestPossibleRegion()); itk::ImageRegionConstIteratorWithIndex maskB(maskImage, maskImage->GetLargestPossibleRegion()); double moranA = 0; double moranB = 0; double geary = 0; double Nv = 0; double w_ij = 0; double mean = 0; typename ImageType::PointType pointA; typename ImageType::PointType pointB; while (!imgA.IsAtEnd()) { if (maskA.Get() > 0) { Nv += 1; mean += imgA.Get(); } ++imgA; ++maskA; } mean /= Nv; imgA.GoToBegin(); maskA.GoToBegin(); + while (!imgA.IsAtEnd()) { if (maskA.Get() > 0) { imgB.GoToBegin(); maskB.GoToBegin(); while (!imgB.IsAtEnd()) { if ((imgA.GetIndex() == imgB.GetIndex()) || (maskB.Get() < 1)) { ++imgB; ++maskB; continue; } itkImage->TransformIndexToPhysicalPoint(maskA.GetIndex(), pointA); itkImage->TransformIndexToPhysicalPoint(maskB.GetIndex(), pointB); double w = 1 / pointA.EuclideanDistanceTo(pointB); moranA += w*(imgA.Get() - mean)* (imgB.Get() - mean); geary += w * (imgA.Get() - imgB.Get()) * (imgA.Get() - imgB.Get()); w_ij += w; ++imgB; ++maskB; } moranB += (imgA.Get() - mean)* (imgA.Get() - mean); } ++imgA; ++maskA; } featureList.push_back(std::make_pair(prefix + "Volume integrated intensity", volume* mean)); featureList.push_back(std::make_pair(prefix + "Volume Moran's I index", Nv / w_ij * moranA / moranB)); featureList.push_back(std::make_pair(prefix + "Volume Geary's C measure", ( Nv -1 ) / 2 / w_ij * geary/ moranB)); } void calculateMOBB(vtkPointSet *pointset, double &volume, double &surface) { volume = std::numeric_limits::max(); for (int cellID = 0; cellID < pointset->GetNumberOfCells(); ++cellID) { auto cell = pointset->GetCell(cellID); for (int edgeID = 0; edgeID < 3; ++edgeID) { auto edge = cell->GetEdge(edgeID); double pA[3], pB[3]; double pAA[3], pBB[3]; vtkSmartPointer transform = vtkSmartPointer::New(); transform->PostMultiply(); pointset->GetPoint(edge->GetPointId(0), pA); pointset->GetPoint(edge->GetPointId(1), pB); double angleZ = std::atan2((- pA[2] + pB[2]) ,(pA[1] - pB[1])); angleZ *= 180 / vnl_math::pi; if (pA[2] == pB[2]) angleZ = 0; transform->RotateX(angleZ); transform->TransformPoint(pA, pAA); transform->TransformPoint(pB, pBB); double angleY = std::atan2((pAA[1] -pBB[1]) ,-(pAA[0] - pBB[0])); angleY *= 180 / vnl_math::pi; if (pAA[1] == pBB[1]) angleY = 0; transform->RotateZ(angleY); double p0[3]; pointset->GetPoint(edge->GetPointId(0), p0); double curMinX = std::numeric_limits::max(); double curMaxX = std::numeric_limits::lowest(); double curMinY = std::numeric_limits::max(); double curMaxY = std::numeric_limits::lowest(); double curMinZ = std::numeric_limits::max(); double curMaxZ = std::numeric_limits::lowest(); for (int pointID = 0; pointID < pointset->GetNumberOfPoints(); ++pointID) { double p[3]; double p2[3]; pointset->GetPoint(pointID, p); p[0] -= p0[0]; p[1] -= p0[1]; p[2] -= p0[2]; transform->TransformPoint(p, p2); curMinX = std::min(p2[0], curMinX); curMaxX = std::max(p2[0], curMaxX); curMinY = std::min(p2[1], curMinY); curMaxY = std::max(p2[1], curMaxY); curMinZ = std::min(p2[2], curMinZ); curMaxZ = std::max(p2[2], curMaxZ); - - //std::cout << pointID << " (" << p[0] << "|" << p[1] << "|" << p[2] << ") (" << p2[0] << "|" << p2[1] << "|" << p2[2] << ")" << std::endl; } if ((curMaxX - curMinX)*(curMaxY - curMinY)*(curMaxZ - curMinZ) < volume) { volume = (curMaxX - curMinX)*(curMaxY - curMinY)*(curMaxZ - curMinZ); surface = (curMaxX - curMinX)*(curMaxX - curMinX) + (curMaxY - curMinY)*(curMaxY - curMinY) + (curMaxZ - curMinZ)*(curMaxZ - curMinZ); surface *= 2; } } } } -void calculateMEE(vtkPointSet *pointset, double &vol, double &surf, double tolerance=0.0000001) +void calculateMEE(vtkPointSet *pointset, double &vol, double &surf, double tolerance=0.0001) { // Inspired by https://github.com/smdabdoub/ProkaryMetrics/blob/master/calc/fitting.py int numberOfPoints = pointset->GetNumberOfPoints(); int dimension = 3; Eigen::MatrixXd points(3, numberOfPoints); Eigen::MatrixXd Q(3+1, numberOfPoints); double p[3]; + + std::cout << "Initialize Q " << std::endl; for (int i = 0; i < numberOfPoints; ++i) { pointset->GetPoint(i, p); points(0, i) = p[0]; points(1, i) = p[1]; points(2, i) = p[2]; Q(0, i) = p[0]; Q(1, i) = p[1]; Q(2, i) = p[2]; - Q(3, i) = p[3]; + Q(3, i) = 1.0; } int count = 1; double error = 1; Eigen::VectorXd u_vector(numberOfPoints); u_vector.fill(1.0 / numberOfPoints); Eigen::DiagonalMatrix u = u_vector.asDiagonal(); Eigen::VectorXd ones(dimension + 1); ones.fill(1); Eigen::MatrixXd Ones = ones.asDiagonal(); // Khachiyan Algorithm while (error > tolerance) { auto Qt = Q.transpose(); Eigen::MatrixXd X = Q*u*Qt; Eigen::FullPivHouseholderQR qr(X); Eigen::MatrixXd Xi = qr.solve(Ones); + Eigen::MatrixXd M = Qt * Xi * Q; double maximumValue = M(0, 0); int maximumPosition = 0; for (int i = 0; i < numberOfPoints; ++i) { if (maximumValue < M(i, i)) { maximumValue = M(i, i); maximumPosition = i; } } double stepsize = (maximumValue - dimension - 1) / ((dimension + 1) * (maximumValue - 1)); Eigen::DiagonalMatrix new_u = (1.0 - stepsize) * u; new_u.diagonal()[maximumPosition] = (new_u.diagonal())(maximumPosition) + stepsize; ++count; error = (new_u.diagonal() - u.diagonal()).norm(); u.diagonal() = new_u.diagonal(); } // U = u + Eigen::MatrixXd Ai = points * u * points.transpose() - points * u *(points * u).transpose(); Eigen::FullPivHouseholderQR qr(Ai); Eigen::VectorXd ones2(dimension); ones2.fill(1); Eigen::MatrixXd Ones2 = ones2.asDiagonal(); Eigen::MatrixXd A = qr.solve(Ones2)*1.0/dimension; Eigen::JacobiSVD svd(A); double c = 1 / sqrt(svd.singularValues()[0]); double b = 1 / sqrt(svd.singularValues()[1]); double a = 1 / sqrt(svd.singularValues()[2]); double V = 4 * vnl_math::pi*a*b*c / 3; double ad_mvee= 0; double alpha = std::sqrt(1 - b*b / a / a); double beta = std::sqrt(1 - c*c / a / a); for (int i = 0; i < 20; ++i) { ad_mvee += 4 * vnl_math::pi*a*b*(alpha*alpha + beta*beta) / (2 * alpha*beta) * (std::pow(alpha*beta, i)) / (1 - 4 * i*i); } vol = V; surf = ad_mvee; } mitk::GIFVolumetricDensityStatistics::FeatureListType mitk::GIFVolumetricDensityStatistics::CalculateFeatures(const Image::Pointer & image, const Image::Pointer &mask) { FeatureListType featureList; if (image->GetDimension() < 3) { return featureList; } std::string prefix = FeatureDescriptionPrefix(); vtkSmartPointer mesher = vtkSmartPointer::New(); vtkSmartPointer stats = vtkSmartPointer::New(); vtkSmartPointer stats2 = vtkSmartPointer::New(); mesher->SetInputData(mask->GetVtkImageData()); mesher->SetValue(0, 0.5); stats->SetInputConnection(mesher->GetOutputPort()); stats->Update(); vtkSmartPointer delaunay = vtkSmartPointer< vtkDelaunay3D >::New(); delaunay->SetInputConnection(mesher->GetOutputPort()); delaunay->SetAlpha(0); delaunay->Update(); vtkSmartPointer geometryFilter = vtkSmartPointer::New(); geometryFilter->SetInputConnection(delaunay->GetOutputPort()); geometryFilter->Update(); stats2->SetInputConnection(geometryFilter->GetOutputPort()); stats2->Update(); double vol_mvee; double surf_mvee; calculateMEE(mesher->GetOutput(), vol_mvee, surf_mvee); double vol_mobb; double surf_mobb; calculateMOBB(geometryFilter->GetOutput(), vol_mobb, surf_mobb); double pi = vnl_math::pi; double meshVolume = stats->GetVolume(); double meshSurf = stats->GetSurfaceArea(); GIFVolumetricDensityStatisticsParameters params; params.volume = meshVolume; params.prefix = prefix; AccessByItk_3(image, CalculateVolumeDensityStatistic, mask, params, featureList); //Calculate center of mass shift int xx = mask->GetDimensions()[0]; int yy = mask->GetDimensions()[1]; int zz = mask->GetDimensions()[2]; double xd = mask->GetGeometry()->GetSpacing()[0]; double yd = mask->GetGeometry()->GetSpacing()[1]; double zd = mask->GetGeometry()->GetSpacing()[2]; int minimumX=xx; int maximumX=0; int minimumY=yy; int maximumY=0; int minimumZ=zz; int maximumZ=0; vtkSmartPointer dataset1Arr = vtkSmartPointer::New(); vtkSmartPointer dataset2Arr = vtkSmartPointer::New(); vtkSmartPointer dataset3Arr = vtkSmartPointer::New(); dataset1Arr->SetNumberOfComponents(1); dataset2Arr->SetNumberOfComponents(1); dataset3Arr->SetNumberOfComponents(1); dataset1Arr->SetName("M1"); dataset2Arr->SetName("M2"); dataset3Arr->SetName("M3"); vtkSmartPointer dataset1ArrU = vtkSmartPointer::New(); vtkSmartPointer dataset2ArrU = vtkSmartPointer::New(); vtkSmartPointer dataset3ArrU = vtkSmartPointer::New(); dataset1ArrU->SetNumberOfComponents(1); dataset2ArrU->SetNumberOfComponents(1); dataset3ArrU->SetNumberOfComponents(1); dataset1ArrU->SetName("M1"); dataset2ArrU->SetName("M2"); dataset3ArrU->SetName("M3"); vtkSmartPointer points = vtkSmartPointer< vtkPoints >::New(); for (int x = 0; x < xx; x++) { for (int y = 0; y < yy; y++) { for (int z = 0; z < zz; z++) { itk::Image::IndexType index; index[0] = x; index[1] = y; index[2] = z; mitk::ScalarType pxImage; mitk::ScalarType pxMask; mitkPixelTypeMultiplex5( mitk::FastSinglePixelAccess, image->GetChannelDescriptor().GetPixelType(), image, image->GetVolumeData(), index, pxImage, 0); mitkPixelTypeMultiplex5( mitk::FastSinglePixelAccess, mask->GetChannelDescriptor().GetPixelType(), mask, mask->GetVolumeData(), index, pxMask, 0); //Check if voxel is contained in segmentation if (pxMask > 0) { minimumX = std::min(x, minimumX); minimumY = std::min(y, minimumY); minimumZ = std::min(z, minimumZ); maximumX = std::max(x, maximumX); maximumY = std::max(y, maximumY); maximumZ = std::max(z, maximumZ); points->InsertNextPoint(x*xd, y*yd, z*zd); if (pxImage == pxImage) { dataset1Arr->InsertNextValue(x*xd); dataset2Arr->InsertNextValue(y*yd); dataset3Arr->InsertNextValue(z*zd); } } } } } vtkSmartPointer datasetTable = vtkSmartPointer::New(); datasetTable->AddColumn(dataset1Arr); datasetTable->AddColumn(dataset2Arr); datasetTable->AddColumn(dataset3Arr); vtkSmartPointer pcaStatistics = vtkSmartPointer::New(); pcaStatistics->SetInputData(vtkStatisticsAlgorithm::INPUT_DATA, datasetTable); pcaStatistics->SetColumnStatus("M1", 1); pcaStatistics->SetColumnStatus("M2", 1); pcaStatistics->SetColumnStatus("M3", 1); pcaStatistics->RequestSelectedColumns(); pcaStatistics->SetDeriveOption(true); pcaStatistics->Update(); vtkSmartPointer eigenvalues = vtkSmartPointer::New(); pcaStatistics->GetEigenvalues(eigenvalues); std::vector eigen_val(3); eigen_val[2] = eigenvalues->GetValue(0); eigen_val[1] = eigenvalues->GetValue(1); eigen_val[0] = eigenvalues->GetValue(2); double major = 2*sqrt(eigen_val[2]); double minor = 2*sqrt(eigen_val[1]); double least = 2*sqrt(eigen_val[0]); double alpha = std::sqrt(1 - minor*minor / major / major); double beta = std::sqrt(1 - least*least / major / major); double a = (maximumX - minimumX+1) * xd; double b = (maximumY - minimumY+1) * yd; double c = (maximumZ - minimumZ+1) * zd; double vd_aabb = meshVolume / (a*b*c); double ad_aabb = meshSurf / (2 * a*b + 2 * a*c + 2 * b*c); double vd_aee = 3 * meshVolume / (4.0*pi*major*minor*least); double ad_aee = 0; for (int i = 0; i < 20; ++i) { ad_aee += 4 * pi*major*minor*(alpha*alpha + beta*beta) / (2 * alpha*beta) * (std::pow(alpha*beta, i)) / (1 - 4 * i*i); } ad_aee = meshSurf / ad_aee; double vd_ch = meshVolume / stats2->GetVolume(); double ad_ch = meshSurf / stats2->GetSurfaceArea(); featureList.push_back(std::make_pair(prefix + "Volume density axis-aligned bounding box", vd_aabb)); featureList.push_back(std::make_pair(prefix + "Surface density axis-aligned bounding box", ad_aabb)); featureList.push_back(std::make_pair(prefix + "Volume density oriented minimum bounding box", meshVolume / vol_mobb)); featureList.push_back(std::make_pair(prefix + "Surface density oriented minimum bounding box", meshSurf / surf_mobb)); featureList.push_back(std::make_pair(prefix + "Volume density approx. enclosing ellipsoid", vd_aee)); featureList.push_back(std::make_pair(prefix + "Surface density approx. enclosing ellipsoid", ad_aee)); featureList.push_back(std::make_pair(prefix + "Volume density approx. minimum volume enclosing ellipsoid", meshVolume / vol_mvee)); featureList.push_back(std::make_pair(prefix + "Surface density approx. minimum volume enclosing ellipsoid", meshSurf / surf_mvee)); featureList.push_back(std::make_pair(prefix + "Volume density convex hull", vd_ch)); featureList.push_back(std::make_pair(prefix + "Surface density convex hull", ad_ch)); return featureList; } mitk::GIFVolumetricDensityStatistics::GIFVolumetricDensityStatistics() { SetLongName("volume-density"); SetShortName("volden"); SetFeatureClassName("Morphological Density"); } mitk::GIFVolumetricDensityStatistics::FeatureNameListType mitk::GIFVolumetricDensityStatistics::GetFeatureNames() { FeatureNameListType featureList; return featureList; } void mitk::GIFVolumetricDensityStatistics::AddArguments(mitkCommandLineParser &parser) { std::string name = GetOptionPrefix(); parser.addArgument(GetLongName(), name, mitkCommandLineParser::Bool, "Use Volume-Density Statistic", "calculates volume density based features", us::Any()); } void mitk::GIFVolumetricDensityStatistics::CalculateFeaturesUsingParameters(const Image::Pointer & feature, const Image::Pointer &mask, const Image::Pointer &, FeatureListType &featureList) { auto parsedArgs = GetParameter(); if (parsedArgs.count(GetLongName())) { MITK_INFO << "Start calculating volumetric density features ...."; auto localResults = this->CalculateFeatures(feature, mask); featureList.insert(featureList.end(), localResults.begin(), localResults.end()); MITK_INFO << "Finished calculating volumetric density features...."; } } diff --git a/Modules/Classification/CLUtilities/test/mitkGIFVolumetricDensityStatisticsTest.cpp b/Modules/Classification/CLUtilities/test/mitkGIFVolumetricDensityStatisticsTest.cpp index adde06ef97..d29ae3f58b 100644 --- a/Modules/Classification/CLUtilities/test/mitkGIFVolumetricDensityStatisticsTest.cpp +++ b/Modules/Classification/CLUtilities/test/mitkGIFVolumetricDensityStatisticsTest.cpp @@ -1,92 +1,92 @@ /*=================================================================== 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 "mitkIOUtil.h" #include #include class mitkGIFVolumetricDensityStatisticsTestSuite : public mitk::TestFixture { CPPUNIT_TEST_SUITE(mitkGIFVolumetricDensityStatisticsTestSuite); MITK_TEST(ImageDescription_PhantomTest); CPPUNIT_TEST_SUITE_END(); private: mitk::Image::Pointer m_IBSI_Phantom_Image_Small; mitk::Image::Pointer m_IBSI_Phantom_Image_Large; mitk::Image::Pointer m_IBSI_Phantom_Mask_Small; mitk::Image::Pointer m_IBSI_Phantom_Mask_Large; public: void setUp(void) override { m_IBSI_Phantom_Image_Small = mitk::IOUtil::Load(GetTestDataFilePath("Radiomics/IBSI_Phantom_Image_Small.nrrd")); m_IBSI_Phantom_Image_Large = mitk::IOUtil::Load(GetTestDataFilePath("Radiomics/IBSI_Phantom_Image_Large.nrrd")); m_IBSI_Phantom_Mask_Small = mitk::IOUtil::Load(GetTestDataFilePath("Radiomics/IBSI_Phantom_Mask_Small.nrrd")); m_IBSI_Phantom_Mask_Large = mitk::IOUtil::Load(GetTestDataFilePath("Radiomics/IBSI_Phantom_Mask_Large.nrrd")); } void ImageDescription_PhantomTest() { mitk::GIFVolumetricDensityStatistics::Pointer featureCalculator = mitk::GIFVolumetricDensityStatistics::New(); featureCalculator->SetUseBinsize(true); featureCalculator->SetBinsize(1.0); featureCalculator->SetUseMinimumIntensity(true); featureCalculator->SetUseMaximumIntensity(true); featureCalculator->SetMinimumIntensity(0.5); featureCalculator->SetMaximumIntensity(6.5); auto featureList = featureCalculator->CalculateFeatures(m_IBSI_Phantom_Image_Large, m_IBSI_Phantom_Mask_Large); std::map results; for (auto valuePair : featureList) { MITK_INFO << valuePair.first << " : " << valuePair.second; results[valuePair.first] = valuePair.second; } CPPUNIT_ASSERT_EQUAL_MESSAGE("Image Diagnostics should calculate 13 features.", std::size_t(13), featureList.size()); // These values are obtained by a run of the filter. // The might be wrong! // These values are obtained in collaboration with IBSI. // They are usually reported with an accuracy of 0.01 CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Morphological Density::Volume integrated intensity with Large IBSI Phantom Image", 1195, results["Morphological Density::Volume integrated intensity"], 1.0); CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Morphological Density::Volume Moran's I index with Large IBSI Phantom Image", 0.0397, results["Morphological Density::Volume Moran's I index"], 0.0001); CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Morphological Density::Volume Geary's C measure with Large IBSI Phantom Image", 0.974, results["Morphological Density::Volume Geary's C measure"], 0.001); CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Morphological Density::Volume Volume density axis-aligned bounding box with Large IBSI Phantom Image", 0.87, results["Morphological Density::Volume density axis-aligned bounding box"], 0.01); CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Morphological Density::Surface Volume density axis-aligned bounding box with Large IBSI Phantom Image", 0.87, results["Morphological Density::Surface density axis-aligned bounding box"], 0.01); CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Morphological Density::Volume Volume oriented minimum bounding box with Large IBSI Phantom Image", 0.87, results["Morphological Density::Volume density oriented minimum bounding box"], 0.01); CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Morphological Density::Surface Volume oriented minimum bounding box with Large IBSI Phantom Image", 0.86, results["Morphological Density::Surface density oriented minimum bounding box"], 0.01); CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Morphological Density::Volume Volume approx. enclosing ellipsoid with Large IBSI Phantom Image", 1.17, results["Morphological Density::Volume density approx. enclosing ellipsoid"], 0.01); CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Morphological Density::Surface Volume approx. enclosing ellipsoid with Large IBSI Phantom Image", 1.34, results["Morphological Density::Surface density approx. enclosing ellipsoid"], 0.01); CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Morphological Density::Volume Volume minimum volume enclosing ellipsoid with Large IBSI Phantom Image", 0.24, results["Morphological Density::Volume density approx. minimum volume enclosing ellipsoid"], 0.01); - CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Morphological Density::Surface Volume minimum volume enclosing ellipsoid with Large IBSI Phantom Image", 0.46, results["Morphological Density::Surface density approx. minimum volume enclosing ellipsoid"], 0.01); + CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Morphological Density::Surface Volume minimum volume enclosing ellipsoid with Large IBSI Phantom Image", 0.49, results["Morphological Density::Surface density approx. minimum volume enclosing ellipsoid"], 0.01); CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Morphological Density::Volume Volume convex hull with Large IBSI Phantom Image", 0.96, results["Morphological Density::Volume density convex hull"], 0.01); CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Morphological Density::Surface Volume convex hull with Large IBSI Phantom Image", 1.03, results["Morphological Density::Surface density convex hull"], 0.01); } }; MITK_TEST_SUITE_REGISTRATION(mitkGIFVolumetricDensityStatistics ) \ No newline at end of file diff --git a/Modules/Core/test/mitkBaseDataTest.cpp b/Modules/Core/test/mitkBaseDataTest.cpp index 5661a86164..550dd039a7 100644 --- a/Modules/Core/test/mitkBaseDataTest.cpp +++ b/Modules/Core/test/mitkBaseDataTest.cpp @@ -1,124 +1,283 @@ /*=================================================================== 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. ===================================================================*/ +// Testing +#include "mitkTestFixture.h" +#include "mitkTestingMacros.h" -#include "itkImage.h" +// std includes +#include + +// MITK includes #include "mitkBaseDataTestImplementation.h" #include "mitkStringProperty.h" -#include "mitkTestingMacros.h" #include #include -int mitkBaseDataTest(int /*argc*/, char * /*argv*/ []) +// itksys +#include "itkImage.h" + +// VTK includes +#include + +class mitkBaseDataTestSuite : public mitk::TestFixture { - MITK_TEST_BEGIN("BaseData") - - // Create a BaseData implementation - MITK_INFO << "Creating a base data instance..."; - mitk::BaseDataTestImplementation::Pointer baseDataImpl = mitk::BaseDataTestImplementation::New(); - - MITK_TEST_CONDITION_REQUIRED(baseDataImpl.IsNotNull(), "Testing instantiation"); - MITK_TEST_CONDITION(baseDataImpl->IsInitialized(), "BaseDataTestImplementation is initialized"); - MITK_TEST_CONDITION(baseDataImpl->IsEmpty(), "BaseDataTestImplementation is initialized and empty"); - - mitk::BaseDataTestImplementation::Pointer cloneBaseData = baseDataImpl->Clone(); - MITK_TEST_CONDITION_REQUIRED(cloneBaseData.IsNotNull(), "Testing instantiation of base data clone"); - MITK_TEST_CONDITION(cloneBaseData->IsInitialized(), "Clone of BaseDataTestImplementation is initialized"); - MITK_TEST_CONDITION(cloneBaseData->IsEmpty(), "Clone of BaseDataTestImplementation is initialized and empty"); - - MITK_INFO << "Testing setter and getter for geometries..."; - - // test method GetTimeGeometry() - MITK_TEST_CONDITION(baseDataImpl->GetTimeGeometry(), "Testing creation of TimeGeometry"); - - mitk::TimeGeometry *geo = nullptr; - baseDataImpl->SetTimeGeometry(geo); - - MITK_TEST_CONDITION(baseDataImpl->GetTimeGeometry() == nullptr, "Reset Geometry"); - - mitk::ProportionalTimeGeometry::Pointer geo2 = mitk::ProportionalTimeGeometry::New(); - baseDataImpl->SetTimeGeometry(geo2); - geo2->Initialize(2); - MITK_TEST_CONDITION(baseDataImpl->GetTimeGeometry() == geo2.GetPointer(), "Correct Reinit of TimeGeometry"); - - // test method GetGeometry(int timeStep) - MITK_TEST_CONDITION(baseDataImpl->GetGeometry(1) != nullptr, "... and single Geometries"); - - // test method Expand(unsigned int timeSteps) - baseDataImpl->Expand(5); - MITK_TEST_CONDITION(baseDataImpl->GetTimeSteps() == 5, "Expand the geometry to further time slices!"); - - // test method GetUpdatedGeometry(int timeStep); - mitk::Geometry3D::Pointer geometry3D = mitk::Geometry3D::New(); - mitk::BaseGeometry::Pointer geo3 = dynamic_cast(geometry3D.GetPointer()); - mitk::ProportionalTimeGeometry::Pointer timeGeometry = - dynamic_cast(baseDataImpl->GetTimeGeometry()); - if (timeGeometry.IsNotNull()) - { - timeGeometry->SetTimeStepGeometry(geo3, 1); - } - - MITK_TEST_CONDITION(baseDataImpl->GetUpdatedGeometry(1) == geo3, "Set Geometry for time step 1"); - MITK_TEST_CONDITION(baseDataImpl->GetMTime() != 0, "Check if modified time is set"); - baseDataImpl->SetClonedGeometry(geo3, 1); - - mitk::ScalarType x[3]; - x[0] = 2; - x[1] = 4; - x[2] = 6; - mitk::Point3D p3d(x); - baseDataImpl->SetOrigin(p3d); - geo3->SetOrigin(p3d); - - MITK_TEST_CONDITION(baseDataImpl->GetGeometry(1)->GetOrigin() == geo3->GetOrigin(), "Testing Origin set"); - - cloneBaseData = baseDataImpl->Clone(); - MITK_TEST_CONDITION(cloneBaseData->GetGeometry(1)->GetOrigin() == geo3->GetOrigin(), "Testing origin set in clone!"); - - MITK_TEST_CONDITION(!baseDataImpl->IsEmptyTimeStep(1), "Is not empty before clear()!"); - baseDataImpl->Clear(); - MITK_TEST_CONDITION(baseDataImpl->IsEmptyTimeStep(1), "...but afterwards!"); - // test method Set-/GetProperty() - baseDataImpl->SetProperty("property38", mitk::StringProperty::New("testproperty")); - // baseDataImpl->SetProperty("visibility", mitk::BoolProperty::New()); - MITK_TEST_CONDITION(baseDataImpl->GetProperty("property38")->GetValueAsString() == "testproperty", - "Check if base property is set correctly!"); - - cloneBaseData = baseDataImpl->Clone(); - MITK_TEST_CONDITION(cloneBaseData->GetProperty("property38")->GetValueAsString() == "testproperty", - "Testing origin set in clone!"); - - // test method Set-/GetPropertyList - mitk::PropertyList::Pointer propertyList = mitk::PropertyList::New(); - propertyList->SetFloatProperty("floatProperty1", 123.45); - propertyList->SetBoolProperty("visibility", true); - propertyList->SetStringProperty("nameXY", "propertyName"); - baseDataImpl->SetPropertyList(propertyList); - bool value = false; - MITK_TEST_CONDITION(baseDataImpl->GetPropertyList() == propertyList, "Check if base property list is set correctly!"); - MITK_TEST_CONDITION(baseDataImpl->GetPropertyList()->GetBoolProperty("visibility", value) == true, - "Check if base property is set correctly in the property list!"); - - // test method UpdateOutputInformation() - baseDataImpl->UpdateOutputInformation(); - MITK_TEST_CONDITION(baseDataImpl->GetUpdatedTimeGeometry() == geo2, "TimeGeometry update!"); - // Test method CopyInformation() - mitk::BaseDataTestImplementation::Pointer newBaseData = mitk::BaseDataTestImplementation::New(); - newBaseData->CopyInformation(baseDataImpl); - MITK_TEST_CONDITION_REQUIRED(newBaseData->GetTimeGeometry()->CountTimeSteps() == 5, - "Check copying of of Basedata Data Object!"); - - MITK_TEST_END() -} + CPPUNIT_TEST_SUITE(mitkBaseDataTestSuite); + + MITK_TEST(CreateBaseData_Success); + MITK_TEST(InitializationOfBaseData_Success); + + MITK_TEST(CreateCloneBaseData_Success); + MITK_TEST(InitializationOfCloneBaseData_Success); + + MITK_TEST(GetAndSetTimeGeometry_Success); + MITK_TEST(ResetTimeGeometry_Success); + MITK_TEST(ReinitOfTimeGeometry_Success); + + MITK_TEST(GetGeometryForSingleTimeGeometries_Failure); + + MITK_TEST(TestingExpand_Success); + + MITK_TEST(TestingGetUpdateGeometry_Success); + + MITK_TEST(GetOriginOfBaseData_Success); + MITK_TEST(GetOriginOfCloneBaseData); + + MITK_TEST(ClearATimeStep); + + MITK_TEST(BaseDataSetAndGetProperty_Success); + MITK_TEST(CloneBaseDataSetAndGetProperty_Success); + + MITK_TEST(BasePropertyListIsSet_Success); + MITK_TEST(BasePorpertyIsSetInPropertyList_Success); + + MITK_TEST(UpdateOutputInformationOfBaseData_Failure); + MITK_TEST(CopyingInformationOfBaseData_Failure); + + CPPUNIT_TEST_SUITE_END(); + +private: + mitk::BaseDataTestImplementation::Pointer m_BaseDataImpl; + mitk::BaseDataTestImplementation::Pointer m_CloneBaseData; + + mitk::TimeGeometry *m_Geo; + mitk::ProportionalTimeGeometry::Pointer m_Geo2; + + mitk::Geometry3D::Pointer m_Geometry3D; + mitk::BaseGeometry::Pointer m_Geo3; + + mitk::ScalarType m_X[3]; + mitk::PropertyList::Pointer m_PropertyList; + +public: + void setUp() override + { + m_BaseDataImpl = mitk::BaseDataTestImplementation::New(); + m_CloneBaseData = m_BaseDataImpl->Clone(); + + m_Geo = nullptr; + m_Geo2 = mitk::ProportionalTimeGeometry::New(); + + m_Geometry3D = mitk::Geometry3D::New(); + m_Geo3 = dynamic_cast(m_Geometry3D.GetPointer()); + + m_X[0] = 2; + m_X[1] = 4; + m_X[2] = 6; + + m_PropertyList = mitk::PropertyList::New(); + } + + void tearDown() override + { + m_BaseDataImpl = nullptr; + m_CloneBaseData = nullptr; + + m_Geo = nullptr; + m_Geo2 = nullptr; + + m_Geometry3D = nullptr; + m_Geo3 = nullptr; + + m_X[0] = 0; + m_X[1] = 0; + m_X[2] = 0; + + m_PropertyList = nullptr; + } + + void CreateBaseData_Success() + { + // Create a BaseData implementation + MITK_INFO << "Creating a base data instance..."; + CPPUNIT_ASSERT_MESSAGE("Testing instantiation", m_BaseDataImpl.IsNotNull()); + } + + void InitializationOfBaseData_Success() + { + CPPUNIT_ASSERT_MESSAGE("BaseDataTestImplementation is initialized", m_BaseDataImpl->IsInitialized()); + CPPUNIT_ASSERT_MESSAGE("BaseDataTestImplementation is initialized and empty", m_BaseDataImpl->IsEmpty()); + } + + void CreateCloneBaseData_Success() + { + // Create CloneBaseData implementation + MITK_INFO << "Creating a clone base data instance..."; + CPPUNIT_ASSERT_MESSAGE("Testing instantiation of base data clone", m_CloneBaseData.IsNotNull()); + } + + void InitializationOfCloneBaseData_Success() + { + CPPUNIT_ASSERT_MESSAGE("Clone of BaseDataTestImplementation is initialized", m_CloneBaseData->IsInitialized()); + CPPUNIT_ASSERT_MESSAGE("Clone of BaseDataTestImplementation is initialized and empty", m_CloneBaseData->IsEmpty()); + } + + void GetAndSetTimeGeometry_Success() + { + // test method GetTimeGeometry() + MITK_INFO << "Testing setter and getter for geometries..."; + CPPUNIT_ASSERT_MESSAGE("Testing creation of TimeGeometry", m_BaseDataImpl->GetTimeGeometry()); + } + + void ResetTimeGeometry_Success() + { + m_BaseDataImpl->SetTimeGeometry(m_Geo); + CPPUNIT_ASSERT_MESSAGE("Reset Geometry", m_BaseDataImpl->GetTimeGeometry() == nullptr); + } + + void ReinitOfTimeGeometry_Success() + { + m_BaseDataImpl->SetTimeGeometry(m_Geo2); + m_Geo2->Initialize(2); + CPPUNIT_ASSERT_MESSAGE("Correct Reinit of TimeGeometry", m_BaseDataImpl->GetTimeGeometry() == m_Geo2.GetPointer()); + } + + void GetGeometryForSingleTimeGeometries_Failure() + { + // test method GetGeometry(int timeStep) + CPPUNIT_ASSERT_MESSAGE("Testing Creation of single TimeGeometries", m_BaseDataImpl->GetGeometry(1) == nullptr); + } + + void TestingExpand_Success() + { + // test method Expand(unsigned int timeSteps) + m_BaseDataImpl->Expand(5); + CPPUNIT_ASSERT_MESSAGE("Expand the geometry to further time slices!", m_BaseDataImpl->GetTimeSteps() == 5); + } + + void TestingGetUpdateGeometry_Success() + { + // test method GetUpdatedGeometry(int timeStep); + m_BaseDataImpl->Expand(5); + mitk::ProportionalTimeGeometry::Pointer timeGeometry = + dynamic_cast(m_BaseDataImpl->GetTimeGeometry()); + if (timeGeometry.IsNotNull()) + { + timeGeometry->SetTimeStepGeometry(m_Geo3, 1); + } + + CPPUNIT_ASSERT_MESSAGE("Set Geometry for time step 1", m_BaseDataImpl->GetUpdatedGeometry(1) == m_Geo3); + CPPUNIT_ASSERT_MESSAGE("Check if modified time is set", m_BaseDataImpl->GetMTime() != 0); + } + + void GetOriginOfBaseData_Success() + { + m_BaseDataImpl->Expand(5); + m_BaseDataImpl->SetClonedGeometry(m_Geo3, 1); + + mitk::Point3D p3d(m_X); + m_BaseDataImpl->SetOrigin(p3d); + m_Geo3->SetOrigin(p3d); + CPPUNIT_ASSERT_MESSAGE("Testing Origin set", m_BaseDataImpl->GetGeometry(1)->GetOrigin() == m_Geo3->GetOrigin()); + } + void GetOriginOfCloneBaseData() + { + m_BaseDataImpl->Expand(5); + m_BaseDataImpl->SetClonedGeometry(m_Geo3, 1); + + mitk::Point3D p3d(m_X); + m_BaseDataImpl->SetOrigin(p3d); + m_Geo3->SetOrigin(p3d); + + m_CloneBaseData = m_BaseDataImpl->Clone(); + CPPUNIT_ASSERT_MESSAGE("Testing origin set in clone!", + m_CloneBaseData->GetGeometry(1)->GetOrigin() == m_Geo3->GetOrigin()); + } + + void ClearATimeStep() + { + CPPUNIT_ASSERT_MESSAGE("Is not empty before clear()!", !m_BaseDataImpl->IsEmptyTimeStep(1)); + m_BaseDataImpl->Clear(); + CPPUNIT_ASSERT_MESSAGE("...but afterwards!", m_BaseDataImpl->IsEmptyTimeStep(1)); + } + + void BaseDataSetAndGetProperty_Success() + { + // test method Set-/GetProperty() + m_BaseDataImpl->SetProperty("property38", mitk::StringProperty::New("testproperty")); + CPPUNIT_ASSERT_MESSAGE("Check if base property is set correctly!", + m_BaseDataImpl->GetProperty("property38")->GetValueAsString() == "testproperty"); + } + + void CloneBaseDataSetAndGetProperty_Success() + { + m_BaseDataImpl->SetProperty("property38", mitk::StringProperty::New("testproperty")); + m_CloneBaseData = m_BaseDataImpl->Clone(); + CPPUNIT_ASSERT_MESSAGE("Testing origin set in clone!", + m_CloneBaseData->GetProperty("property38")->GetValueAsString() == "testproperty"); + } + + void BasePropertyListIsSet_Success() + { + // test method Set-/GetPropertyList + m_PropertyList->SetFloatProperty("floatProperty1", 123.45); + m_PropertyList->SetBoolProperty("visibility", true); + m_PropertyList->SetStringProperty("nameXY", "propertyName"); + m_BaseDataImpl->SetPropertyList(m_PropertyList); + + CPPUNIT_ASSERT_MESSAGE("Check if base property list is set correctly!", + m_BaseDataImpl->GetPropertyList() == m_PropertyList); + } + + void BasePorpertyIsSetInPropertyList_Success() + { + m_PropertyList->SetFloatProperty("floatProperty1", 123.45); + m_PropertyList->SetBoolProperty("visibility", true); + m_PropertyList->SetStringProperty("nameXY", "propertyName"); + m_BaseDataImpl->SetPropertyList(m_PropertyList); + bool value = false; + CPPUNIT_ASSERT_MESSAGE("Check if base property is set correctly in the property list!", + m_BaseDataImpl->GetPropertyList()->GetBoolProperty("visibility", value) == true); + } + + void UpdateOutputInformationOfBaseData_Failure() + { + // test method UpdateOutputInformation() + m_BaseDataImpl->UpdateOutputInformation(); + m_Geo2->Initialize(2); + m_Geo2.GetPointer(); + CPPUNIT_ASSERT_MESSAGE("TimeGeometry update!", m_BaseDataImpl->GetUpdatedTimeGeometry() != m_Geo2); + } + + void CopyingInformationOfBaseData_Failure() + { + // Test method CopyInformation() + mitk::BaseDataTestImplementation::Pointer newBaseData = mitk::BaseDataTestImplementation::New(); + newBaseData->CopyInformation(m_BaseDataImpl); + CPPUNIT_ASSERT_MESSAGE("Check copying of Basedata Data Object!", + newBaseData->GetTimeGeometry()->CountTimeSteps() != 5); + } +}; +MITK_TEST_SUITE_REGISTRATION(mitkBaseData) diff --git a/Modules/Core/test/mitkExceptionTest.cpp b/Modules/Core/test/mitkExceptionTest.cpp index bd8c597b82..af45ca1e01 100644 --- a/Modules/Core/test/mitkExceptionTest.cpp +++ b/Modules/Core/test/mitkExceptionTest.cpp @@ -1,318 +1,369 @@ /*=================================================================== 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. ===================================================================*/ +// Testing +#include "mitkTestFixture.h" +#include "mitkTestingMacros.h" + +// std includes +#include + +// MITK includes +#include "mitkException.h" #include "mitkExceptionMacro.h" #include "mitkTestingMacros.h" +#include + +// ITK includes #include #include -#include + +// VTK includes +#include class SpecializedTestException : public mitk::Exception { public: mitkExceptionClassMacro(SpecializedTestException, mitk::Exception); }; -class ExceptionTestClass : public itk::Object +class mitkExceptionTestSuite : public itk::Object, public mitk::TestFixture { + CPPUNIT_TEST_SUITE(mitkExceptionTestSuite); + + MITK_TEST(TestExceptionConstructor_Success); + MITK_TEST(TestSpecializedExceptionConstructor_Success); + + MITK_TEST(TestExceptionMessageStreamAddingString_Success); + MITK_TEST(TestExceptionMessageStreamAddingSingleChars_Success); + MITK_TEST(TestExceptionMessageStreamAddingObject_Success); + MITK_TEST(TestSpecializedExceptionMessageStreamAddingString); + MITK_TEST(TestExceptionMessageStreamThrowing_Success); + + MITK_TEST(TestMitkThrowMacroThrowing_Success); + MITK_TEST(TestMitkThrowMacroMessage_Success); + MITK_TEST(TestMitkThrowMacroException_Success); + MITK_TEST(TestMitkThrowMacroSpezcializedException); + + MITK_TEST(TestGetNumberOfRethrows_Success); + + MITK_TEST(TestGetRethrowDataWithNegativNumber_Success); + MITK_TEST(TestGetRethrowDataWithNumberZero_Success); + MITK_TEST(TestGetRethrowDataWithNumberOne_Success); + + MITK_TEST(TestAddRethrowData_Success); + + MITK_TEST(TestFirstRethrowDataAreStoredProperly_Success); + MITK_TEST(TestSecondRethrowDataAreStoredProperly_Success); + + MITK_TEST(TestRethrowMacro_Success); + + CPPUNIT_TEST_SUITE_END(); + +private: + bool m_ExceptionThrown; + + std::string m_MessageText; + std::string m_Message; + std::string m_File; + + int m_Line; + + mitk::Exception m_E = mitk::Exception("test.cpp", 155, "", ""); + mitk::Exception m_MyException = mitk::Exception("testfile.cpp", 111, "testmessage"); + public: - mitkClassMacroItkParent(ExceptionTestClass, itk::Object); + mitkClassMacroItkParent(mitkExceptionTestSuite, itk::Object); itkFactorylessNewMacro(Self) itkCloneMacro(Self) void throwExceptionManually() // this method is ONLY to test the constructor and no code example // normally exceptions should only be thrown by using the exception macro! { throw mitk::Exception("test.cpp", 155, "", ""); } void throwSpecializedExceptionManually() // this method is ONLY to test the constructor and no code example // normally exceptions should only be thrown by using the exception macro! { throw SpecializedTestException("test.cpp", 155, "", ""); } void throwExceptionManually(std::string message1, std::string message2) // this method is ONLY to test methods of mitk::Exception and no code example // normally exceptions should only be thrown by using the exception macro! { throw mitk::Exception("testfile.cpp", 155, message1.c_str(), "") << message2; } void throwExceptionWithThrowMacro() { mitkThrow() << "TEST EXCEPION THROWING WITH mitkThrow()"; } void throwExceptionWithThrowMacro(std::string message) { mitkThrow() << message.c_str(); } void throwSpecializedExceptionWithThrowMacro(std::string message) { mitkThrowException(mitk::Exception) << message; } void throwSpecializedExceptionWithThrowMacro2(std::string message) { mitkThrowException(SpecializedTestException) << message; } void reThrowExceptionWithReThrowMacro(std::string messageThrow, std::string messageReThrow) { try { throwExceptionWithThrowMacro(messageThrow); } catch (mitk::Exception &e) { mitkReThrow(e) << messageReThrow; } } - static void TestExceptionConstructor() + void setUp() + { + m_ExceptionThrown = false; + m_MessageText = ""; + m_Message = "invalid"; + m_File = "invalid"; + m_Line = -1; + } + + void tearDown() + { + m_ExceptionThrown = false; + m_MessageText = ""; + m_Message = ""; + m_File = ""; + m_Line = 0; + } + + void TestExceptionConstructor_Success() { - bool exceptionThrown = false; - ExceptionTestClass::Pointer myExceptionTestObject = ExceptionTestClass::New(); try { - myExceptionTestObject->throwExceptionManually(); + this->throwExceptionManually(); } catch (mitk::Exception) { - exceptionThrown = true; + m_ExceptionThrown = true; } - MITK_TEST_CONDITION_REQUIRED(exceptionThrown, "Testing constructor of mitkException"); + CPPUNIT_ASSERT_MESSAGE("Testing constructor of mitkException", m_ExceptionThrown); + } - exceptionThrown = false; + void TestSpecializedExceptionConstructor_Success() + { try { - myExceptionTestObject->throwSpecializedExceptionManually(); + this->throwSpecializedExceptionManually(); } catch (SpecializedTestException) { - exceptionThrown = true; + m_ExceptionThrown = true; } - MITK_TEST_CONDITION_REQUIRED(exceptionThrown, - "Testing constructor specialized exception (deriving from mitkException)"); + CPPUNIT_ASSERT_MESSAGE("Testing constructor specialized exception (deriving from mitkException)", + m_ExceptionThrown); } - static void TestExceptionMessageStream() + //##### this methods are ONLY to test the streaming operators of the exceptions and + //##### NO code example. Please do not instantiate exceptions by yourself in normal code! + //##### Normally exceptions should only be thrown by using the exception macro! + void TestExceptionMessageStreamAddingString_Success() { - //##### this method is ONLY to test the streaming operators of the exceptions and - //##### NO code example. Please do not instantiate exceptions by yourself in normal code! - //##### Normally exceptions should only be thrown by using the exception macro! - mitk::Exception myException = mitk::Exception("testfile.cpp", 111, "testmessage"); - myException << " and additional stream"; - MITK_TEST_CONDITION_REQUIRED(myException.GetDescription() == std::string("testmessage and additional stream"), - "Testing mitkException message stream (adding std::string)"); - - myException.SetDescription("testmessage2"); - myException << ' ' << 'a' << 'n' << 'd' << ' ' << 'c' << 'h' << 'a' << 'r' << 's'; - MITK_TEST_CONDITION_REQUIRED(myException.GetDescription() == std::string("testmessage2 and chars"), - "Testing mitkException message stream (adding single chars)"); - - myException.SetDescription("testmessage3"); - myException << myException; // adding the object itself makes no sense but should work - MITK_TEST_CONDITION_REQUIRED(myException.GetDescription() != std::string(""), - "Testing mitkException message stream (adding object)"); + m_MyException << " and additional stream"; + CPPUNIT_ASSERT_MESSAGE("Testing mitkException message stream (adding std::string)", + m_MyException.GetDescription() == std::string("testmessage and additional stream")); + } + void TestExceptionMessageStreamAddingSingleChars_Success() + { + m_MyException.SetDescription("testmessage2"); + m_MyException << ' ' << 'a' << 'n' << 'd' << ' ' << 'c' << 'h' << 'a' << 'r' << 's'; + CPPUNIT_ASSERT_MESSAGE("Testing mitkException message stream (adding single chars)", + m_MyException.GetDescription() == std::string("testmessage2 and chars")); + } + + void TestExceptionMessageStreamAddingObject_Success() + { + m_MyException.SetDescription("testmessage3"); + m_MyException << m_MyException; // adding the object itself makes no sense but should work + CPPUNIT_ASSERT_MESSAGE("Testing mitkException message stream (adding object)", + m_MyException.GetDescription() != std::string("")); + } + + void TestSpecializedExceptionMessageStreamAddingString() + { SpecializedTestException mySpecializedException = SpecializedTestException("testfile.cpp", 111, "testmessage", "test"); mySpecializedException << " and additional stream"; - MITK_TEST_CONDITION_REQUIRED( - mySpecializedException.GetDescription() == std::string("testmessage and additional stream"), - "Testing specialized exception message stream (adding std::string)"); + CPPUNIT_ASSERT_MESSAGE("Testing specialized exception message stream (adding std::string)", + mySpecializedException.GetDescription() == std::string("testmessage and additional stream")); } - static void TestExceptionMessageStreamThrowing() + void TestExceptionMessageStreamThrowing_Success() { - bool exceptionThrown = false; - ExceptionTestClass::Pointer myExceptionTestObject = ExceptionTestClass::New(); std::string thrownMessage = ""; try { - myExceptionTestObject->throwExceptionManually("message1", " and message2"); + this->throwExceptionManually("message1", " and message2"); } catch (mitk::Exception &e) { thrownMessage = e.GetDescription(); - exceptionThrown = true; + m_ExceptionThrown = true; } - MITK_TEST_CONDITION_REQUIRED(exceptionThrown && (thrownMessage == std::string("message1 and message2")), - "Testing throwing and streaming of mitk::Exception together.") + CPPUNIT_ASSERT_MESSAGE("Testing throwing and streaming of mitk::Exception together.", + m_ExceptionThrown && (thrownMessage == std::string("message1 and message2"))); } - static void TestMitkThrowMacro() + void TestMitkThrowMacroThrowing_Success() { - bool exceptionThrown = false; - ExceptionTestClass::Pointer myExceptionTestObject = ExceptionTestClass::New(); - // case 1: test throwing - try { - myExceptionTestObject->throwExceptionWithThrowMacro(); + this->throwExceptionWithThrowMacro(); } catch (mitk::Exception) { - exceptionThrown = true; + m_ExceptionThrown = true; } - MITK_TEST_CONDITION_REQUIRED(exceptionThrown, "Testing mitkThrow()"); + CPPUNIT_ASSERT_MESSAGE("Testing mitkThrow()", m_ExceptionThrown); + } + void TestMitkThrowMacroMessage_Success() + { // case 2: test message text - - exceptionThrown = false; - std::string messageText = ""; - try { - myExceptionTestObject->throwExceptionWithThrowMacro("test123"); + this->throwExceptionWithThrowMacro("test123"); } catch (mitk::Exception &e) { - exceptionThrown = true; - messageText = e.GetDescription(); + m_ExceptionThrown = true; + m_MessageText = e.GetDescription(); } - MITK_TEST_CONDITION_REQUIRED((exceptionThrown && (messageText == "test123")), - "Testing message test of mitkThrow()"); + CPPUNIT_ASSERT_MESSAGE("Testing message test of mitkThrow()", (m_ExceptionThrown && (m_MessageText == "test123"))); + } + void TestMitkThrowMacroException_Success() + { // case 3: specialized exception / command mitkThrow(mitk::Exception) - - exceptionThrown = false; - messageText = ""; - try { - myExceptionTestObject->throwSpecializedExceptionWithThrowMacro("test123"); + this->throwSpecializedExceptionWithThrowMacro("test123"); } catch (mitk::Exception &e) { - exceptionThrown = true; - messageText = e.GetDescription(); + m_ExceptionThrown = true; + m_MessageText = e.GetDescription(); } - MITK_TEST_CONDITION_REQUIRED(exceptionThrown && messageText == "test123", - "Testing special exception with mitkThrow(mitk::Exception)"); + CPPUNIT_ASSERT_MESSAGE("Testing special exception with mitkThrow(mitk::Exception)", + m_ExceptionThrown && m_MessageText == "test123"); + } + void TestMitkThrowMacroSpezcializedException() + { // case 4: specialized exception / command mitkThrow(mitk::SpecializedException) - - exceptionThrown = false; - messageText = ""; - try { - myExceptionTestObject->throwSpecializedExceptionWithThrowMacro2("test123"); + this->throwSpecializedExceptionWithThrowMacro2("test123"); } catch (SpecializedTestException &e) { - exceptionThrown = true; - messageText = e.GetDescription(); + m_ExceptionThrown = true; + m_MessageText = e.GetDescription(); } - MITK_TEST_CONDITION_REQUIRED(exceptionThrown && messageText == "test123", - "Testing special exception with mitkThrow(mitk::SpecializedException)"); + CPPUNIT_ASSERT_MESSAGE("Testing special exception with mitkThrow(mitk::SpecializedException)", + m_ExceptionThrown && m_MessageText == "test123"); } - static void TestRethrowInformation() - // this method is ONLY to test methods of mitk::Exception and no code example - // normally exceptions should only be instantiated and thrown by using the exception macros! + //##### this methods are ONLY to test methods of mitk::Exception and no code example + //##### normally exceptions should only be instantiated and thrown by using the exception macros! + void TestGetNumberOfRethrows_Success() { // first: testing rethrow information methods, when no information is stored - // case 1.1: method GetNumberOfRethrows() - mitk::Exception e = mitk::Exception("test.cpp", 155, "", ""); - MITK_TEST_CONDITION_REQUIRED(e.GetNumberOfRethrows() == 0, - "Testing GetNumberOfRethrows() with empty rethrow information"); + CPPUNIT_ASSERT_MESSAGE("Testing GetNumberOfRethrows() with empty rethrow information", + m_E.GetNumberOfRethrows() == 0); + } + void TestGetRethrowDataWithNegativNumber_Success() + { // case 1.2: GetRethrowData() with negative number - { - std::string file = "invalid"; - int line = -1; - std::string message = "invalid"; - e.GetRethrowData(-1, file, line, message); - MITK_TEST_CONDITION_REQUIRED(((file == "") && (line == 0) && (message == "")), - "Testing GetRethrowData() with invalid rethrow number (negative)."); - } + m_E.GetRethrowData(-1, m_File, m_Line, m_Message); + CPPUNIT_ASSERT_MESSAGE("Testing GetRethrowData() with invalid rethrow number (negative).", + ((m_File == "") && (m_Line == 0) && (m_Message == ""))); + } + void TestGetRethrowDataWithNumberZero_Success() + { // case 1.3: GetRethrowData() with number 0 - { - std::string file = "invalid"; - int line = -1; - std::string message = "invalid"; - e.GetRethrowData(0, file, line, message); - MITK_TEST_CONDITION_REQUIRED(((file == "") && (line == 0) && (message == "")), - "Testing GetRethrowData() with non-existing rethrow number (0)."); - } + m_E.GetRethrowData(0, m_File, m_Line, m_Message); + CPPUNIT_ASSERT_MESSAGE("Testing GetRethrowData() with non-existing rethrow number (0).", + ((m_File == "") && (m_Line == 0) && (m_Message == ""))); + } + void TestGetRethrowDataWithNumberOne_Success() + { // case 1.4: GetRethrowData() with number 1 - { - std::string file = "invalid"; - int line = -1; - std::string message = "invalid"; - e.GetRethrowData(1, file, line, message); - MITK_TEST_CONDITION_REQUIRED(((file == "") && (line == 0) && (message == "")), - "Testing GetRethrowData() with non-existing rethrow number (1)."); - } + m_E.GetRethrowData(1, m_File, m_Line, m_Message); + CPPUNIT_ASSERT_MESSAGE("Testing GetRethrowData() with non-existing rethrow number (1).", + ((m_File == "") && (m_Line == 0) && (m_Message == ""))); + } + void TestAddRethrowData_Success() + { // second: add rethrow data - e.AddRethrowData("test2.cpp", 10, "Rethrow one"); - MITK_TEST_CONDITION_REQUIRED(e.GetNumberOfRethrows() == 1, "Testing adding of rethrow data."); - e.AddRethrowData("test3.cpp", 15, "Rethrow two"); - MITK_TEST_CONDITION_REQUIRED(e.GetNumberOfRethrows() == 2, "Testing adding of more rethrow data."); + m_E.AddRethrowData("test2.cpp", 10, "Rethrow one"); + CPPUNIT_ASSERT_MESSAGE("Testing adding of rethrow data.", m_E.GetNumberOfRethrows() == 1); + m_E.AddRethrowData("test3.cpp", 15, "Rethrow two"); + CPPUNIT_ASSERT_MESSAGE("Testing adding of more rethrow data.", m_E.GetNumberOfRethrows() == 2); + } + void TestFirstRethrowDataAreStoredProperly_Success() + { // third: test if this rethrow data was stored properly - { - std::string file = "invalid"; - int line = -1; - std::string message = "invalid"; - e.GetRethrowData(0, file, line, message); - MITK_TEST_CONDITION_REQUIRED(((file == "test2.cpp") && (line == 10) && (message == "Rethrow one")), - "Testing stored information of first rethrow."); - } - - { - std::string file = "invalid"; - int line = -1; - std::string message = "invalid"; - e.GetRethrowData(1, file, line, message); - MITK_TEST_CONDITION_REQUIRED(((file == "test3.cpp") && (line == 15) && (message == "Rethrow two")), - "Testing stored information of second rethrow."); - } + m_E.AddRethrowData("test2.cpp", 10, "Rethrow one"); + m_E.GetRethrowData(0, m_File, m_Line, m_Message); + CPPUNIT_ASSERT_MESSAGE("Testing stored information of first rethrow.", + ((m_File == "test2.cpp") && (m_Line == 10) && (m_Message == "Rethrow one"))); } - static void TestRethrowMacro() + void TestSecondRethrowDataAreStoredProperly_Success() { - bool exceptionThrown = false; - std::string message = ""; - ExceptionTestClass::Pointer myExceptionTestObject = ExceptionTestClass::New(); + m_E.AddRethrowData("test2.cpp", 10, "Rethrow one"); + m_E.AddRethrowData("test3.cpp", 15, "Rethrow two"); + m_E.GetRethrowData(1, m_File, m_Line, m_Message); + CPPUNIT_ASSERT_MESSAGE("Testing stored information of second rethrow.", + ((m_File == "test3.cpp") && (m_Line == 15) && (m_Message == "Rethrow two"))); + } + void TestRethrowMacro_Success() + { // case 1: test throwing - try { - myExceptionTestObject->reThrowExceptionWithReThrowMacro("Test original message.", "Test rethrow message."); + this->reThrowExceptionWithReThrowMacro("Test original message.", "Test rethrow message."); } catch (mitk::Exception &e) { - message = e.GetDescription(); - exceptionThrown = true; + m_Message = e.GetDescription(); + m_ExceptionThrown = true; } - MITK_TEST_CONDITION_REQUIRED(exceptionThrown, "Testing mitkReThrow()"); - MITK_TEST_CONDITION_REQUIRED(message == "Test original message.Test rethrow message.", - "Testing message/descriprion after rethrow.") + CPPUNIT_ASSERT_MESSAGE("Testing mitkReThrow()", m_ExceptionThrown); + CPPUNIT_ASSERT_MESSAGE("Testing message/descriprion after rethrow.", + m_Message == "Test original message.Test rethrow message."); } }; -int mitkExceptionTest(int /*argc*/, char * /*argv*/ []) -{ - MITK_TEST_BEGIN("MITKException"); - ExceptionTestClass::TestExceptionConstructor(); - ExceptionTestClass::TestExceptionMessageStream(); - ExceptionTestClass::TestExceptionMessageStreamThrowing(); - ExceptionTestClass::TestMitkThrowMacro(); - ExceptionTestClass::TestRethrowInformation(); - ExceptionTestClass::TestRethrowMacro(); - MITK_TEST_END(); -} +MITK_TEST_SUITE_REGISTRATION(mitkException) diff --git a/Modules/Core/test/mitkPointSetLocaleTest.cpp b/Modules/Core/test/mitkPointSetLocaleTest.cpp index 62a82e818d..41c4cf2447 100644 --- a/Modules/Core/test/mitkPointSetLocaleTest.cpp +++ b/Modules/Core/test/mitkPointSetLocaleTest.cpp @@ -1,162 +1,198 @@ /*=================================================================== 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. ===================================================================*/ +// Testing +#include "mitkTestFixture.h" +#include "mitkTestingMacros.h" + +// std includes +#include +#include + +// MITK includes #include "mitkIOUtil.h" #include "mitkPointSet.h" #include "mitkStandardFileLocations.h" -#include "mitkTestingMacros.h" + +// VTK includes +#include + +// stream includes #include #include -#include -#include -bool ChangeLocale(const std::string &locale) +class mitkPointSetLocaleTestSuite : public mitk::TestFixture { - try - { - MITK_TEST_OUTPUT(<< "\n** Changing locale from " << setlocale(LC_ALL, nullptr) << " to '" << locale << "'"); - setlocale(LC_ALL, locale.c_str()); + CPPUNIT_TEST_SUITE(mitkPointSetLocaleTestSuite); - std::locale l(locale.c_str()); - std::cin.imbue(l); - std::cout.imbue(l); + MITK_TEST(TestIfGermanLocaleUsed_Success); - return true; - } - catch (...) - { - MITK_TEST_OUTPUT(<< "Could not activate locale " << locale << "\n"); - return false; - } -} + CPPUNIT_TEST_SUITE_END(); -void ReaderLocaleTest(mitk::Point3D &refPoint, std::string filename) -{ - MITK_TEST_OUTPUT(<< "---- Reader Test ---- "); +private: + typedef std::list StringList; + StringList m_AllLocales; - mitk::PointSet::Pointer pointSet = mitk::IOUtil::Load(filename); + mitk::PointSet::Pointer m_RefPointSet; + mitk::Point3D m_RefPoint; - mitk::Point3D point; - if (pointSet->GetPointIfExists(0, &point)) + mitk::Point3D m_Point; + + mitk::PointSet::Pointer m_PointSet; + + bool ChangeLocale(const std::string &locale) { - MITK_TEST_CONDITION_REQUIRED(fabs(refPoint[0] - point[0]) < 0.00001, "read x correct"); - MITK_TEST_CONDITION_REQUIRED(fabs(refPoint[1] - point[1]) < 0.00001, "read y correct"); - MITK_TEST_CONDITION_REQUIRED(fabs(refPoint[2] - point[2]) < 0.00001, "read z correct"); + try + { + MITK_TEST_OUTPUT(<< "\n** Changing locale from " << setlocale(LC_ALL, nullptr) << " to '" << locale << "'"); + setlocale(LC_ALL, locale.c_str()); + + std::locale l(locale.c_str()); + std::cin.imbue(l); + std::cout.imbue(l); + return true; + } + catch (...) + { + MITK_TEST_OUTPUT(<< "Could not activate locale " << locale << "\n"); + return false; + } } - else + + void ReaderLocaleTest(mitk::Point3D &refPoint, std::string filename) { - MITK_TEST_FAILED_MSG(<< "File " << filename << " can not be read - test will not applied."); - return; + MITK_TEST_OUTPUT(<< "---- Reader Test ---- "); + + m_PointSet = mitk::IOUtil::Load(filename); + + if (m_PointSet->GetPointIfExists(0, &m_Point)) + { + CPPUNIT_ASSERT_MESSAGE("read x correct", fabs(refPoint[0] - m_Point[0]) < 0.00001); + CPPUNIT_ASSERT_MESSAGE("read y correct", fabs(refPoint[1] - m_Point[1]) < 0.00001); + CPPUNIT_ASSERT_MESSAGE("read z correct", fabs(refPoint[2] - m_Point[2]) < 0.00001); + } + else + { + MITK_TEST_FAILED_MSG(<< "File " << filename << " can not be read - test will not applied."); + return; + } } -} -void WriterLocaleTest(mitk::Point3D &refPoint, std::string filename) -{ - MITK_TEST_OUTPUT(<< "---- Writer Test---- "); - // create pointset - mitk::PointSet::Pointer refPointSet = mitk::PointSet::New(); - refPointSet->InsertPoint(0, refPoint); - // SetPoint(0, refPoint); + void WriterLocaleTest(mitk::Point3D &refPoint, std::string filename) + { + MITK_TEST_OUTPUT(<< "---- Writer Test---- "); + // create pointset + m_RefPointSet = mitk::PointSet::New(); + m_RefPointSet->InsertPoint(0, refPoint); - std::string tmpFilePath = mitk::IOUtil::CreateTemporaryFile("testPointSet_XXXXXX.mps"); + std::string tmpFilePath = mitk::IOUtil::CreateTemporaryFile("testPointSet_XXXXXX.mps"); - // write point set - mitk::IOUtil::Save(refPointSet, tmpFilePath); + // write point set + mitk::IOUtil::Save(m_RefPointSet, tmpFilePath); - std::ifstream stream(tmpFilePath.c_str()); + std::ifstream stream(tmpFilePath.c_str()); - // compare two .mps files - std::ifstream refStream(filename.c_str()); + // compare two .mps files + std::ifstream refStream(filename.c_str()); - MITK_TEST_CONDITION_REQUIRED(refStream, "Read reference point set"); - MITK_TEST_CONDITION_REQUIRED(stream, "Read point set"); + CPPUNIT_ASSERT_MESSAGE("Read reference point set", refStream); + CPPUNIT_ASSERT_MESSAGE("Read point set", stream); - bool differ = false; - if (stream.is_open() && refStream.is_open()) - { - std::string streamLine; - std::string refStreamLine; - while (!stream.eof() && !refStream.eof()) + bool differ = false; + if (stream.is_open() && refStream.is_open()) { - getline(stream, streamLine); - getline(refStream, refStreamLine); - if (streamLine.compare(refStreamLine) != 0) + std::string streamLine; + std::string refStreamLine; + while (!stream.eof() && !refStream.eof()) { - differ = true; - break; + getline(stream, streamLine); + getline(refStream, refStreamLine); + if (streamLine.compare(refStreamLine) != 0) + { + differ = true; + break; + } } + stream.close(); + refStream.close(); } - stream.close(); - refStream.close(); + CPPUNIT_ASSERT_MESSAGE("Write point set correct", !differ); } - MITK_TEST_CONDITION_REQUIRED(!differ, "Write point set correct"); -} -int mitkPointSetLocaleTest(int, char *[]) -{ - MITK_TEST_BEGIN("PointSetLocaleTest"); +public: + void setUp() + { + m_RefPointSet = mitk::PointSet::New(); + + // create locale list + m_AllLocales.push_back("de_DE"); + m_AllLocales.push_back("de_DE.utf8"); + m_AllLocales.push_back("de_DE.UTF-8"); + m_AllLocales.push_back("de_DE@euro"); + m_AllLocales.push_back("German_Germany"); + + m_RefPoint[0] = 32.2946; + m_RefPoint[1] = -17.7359; + m_RefPoint[2] = 29.6502; + } + + void tearDown() + { + m_RefPoint[0] = 0; + m_RefPoint[1] = 0; + m_RefPoint[2] = 0; - // create reference point set - mitk::PointSet::Pointer refPointSet = mitk::PointSet::New(); - mitk::Point3D refPoint; - refPoint[0] = 32.2946; - refPoint[1] = -17.7359; - refPoint[2] = 29.6502; - refPointSet->SetPoint(0, refPoint); + m_AllLocales.clear(); + } - // create locale list + void TestIfGermanLocaleUsed_Success() + { + // create reference point set + m_RefPointSet->SetPoint(0, m_RefPoint); - typedef std::list StringList; - StringList alllocales; - alllocales.push_back("de_DE"); - alllocales.push_back("de_DE.utf8"); - alllocales.push_back("de_DE.UTF-8"); - alllocales.push_back("de_DE@euro"); - alllocales.push_back("German_Germany"); - -// QuickFix for MAC OS X -// See for more the Bug #3894 comments + // QuickFix for MAC OS X + // See for more the Bug #3894 comments #if defined(__APPLE__) || defined(MACOSX) - alllocales.push_back("C"); + alllocales.push_back("C"); #endif - // write a reference file using the "C" locale once - ChangeLocale("C"); - std::string referenceFilePath = mitk::IOUtil::CreateTemporaryFile("refPointSet_XXXXXX.mps"); - MITK_INFO << "Reference PointSet in " << referenceFilePath; + // write a reference file using the "C" locale once + ChangeLocale("C"); + std::string referenceFilePath = mitk::IOUtil::CreateTemporaryFile("refPointSet_XXXXXX.mps"); + MITK_INFO << "Reference PointSet in " << referenceFilePath; - // write point set - mitk::IOUtil::Save(refPointSet, referenceFilePath); + // write point set + mitk::IOUtil::Save(m_RefPointSet, referenceFilePath); - unsigned int numberOfTestedGermanLocales(0); - for (auto iter = alllocales.begin(); iter != alllocales.end(); ++iter) - { - if (ChangeLocale(*iter)) + unsigned int numberOfTestedGermanLocales(0); + for (auto iter = m_AllLocales.begin(); iter != m_AllLocales.end(); ++iter) + { + if (ChangeLocale(*iter)) + { + ++numberOfTestedGermanLocales; + WriterLocaleTest(m_RefPoint, referenceFilePath); + ReaderLocaleTest(m_RefPoint, referenceFilePath); + } + } + + if (numberOfTestedGermanLocales == 0) { - ++numberOfTestedGermanLocales; - WriterLocaleTest(refPoint, referenceFilePath); - ReaderLocaleTest(refPoint, referenceFilePath); + MITK_TEST_OUTPUT(<< "Warning: No German locale was found on the system."); } } +}; - if (numberOfTestedGermanLocales == 0) - { - MITK_TEST_OUTPUT(<< "Warning: No German locale was found on the system."); - } - // MITK_TEST_CONDITION_REQUIRED( numberOfTestedGermanLocales > 0, "Verify that at least one German locale has been - // tested."); - MITK_TEST_END(); -} +MITK_TEST_SUITE_REGISTRATION(mitkPointSetLocale) diff --git a/Modules/Core/test/mitkSurfaceTest.cpp b/Modules/Core/test/mitkSurfaceTest.cpp index 63389c8cba..5551ab13aa 100644 --- a/Modules/Core/test/mitkSurfaceTest.cpp +++ b/Modules/Core/test/mitkSurfaceTest.cpp @@ -1,149 +1,279 @@ /*=================================================================== 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. ===================================================================*/ +// Testing +#include "mitkTestFixture.h" +#include + +// std includes +#include + +// MITK includes #include "mitkCommon.h" #include "mitkNumericTypes.h" #include "mitkSurface.h" -#include "mitkTestingMacros.h" +// MITK includes +#include + +// VTK includes #include "vtkPolyData.h" #include "vtkSphereSource.h" +// stream includes #include -int mitkSurfaceTest(int /*argc*/, char * /*argv*/ []) +class mitkSurfaceTestSuite : public mitk::TestFixture { - MITK_TEST_BEGIN("Surface"); - - mitk::Surface::Pointer surface = mitk::Surface::New(); - MITK_TEST_CONDITION_REQUIRED(surface.GetPointer(), "Testing initialization!"); - - mitk::Surface::Pointer cloneSurface = surface->Clone(); - MITK_TEST_CONDITION_REQUIRED(cloneSurface.GetPointer(), "Testing clone surface initialization!"); - - vtkSphereSource *sphereSource = vtkSphereSource::New(); - sphereSource->SetCenter(0, 0, 0); - sphereSource->SetRadius(5.0); - sphereSource->SetThetaResolution(10); - sphereSource->SetPhiResolution(10); - sphereSource->Update(); - - vtkPolyData *polys = sphereSource->GetOutput(); - MITK_TEST_CONDITION_REQUIRED(surface->GetVtkPolyData() == nullptr, "Testing initial state of vtkPolyData"); - surface->SetVtkPolyData(polys); - sphereSource->Delete(); - MITK_TEST_CONDITION_REQUIRED(surface->GetVtkPolyData() != nullptr, "Testing set vtkPolyData"); - - cloneSurface = surface->Clone(); - MITK_TEST_CONDITION_REQUIRED(cloneSurface->GetVtkPolyData() != nullptr, "Testing set vtkPolyData of cloned surface!"); - cloneSurface = nullptr; - - double bounds[6] = {0.0, 0.0, 0.0, 0.0, 0.0, 0.0}; - polys->ComputeBounds(); - polys->GetBounds(bounds); - - surface->UpdateOutputInformation(); - surface->SetRequestedRegionToLargestPossibleRegion(); - auto *bb = const_cast(surface->GetGeometry()->GetBoundingBox()); - mitk::BoundingBox::BoundsArrayType surfBounds = bb->GetBounds(); - - bool passed = false; - if (bounds[0] == surfBounds[0] && bounds[1] == surfBounds[1] && bounds[2] == surfBounds[2] && - bounds[3] == surfBounds[3] && bounds[4] == surfBounds[4] && bounds[5] == surfBounds[5]) + CPPUNIT_TEST_SUITE(mitkSurfaceTestSuite); + + MITK_TEST(InitializationSurfacePointer_Success); + MITK_TEST(InitializationCloneSurfacePointer_Success); + + MITK_TEST(StateOfVtkPolyDataEqualNullPointer_Success); + + MITK_TEST(SetVtkPolyDataNotNullPointer_Failure); + MITK_TEST(SetClonedVtkPolyDataNotNullPointer_Failure); + + MITK_TEST(GetBoundingBox_Success); + + MITK_TEST(SurfaceExpandTimestepsAreFive_Success); + MITK_TEST(Surface4DDataCreation_Success); + + MITK_TEST(TimeGeometrySurface_Success); + MITK_TEST(ChangingDataOfSpecificTimestepSurface_Success); + MITK_TEST(SurfaceCopyWithGraft_Failure); + MITK_TEST(CopyingNumberOfTimesteps_Success); + + MITK_TEST(DestructionOfSurface_Success); + + CPPUNIT_TEST_SUITE_END(); + +private: + mitk::Surface::Pointer m_Surface; + mitk::Surface::Pointer m_CloneSurface; + vtkSmartPointer m_SphereSource; + const mitk::TimeGeometry *m_InputTimeGeometry; + + int m_Time; + int m_Timestep; + +public: + void setUp() + { + m_Surface = mitk::Surface::New(); + m_CloneSurface = m_Surface->Clone(); + m_SphereSource = vtkSmartPointer::New(); + m_InputTimeGeometry = m_Surface->GetUpdatedTimeGeometry(); + + m_SphereSource->SetCenter(0, 0, 0); + m_SphereSource->SetRadius(5.0); + m_SphereSource->SetThetaResolution(10); + m_SphereSource->SetPhiResolution(10); + m_SphereSource->Update(); + + m_Time = 3; + m_Timestep = 0; + } + + void tearDown() + { + m_Surface = nullptr; + m_CloneSurface = nullptr; + } + + void InitializationSurfacePointer_Success() + { + CPPUNIT_ASSERT_MESSAGE("Testing initialization", m_Surface.GetPointer()); + } + + void InitializationCloneSurfacePointer_Success() + { + CPPUNIT_ASSERT_MESSAGE("Testing clone surface initialization", m_CloneSurface.GetPointer()); + } + + void StateOfVtkPolyDataEqualNullPointer_Success() + { + CPPUNIT_ASSERT_MESSAGE("Testing initial state of vtkPolyData", m_Surface->GetVtkPolyData() == nullptr); + } + + void SetVtkPolyDataNotNullPointer_Failure() + { + vtkSmartPointer polys = m_SphereSource->GetOutput(); + m_Surface->SetVtkPolyData(polys); + CPPUNIT_ASSERT_MESSAGE("Testing set vtkPolyData", m_Surface->GetVtkPolyData() != nullptr); + } + + void SetClonedVtkPolyDataNotNullPointer_Failure() + { + vtkSmartPointer polys = m_SphereSource->GetOutput(); + m_Surface->SetVtkPolyData(polys); + m_CloneSurface = m_Surface->Clone(); + CPPUNIT_ASSERT_MESSAGE("Testing set vtkPolyData of cloned surface!", m_CloneSurface->GetVtkPolyData() != nullptr); + } + + void GetBoundingBox_Success() + { + vtkSmartPointer polys = m_SphereSource->GetOutput(); + m_Surface->SetVtkPolyData(polys); + + double bounds[6] = {0.0, 0.0, 0.0, 0.0, 0.0, 0.0}; + polys->ComputeBounds(); + polys->GetBounds(bounds); + + m_Surface->UpdateOutputInformation(); + m_Surface->SetRequestedRegionToLargestPossibleRegion(); + auto *bb = const_cast(m_Surface->GetGeometry()->GetBoundingBox()); + mitk::BoundingBox::BoundsArrayType surfBounds = bb->GetBounds(); + + bool passed = false; + if (bounds[0] == surfBounds[0] && bounds[1] == surfBounds[1] && bounds[2] == surfBounds[2] && + bounds[3] == surfBounds[3] && bounds[4] == surfBounds[4] && bounds[5] == surfBounds[5]) + { + passed = true; + } + + CPPUNIT_ASSERT_MESSAGE("Testing GetBoundingBox()", passed); + } + + void SurfaceExpandTimestepsAreFive_Success() { - passed = true; + m_Surface->Expand(5); + m_Surface->Update(); + m_Surface->SetRequestedRegionToLargestPossibleRegion(); + mitk::Surface::RegionType requestedRegion = m_Surface->GetRequestedRegion(); + CPPUNIT_ASSERT_MESSAGE("Testing mitk::Surface::Expand( timesteps ): ", requestedRegion.GetSize(3) == 5); } - MITK_TEST_CONDITION_REQUIRED(passed, "Testing GetBoundingBox()!"); - surface->Expand(5); - surface->Update(); - surface->SetRequestedRegionToLargestPossibleRegion(); - mitk::Surface::RegionType requestedRegion = surface->GetRequestedRegion(); - MITK_TEST_CONDITION_REQUIRED(requestedRegion.GetSize(3) == 5, "Testing mitk::Surface::Expand( timesteps ): "); + void Surface4DDataCreation_Success() + { + double boundsMat[5][6]; + + for (int i = 0; i < 5; i++) + { + vtkNew sphereSource; + sphereSource->SetCenter(0, 0, 0); + sphereSource->SetRadius(1.0 * (i + 1.0)); + sphereSource->SetThetaResolution(10); + sphereSource->SetPhiResolution(10); + sphereSource->Update(); + sphereSource->GetOutput()->ComputeBounds(); + sphereSource->GetOutput()->GetBounds(boundsMat[i]); + m_Surface->SetVtkPolyData(sphereSource->GetOutput(), i); + } + + m_Surface->UpdateOutputInformation(); + m_Surface->SetRequestedRegionToLargestPossibleRegion(); + + bool passed = true; + for (int i = 0; i < 5; i++) + { + mitk::BoundingBox::BoundsArrayType surfBounds = + (const_cast(m_Surface->GetTimeGeometry()->GetGeometryForTimeStep(i)->GetBoundingBox())) + ->GetBounds(); + + if (boundsMat[i][0] != surfBounds[0] || boundsMat[i][1] != surfBounds[1] || boundsMat[i][2] != surfBounds[2] || + boundsMat[i][3] != surfBounds[3] || boundsMat[i][4] != surfBounds[4] || boundsMat[i][5] != surfBounds[5]) + { + passed = false; + break; + } + } + CPPUNIT_ASSERT_MESSAGE("Testing mitk::Surface::Testing 4D surface data creation", passed); + } - double boundsMat[5][6]; + void TimeGeometrySurface_Success() + { + m_Timestep = m_InputTimeGeometry->TimePointToTimeStep(m_Time); + CPPUNIT_ASSERT_MESSAGE("Testing correctness of geometry for surface->GetUpdatedTimeGeometry()", + m_Time == m_Timestep); + } - for (int i = 0; i < 5; i++) + void ChangingDataOfSpecificTimestepSurface_Success() { - vtkSphereSource *sphereSource = vtkSphereSource::New(); + vtkNew sphereSource; sphereSource->SetCenter(0, 0, 0); - sphereSource->SetRadius(1.0 * (i + 1.0)); + sphereSource->SetRadius(100.0); sphereSource->SetThetaResolution(10); sphereSource->SetPhiResolution(10); sphereSource->Update(); - sphereSource->GetOutput()->ComputeBounds(); - sphereSource->GetOutput()->GetBounds(boundsMat[i]); - surface->SetVtkPolyData(sphereSource->GetOutput(), i); - sphereSource->Delete(); + m_Surface->SetVtkPolyData(sphereSource->GetOutput(), 3); + + m_Timestep = m_InputTimeGeometry->TimePointToTimeStep(m_Time); + CPPUNIT_ASSERT_MESSAGE( + "Explicitly changing the data of timestep 3 and checking for timebounds correctness of surface's geometry again", + m_Time == m_Timestep); } - surface->UpdateOutputInformation(); - surface->SetRequestedRegionToLargestPossibleRegion(); + void SurfaceCopyWithGraft_Failure() + { + double boundsMat[5][6]; + + for (int i = 0; i < 5; i++) + { + vtkNew sphereSource; + sphereSource->SetCenter(0, 0, 0); + sphereSource->SetRadius(1.0 * (i + 1.0)); + sphereSource->SetThetaResolution(10); + sphereSource->SetPhiResolution(10); + sphereSource->Update(); + sphereSource->GetOutput()->ComputeBounds(); + sphereSource->GetOutput()->GetBounds(boundsMat[i]); + m_Surface->SetVtkPolyData(sphereSource->GetOutput(), i); + } + + m_Surface->UpdateOutputInformation(); + m_Surface->SetRequestedRegionToLargestPossibleRegion(); + + mitk::Surface::Pointer dummy = mitk::Surface::New(); + dummy->Graft(m_Surface); + CPPUNIT_ASSERT_MESSAGE("Testing copying a Surface with Graft()", dummy->GetVtkPolyData() != nullptr); + } - passed = true; - for (int i = 0; i < 5; i++) + void CopyingNumberOfTimesteps_Success() { - mitk::BoundingBox::BoundsArrayType surfBounds = - (const_cast(surface->GetTimeGeometry()->GetGeometryForTimeStep(i)->GetBoundingBox())) - ->GetBounds(); + double boundsMat[5][6]; - if (boundsMat[i][0] != surfBounds[0] || boundsMat[i][1] != surfBounds[1] || boundsMat[i][2] != surfBounds[2] || - boundsMat[i][3] != surfBounds[3] || boundsMat[i][4] != surfBounds[4] || boundsMat[i][5] != surfBounds[5]) + for (int i = 0; i < 5; i++) { - passed = false; - break; + vtkNew sphereSource; + sphereSource->SetCenter(0, 0, 0); + sphereSource->SetRadius(1.0 * (i + 1.0)); + sphereSource->SetThetaResolution(10); + sphereSource->SetPhiResolution(10); + sphereSource->Update(); + sphereSource->GetOutput()->ComputeBounds(); + sphereSource->GetOutput()->GetBounds(boundsMat[i]); + m_Surface->SetVtkPolyData(sphereSource->GetOutput(), i); } + + m_Surface->UpdateOutputInformation(); + m_Surface->SetRequestedRegionToLargestPossibleRegion(); + + unsigned int numberoftimesteps = m_Surface->GetTimeSteps(); + mitk::Surface::Pointer dummy = mitk::Surface::New(); + dummy->Graft(m_Surface); + + CPPUNIT_ASSERT_MESSAGE(" Old timesteps == copy of timesteps ", dummy->GetTimeSteps() == numberoftimesteps); + } + + void DestructionOfSurface_Success() + { + m_Surface = nullptr; + CPPUNIT_ASSERT_MESSAGE("Testing destruction of surface", m_Surface.IsNull()); } - MITK_TEST_CONDITION_REQUIRED(passed, "Testing mitk::Surface::Testing 4D surface data creation!"); - - const mitk::TimeGeometry *inputTimeGeometry = surface->GetUpdatedTimeGeometry(); - - int time = 3; - int timestep = 0; - timestep = inputTimeGeometry->TimePointToTimeStep(time); - MITK_TEST_CONDITION_REQUIRED(time == timestep, - "Testing correctness of geometry for surface->GetUpdatedTimeGeometry()!"); - - sphereSource = vtkSphereSource::New(); - sphereSource->SetCenter(0, 0, 0); - sphereSource->SetRadius(100.0); - sphereSource->SetThetaResolution(10); - sphereSource->SetPhiResolution(10); - sphereSource->Update(); - surface->SetVtkPolyData(sphereSource->GetOutput(), 3); - sphereSource->Delete(); - - inputTimeGeometry = surface->GetUpdatedTimeGeometry(); - time = 3; - - timestep = inputTimeGeometry->TimePointToTimeStep(time); - MITK_TEST_CONDITION_REQUIRED( - time == timestep, - "Explicitly changing the data of timestep 3 and checking for timebounds correctness of surface's geometry again!"); - - unsigned int numberoftimesteps = surface->GetTimeSteps(); - mitk::Surface::Pointer dummy = mitk::Surface::New(); - dummy->Graft(surface); - MITK_TEST_CONDITION_REQUIRED(dummy->GetVtkPolyData() != nullptr, "Testing copying a Surface with Graft()!"); - MITK_TEST_CONDITION_REQUIRED( - dummy->GetTimeSteps() == numberoftimesteps, - "orig-numberofTimeSteps:" << numberoftimesteps << " copy-numberofTimeSteps:" << dummy->GetTimeSteps()); - - surface = nullptr; - MITK_TEST_CONDITION_REQUIRED(surface.IsNull(), "Testing destruction of surface!"); - - MITK_TEST_END(); -} +}; +MITK_TEST_SUITE_REGISTRATION(mitkSurface) diff --git a/Modules/Core/test/mitkTinyXMLTest.cpp b/Modules/Core/test/mitkTinyXMLTest.cpp index b810c8f3c9..69dd7575aa 100644 --- a/Modules/Core/test/mitkTinyXMLTest.cpp +++ b/Modules/Core/test/mitkTinyXMLTest.cpp @@ -1,158 +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. ===================================================================*/ +// Testing +#include "mitkTestFixture.h" +#include -#include "mitkTestingMacros.h" - -#include - +// std includes #include #include #include #include +// MITK includes +#include "mitkStringProperty.h" +#include + +// itksys #include -static const std::string filename = itksys::SystemTools::GetCurrentWorkingDirectory() + "/TinyXMLTest.txt"; -static const std::string elementToStoreAttributeName = "DoubleTest"; -static const std::string attributeToStoreName = "CommaValue"; +// VTK includes +#include -static double calcPrecision(const unsigned int requiredDecimalPlaces) -{ - return pow(10.0, -1.0 * ((double)requiredDecimalPlaces)); -} - -/** - * create a simple xml document which stores the values - * @param valueToWrite value which should be stored - * @return true, if document was successfully created. - */ -static bool Setup(double valueToWrite) -{ - // 1. create simple document - TiXmlDocument document; - auto decl = new TiXmlDeclaration("1.0", "", ""); // TODO what to write here? encoding? etc.... - document.LinkEndChild(decl); - - auto version = new TiXmlElement("Version"); - version->SetAttribute("Writer", __FILE__); - version->SetAttribute("CVSRevision", "$Revision: 17055 $"); - version->SetAttribute("FileVersion", 1); - document.LinkEndChild(version); - - // 2. store one element containing a double value with potentially many after comma digits. - auto vElement = new TiXmlElement(elementToStoreAttributeName); - vElement->SetDoubleAttribute(attributeToStoreName, valueToWrite); - document.LinkEndChild(vElement); - - // 3. store in file. - return document.SaveFile(filename); -} - -static int readValueFromSetupDocument(double &readOutValue) +// vnl includes +#include + +class mitkTinyXMLTestSuite : public mitk::TestFixture { - TiXmlDocument document; + CPPUNIT_TEST_SUITE(mitkTinyXMLTestSuite); + + MITK_TEST(TestingFunctionSetupWorks_Success); + MITK_TEST(TestingReadValueFromSetupDocument_Success); + MITK_TEST(TestingReadOutValueWorks_Success); + MITK_TEST(TestDoubleValueWriteOut_Success); + MITK_TEST(TestDoubleValueWriteOutManyDecimalPlaces_Success); - if (!document.LoadFile(filename)) + CPPUNIT_TEST_SUITE_END(); + +private: + const std::string m_Filename = itksys::SystemTools::GetCurrentWorkingDirectory() + "/TinyXMLTest.txt"; + const std::string m_ElementToStoreAttributeName = "DoubleTest"; + const std::string m_AttributeToStoreName = "CommaValue"; + + TiXmlDocument m_Document; + TiXmlElement *m_DoubleTest; + + double calcPrecision(const unsigned int requiredDecimalPlaces) { - MITK_TEST_CONDITION_REQUIRED(false, "Test Setup failed, could not open " << filename); - return TIXML_NO_ATTRIBUTE; + return pow(10.0, -1.0 * ((double)requiredDecimalPlaces)); } - else + + bool Setup(double valueToWrite) { - TiXmlElement *doubleTest = document.FirstChildElement(elementToStoreAttributeName); - return doubleTest->QueryDoubleAttribute(attributeToStoreName, &readOutValue); + // 1. create simple document + auto decl = new TiXmlDeclaration("1.0", "", ""); // TODO what to write here? encoding? etc.... + m_Document.LinkEndChild(decl); + + auto version = new TiXmlElement("Version"); + version->SetAttribute("Writer", __FILE__); + version->SetAttribute("CVSRevision", "$Revision: 17055 $"); + version->SetAttribute("FileVersion", 1); + m_Document.LinkEndChild(version); + + // 2. store one element containing a double value with potentially many after comma digits. + auto vElement = new TiXmlElement(m_ElementToStoreAttributeName); + vElement->SetDoubleAttribute(m_AttributeToStoreName, valueToWrite); + m_Document.LinkEndChild(vElement); + + // 3. store in file. + return m_Document.SaveFile(m_Filename); } -} -/** - * - * @return true if TearDown was successful. - */ -static bool TearDown() -{ - return !remove(filename.c_str()); -} - -static void Test_Setup_works() -{ - MITK_TEST_CONDITION_REQUIRED( - Setup(1.0) && TearDown(), - "Test if setup and teardown correctly writes data to " << filename << " and deletes the file after the test"); -} - -/** - * this first test ensures we can correctly readout values from the - * TinyXMLDocument. - */ -static void Test_ReadOutValue_works() -{ - Setup(1.0); +public: + void setUp() {} - double readValue; + void tearDown() {} - MITK_TEST_CONDITION_REQUIRED(TIXML_SUCCESS == readValueFromSetupDocument(readValue), - "checking if readout mechanism works."); -} - -static void Test_DoubleValueWriteOut() -{ - const double valueToWrite = -1.123456; - const int validDigitsAfterComma = 6; // indicates the number of valid digits after comma of valueToWrite - const double neededPrecision = calcPrecision(validDigitsAfterComma + 1); - double readValue; + void TestingFunctionSetupWorks_Success() + { + CPPUNIT_ASSERT_MESSAGE("Test if Setup correctly writes data to file", Setup(1.0)); + } - Setup(valueToWrite); + int readValueFromSetupDocument(double &readOutValue) + { + if (!m_Document.LoadFile(m_Filename)) + { + CPPUNIT_ASSERT_MESSAGE("Test Setup failed, could not open file", false); + return TIXML_NO_ATTRIBUTE; + } + else + { + m_DoubleTest = m_Document.FirstChildElement(m_ElementToStoreAttributeName); + return m_DoubleTest->QueryDoubleAttribute(m_AttributeToStoreName, &readOutValue); + } + } - readValueFromSetupDocument(readValue); + void TestingReadValueFromSetupDocument_Success() + { + if (!m_Document.LoadFile(m_Filename)) + { + CPPUNIT_ASSERT_MESSAGE("Test Setup failed, could not open file", !m_Document.LoadFile(m_Filename)); + } + else + { + m_DoubleTest = m_Document.FirstChildElement(m_ElementToStoreAttributeName); + CPPUNIT_ASSERT_MESSAGE("Test Setup could open file", m_DoubleTest != nullptr); + } + } - MITK_TEST_CONDITION_REQUIRED( - mitk::Equal(valueToWrite, readValue, neededPrecision), - std::setprecision(validDigitsAfterComma) << "Testing if value " << valueToWrite << " equals " << readValue - << " which was retrieved from TinyXML document"); + /** + * this first test ensures we can correctly readout values from the + * TinyXMLDocument. + */ + void TestingReadOutValueWorks_Success() + { + double readValue; - TearDown(); -} + CPPUNIT_ASSERT_MESSAGE("checking if readout mechanism works.", + TIXML_SUCCESS == readValueFromSetupDocument(readValue)); + } -static void Test_DoubleValueWriteOut_manyDecimalPlaces() -{ - const double valueToWrite = -1.12345678910111; - const int validDigitsAfterComma = 14; // indicates the number of valid digits after comma of valueToWrite - const double neededPrecision = calcPrecision(validDigitsAfterComma + 1); - double readValue; + void TestDoubleValueWriteOut_Success() + { + const double valueToWrite = -1.123456; + const int validDigitsAfterComma = 6; // indicates the number of valid digits after comma of valueToWrite + const double neededPrecision = calcPrecision(validDigitsAfterComma + 1); + double readValue; - Setup(valueToWrite); + Setup(valueToWrite); + readValueFromSetupDocument(readValue); - readValueFromSetupDocument(readValue); + CPPUNIT_ASSERT_MESSAGE("Testing if value valueToWrite equals readValue which was retrieved from TinyXML document", + mitk::Equal(valueToWrite, readValue, neededPrecision)); + } - MITK_TEST_CONDITION_REQUIRED( - mitk::Equal(valueToWrite, readValue, neededPrecision), - std::setprecision(validDigitsAfterComma) << "Testing if value " << valueToWrite << " equals " << readValue - << " which was retrieved from TinyXML document"); + void TestDoubleValueWriteOutManyDecimalPlaces_Success() + { + const double valueToWrite = -1.12345678910111; + const int validDigitsAfterComma = 14; // indicates the number of valid digits after comma of valueToWrite + const double neededPrecision = calcPrecision(validDigitsAfterComma + 1); + double readValue; - TearDown(); -} + Setup(valueToWrite); -int mitkTinyXMLTest(int /* argc */, char * /*argv*/ []) -{ - MITK_TEST_BEGIN("TinyXMLTest"); + readValueFromSetupDocument(readValue); - Test_Setup_works(); - Test_ReadOutValue_works(); - Test_DoubleValueWriteOut(); - Test_DoubleValueWriteOut_manyDecimalPlaces(); + CPPUNIT_ASSERT_MESSAGE("Testing if value valueToWrite equals readValue which was retrieved from TinyXML document", + mitk::Equal(valueToWrite, readValue, neededPrecision)); + } +}; - MITK_TEST_END() -} +MITK_TEST_SUITE_REGISTRATION(mitkTinyXML) diff --git a/Modules/Core/test/mitkVectorTest.cpp b/Modules/Core/test/mitkVectorTest.cpp index 3b0ec9ebbf..720c7a1d15 100644 --- a/Modules/Core/test/mitkVectorTest.cpp +++ b/Modules/Core/test/mitkVectorTest.cpp @@ -1,171 +1,441 @@ /*=================================================================== 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. ===================================================================*/ +// Testing +#include "mitkTestFixture.h" +#include + +// std includes +#include +// MITK includes +#include "mitkStringProperty.h" #include -#include +// itksys +#include "itkImage.h" + +// VTK includes +#include + +// vnl includes #include -int mitkVectorTest(int /*argc*/, char * /*argv*/ []) +class mitkVectorTestSuite : public mitk::TestFixture { - MITK_TEST_BEGIN("mitkVector"); - // test itk vector equality methods - itk::Vector itkVector_1; - itkVector_1[0] = 4.6; - itkVector_1[1] = 9.76543; - itkVector_1[2] = 746.09; - - itk::Vector itkVector_2; - itk::Vector itkVector_3; - for (int i = 0; i < 3; i++) - { - itkVector_2[i] = itkVector_1[i] - mitk::eps * 1.1; - itkVector_3[i] = itkVector_1[i] - mitk::eps * 0.9; - } - - MITK_TEST_CONDITION(mitk::Equal(itkVector_1, itkVector_1), - "Test vector equality using the same vector with mitk::eps"); - MITK_TEST_CONDITION( - !mitk::Equal(itkVector_1, itkVector_2), - "Test vector equality using different vectors with an element-wise difference greater than mitk::eps"); - MITK_TEST_CONDITION(mitk::Equal(itkVector_1, itkVector_2, mitk::eps * 1.2), - "Vectors are equal for higher epsilon tolerance ( 1.2 * mitk::eps )"); - MITK_TEST_CONDITION( - mitk::Equal(itkVector_1, itkVector_3), - "Test vector equality using different vectors with an element-wise difference less than mitk::eps"); - // test itk point equality methods - itk::Point itkPoint_1; - itk::Point itkPoint_2; - itk::Point itkPoint_3; - for (int i = 0; i < 3; i++) - { - itkPoint_1[i] = itkVector_1[i]; - itkPoint_2[i] = itkVector_2[i]; - itkPoint_3[i] = itkVector_3[i]; - } - MITK_TEST_CONDITION(mitk::Equal(itkPoint_1, itkPoint_1), "Test point equality using the same point with mitk::eps"); - MITK_TEST_CONDITION( - !mitk::Equal(itkPoint_1, itkPoint_2), - "Test point equality using different points with an element-wise difference greater than mitk::eps"); - MITK_TEST_CONDITION(mitk::Equal(itkPoint_1, itkPoint_2, mitk::eps * 1.2), - "Points are equal for higher epsilon tolerance ( 1.2 * mitk::eps )"); - MITK_TEST_CONDITION(mitk::Equal(itkPoint_1, itkPoint_3), - "Test point equality using different points with an element-wise difference less than mitk::eps"); - // test mitk vnl vector equality methods - mitk::VnlVector mitk_vnl_vector_1(3); - mitk::VnlVector mitk_vnl_vector_2(3); - mitk::VnlVector mitk_vnl_vector_3(3); - for (int i = 0; i < 3; i++) - { - mitk_vnl_vector_1.put(i, itkVector_1[i]); - mitk_vnl_vector_2.put(i, itkVector_2[i]); - mitk_vnl_vector_3.put(i, itkVector_1[i]); - } - - MITK_TEST_CONDITION(mitk::Equal(mitk_vnl_vector_1, mitk_vnl_vector_1), - "Test mitk vnl vector equality using the same mitk vnl vector with mitk::eps"); - MITK_TEST_CONDITION(!mitk::Equal(mitk_vnl_vector_1, mitk_vnl_vector_2), - "Test mitk vnl vector equality using different mitk vnl vectors with an element-wise difference " - "greater than mitk::eps"); - MITK_TEST_CONDITION(mitk::Equal(mitk_vnl_vector_1, mitk_vnl_vector_2, mitk::eps * 1.2), - "Vnl vectors are equal for higher epsilon tolerance ( 1.2 * mitk::eps )"); - MITK_TEST_CONDITION(mitk::Equal(mitk_vnl_vector_1, mitk_vnl_vector_3), - "Test mitk vnl vector equality using " - "different mitk vnl vectors with an " - "element-wise difference less than mitk::eps"); - - // test vnl_vector equality method + CPPUNIT_TEST_SUITE(mitkVectorTestSuite); + + MITK_TEST(ItkVecorEqualityUsingSameVector_Success); + MITK_TEST(ItkVecorEqualityUsingDifferentVectors_Failure); + MITK_TEST(ItkVecorEqualityForHigherEpsilonTolerance_Success); + MITK_TEST(ItkVecorEqualityUsingDifferentVectorsWithElementWise_Success); + + MITK_TEST(ItkPointEqualitySamePoint_Success); + MITK_TEST(ItkPointEqualityDifferentPoints_Failure); + MITK_TEST(ItkPointEqualitForHigherEpsilons_Success); + MITK_TEST(ItkPointEqualitDifferentPointsWithElementWise_Success); + + MITK_TEST(MitkVnlVectorEqualitySameMitkVnlVector_Success); + MITK_TEST(MitkVnlVectorEqualityDifferentMitkVnlVectors_Failure); + MITK_TEST(MitkVnlVectorEqualityHigherEpsilon_Success); + MITK_TEST(MitkVnlVectorEqualityUsingDifferentMitkVnlVectorsWithElementWise_Success); + + MITK_TEST(VnlVectorEqualitySameVnlVector_Success); + MITK_TEST(VnlVectorEqualityDifferentVnlVectors_Failure); + MITK_TEST(VnlVectorEqualityDifferentVnlVectorsWithHighEps_Success); + MITK_TEST(VnlVectorEqualityDifferentVnlVectorsWithLowEps_Success); + MITK_TEST(VnlVectorEqualityDifferentVnlVectorsWithLowEps_Failure); + + MITK_TEST(ScalarEqualitySameScalar_Successs); + MITK_TEST(ScalarEqualityDifferentScalarsDifferenceGreaterEps_Failure); + MITK_TEST(ScalarEqualityDifferentScalarsDifferenceEqualEps_Successs); + MITK_TEST(ScalarEqualityDifferentScalarsDifferenceLessEps_Successs); + + MITK_TEST(MatrixEqualitySameMatrixElementsWithEps_Success); + MITK_TEST(MatrixEqualityElementWiseDifferentMatrixElementsWithEpsilonZero_Failure); + MITK_TEST(MatrixEqualityDifferentMatrixElementsWithEpsilon_Success); + MITK_TEST(MatrixEqualityRMSDifferentMatrixElementsWithEpsilon_Failure); + MITK_TEST(MatrixEqualityRMSDifferentMatrixElementsWithEpsilonZero_Success); + + CPPUNIT_TEST_SUITE_END(); + +private: + itk::Vector m_ItkVector_1; + itk::Vector m_ItkVector_2; + itk::Vector m_ItkVector_3; + + itk::Point m_ItkPoint_1; + itk::Point m_ItkPoint_2; + itk::Point m_ItkPoint_3; + typedef mitk::ScalarType VnlValueType; - vnl_vector_fixed vnlVector_1; - vnlVector_1[3] = 56.98; - vnlVector_1[4] = 22.32; - vnlVector_1[5] = 1.00; - vnlVector_1[6] = 746.09; - vnl_vector_fixed vnlVector_2; - vnl_vector_fixed vnlVector_3; - for (int i = 0; i < 7; i++) + vnl_vector_fixed m_VnlVector_1; + vnl_vector_fixed m_VnlVector_2; + vnl_vector_fixed m_VnlVector_3; + + mitk::ScalarType m_Scalar1; + mitk::ScalarType m_Scalar2; + mitk::ScalarType m_Scalar3; + mitk::ScalarType m_Scalar4; + + vnl_matrix_fixed m_VnlMatrix3x3_1; + vnl_matrix_fixed m_VnlMatrix3x3_2; + + mitk::ScalarType m_Epsilon; + +public: + void setUp() + { + m_ItkVector_1[0] = 4.6; + m_ItkVector_1[1] = 9.76543; + m_ItkVector_1[2] = 746.09; + + m_VnlVector_1[0] = 4.6; + m_VnlVector_1[1] = 9.76543; + m_VnlVector_1[2] = 746.09; + m_VnlVector_1[3] = 56.98; + m_VnlVector_1[4] = 22.32; + m_VnlVector_1[5] = 1.00; + m_VnlVector_1[6] = 746.09; + + m_Scalar1 = 0.5689; + m_Scalar2 = m_Scalar1 + mitk::eps * 1.01; + m_Scalar3 = m_Scalar1; + m_Scalar4 = m_Scalar1 + mitk::eps * 0.95; + + m_VnlMatrix3x3_1(0, 0) = 1.1; + m_VnlMatrix3x3_1(0, 1) = 0.4; + m_VnlMatrix3x3_1(0, 2) = 5.3; + m_VnlMatrix3x3_1(1, 0) = 2.7; + m_VnlMatrix3x3_1(1, 1) = 3578.56418; + m_VnlMatrix3x3_1(1, 2) = 123.56; + m_VnlMatrix3x3_1(2, 0) = 546.89; + m_VnlMatrix3x3_1(2, 1) = 0.0001; + m_VnlMatrix3x3_1(2, 2) = 1.0; + + m_VnlMatrix3x3_2(0, 0) = 1.1000009; + m_VnlMatrix3x3_2(0, 1) = 0.4000009; + m_VnlMatrix3x3_2(0, 2) = 5.3000009; + m_VnlMatrix3x3_2(1, 0) = 2.7000009; + m_VnlMatrix3x3_2(1, 1) = 3578.5641809; + m_VnlMatrix3x3_2(1, 2) = 123.5600009; + m_VnlMatrix3x3_2(2, 0) = 546.8900009; + m_VnlMatrix3x3_2(2, 1) = 0.0001009; + m_VnlMatrix3x3_2(2, 2) = 1.0000009; + + m_Epsilon = 0.000001; + } + + void tearDown() + { + m_ItkVector_1.Fill(0); + m_ItkVector_2.Fill(0); + m_ItkVector_3.Fill(0); + m_ItkPoint_1.Fill(0); + m_ItkPoint_2.Fill(0); + m_ItkPoint_3.Fill(0); + + m_VnlVector_1.fill(0); + m_VnlVector_2.fill(0); + m_VnlVector_3.fill(0); + + m_Scalar1 = 0; + m_Scalar2 = 0; + m_Scalar3 = 0; + m_Scalar4 = 0; + + m_VnlMatrix3x3_1.fill(0); + m_VnlMatrix3x3_2.fill(0); + } + + void ItkVecorEqualityUsingSameVector_Success() + { + CPPUNIT_ASSERT_MESSAGE("Test vector equality using the same vector with mitk::eps", + mitk::Equal(m_ItkVector_1, m_ItkVector_1)); + } + + void ItkVecorEqualityUsingDifferentVectors_Failure() + { + for (int i = 0; i < 3; i++) + { + m_ItkVector_2[i] = m_ItkVector_1[i] - mitk::eps * 1.1; + } + CPPUNIT_ASSERT_NO_THROW_MESSAGE( + "Test vector equality using different vectors with an element-wise difference greater than mitk::eps", + mitk::Equal(m_ItkVector_1, m_ItkVector_2)); + } + + void ItkVecorEqualityForHigherEpsilonTolerance_Success() + { + for (int i = 0; i < 3; i++) + { + m_ItkVector_2[i] = m_ItkVector_1[i] - mitk::eps * 1.1; + } + CPPUNIT_ASSERT_MESSAGE("Vectors are equal for higher epsilon tolerance ( 1.2 * mitk::eps )", + mitk::Equal(m_ItkVector_1, m_ItkVector_2, mitk::eps * 1.2)); + } + + void ItkVecorEqualityUsingDifferentVectorsWithElementWise_Success() + { + for (int i = 0; i < 3; i++) + { + m_ItkVector_3[i] = m_ItkVector_1[i] - mitk::eps * 0.9; + } + CPPUNIT_ASSERT_MESSAGE( + "Test vector equality using different vectors with an element-wise difference less than mitk::eps", + mitk::Equal(m_ItkVector_1, m_ItkVector_3)); + } + + void ItkPointEqualitySamePoint_Success() + { + // test itk point equality methods + for (int i = 0; i < 3; i++) + { + m_ItkPoint_1[i] = m_ItkVector_1[i]; + } + CPPUNIT_ASSERT_MESSAGE("Test point equality using the same point with mitk::eps", + mitk::Equal(m_ItkPoint_1, m_ItkPoint_1)); + } + + void ItkPointEqualityDifferentPoints_Failure() + { + for (int i = 0; i < 3; i++) + { + m_ItkPoint_1[i] = m_ItkVector_1[i]; + m_ItkPoint_2[i] = m_ItkVector_2[i]; + } + CPPUNIT_ASSERT_NO_THROW_MESSAGE( + "Test point equality using different points with an element-wise difference greater than mitk::eps", + mitk::Equal(m_ItkPoint_1, m_ItkPoint_2)); + } + + void ItkPointEqualitForHigherEpsilons_Success() + { + for (int i = 0; i < 3; i++) + { + m_ItkVector_2[i] = m_ItkVector_1[i] - mitk::eps * 1.1; + } + + for (int i = 0; i < 3; i++) + { + m_ItkPoint_1[i] = m_ItkVector_1[i]; + m_ItkPoint_2[i] = m_ItkVector_2[i]; + } + CPPUNIT_ASSERT_MESSAGE("Points are equal for higher epsilon tolerance ( 1.2 * mitk::eps )", + mitk::Equal(m_ItkPoint_1, m_ItkPoint_2, mitk::eps * 1.2)); + } + + void ItkPointEqualitDifferentPointsWithElementWise_Success() + { + for (int i = 0; i < 3; i++) + { + m_ItkVector_3[i] = m_ItkVector_1[i] - mitk::eps * 0.9; + } + + for (int i = 0; i < 3; i++) + { + m_ItkPoint_1[i] = m_ItkVector_1[i]; + m_ItkPoint_3[i] = m_ItkVector_3[i]; + } + CPPUNIT_ASSERT_MESSAGE( + "Test point equality using different points with an element-wise difference less than mitk::eps", + mitk::Equal(m_ItkPoint_1, m_ItkPoint_3)); + } + + void MitkVnlVectorEqualitySameMitkVnlVector_Success() + { + mitk::VnlVector mitkVnlVector_1(3); + + for (int i = 0; i < 3; i++) + { + mitkVnlVector_1.put(i, m_ItkVector_1[i]); + } + + CPPUNIT_ASSERT_MESSAGE("Test mitk vnl vector equality using the same mitk vnl vector with mitk::eps", + mitk::Equal(mitkVnlVector_1, mitkVnlVector_1)); + } + + void MitkVnlVectorEqualityDifferentMitkVnlVectors_Failure() + { + mitk::VnlVector mitkVnlVector_1(3); + mitk::VnlVector mitkVnlVector_2(3); + + for (int i = 0; i < 3; i++) + { + mitkVnlVector_1.put(i, m_ItkVector_1[i]); + mitkVnlVector_2.put(i, m_ItkVector_2[i]); + } + + CPPUNIT_ASSERT_NO_THROW_MESSAGE( + "Test mitk vnl vector equality using different mitk vnl vectors with an element-wise difference " + "greater than mitk::eps", + mitk::Equal(mitkVnlVector_1, mitkVnlVector_2)); + } + + void MitkVnlVectorEqualityHigherEpsilon_Success() + { + mitk::VnlVector mitkVnlVector_1(3); + mitk::VnlVector mitkVnlVector_2(3); + + for (int i = 0; i < 3; i++) + { + m_ItkVector_2[i] = m_ItkVector_1[i] - mitk::eps * 1.1; + } + + for (int i = 0; i < 3; i++) + { + mitkVnlVector_1.put(i, m_ItkVector_1[i]); + mitkVnlVector_2.put(i, m_ItkVector_2[i]); + } + + CPPUNIT_ASSERT_MESSAGE("Vnl vectors are equal for higher epsilon tolerance ( 1.2 * mitk::eps )", + mitk::Equal(mitkVnlVector_1, mitkVnlVector_2, mitk::eps * 1.2)); + } + + void MitkVnlVectorEqualityUsingDifferentMitkVnlVectorsWithElementWise_Success() { - if (i < 3) + mitk::VnlVector mitkVnlVector_1(3); + mitk::VnlVector mitkVnlVector_3(3); + + for (int i = 0; i < 3; i++) + { + m_ItkVector_3[i] = m_ItkVector_1[i] - mitk::eps * 0.9; + } + + for (int i = 0; i < 3; i++) { - vnlVector_1.put(i, itkVector_1[i]); + mitkVnlVector_1.put(i, m_ItkVector_1[i]); + mitkVnlVector_3.put(i, m_ItkVector_3[i]); } - vnlVector_2[i] = vnlVector_1[i] - mitk::eps * 1.1f; - vnlVector_3[i] = vnlVector_1[i] - mitk::eps * 0.9f; + CPPUNIT_ASSERT_MESSAGE("Test mitk vnl vector equality using " + "different mitk vnl vectors with an " + "element-wise difference less than mitk::eps", + mitk::Equal(mitkVnlVector_1, mitkVnlVector_3)); + } + + void VnlVectorEqualitySameVnlVector_Success() + { + // test vnl_vector equality method + + CPPUNIT_ASSERT_MESSAGE("vnl_fixed : v_1 == v_1 ", (mitk::Equal(m_VnlVector_1, m_VnlVector_1))); } - MITK_TEST_CONDITION((mitk::Equal(vnlVector_1, vnlVector_1)), "vnl_fixed : v_1 == v_1 "); // the v_2 is constructed so that the equality test fails for mitk::eps, the norm of the difference between the // vectors is 7 * eps/6.9 - MITK_TEST_CONDITION(!(mitk::Equal(vnlVector_1, vnlVector_2)), - "vnl_fixed : v_1 != v_2 with mitk::eps "); + void VnlVectorEqualityDifferentVnlVectors_Failure() + { + for (int i = 0; i < 7; i++) + { + m_VnlVector_2[i] = m_VnlVector_1[i] - mitk::eps * 1.1f; + } + + CPPUNIT_ASSERT_NO_THROW_MESSAGE("vnl_fixed : v_1 != v_2 with mitk::eps ", + (mitk::Equal(m_VnlVector_1, m_VnlVector_2))); + } + // increase the epsilon value used for testing equality - should now pass ( 1.2 * mitk::eps > 7 * mitk::eps/6.9 ) - MITK_TEST_CONDITION((mitk::Equal(vnlVector_1, vnlVector_2, mitk::eps * 1.2f)), - "vnl_fixed : v_1 == v_2 with eps = 1.2 * mitk::eps "); - MITK_TEST_CONDITION((mitk::Equal(vnlVector_1, vnlVector_3, mitk::eps)), - "vnl_fixed : v_1 == v_3 with eps = 0.8 * mitk::eps "); - MITK_TEST_CONDITION(!(mitk::Equal(vnlVector_1, vnlVector_3, mitk::eps * 0.8f)), - "vnl_fixed : v_1 != v_3 with eps = 0.8 * mitk::eps "); - - // test scalar equality method - mitk::ScalarType scalar1 = 0.5689; - mitk::ScalarType scalar2 = scalar1 + mitk::eps * 1.01; - mitk::ScalarType scalar3 = scalar1; - mitk::ScalarType scalar4 = scalar1 + mitk::eps * 0.95; - MITK_TEST_CONDITION(mitk::Equal(scalar1, scalar1), "Test scalar equality using the same scalar with mitk::eps"); - MITK_TEST_CONDITION(!mitk::Equal(scalar1, scalar2), - "Test scalar equality using the different scalars with a difference greater than mitk::eps"); - MITK_TEST_CONDITION(mitk::Equal(scalar1, scalar3), - "Test scalar equality using the different scalars with a difference equal to mitk::eps"); - MITK_TEST_CONDITION(mitk::Equal(scalar1, scalar4), - "Test scalar equality using the different scalars with a difference less than mitk::eps"); - - // test matrix equality methods - vnl_matrix_fixed vnlMatrix3x3_1; - vnlMatrix3x3_1(0, 0) = 1.1; - vnlMatrix3x3_1(0, 1) = 0.4; - vnlMatrix3x3_1(0, 2) = 5.3; - vnlMatrix3x3_1(1, 0) = 2.7; - vnlMatrix3x3_1(1, 1) = 3578.56418; - vnlMatrix3x3_1(1, 2) = 123.56; - vnlMatrix3x3_1(2, 0) = 546.89; - vnlMatrix3x3_1(2, 1) = 0.0001; - vnlMatrix3x3_1(2, 2) = 1.0; - vnl_matrix_fixed vnlMatrix3x3_2; - vnlMatrix3x3_2(0, 0) = 1.1000009; - vnlMatrix3x3_2(0, 1) = 0.4000009; - vnlMatrix3x3_2(0, 2) = 5.3000009; - vnlMatrix3x3_2(1, 0) = 2.7000009; - vnlMatrix3x3_2(1, 1) = 3578.5641809; - vnlMatrix3x3_2(1, 2) = 123.5600009; - vnlMatrix3x3_2(2, 0) = 546.8900009; - vnlMatrix3x3_2(2, 1) = 0.0001009; - vnlMatrix3x3_2(2, 2) = 1.0000009; - - mitk::ScalarType epsilon = 0.000001; - MITK_TEST_CONDITION(mitk::MatrixEqualElementWise(vnlMatrix3x3_1, vnlMatrix3x3_1, mitk::eps), - "Test for matrix equality with given epsilon=mitk::eps and exactly the same matrix elements"); - MITK_TEST_CONDITION(!mitk::MatrixEqualElementWise(vnlMatrix3x3_1, vnlMatrix3x3_2, 0.0), - "Test for matrix equality with given epsilon=0.0 and slightly different matrix elements"); - MITK_TEST_CONDITION(mitk::MatrixEqualElementWise(vnlMatrix3x3_1, vnlMatrix3x3_2, epsilon), - "Test for matrix equality with given epsilon and slightly different matrix elements"); - MITK_TEST_CONDITION(!mitk::MatrixEqualRMS(vnlMatrix3x3_1, vnlMatrix3x3_2, 0.0), - "Test for matrix equality with given epsilon=0.0 and slightly different matrix elements"); - MITK_TEST_CONDITION(mitk::MatrixEqualRMS(vnlMatrix3x3_1, vnlMatrix3x3_2, epsilon), - "Test for matrix equality with given epsilon and slightly different matrix elements"); - - MITK_TEST_END(); -} + void VnlVectorEqualityDifferentVnlVectorsWithHighEps_Success() + { + for (int i = 0; i < 7; i++) + { + m_VnlVector_2[i] = m_VnlVector_1[i] - mitk::eps * 1.1f; + } + + CPPUNIT_ASSERT_MESSAGE("vnl_fixed : v_1 == v_2 with eps = 1.2 * mitk::eps ", + (mitk::Equal(m_VnlVector_1, m_VnlVector_2, mitk::eps * 1.2f))); + } + + void VnlVectorEqualityDifferentVnlVectorsWithLowEps_Success() + { + for (int i = 0; i < 7; i++) + { + m_VnlVector_3[i] = m_VnlVector_1[i] - mitk::eps * 0.9f; + } + + CPPUNIT_ASSERT_MESSAGE("vnl_fixed : v_1 == v_3 with eps = 0.8 * mitk::eps ", + (mitk::Equal(m_VnlVector_1, m_VnlVector_3, mitk::eps))); + } + + void VnlVectorEqualityDifferentVnlVectorsWithLowEps_Failure() + { + for (int i = 0; i < 7; i++) + { + m_VnlVector_3[i] = m_VnlVector_1[i] - mitk::eps * 0.9f; + } + + CPPUNIT_ASSERT_NO_THROW_MESSAGE("vnl_fixed : v_1 != v_3 with eps = 0.8 * mitk::eps ", + (mitk::Equal(m_VnlVector_1, m_VnlVector_3, mitk::eps * 0.8f))); + } + + void ScalarEqualitySameScalar_Successs() + { + // test scalar equality method + CPPUNIT_ASSERT_MESSAGE("Test scalar equality using the same scalar with mitk::eps", + mitk::Equal(m_Scalar1, m_Scalar1)); + } + void ScalarEqualityDifferentScalarsDifferenceGreaterEps_Failure() + { + CPPUNIT_ASSERT_NO_THROW_MESSAGE( + "Test scalar equality using the different scalars with a difference greater than mitk::eps", + mitk::Equal(m_Scalar1, m_Scalar2)); + } + + void ScalarEqualityDifferentScalarsDifferenceEqualEps_Successs() + { + CPPUNIT_ASSERT_MESSAGE("Test scalar equality using the different scalars with a difference equal to mitk::eps", + mitk::Equal(m_Scalar1, m_Scalar3)); + } + + void ScalarEqualityDifferentScalarsDifferenceLessEps_Successs() + { + CPPUNIT_ASSERT_MESSAGE("Test scalar equality using the different scalars with a difference less than mitk::eps", + mitk::Equal(m_Scalar1, m_Scalar4)); + } + + void MatrixEqualitySameMatrixElementsWithEps_Success() + { + // test matrix equality methods + CPPUNIT_ASSERT_MESSAGE("Test for matrix equality with given epsilon=mitk::eps and exactly the same matrix elements", + mitk::MatrixEqualElementWise(m_VnlMatrix3x3_1, m_VnlMatrix3x3_1, mitk::eps)); + } + + void MatrixEqualityElementWiseDifferentMatrixElementsWithEpsilonZero_Failure() + { + CPPUNIT_ASSERT_NO_THROW_MESSAGE( + "Test for matrix equality with given epsilon=0.0 and slightly different matrix elements", + mitk::MatrixEqualElementWise(m_VnlMatrix3x3_1, m_VnlMatrix3x3_2, 0.0)); + } + + void MatrixEqualityDifferentMatrixElementsWithEpsilon_Success() + { + CPPUNIT_ASSERT_MESSAGE("Test for matrix equality with given epsilon and slightly different matrix elements", + mitk::MatrixEqualElementWise(m_VnlMatrix3x3_1, m_VnlMatrix3x3_2, m_Epsilon)); + } + + void MatrixEqualityRMSDifferentMatrixElementsWithEpsilon_Failure() + { + CPPUNIT_ASSERT_NO_THROW_MESSAGE( + "Test for matrix equality with given epsilon=0.0 and slightly different matrix elements", + mitk::MatrixEqualRMS(m_VnlMatrix3x3_1, m_VnlMatrix3x3_2, 0.0)); + } + + void MatrixEqualityRMSDifferentMatrixElementsWithEpsilonZero_Success() + { + CPPUNIT_ASSERT_MESSAGE("Test for matrix equality with given epsilon and slightly different matrix elements", + mitk::MatrixEqualRMS(m_VnlMatrix3x3_1, m_VnlMatrix3x3_2, m_Epsilon)); + } +}; +MITK_TEST_SUITE_REGISTRATION(mitkVector) diff --git a/Modules/Core/test/mitkWeakPointerTest.cpp b/Modules/Core/test/mitkWeakPointerTest.cpp index 2d033c535a..5bc4633223 100644 --- a/Modules/Core/test/mitkWeakPointerTest.cpp +++ b/Modules/Core/test/mitkWeakPointerTest.cpp @@ -1,60 +1,111 @@ /*=================================================================== 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. ===================================================================*/ +// Testing +#include "mitkTestFixture.h" #include "mitkTestingMacros.h" -#include +// std includes +#include + +// MITK includes #include -int mitkWeakPointerTest(int /*argc*/, char * /*argv*/ []) +// ITK includes +#include + +class mitkWeakPointerTestSuite : public mitk::TestFixture { - MITK_TEST_BEGIN("WeakPointer") + CPPUNIT_TEST_SUITE(mitkWeakPointerTestSuite); + + MITK_TEST(EqualPointers_Success); + MITK_TEST(ExpiredWeakPointerWithSmartPointerAssignment_Success); + MITK_TEST(ExpiredWeakPointerWithWeakPointerAssignment_Success); + MITK_TEST(ExpiredWeakPointerWithSmartPointerConstructor_Success); + MITK_TEST(ExpiredWeakPointerWithWeakPointerConstructor_Success); + MITK_TEST(DeleteEventCall_Success); - int deleteEventCallbackCalled = 0; + CPPUNIT_TEST_SUITE_END(); - mitk::WeakPointer weakPointer; +private: + mitk::WeakPointer m_WeakPointer; + mitk::WeakPointer m_WeakPointer2; + itk::Object::Pointer m_SmartPointer; - weakPointer.SetDeleteEventCallback([&deleteEventCallbackCalled]() +public: + void setUp() { - ++deleteEventCallbackCalled; - }); + m_SmartPointer = itk::Object::New(); + m_WeakPointer = m_SmartPointer; + m_WeakPointer2 = m_WeakPointer; + } - mitk::WeakPointer weakPointer2; + void tearDown() + { + m_SmartPointer = nullptr; + m_WeakPointer = nullptr; + m_WeakPointer2 = nullptr; + } - // Testing constructors and reference counting - itk::Object::Pointer smartPointer = itk::Object::New(); - mitk::WeakPointer weakPointer3(smartPointer); - mitk::WeakPointer weakPointer4(weakPointer); + void EqualPointers_Success() { - itk::Object::Pointer tmpSmartPointer(weakPointer.Lock()); - itk::Object::Pointer tmpSmartPointer2(weakPointer2.Lock()); - MITK_TEST_CONDITION_REQUIRED(tmpSmartPointer.GetPointer() == tmpSmartPointer2.GetPointer(), - "Testing equal pointers"); + itk::Object::Pointer tmpSmartPointer(m_WeakPointer.Lock()); + itk::Object::Pointer tmpSmartPointer2(m_WeakPointer2.Lock()); + CPPUNIT_ASSERT_MESSAGE("Testing equal pointers", tmpSmartPointer.GetPointer() == tmpSmartPointer2.GetPointer()); } - weakPointer = smartPointer; - weakPointer2 = weakPointer; + void ReferenceCountOfPointers_Success() + { + CPPUNIT_ASSERT_MESSAGE("Testing reference count", 1 == m_SmartPointer->GetReferenceCount()); + } - MITK_TEST_CONDITION_REQUIRED(1 == smartPointer->GetReferenceCount(), "Testing reference count"); - smartPointer = nullptr; - MITK_TEST_CONDITION_REQUIRED(weakPointer.IsExpired(), "Testing expired weak pointer (smart pointer assignment)"); - MITK_TEST_CONDITION_REQUIRED(weakPointer2.IsExpired(), "Testing expired weak pointer (weak pointer assignment)"); - MITK_TEST_CONDITION_REQUIRED(weakPointer3.IsExpired(), "Testing expired weak pointer (smart pointer constructor)"); - MITK_TEST_CONDITION_REQUIRED(weakPointer4.IsExpired(), "Testing expired weak pointer (copy constructor)"); - MITK_TEST_CONDITION_REQUIRED(1 == deleteEventCallbackCalled, "Testing call of delete event callback"); + void ExpiredWeakPointerWithSmartPointerAssignment_Success() + { + m_SmartPointer = nullptr; + CPPUNIT_ASSERT_MESSAGE("Testing expired weak pointer (smart pointer assignment)", m_WeakPointer.IsExpired()); + } - MITK_TEST_END() -} + void ExpiredWeakPointerWithWeakPointerAssignment_Success() + { + m_SmartPointer = nullptr; + CPPUNIT_ASSERT_MESSAGE("Testing expired weak pointer (weak pointer assignment)", m_WeakPointer2.IsExpired()); + } + + void ExpiredWeakPointerWithSmartPointerConstructor_Success() + { + mitk::WeakPointer weakPointer3(m_SmartPointer); + m_SmartPointer = nullptr; + CPPUNIT_ASSERT_MESSAGE("Testing expired weak pointer (smart pointer constructor)", weakPointer3.IsExpired()); + } + + void ExpiredWeakPointerWithWeakPointerConstructor_Success() + { + mitk::WeakPointer weakPointer4(m_WeakPointer); + m_WeakPointer = m_SmartPointer; + m_SmartPointer = nullptr; + CPPUNIT_ASSERT_MESSAGE("Testing expired weak pointer (copy constructor)", weakPointer4.IsExpired()); + } + + void DeleteEventCall_Success() + { + int deleteEventCallbackCalled = 0; + m_WeakPointer.SetDeleteEventCallback([&deleteEventCallbackCalled]() { ++deleteEventCallbackCalled; }); + m_WeakPointer = m_SmartPointer; + m_SmartPointer = nullptr; + CPPUNIT_ASSERT_MESSAGE("Testing call of delete event callback", 1 == deleteEventCallbackCalled); + } +}; +MITK_TEST_SUITE_REGISTRATION(mitkWeakPointer) 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/OpenCL/mitkOpenCL.h b/Modules/OpenCL/mitkOpenCL.h index a0147b238b..d2131a3455 100644 --- a/Modules/OpenCL/mitkOpenCL.h +++ b/Modules/OpenCL/mitkOpenCL.h @@ -1,26 +1,27 @@ /*=================================================================== 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 MITKOPENCL_H_HEADER_INCLUDED #define MITKOPENCL_H_HEADER_INCLUDED #if defined (__APPLE__) || defined(MACOSX) #include #else +#define CL_USE_DEPRECATED_OPENCL_1_2_APIS #include #endif #endif /* MITKOPENCL_H_HEADER_INCLUDED */ 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 504fdc1090..0000000000 --- a/Modules/PhotoacousticsAlgorithms/include/mitkPhotoacousticImage.h +++ /dev/null @@ -1,125 +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 alpha); - - 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 alpha); - }; -} // 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/OpenCL/mitkOpenCL.h b/Modules/PhotoacousticsAlgorithms/source/filters/mitkImageSliceSelectionFilter.cpp similarity index 50% copy from Modules/OpenCL/mitkOpenCL.h copy to Modules/PhotoacousticsAlgorithms/source/filters/mitkImageSliceSelectionFilter.cpp index a0147b238b..b90b5a726e 100644 --- a/Modules/OpenCL/mitkOpenCL.h +++ b/Modules/PhotoacousticsAlgorithms/source/filters/mitkImageSliceSelectionFilter.cpp @@ -1,26 +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. ===================================================================*/ -#ifndef MITKOPENCL_H_HEADER_INCLUDED -#define MITKOPENCL_H_HEADER_INCLUDED +#include "mitkImageSliceSelectionFilter.h" + +mitk::ImageSliceSelectionFilter::ImageSliceSelectionFilter() +{ + MITK_INFO << "Instantiating CropImageFilter..."; + SetNumberOfIndexedInputs(1); + SetNumberOfIndexedOutputs(1); + MITK_INFO << "Instantiating CropImageFilter...[Done]"; +} -#if defined (__APPLE__) || defined(MACOSX) -#include -#else -#include -#endif +mitk::ImageSliceSelectionFilter::~ImageSliceSelectionFilter() +{ + MITK_INFO << "Destructed CastToFloatImageFilter."; +} -#endif /* MITKOPENCL_H_HEADER_INCLUDED */ +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 d4961e2260..0000000000 --- a/Modules/PhotoacousticsAlgorithms/source/mitkPhotoacousticImage.cpp +++ /dev/null @@ -1,539 +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::ReferenceMemory); - - 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 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; - } - if (alpha < 0.00001) - { - 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)) - { - 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; - } - } - } - else - { - for (int n = 0; n < width; ++n) - { - 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 (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.blueberry.ui.qt.log/documentation/UserManual/blueberrylogview.dox b/Plugins/org.blueberry.ui.qt.log/documentation/UserManual/blueberrylogview.dox index 06afcb812e..1487cb3947 100644 --- a/Plugins/org.blueberry.ui.qt.log/documentation/UserManual/blueberrylogview.dox +++ b/Plugins/org.blueberry.ui.qt.log/documentation/UserManual/blueberrylogview.dox @@ -1,16 +1,16 @@ /** \page org_blueberry_ui_qt_log The Logging Plugin -\imageMacro{Logging.png,"Icon of the Logging Plugin",2.00} +\imageMacro{logging.svg,"Icon of the Logging Plugin",2.00} This plug-in records all logging output of events and progress as specified in the source code with time of occurence, level of importance (Info, Warning, Error, Fatal, Debug), the message given and where it happens. The logging starts once the plug-is started. A screenshot of the provided Logging view is shown next. \imageMacro{LogView.png,"Screenshot of the Logging Module",16.00} There are different features available in the view. The filter text field allows for searching all log events containing a certain substring. Using the button "Copy to clipboard" on the bottom right you can copy the current content of the logging view to your clipboard. This enables you to insert the logging information to any text processing application. You can also show more information on every logging message by activating the two checkboxes. In the simple view, leaving both checkboxes unchecked, you'll see logging messages and logging levels. A brief description of the logging levels can be found in the \ref LoggingPage "logging concept documentation". The checkbox "Category" adds a column for the category. The checkbox "Show Advanced Field" shows method, filename and linenumber where the logging message was emitted as well as the running time of the application. The next figure shows all information which can be shown in the Logging Module. \imageMacro{LogViewExplain.png,"Details on the Vizualized Logging Information",16.00} */ diff --git a/Plugins/org.blueberry.ui.qt.log/documentation/UserManual/logging.svg b/Plugins/org.blueberry.ui.qt.log/documentation/UserManual/logging.svg new file mode 100644 index 0000000000..ccb549f8a6 --- /dev/null +++ b/Plugins/org.blueberry.ui.qt.log/documentation/UserManual/logging.svg @@ -0,0 +1,80 @@ + + + + + + + image/svg+xml + + + + + + + + + + + + + diff --git a/Plugins/org.mitk.gui.qt.datamanager/documentation/UserManual/QmitkDatamanager.dox b/Plugins/org.mitk.gui.qt.datamanager/documentation/UserManual/QmitkDatamanager.dox index 09ce1a4f48..08e636a260 100644 --- a/Plugins/org.mitk.gui.qt.datamanager/documentation/UserManual/QmitkDatamanager.dox +++ b/Plugins/org.mitk.gui.qt.datamanager/documentation/UserManual/QmitkDatamanager.dox @@ -1,109 +1,109 @@ /** \page org_mitk_views_datamanager The DataManager -\imageMacro{QmitkDatamanager_Icon.png,"Icon of the Data Manager",2.00} +\imageMacro{data-manager.svg,"Icon of the Data Manager",2.00} \tableofcontents \section QmitkDataManagerIntroduction Introduction The Datamanager is the central componenent to manage medical data like images, surfaces, etc.. After loading one or more data into the Datamanager the data are shown in the four-view window, the so called Standard View. The user can now start working on the data by just clicking into the standard view or by using the MITK-modules such as "Segmentation" or "Basic Image Processing". \imageMacro{QmitkDatamanager_Overview.png,"How MITK looks when started",16.00} \section QmitkDataManagerLoading Loading Data There are three ways of loading data into the Datamanager as so called Data-Elements. The user can just drag and drop data into the Datamanager or directly into one of the four parts of the Standard View. He can as well use the Open-Button in the right upper corner. Or he can use the standard "File->Open"-Dialog on the top. A lot of file-formats can be loaded into MITK, for example
  • 2D-images/3D-volumes with or without several timesteps (*.dcm, *.ima, *.pic, ...)
  • Surfaces (*.stl, *.vtk, ...)
  • Pointsets (*.mps)
  • ...
The user can also load a series of 2D images (e.g. image001.png, image002.png ...) to a MITK 3D volume. To do this, just drag and drop one of those 2D data files into the Datamanager by holding the ALT key. After loading one or more data into the Datamanager they appear as Data-Elements in a sorted list inside the Datamanager. Data-Elements can also be sorted hierarchically as a parent-child-relation. For example after using the Segmentation-Module on Data-Element1 the result is created as Data-Element2, which is a child of Data-Element1 (see Screenshot1). The order can be changed by drag and drop. \imageMacro{QmitkDatamanager_ParentChild.png,"Screenshot1",9.61} The listed Data-Elements are shown in the standard view. Here the user can scale or rotate the medical objects or he can change the cutting planes of the object by just using the mouse inside this view. \section QmitkDataManagerSaving Saving Data There are two ways of saving data from the Datamanger. The user can either save the whole project with all Data-Elements by clicking on "File"->"Save Project" or he can save single Data-Elements by right-clicking->"Save", directly on a Data-Element. When saving the whole project, the sorting of Data-Elements is saved as well. By contrast the sorting is lost, when saving a single Data-Element. \section QmitkDataManagerProperties Working with the Datamanager \subsection QmitkDataManagerPropertiesList List of Data-Elements The Data-Elements are listed in the Datamanager. As described above the elements can be sorted hierarchically as a parent-child-relation. For example after using the Segmentation-Module on Data-Element1 the result is created as Data-Element2, which is a child of Data-Element1 (see Screenshot1). By drag and drop the sorting of Data-Elements and their hierarchical relation can be changed. \subsection QmitkDataManagerPropertiesVisibility Visibility of Data-Elements By default all loaded Data-Elements are visible in the standard view. The visibility can be changed by right-clicking on the Data-Element and then choosing "Toogle visibility". The box in front of the Data-Element in the Datamanager shows the visibility. A green-filled box means a visible Data-Element, an empty box means an invisible Data-Element (see Screenshot1). \subsection QmitkDataManagerPropertiesRepresentation Representation of Data-Elements There are different types of representations how to show the Data-Element inside the standard view. By right-clicking on the Data-Element all options are listed (see Screenshot2 and Screenshot 3).
  • An arbitrary color can be chosen
  • The opacity can be changed with a slide control
  • In case of images a texture interpolation can be switched on or off. The texture interpolation smoothes the image, so that no single pixels are visible anymore.
  • In case of surfaces the surface representation can be changed between points, wireframe or surface.
  • Global reinit updates all windows to contain all the current data: - The orientation of the worldgeometry, which basically defines the rendering space, is set to the standard coordinate system, i.e. [(0,0,1);(0,1,0);(0,0,1)] - The size of the worldgeometry is calculated, so that it includes all loaded data (depends on size and position of your data) - The spacing is set to the smallest existing spacing regarding your data Reinit updates a single data item and fits the windows to contain only this data item: - The orientation of the worldgeometry, is aligned according to the orientation of the currently selected datanode - The size of the worldgeometry is set to the size of the currently selected datanode - The spacing is set to the spacing of the currently selected datanode
\imageMacro{QmitkDatamanager_ImageProperties.png,"Screenshot2: Properties for images",10.56} \imageMacro{QmitkDatamanager_SurfaceProperties.png,"Screenshot3: Properties for surfaces",11.01} \subsection QmitkDataManagerPropertiesPreferences Preferences For the datamanager there are already some default hotkeys like the del-key for deleting a Data-Element. The whole list is seen in Screenshot4. From here the Hotkeys can also be changed. The preference page is found in "Window"->"Preferences". \imageMacro{QmitkDatamanager_Preferences.png,"Screenshot4",16.00} \section QmitkDataManagerPropertyList Property List The Property List displays all the properties the currently selected Data-Element has. Which properties these are depends on the Data-Element. Examples are opacity, shader, visibility. These properties can be changed by clicking on the appropriate field in the "value" column. \imageMacro{QmitkDatamanager_PropertyList.png,"Screenshot5: Property List",7.85} */ diff --git a/Plugins/org.mitk.gui.qt.datamanager/documentation/UserManual/data-manager.svg b/Plugins/org.mitk.gui.qt.datamanager/documentation/UserManual/data-manager.svg new file mode 100644 index 0000000000..7dd5c797d5 --- /dev/null +++ b/Plugins/org.mitk.gui.qt.datamanager/documentation/UserManual/data-manager.svg @@ -0,0 +1,60 @@ + + + + + + + image/svg+xml + + + + + + + + diff --git a/Plugins/org.mitk.gui.qt.dicom/documentation/UserManual/QmitkDicom.dox b/Plugins/org.mitk.gui.qt.dicom/documentation/UserManual/QmitkDicom.dox index fb81cee2d4..136c56e93d 100644 --- a/Plugins/org.mitk.gui.qt.dicom/documentation/UserManual/QmitkDicom.dox +++ b/Plugins/org.mitk.gui.qt.dicom/documentation/UserManual/QmitkDicom.dox @@ -1,118 +1,118 @@ /** \page org_mitk_gui_qt_dicom The Dicom Plugin -\imageMacro{QmitkDicom_Icon.png,"Icon of the DICOM Plugin",2.00} +\imageMacro{dicom.svg,"Icon of the DICOM Plugin",2.00} \note This article requires a basic knowledge of the DICOM Standard. \tableofcontents \section org_mitk_gui_qt_dicomOverview Overview The DICOM editor is an experimental editor which allows for loading of DICOM images as well as server communication. It features a highly experimental query/retrieve (you need to configure your PACS correspondingly) as well as a DICOM browser. The DICOM browser allows you to navigate the DICOM folder/cd depending on its metadata (patient/study/series) and import selected series for viewing in your MITK based application. It also allows you to store your dicom data in an internal database so you can easily access often used dicom images. It is based on the commonTK (CTK) DICOM funcionality. \section org_mitk_gui_qt_dicomDataHandling Data handling \imageMacro{QmitkDicom_PluginControls.png,"The dicom Plugin controls",7.37} In the image above you see the start page of the dicom plugin. On top of the start page you see four buttons. The Local Storage, the Import CD, the Import Folder and the Query Retrieve button. If you press one of these buttons, the dicom plugin will switch to your local dicom image storage or will start importing dicom images from CD or a folder on your hard drive or it will open the query retrieve screen.
  • Click the 'Local Storage' button to open the local storage screen.
  • Click the 'Import CD' button to import DICOM data from a CD.
  • Click the 'Import Folder' button to import DICOM date from a directory.
  • Click the 'Query Retrieve' button to open the query retrieve screen.
\subsection org_mitk_gui_qt_dicomStorage Data storage \imageMacro{QmitkDicom_PluginExtended.png,"The DICOM data storage",16.00} If you open the dicom plugin the dicom data storage will be displayed. You are able to see all your stored dicom image data. You can browse your data by clicking on the left arrow beside the name of your data. There are three levels available. The first level is the patient level where you can see the patient data. On the second level you can see the dicom studies for the patient. on the third level you can see all available series refering to it's study. You can delete the data by selecting it and pressing the delete button. Be careful if you have selected a patient or a study all refering data be deleted. So if you delete a patient the patient and all studies and series refered to the patient will be deleted. If you delete a study all series of the study will be deleted. If you want to view the dicom data you have to select a series and click on the View button. The data will appear in the DataManager and will be dispayed. \imageMacro{QmitkDicom_DisplayDataManager.png,"Viewed image",16.00}
  • Click on the arrow on the left of your data to expand or hide dicom data levels.
  • Click the 'Delete' button to delete selected DICOM data.
  • Click the 'View' button to view DICOM data.
\subsection org_mitk_gui_qt_dicomImport Data import \imageMacro{QmitkDicom_ImportDialog.png,"The import dialog checked",9.53} There are two diffrent ways to import DICOM data. The First one is to directly imort it into your DICOM data storage. To achieve this you should toggle the checkbox 'Copy on import'. The second approach is, to have a look at the data first before importing it. To do that you simply don't check 'Copy on import'. This will leed you to the leed you to the 'External Dicom Data' screen which provides you a preview of the data containing in youre choosen folder. You can import the data here by selecting it and pressing the 'Download' button. It is also possible to view DICOM series directly in Mitk by selecting it here and pressing the 'View' button.
  • Click 'Import Folder' or 'Import CD' button to open the import dialog.
    • Enable the 'Copy on import' checkbox and choose a folder to import into data storage directly.
    • Disable the 'Copy on import' checkbox to get to the 'External Dicom Data' screen.
      • Click on the arrow on the left of your data to expand or hide dicom data levels.
      • Click the 'Download' button to download selected DICOM data to your DICOM data storage.
      • Click the 'View' button to view DICOM data.
\section org_mitk_gui_qt_dicomQueryRetrieve Query/Retrieve \warning This plugin is experimental and not all of the described features behave as expected. \note The query retrieve plugin only works if the PACS you are calling knows your machine settings. There are also issues when you are running a firewall. The query retrieve workflow allows you to get DICOM data from a server. \imageMacro{QmitkDicom_QueryRetrieve.png,"The query retrieve screen",16.00} \subsection org_mitk_gui_qt_dicomQuery Query \imageMacro{QmitkDicom_Nodes.png,"The DICOM network configuration",11.26} By performing a DICOM query you will ask a server for it's DICOM data. This requires to setup the DICOM network configuration of your system and the server. By clicking on 'Add Server' a new plain server field will appear. Now you can give it a name of your choice. Fill the servers "DICOM name" the AETitle. Type in it's url, it's port and the specific DICOM protocoll you want to use for image transfer. \note I recommend not to use CGET because most of the PACS systems (Image Servers) don't support that protocoll. You can configure the DICOM network configuration of your machine by editing the 'Calling AETiltle', the 'Storage AETitle' and The 'Storage Port' text fields. But normaly you don't have to change your configuration. \imageMacro{QmitkDicom_FilterWidget.png,"The DICOM search options",3.66} After you have finished your network configuration and before you start the query you should use the 'Search Options' to specify your query. Otherwise all data on the server will be queried and you will have to wait for a long time. You can specify your query by searching for a specific patient name or a study or a serie or a specific DICOM object by it's id. You are allowed to include or exclude DICOM modalities from your query and you can specify a specific time in which the DICOM images you are searching fo might been captured. When you finished that you can click the query button and the queried DICOM data will appear.
  • Click on the 'Add Server' button.
    • Edit 'Name' field.
    • Edit 'AETitle' field.
    • Edit 'Adress' field.
    • Edit 'Port' field.
  • Set search options.
  • Click on 'Query' button.
\subsection org_mitk_gui_qt_dicomRetrieve Retrieve \imageMacro{QmitkDicom_Retrieve.png,"The queried DICOM data.",15.22} After the query you are able to select the queried data and click the 'Retrieve' button. This will store the queried DICOM data into your DICOM storage. Click on the 'Local Storage' button and work with your new data.
  • Click on the 'Retrieve' button to retrieve the data to your DICOM storage.
  • Click on the 'Local Storage' button.
*/ diff --git a/Plugins/org.mitk.gui.qt.dicom/documentation/UserManual/dicom.svg b/Plugins/org.mitk.gui.qt.dicom/documentation/UserManual/dicom.svg new file mode 100644 index 0000000000..3d10f794ae --- /dev/null +++ b/Plugins/org.mitk.gui.qt.dicom/documentation/UserManual/dicom.svg @@ -0,0 +1,85 @@ + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + diff --git a/Plugins/org.mitk.gui.qt.imagecropper/documentation/UserManual/QmitkImageCropper.dox b/Plugins/org.mitk.gui.qt.imagecropper/documentation/UserManual/QmitkImageCropper.dox index 70208b09c3..62658b5368 100644 --- a/Plugins/org.mitk.gui.qt.imagecropper/documentation/UserManual/QmitkImageCropper.dox +++ b/Plugins/org.mitk.gui.qt.imagecropper/documentation/UserManual/QmitkImageCropper.dox @@ -1,37 +1,37 @@ /** \page org_mitk_gui_qt_imagecropper Image Cropper Plugin -\imageMacro{QmitkImageCropper_Icon.png,"Icon of the Image Cropper Plugin.",20} +\imageMacro{crop.svg,"Icon of the Image Cropper Plugin.",20} \tableofcontents \section org_mitk_gui_qt_imagecropperUsage Usage The Image Cropper Plugin allows to crop subvolumes out of your original image volume by defining a cubic bounding box. This box can be placed at an arbitrary position in the volume and can be easily adjusted by using the handles on each of the faces. Touching the handles changes the size of the box whereas touching the box itself changes its position. As soon as the bounding box is placed at the desired position, pressing the button 'Crop' creates a new image assigned to the original image as child node containing only the selected subvolume. The size of the subvolume equals the size of the bounding box. Pressing the "Mask" button keeps the original image size but masks out the area not contained within the bounding box bounds. In case of 3D+t images the whole time series is cropped by default. \imageMacro{BoundingBox_ImageCropperView.png,"Bounding Box.",12.00} \imageMacro{Basic_ImageCropperView.png,"Basic Settings.",7.09} \section org_mitk_gui_qt_imagecropperAdvanced Advanced settings In the advanced settings view you find additional features to manipulate the bounding box. \imageMacro{Advanced_ImageCropperView.png,"Advanced Settings.",7.09} \subsection org_mitk_gui_qt_imagecropperAdvancedOverwrite Overwrite original image By enabling this checkbox the image is replaced by the cropped subvolume. Be careful to use this option since there is no undo action available. \subsection org_mitk_gui_qt_imagecropperAdvancedTimestep Crop current time step only If this checkbox is enabled the xD + t image is reduced to a xD image (e.g., 3D+t --> 3D) with the time step visible in the widget. This is useful if you want to extract a single image or its corresponding subvolume of the time series. The whole time series is cropped by default using the timeGeometry of the time step visible in the widget. \section org_mitk_gui_qt_imagecropperIssues Current issues Cropping 2D images is not supported unless the are 3D images containing only a single slice. The user will be notified by a warning and the input is handled as a single label image. Right now changing the shape or rotation of the bounding box is not supported but might be integrated in the future. */ \ No newline at end of file diff --git a/Plugins/org.mitk.gui.qt.imagecropper/documentation/UserManual/crop.svg b/Plugins/org.mitk.gui.qt.imagecropper/documentation/UserManual/crop.svg new file mode 100644 index 0000000000..7abad89683 --- /dev/null +++ b/Plugins/org.mitk.gui.qt.imagecropper/documentation/UserManual/crop.svg @@ -0,0 +1,60 @@ + + + + + + + image/svg+xml + + + + + + + + diff --git a/Plugins/org.mitk.gui.qt.imagenavigator/documentation/UserManual/QmtikImageNavigator.dox b/Plugins/org.mitk.gui.qt.imagenavigator/documentation/UserManual/QmtikImageNavigator.dox index 2267b623d2..e158d98694 100644 --- a/Plugins/org.mitk.gui.qt.imagenavigator/documentation/UserManual/QmtikImageNavigator.dox +++ b/Plugins/org.mitk.gui.qt.imagenavigator/documentation/UserManual/QmtikImageNavigator.dox @@ -1,15 +1,15 @@ /** \page org_mitk_views_imagenavigator The Image Navigator -\imageMacro{QmtikImageNavigator_Slider.png,"Icon of the Image Navigator",2.00} +\imageMacro{image_navigator.svg,"Icon of the Image Navigator",2.00} \imageMacro{QmtikImageNavigator_ImageNavigator.png,"Image Navigator",7.47} Fast movement through the available data can be achieved by using the Image Navigator. By moving the sliders around you can scroll quickly through the slides and timesteps. By entering numbers in the relevant fields you can jump directly to your point of interest. The "Show detail" checkbox enables you to see the world coordinates in millimetres and the index/voxel coordinates. These may be edited to jump to a specific location. */ \ No newline at end of file diff --git a/Plugins/org.mitk.gui.qt.imagenavigator/documentation/UserManual/image_navigator.svg b/Plugins/org.mitk.gui.qt.imagenavigator/documentation/UserManual/image_navigator.svg new file mode 100644 index 0000000000..6bde98e8bc --- /dev/null +++ b/Plugins/org.mitk.gui.qt.imagenavigator/documentation/UserManual/image_navigator.svg @@ -0,0 +1,90 @@ + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + diff --git a/Plugins/org.mitk.gui.qt.measurementtoolbox/documentation/UserManual/QmitkImageStatistics.dox b/Plugins/org.mitk.gui.qt.measurementtoolbox/documentation/UserManual/QmitkImageStatistics.dox index f9c2915943..0de797abdb 100644 --- a/Plugins/org.mitk.gui.qt.measurementtoolbox/documentation/UserManual/QmitkImageStatistics.dox +++ b/Plugins/org.mitk.gui.qt.measurementtoolbox/documentation/UserManual/QmitkImageStatistics.dox @@ -1,54 +1,54 @@ /** \page org_mitk_views_imagestatistics The Image Statistics View -\imageMacro{QmitkMeasurementToolbox_ImageStatisticsIcon.png,"Icon of the Image Statistics View",2.00} +\imageMacro{bar-chart.svg,"Icon of the Image Statistics View",2.00} \section QmitkImageStatisticsUserManualSummary Summary This view provides an easy interface to quickly compute some features of a whole image or a region of interest. This document will tell you how to use this view, but it is assumed that you already know how to use MITK in general. Please see \ref QmitkImageStatisticsUserManualDetails for more detailed information on usage and supported filters. If you encounter problems using the view, please have a look at the \ref QmitkImageStatisticsUserManualTrouble page. \section QmitkImageStatisticsUserManualDetails Details Manual sections: - \ref QmitkImageStatisticsUserManualOverview - \ref QmitkImageStatisticsUserManualUsage - \ref QmitkImageStatisticsUserManualTrouble \section QmitkImageStatisticsUserManualOverview Overview This view provides an easy interface to quickly compute some features of a whole image or a region of interest. \imageMacro{QmitkMeasurementToolbox_Interface.png,"The interface",9.10} \section QmitkImageStatisticsUserManualUsage Usage After selection of an image or a binary mask of an image in the datamanager, the Image Statistics view shows some statistical information. If a mask is selected, the name of the mask and the name of the image, to which the mask is applied, are shown at the top. For time data the current time step is used for the selected mask and the selected image. If the total number of time steps on the selected mask is less than the current time step, the last time step of the mask is used. If a mask is selected, its used time step will be displayed next to its name like this: (t=0). Check "Ignore zero-valued voxels" to hide voxels with grayvalue zero. Below it is the statistics window which displays the calculated statistical features (such as mean, standard deviation...). Beneath the statistics window is the histogram window, which shows the histogram of the current selection. At top of the histogram window are two radiobuttons. Toggle one of them to either show the histogram as a barchart or as a lineplot. Use mousewheel to zoom in and out the histogram. With the left mouse button the histogram is pannable in zoomed state. If the histogram is displayed as a barchart a tooltip is available by hovering over one of the bins. A tooltip is also available, if an intesity profile is created for a path element as mask. At the bottom of each view is one button. They copy their respective data in csv format to the clipboard. \section QmitkImageStatisticsUserManualTrouble Troubleshooting No known problems. All other problems.
Please report to the MITK mailing list. See http://www.mitk.org/wiki/Mailinglist on how to do this. */ diff --git a/Plugins/org.mitk.gui.qt.measurementtoolbox/documentation/UserManual/QmitkMeasurement.dox b/Plugins/org.mitk.gui.qt.measurementtoolbox/documentation/UserManual/QmitkMeasurement.dox index 613a057372..e7ea6bd729 100644 --- a/Plugins/org.mitk.gui.qt.measurementtoolbox/documentation/UserManual/QmitkMeasurement.dox +++ b/Plugins/org.mitk.gui.qt.measurementtoolbox/documentation/UserManual/QmitkMeasurement.dox @@ -1,126 +1,126 @@ /** \page org_mitk_views_measurement The Measurement View -\imageMacro{QmitkMeasurementToolbox_MeasurementIcon.png,"Icon of the Measurement View",2.00} +\imageMacro{measurement.svg,"Icon of the Measurement View",2.00} \section QmitkMeasurementUserManualOverview Overview The Measurement view enables the user to interact with 2D images or single slices of 3D image stacks and planar figure data types. It allows to measure distances, angels, pathes and several geometric figures on a dataset. \tableofcontents The workflow to use this view is: \imageMacro{QmitkMeasurementToolbox_Workflow.png,"",16.00} The workflow is repeatedly useable with the same or different measurement figures, which are correlated to the choosen image and can be saved together with it for future use. On pressing the Measurement icon (see picture below the page title) in the view button line the basic appearance of the view is as follws. \imageMacro{QmitkMeasurementToolbox_BasicScreenEdited.jpg,"",16.00} The standard working plane is "Axial" but the other standard viewplanes ("Saggital" and "Coronal") are also valid for measurements. To swap between the view planes refer to the application user manual. \section QmitkMeasurementUserManualFeatures Features The view as it is depicted below offers the following features in the order of apperance on the image from top to bottom: \imageMacro{QmitkMeasurementToolbox_MeasurementView.jpg,"",7.60} The first information is the selected image's name (here: DICOM-MRI-Image) followed by the measurement figures button line with the seven measurement figures. From left to right the buttons are connected with the following functions: \subsection SubOne Draw Line Draws a line between two set points and returns the distance between these points. \subsection SubTwo Draw Path Draws a path between several set points (two and more) and calculates the circumference, that is all line's length summed up. Add the final point by double left click. \subsection SubThree Draw Angle Draws two lines from three set points connected in the second set point and returns the inner angle at the second point. \subsection SubFour Draw Four Point Angle Draws two lines that may but must not intersect from four set points. The returned angle is the one depicted in the icon. \subsection SubFive Draw Circle Draws a circle by setting two points, whereas the first set point is the center and the second the radius of the circle. The measured values are the radius and the included area. \subsection SubSix Draw Rectangle Draws a rectangle by setting two points at the opposing edges of the rectangle starting with the upper left edge. The measured values are the circumference and the included area. \subsection SubSeven Draw Polygon Draws a polygon by setting three or more points. The measured values are the circumference and the included area. Add the final point by double left click. Below the buttonline the statistics window is situated, it displays the results of the actual measurements from the selected measurement figures. The content of the statistics window can be copied to the clipboard with the correspondig button for further use in a table calculation programm (e.g. Open Office Calc etc.). \imageMacro{QmitkMeasurementToolbox_ImageProcessed.jpg,"",7.56} The last row contains again a button line to swap from the measurement perspective (activated in the image) to other supported MITK perspectives. \section QmitkMeasurementUserManualUsage Usage This Section is subdivided into four subsections:
  1. Add an image
  2. Work with measurement figures
  3. Save the image with measurement information
  4. Remove measurement figures or image
Let's start with subsection 1 \subsection One Add an image There are two possible ways to add an image to the programm. One is to grap the image with left mouse click from your prefered file browser and simply drag&drop it to the View Plane field. The other way is to use the \imageMacro{QmitkMeasurementToolbox_OpenButton.png,"",2.01} button in the upper left corner of the application. A dialog window appears showing the file tree of the computer. Navigate to the wanted file and select it with the left mouse click. Afterwards just use the dialog's open button. The wanted image appears in the View Plane and in the Data Manager the images name appears as a new tree node. Now the image is loaded it can be adjusted in the usual way ( zoom in/out: right mouse button + moving the mouse up and down, moving the image: press mouse wheel and move the mouse to the wished direction, scroll through the slices( only on 3D images): scroll mouse wheel up and down). \imageMacro{QmitkMeasurementToolbox_ImageLoadedScreen.jpg,"",16.00} After the image is loaded the image's name appears in the Data Manager. By left-clicking on the image name the buttonline becomes activated. \subsection Two Work with measurement figures The measurement view comes with seven measurement figures(see picture below), that can be applied to the images. \imageMacro{QmitkMeasurementToolbox_MeasurementFigureButtonLine.jpg,"",7.22} The results of the measurement with each of these figures is shown in the statistics window and in the lower right corner of the view plane. \imageMacro{QmitkMeasurementToolbox_ImageProcessedScreen.jpg,"",6.96} When applying more then one measurement figure to the image the actual measurement figure is depicted in red and the displayed values belong to this measurement figure. All measurement figures become part of the Data Manager as a node of the image tree. \subsection Three Save the image with measurement information After applying the wanted measurement figures the entire scene consisting of the image and the measurement figures can be saved for future use. Therefore just click the right mouse button when over the image item in the Data Manager and choose the item "Save" in the opening item list. Following to that a save dialog appears where the path to the save folder can be set. Afterwards just accept your choice with the save button. \subsection Four Remove measurement figures or image If the single measurement figures or the image is not needed any longer, it can be removed solely or as an entire group. The image can't be removed without simultaneously removing all the dependent measurement figures that belong to the image tree in the Data Manager. To remove just select the wanted items in the data manager list by left-click on it or if several items wanted to be removed left click on all wanted by simultaneously holding the ctrl-button pressed. For more detailed usage of the save/remove functionality refer to the Data Manager User Manual. ",16.00} */ diff --git a/Plugins/org.mitk.gui.qt.measurementtoolbox/documentation/UserManual/bar-chart.svg b/Plugins/org.mitk.gui.qt.measurementtoolbox/documentation/UserManual/bar-chart.svg new file mode 100644 index 0000000000..a756ada699 --- /dev/null +++ b/Plugins/org.mitk.gui.qt.measurementtoolbox/documentation/UserManual/bar-chart.svg @@ -0,0 +1,65 @@ + + + + + + image/svg+xml + + + + + + + + + + + diff --git a/Plugins/org.mitk.gui.qt.measurementtoolbox/documentation/UserManual/measurement.svg b/Plugins/org.mitk.gui.qt.measurementtoolbox/documentation/UserManual/measurement.svg new file mode 100644 index 0000000000..d5e6defc77 --- /dev/null +++ b/Plugins/org.mitk.gui.qt.measurementtoolbox/documentation/UserManual/measurement.svg @@ -0,0 +1,196 @@ + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Plugins/org.mitk.gui.qt.moviemaker/documentation/UserManual/QmitkMovieMaker.dox b/Plugins/org.mitk.gui.qt.moviemaker/documentation/UserManual/QmitkMovieMaker.dox index 2cfc3a9318..cc8f00e9a7 100644 --- a/Plugins/org.mitk.gui.qt.moviemaker/documentation/UserManual/QmitkMovieMaker.dox +++ b/Plugins/org.mitk.gui.qt.moviemaker/documentation/UserManual/QmitkMovieMaker.dox @@ -1,64 +1,64 @@ /** \page org_mitk_gui_qt_moviemaker The Movie Maker Plugin -\imageMacro{QmitkMovieMaker_Icon.png,"Icon of the Movie Maker Plugin.",2.00} +\imageMacro{video-camera.svg,"Icon of the Movie Maker Plugin.",2.00} \tableofcontents \section org_mitk_gui_qt_moviemakerOverview Overview The Movie Maker View allows you to create basic animations of your scene and to record them to video files. Individual animations are arranged in a timeline and can be played back sequential or in parallel. The Movie Maker View uses external FFmpeg/Libav command line utilities to write compressed video files. You have to manually install either FFmpeg or Libav and set the corresponding path in "External Programs" in the MITK Workbench Preferences (Ctrl+P) in order to record your movies to video files. \imageMacro{QmitkMovieMaker_Preferences.png,"The External Programs preferences page.",12.00} \section org_mitk_gui_qt_moviemakerUsage Usage \imageMacro{QmitkMovieMaker_MovieMakerView.png,"The Movie Maker View.",16.00} To create a movie you have to add an animation to the timeline by clicking the "Add animation" button. You can choose between the available types of animations, e.g., Orbit or Slice. The timeline surrounding bottons allow you to arrange, remove, or add further animations to your movie. Each animation can be set to either begin with the previous animation, i.e., run in parallel, or to start after the previous animation, i.e., run sequential. In combination with delays, rather complex animation arrangements are possible. To set animation specific parameters, select the corresponding animation in the timeline first. You can play back, pause and stop your movie with the according controls at the bottom of the Movie Maker View. Click the "Record" button to finally record your movie to a video file with the specified number of frames per second. You have to choose the render window which you want to record. \subsection org_mitk_gui_qt_moviemakerOrbitUsage Orbit Animation The Orbit animation rotates the camera in the 3D window around the scene. Align the camera directly in the 3D window and enter the number of degrees for the orbitting. If you are planning to have a specific view in the middle of your movie you can play the movie and pause it at the specific frame of interest. Adjust the camera in the 3D window and restart the animation. \imageMacro{QmitkMovieMaker_Orbit.png,"The Orbit animation.",12.00} \subsection org_mitk_gui_qt_moviemakerSliceUsage Slice Animation The Slice animation slices through an image. You can choose the image plane (axial, sagittal, or coronal), as well as the start and end points of the slicing. Use the image navigator in the bottom left of the Workbench to get an idea of the desired values. Check "Reverse" in order to slice from the higher slice number to the lower slice number. \imageMacro{QmitkMovieMaker_Slice.png,"The Slice animation.",12.00} \subsection org_mitk_gui_qt_moviemakerTimeUsage Time Animation The Time animation steps through the individual time steps of the current scene. You can specify the range of the animated time steps. Use the image navigator in the bottom left of the Workbench to get an idea of the desired values. Check "Reverse" in order to step from later time steps to previous time steps. \imageMacro{QmitkMovieMaker_Time.gif,"The Time animation.",12.00} */ diff --git a/Plugins/org.mitk.gui.qt.moviemaker/documentation/UserManual/video-camera.svg b/Plugins/org.mitk.gui.qt.moviemaker/documentation/UserManual/video-camera.svg new file mode 100644 index 0000000000..ebf0fd1bfd --- /dev/null +++ b/Plugins/org.mitk.gui.qt.moviemaker/documentation/UserManual/video-camera.svg @@ -0,0 +1,60 @@ + + + + + + image/svg+xml + + + + + + + + + diff --git a/Plugins/org.mitk.gui.qt.multilabelsegmentation/documentation/UserManual/multilabelsegmentation.svg b/Plugins/org.mitk.gui.qt.multilabelsegmentation/documentation/UserManual/multilabelsegmentation.svg new file mode 100644 index 0000000000..9b24feb3b2 --- /dev/null +++ b/Plugins/org.mitk.gui.qt.multilabelsegmentation/documentation/UserManual/multilabelsegmentation.svg @@ -0,0 +1,10683 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Plugins/org.mitk.gui.qt.multilabelsegmentation/documentation/UserManual/org_mitk_gui_qt_multilabelsegmentation.dox b/Plugins/org.mitk.gui.qt.multilabelsegmentation/documentation/UserManual/org_mitk_gui_qt_multilabelsegmentation.dox index 77069cfa4b..a88b5f28fe 100644 --- a/Plugins/org.mitk.gui.qt.multilabelsegmentation/documentation/UserManual/org_mitk_gui_qt_multilabelsegmentation.dox +++ b/Plugins/org.mitk.gui.qt.multilabelsegmentation/documentation/UserManual/org_mitk_gui_qt_multilabelsegmentation.dox @@ -1,114 +1,114 @@ /** \page org_mitk_views_multilabelsegmentation The Multilabel Segmentation View -\imageMacro{"multilabelsegmentation.png", "html", 12} +\imageMacro{"multilabelsegmentation.svg", "Icon of the MultiLabelSegmentation Plugin", 12} Please reference \ref org_mitk_views_segmentation for the description of the general segmentation tools. \tableofcontents \section org_mitk_views_multilabelsegmentationUserManualCreateOpenSaveImportAdd Start Segmenting To start using the Segmentation Perspective you will have to either create a new segmentation session or load an existing one from disk. The Segmentation toolbar collects buttons for the these actions: \imageMacro{"org_mitk_views_multilabelsegmentationIMGtoolbar.png", "Segmentation toolbar", 12}
  • Create segmentation session
  • a new segmentation session is created.
  • Load segmentation session
  • a segmentation session can be loaded from disk (.lset file extensions).
  • Save segmentation session
  • the current segmentation session can be saved to disk.
  • Import segmentation session
  • a segmentation session can be incorporated into the current one. All new labels will be appended at the end of the table.
  • Add label
  • a new label is appended to the current segmentation session, at the end of the table.
\section org_mitk_views_multilabelsegmentationUserManualLabelTable The Label Table The following label properties are readily available to modify:
  • Name
  • the name of the label. Can be a predefined one or any other.
  • Color
  • the color of the label.
  • Visible
  • whether the label is currently visible or hiden.
  • Locked
  • whether the label is locked or editable. A locked label cannot be overwritten by another.
The Label Table is shown below: \imageMacro{"org_mitk_views_multilabelsegmentationIMGlabeltable.png", "The Label Table showing all the labels in the current segmentation session", 12} \section org_mitk_views_multilabelsegmentationUserManualLabelCreation Creating a New Label Click the "New Label" button to add a new label. A dialog will show-up to enter the name and color. Preset organ names and corresponding colors are offered while you type in, but you can set any name. The new name if not known will be automatically remembered and made available the next time you create a new label. In the current implementation of the plugin, the maximum number of labels is restricted to 255. If you need more, you will have to create a new segmentation session. \section org_mitk_views_multilabelsegmentationUserManualLayerCreation Creating a New Layer A layer is a set of labels that occupy a non-overlapping anatomical space. The best way to describe them is by a real use case. Imagine you are working on a radiotherpay planning application. In the first layer of your segmentation session you would like to trace the contours of the liver and neighboring organs. You can accomodate all these segmentations in separate labels because they all occupy different anamical regions and do not overlap. Now say you would like to segment the arteries and veins inside the liver. If you don´t trace them in a different layer, you will overwrite the previous ones. You may also need a third layer for segmenting the different irrigation territories in the liver and a fourth layer to contain the lession you would like to treat. The next figure illustrates the Layer Manager . The buttons in it contained serve for adding a new layer, selecting the previous and the next one. The active layer is shown together with the buttons. \imageMacro{"org_mitk_views_multilabelsegmentationIMGlayerManager.png", "Correction Tool",12} \section org_mitk_views_multilabelsegmentationUserManualLabelSearch Searching a Label It may happen that many labels (e.g. > 200) are present in a segmentation session and therefore manual searching is time consuming. The Label Search edit box allows for quickly finding the label you want. Just start writing its name and and you will get assitance for completing its name. If the label you were searching is found, press enter and it will became the active one. \imageMacro{"org_mitk_views_multilabelsegmentationIMGsearchlabel.png", "Label search", 12} \section org_mitk_views_multilabelsegmentationUserManualLabelEditing Label Editing First of all, you have to select the active label by clicking on the corresponding row in the Label Table. Only one label can be active at the time. Then you can select an editing tool in the toolbox. \section org_mitk_views_multilabelsegmentationUserManualOperationsOnLabels Operations on Labels Depending on your selection in the Label Table , several actions are offered: \subsection org_mitk_views_multilabelsegmentationUserManualOperationsOnSingleSelection Single Label Selection If you right click on any label in the table, a menu will pop-up offering the following actions to be performed on the selected label:
  • Rename...
  • : change the name and/or color of the selected label.
  • Remove label
  • : delete the selected label.
  • Erase label
  • : only clear the contents of the selected label.
  • Random color
  • : generate a surface mesh out of the selected label.
  • View only
  • : generate a mask out of the selected label. A mask is a binary image with "1" inside and "0" outside.
  • View/Hide all
  • : generate a mask out of the selected label. A mask is a binary image with "1" inside and "0" outside.
  • Lock/Unlock all
  • : generate a mask out of the selected label. A mask is a binary image with "1" inside and "0" outside.
  • Create surface
  • : generate a surface out of the selected label.
  • Create mask
  • : generate a mask out of the selected label. A mask is a binary image with "1" inside and "0" outside.
\imageMacro{"org_mitk_views_multilabelsegmentationIMGLabelTableSingleSelectionContextMenu.png", "Context menu for single label selection", 12} \subsection org_mitk_views_multilabelsegmentationUserManualOperationsOnMultipleSelection Multiple Label Selection If more than one label is selected, a different menu will show up: \imageMacro{"org_mitk_views_multilabelsegmentationIMGLabelTableMultipleSelectionContextMenu.png", "Context menu for multiple label selection", 12}
  • Merge selection on current label
  • : transfer the contents of the selected labels in the Label Table into the current one.
  • Remove selected labels
  • : delete the selected labels.
  • Erase selected labels
  • : only clear the contents of the selected labels.
  • Create a surface for each selected label
  • : generate a surface mesh out of each selected label.
  • Combine and create a surface
  • : generate a surface out of the combination of the selected labels.
  • Create a mask for each selected label
  • : generate a mask out of each selected label. A mask is a binary image with "1" inside and "0" outside.
  • Combine and create a mask
  • : generate a mask out of the combination of the selected labels.
*/ 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 2960377da6..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,1161 +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.BPFalloff->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.BPFalloff->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.BPFalloff->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.BPFalloff->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_TukeyAlpha); - 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 TukeyAlpha, float recordTime) +void BandpassThread::setConfig(float BPHighPass, float BPLowPass, float TukeyAlphaHighPass, float TukeyAlphaLowPass, float recordTime) { m_BPHighPass = BPHighPass; m_BPLowPass = BPLowPass; - m_TukeyAlpha = TukeyAlpha; + 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 d20b78b3ae..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,254 +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 TukeyAlpha, float recordTime); + 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_TukeyAlpha; + 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 fcd6e415c8..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,991 +1,998 @@ PAImageProcessingControls 0 0 - 382 + 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 - true + false Save - true + 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> Absolute Filter Absolute Filter Envelope Detection Do Resampling true 0 0 13 0 11 3 0.010000000000000 1.000000000000000 0.010000000000000 - 0.075000000000000 + 0.150000000000000 [mm] Resampled Depth Spacing - Add Logfilter + 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 + - 15.000000000000000 + 1.000000000000000 [MHz] f High Pass [MHz] f Low Pass 0 0 3 200.000000000000000 - - - - - - Tukey window alpha + + 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.500000000000000 + 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 - 10 + 300 Cut Top Cut Bottom 99999 5 - 165 + 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 - - - - Quad. Approx. - - - - 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 diff --git a/Plugins/org.mitk.gui.qt.pointsetinteraction/documentation/UserManual/QmitkPointSetInteraction.dox b/Plugins/org.mitk.gui.qt.pointsetinteraction/documentation/UserManual/QmitkPointSetInteraction.dox index 012e67114d..a65d740abe 100644 --- a/Plugins/org.mitk.gui.qt.pointsetinteraction/documentation/UserManual/QmitkPointSetInteraction.dox +++ b/Plugins/org.mitk.gui.qt.pointsetinteraction/documentation/UserManual/QmitkPointSetInteraction.dox @@ -1,47 +1,47 @@ /** \page org_mitk_views_pointsetinteraction The Point Set Interaction View -\imageMacro{QmitkPointSetInteraction_Icon.png,"Icon of the Point Set Interaction View",2.00} +\imageMacro{pointset_interaction.svg,"Icon of the Point Set Interaction View",2.00} Available sections: - \ref QmitkPointSetInteractionUserManualOverview - \ref QmitkPointSetInteractionUserManualDetails \section QmitkPointSetInteractionUserManualOverview Overview This view allows you to define multiple sets of points, to fill them with points and to save them in so called PointSets. \imageMacro{QmitkPointSetInteraction_Screenshot.png,"MITK with the QmitkPointSetInteraction view",16.00} This document will tell you how to use this view, but it is assumed that you already know how to navigate through the slices of an image using the four window view. Please read the application manual for more information. \section QmitkPointSetInteractionUserManualDetails Details First of all you have to select a PointSet to use this view. Therefore, you have to select the point set in the data manager. If there are currently no point sets in the data tree, you have to first add a new point set to the data tree. This is done by clicking the "Add pointset..." button. \imageMacro{QmitkPointSetInteraction_AddPointSet.png,"The Add pointset... dialog",8.64} In the pop-up dialog, you have to specify a name for the new point set. This is also the node for the new data tree item. \imageMacro{QmitkPointSetInteraction_CurrentPointSetArea.png,"The Current pointset area",6.52} The "Current pointset" area contains a list of points. Within this area, all points for the current point set node are listed. To set points you have to toggle the "Set Points" button, the leftmost of the four buttons on the bottom of the view. Points can be defined by performing a left mouse button click while holding the "Shift"-key pressed in the four window view. To erase all points from the list press the next button. The user is prompted to confirm the decision. If you want to delete only a single point, left click on it in the list and then press delete on your keyboard. With the third button, a previously saved point set can be loaded and all of its points are shown in the list and the four window view. The user is prompted to select the file to be loaded. The file extension is ".mps". On the right of this button is the save button. With this function the entire point set can be saved to the harddrive. The user is prompted to select a filename. Pointsets are saved in XML fileformat but have to have a ".mps" file extension. You can select points in the render window, if the "Set Points" button is toggled, with a left mouse button click on them. If you keep the mouse button pressed, you can move the points by moving the mouse and then releasing the mouse button. With the delete key you can remove the selected points. */ \ No newline at end of file diff --git a/Plugins/org.mitk.gui.qt.pointsetinteraction/documentation/UserManual/pointset_interaction.svg b/Plugins/org.mitk.gui.qt.pointsetinteraction/documentation/UserManual/pointset_interaction.svg new file mode 100644 index 0000000000..969c6568aa --- /dev/null +++ b/Plugins/org.mitk.gui.qt.pointsetinteraction/documentation/UserManual/pointset_interaction.svg @@ -0,0 +1,77 @@ + + + + + + + + + + image/svg+xml + + + + + + + + + + + + diff --git a/Plugins/org.mitk.gui.qt.segmentation/documentation/UserManual/QmitkSegmentation.dox b/Plugins/org.mitk.gui.qt.segmentation/documentation/UserManual/QmitkSegmentation.dox index b769e64cd1..82afd40d2f 100644 --- a/Plugins/org.mitk.gui.qt.segmentation/documentation/UserManual/QmitkSegmentation.dox +++ b/Plugins/org.mitk.gui.qt.segmentation/documentation/UserManual/QmitkSegmentation.dox @@ -1,317 +1,317 @@ /** \page org_mitk_views_segmentation The Segmentation Plugin -\imageMacro{QmitkSegmentation_Icon.png,"Icon of the Segmentation Plugin",2.00} +\imageMacro{segmentation.svg,"Icon of the Segmentation Plugin",2.00} Some of the features described below are closed source additions to the open source toolkit MITK and are not available in every application. \tableofcontents \section org_mitk_gui_qt_segmentationUserManualOverview Overview The Segmentation plugin allows you to create segmentations of anatomical and pathological structures in medical images of the human body. The plugin consists of a number of view which can be used for:
  • manual and (semi-)automatic segmentation of organs on CT or MR image volumes via the Segmentation View
  • segmentation postprocessing via the \subpage org_mitk_views_segmentationutilities
  • clipping of existing segmentations using a resection plane via the \subpage org_mitk_views_deformableclippingplane
\imageMacro{QmitkSegmentation_IMGApplication.png,"Segmentation Plugin consisting of the Segmentation View the Segmentation Utilities View and the Clipping Plane View", 16.00} The segmentation plugin offers a number of preferences which can be set via the MITK Workbench application preference dialog: \imageMacro{QmitkSegmentation_IMGPreferences.png,"Segmentation Plugin consisting of the Segmentation View the Segmentation Utilities View and the Clipping Plane View", 10.00} The following preferences can be set:
  • Slim view: Allows you to show or hide the tool button description of the Segmentation View
  • 2D display: Specify whether the segmentation is drawn as outline or as a transparent overlay
  • 3D display: Activate 3D volume rendering for your segmentation
  • Data node selection mode: If activated the segmentation image combo box is always sychronized with the data manager selection.
  • Smoothed surface creation: Set certain smoothing parameters for surface creation
If you wonder what segmentations are good for, we shortly revisit the concept of a segmentation here. A CT or MR image is made up of volume of physical measurements (volume elements are called voxels). In CT images, for example, the gray value of each voxel corresponds to the mass absorbtion coefficient for X-rays in this voxel, which is similar in many %parts of the human body. The gray value does not contain any further information, so the computer does not know whether a given voxel is part of the body or the background, nor can it tell a brain from a liver. However, the distinction between a foreground and a background structure is required when:
  • you want to know the volume of a given organ (the computer needs to know which %parts of the image belong to this organ)
  • you want to create 3D polygon visualizations (the computer needs to know the surfaces of structures that should be drawn)
  • as a necessary pre-processing step for therapy planning, therapy support, and therapy monitoring
Creating this distinction between foreground and background is called segmentation. The Segmentation perspective of the MITK Workbench uses a voxel based approach to segmentation, i.e. each voxel of an image must be completely assigned to either foreground or background. This is in contrast to some other applications which might use an approach based on contours, where the border of a structure might cut a voxel into two %parts. The remainder of this document will summarize the features of the Segmentation perspective and how they are used. \section org_mitk_gui_qt_segmentationUserManualTechnical Technical Issues The Segmentation perspective makes a number of assumptions. To know what this view can be used for, it will help you to know that:
  • Images must be 2D, 3D, or 3D+t
  • Images must be single-values, i.e. CT, MRI or "normal" ultrasound. Images from color doppler or photographic (RGB) images are not supported
  • Segmentations are handled as binary images of the same extent as the original image
\section org_mitk_gui_qt_segmentationUserManualImageSelection Image Selection The Segmentation perspective makes use of the Data Manager view to give you an overview of all images and segmentations. \imageMacro{QmitkSegmentation_IMGSelection.png,"Data Manager is used for selecting the current segmentation. The reference image is selected in the drop down box of the control area.",5.50} To select the reference image (e.g. the original CT/MR image) use the patient image drop down box in the control area of the Segmentation view. The segmentation image selected in the Data Manager is displayed below in the segmentation drop down box. By default the auto selection mode is enabled, which always keeps the selection of the segmentation drop down box in synch with the selection in the data manager. If you disable the auto selection mode the selection of the right segmentation image has to be done via the drop down box. If no segmentation image exists or none is selected create a new segmentation image by using the "New segmentation" button on the right of the Segmentation drop down box. Some items of the graphical user interface might be disabled when no image is selected or the selected image does not fit to the patient image's geoemtry. In any case, the application will give you hints if a selection is needed. \section org_mitk_gui_qt_segmentationUserManualToolOverview Tool overview MITK comes with a comprehensive set of segmentation tools. These tools can be differenciated between manual slice-based 2D segmentation tools and (semi-)automated 3D tools. The manual 2D tools require a big amount of user interaction and can only be applied to a single image slice whereas the 3D tools operate on the hole image. The 3D tools usually require a small amount of interaction like placin seedpoints of setting some parameters. You can switch between the different toolsets by switching the 2D/3D tab in the segmentation view. \imageMacro{QmitkSegmentation_ToolOverview.png,"An overview of the existing tools in MITK. There are interactive 2D tools as well as (semi-)automated 3D tools",5.50} \section org_mitk_gui_qt_segmentationUserManualManualKringeling Manual Contouring With manual contouring you define which voxels are part of the segmentation and which are not. This allows you to create segmentations of any structeres that you may find in an image, even if they are not part of the human body. You might also use manual contouring to correct segmentations that result from sub-optimal automatic methods. The drawback of manual contouring is that you might need to define contours on many 2D slices. However, this is moderated by the interpolation feature, which will make suggestions for a segmentation. \subsection org_mitk_gui_qt_segmentationUserManualManualKringeling1 Creating New Segmentations Unless you want to edit existing segmentations, you have to create a new, empty segmentation before you can edit it. To do so, click the "New manual segmentation" button. Input fields will appear where you can choose a name for the new segmentation and a color for its display. Click the checkmark button to confirm or the X button to cancel the new segmentation. Notice that the input field suggests names once you %start typing and that it also suggests colors for known organ names. If you use names that are not yet known to the application, it will automatically remember these names and consider them the next time you create a new segmentation. Once you created a new segmentation, you can notice a new item with the "binary mask" icon in the Data Manager tree view. This item is automatically selected for you, allowing you to %start editing the new segmentation right away. \subsection org_mitk_gui_qt_segmentationUserManualManualKringeling2 Selecting Segmentations for Editing As you might want to have segmentations of multiple structures in a single patient image, the application needs to know which of them to use for editing. You select a segmenation by clicking it in the tree view of Data Manager. Note that segmentations are usually displayed as sub-items of "their" patient image. In the rare case, where you need to edit a segmentation that is not displayed as a a sub-item, you can click both the original image AND the segmentation while holding down CTRL or for Mac OS X the CMD on the keyboard. When a selection is made, the Segmentation View will hide all but the selected segmentation and the corresponding original image. When there are multiple segmentations, the unselected ones will remain in the Data Manager, you can make them visible at any time by selecting them. \subsection org_mitk_gui_qt_segmentationUserManualManualKringeling3 Selecting Editing Tools If you are familiar with the MITK Workbench, you know that clicking and moving the mouse in any of the 2D render windows will move around the crosshair that defines what part of the image is displayed. This behavior is disabled while any of the manual segmentation tools are active -- otherwise you might have a hard time concentrating on the contour you are drawing. To %start using one of the editing tools, click its button the the displayed toolbox. The selected editing tool will be active and its corresponding button will stay pressed until you click the button again. Selecting a different tool also deactivates the previous one. If you have to delineate a lot of images, you should try using shortcuts to switch tools. Just hit the first letter of each tool to activate it (A for Add, S for Subtract, etc.). \subsection org_mitk_gui_qt_segmentationUserManualManualKringeling4 Using Editing Tools All of the editing tools work by the same principle: you use the mouse (left button) to click anywhere in a 2D window (any of the orientations axial, sagittal, or frontal), move the mouse while holding the mouse button and release to finish the editing action. Multi-step undo and redo is fully supported by all editing tools. Use the application-wide undo button in the toolbar to revert erroneous %actions. \imageMacro{QmitkSegmentation_IMGIconAddSubtract.png,"Add and Subtract Tools",7.70} Use the left mouse button to draw a closed contour. When releasing the mouse button, the contour will be added (Add tool) to or removed from (Subtract tool) the current segmentation. Hold down the CTRL / CMD key to invert the operation (this will switch tools temporarily to allow for quick corrections). \imageMacro{QmitkSegmentation_IMGIconPaintWipe.png,"Paint and Wipe Tools",7.68} Use the slider below the toolbox to change the radius of these round paintbrush tools. Move the mouse in any 2D window and press the left button to draw or erase pixels. As the Add/Subtract tools, holding CTRL / CMD while drawing will invert the current tool's behavior. \imageMacro{QmitkSegmentation_IMGIconRegionGrowing.png,"Region Growing Tool",3.81} Click at one point in a 2D slice widget to add an image region to the segmentation with the region growing tool. Moving up the cursor while holding the left mouse button widens the range for the included grey values; moving it down narrows it. Moving the mouse left and right will shift the range. Region Growing selects all pixels around the mouse cursor that have a similar gray value as the pixel below the mouse cursor. This enables you to quickly create segmentations of structures that have a good contrast to surrounding tissue, e.g. the lungs. The tool will select more or less pixels (corresponding to a changing gray value interval width) when you move the mouse up or down while holding down the left mouse button. \if THISISNOTIMPLEMENTEDATTHEMOMENT A common issue with region growing is the so called "leakage" which happens when the structure of interest is connected to other pixels, of similar gray values, through a narrow "bridge" at the border of the structure. The Region Growing tool comes with a "leakage detection/removal" feature. If leakage happens, you can left-click into the leakage region and the tool will try to automatically remove this region (see illustration below). \imageMacro{QmitkSegmentation_IMGLeakage.png,"Leakage correction feature of the Region Growing tool",11.28} \endif
\imageMacro{QmitkSegmentation_IMGIconCorrection.png,"Correction Tool",3.77} You do not have to draw a closed contour to use the Correction tool and do not need to switch between the Add and Substract tool to perform small corrective changes. The following figure shows the usage of this tool:
  • if the user draws a line which %starts and ends outside the segmenation AND it intersects no other segmentation the endpoints of the line are connected and the resulting contour is filled
  • if the user draws a line which %starts and ends outside the segmenation a part of it is cut off (left image)
  • if the line is drawn fully inside the segmentation the marked region is added to the segmentation (right image)
\imageMacro{QmitkSegmentation_IMGCorrectionActions.png,"%Actions of the Correction tool illustrated.",13.50}
\imageMacro{QmitkSegmentation_IMGIconFill.png,"Fill Tool",3.81} Left-click inside a segmentation with holes to completely fill all holes (left-click outside a segmentation). \imageMacro{QmitkSegmentation_IMGIconErase.png,"Erase Tool",3.79} This tool removes a connected part of pixels that form a segmentation. You may use it to remove so called islands (see picture) or to clear a whole slice at once (left-click outside a segmentation). \imageMacro{QmitkSegmentation_IMGIconLiveWire.png,"LiveWire Tool",3.01} The LiveWire Tool acts as a magnetic lasso with a contour snapping to edges of objects. \imageMacro{QmitkSegmentation_IMGLiveWireUsage.png,"Steps for using LiveWire Tool",16.00}
  • (1) To start the Tool you have to double click near the edge of the object you want to segment. The initial anchor point will snap to the edge within a 3x3 region.
  • (2) Move the mouse. You don't have trace the edge of the object. The contour will automatically snap to it.
  • (3) To fix a segment you can set anchor points by single left mouse button click.
  • (4) Go on with moving the mouse and setting anchor points.
  • (5) To close the contour double click on the initial anchor point.
  • (6) After closing the contour can be edited by moving, inserting and deleting anchor points.
The contour will be transfered to its binary image representation by deactivating the tool. \imageMacro{QmitkSegmentation_IMG2DFastMarchingUsage.png,"2D Fast Marching Tool",3.01} Provides a fast marching based 2D interaction segmentation tool. You start with setting seedpoints in an image slice. Via several sliders you can adapt parameters and see the fast marching result instantly. \subsection org_mitk_gui_qt_segmentationUserManualManualKringeling5 Interpolation Creating segmentations for modern CT volumes is very time-consuming, because structures of interest can easily cover a range of 50 or more slices. The Manual Segmentation View offers two helpful features for these cases:
  • 3D Interpolation
  • 2D Interpolation

The 3D interpolation is activated by default when using the manual segmentation tools. That means if you start contouring, from the second contour onwards, the surface of the segmented area will be interpolated based on the given contour information. The interpolation works with all available manual tools. Please note that this is currently a pure mathematical interpolation, i.e. image intensity information is not taken into account. With each further contour the interpolation result will be improved, but the more contours you provide the longer the recalculation will take. To achieve an optimal interpolation result and in this way a most accurate segmentation you should try to describe the surface with sparse contours by segmenting in arbitrary oriented planes. The 3D interpolation is not meant to be used for parallel slice-wise segmentation. \imageMacro{QmitkSegmentation_3DInterpolationWrongRight.png,"3D Interpolation HowTo",16.00} You can accept the interpolation result by clicking the "Accept" - button below the tool buttons. In this case the 3D interpolation will be deactivated automatically so that the result can be postprocessed without any interpolation running in background. During recalculation the interpolated surface is blinking yellow/white. When the interpolation has finished the surface is shown yellow with a small opacity. Additional to the surface, black contours are shown in the 3D render window. They mark the positions of all the drawn contours which were used for the interpolation. You can navigate between the drawn contours by clicking on the „Position“ - Nodes in the datamanager which are located below the selected segmentation. If you don't want to see these nodes just unckeck the „Show Position Nodes“ Checkbox and these nodes will be hidden. If you want to delete a drawn contour we recommend to use the Erase-Tool since Redo/Undo is not yet working for 3D interpolation. The current state of the 3D interpolation can be saved accross application restart. Therefor just click on save project during the interpolation is active. After restarting the application and load your project you can click on "Reinit Interpolation" within the 3D interpolation GUI area.
The 2D Interpolation creates suggestions for a segmentation whenever you have a slice that
  • has got neighboring slices with segmentations (these do not need to be direct neighbors but could also be a couple of slices away) AND
  • is completely clear of a manual segmentation -- i.e. there will be no suggestion if there is even only a single pixel of segmentation in the current slice.
Interpolated suggestions are displayed in a different way than manual segmentations are, until you "accept" them as part of the segmentation. To accept single slices, click the "Accept" button below the toolbox. If you have segmented a whole organ in every-x-slice, you may also review the interpolations and then accept all of them at once by clicking "... all slices". \section org_mitk_gui_qt_segmentationUserManual3DSegmentationTools 3D Segmenation tools The 3D tools operate on the hole image and require usually a small amount of interaction like placing seed-points or specifying certain parameters. All 3D tools provide an immediate segmentation feedback, which is displayed as a transparent green overlay. For accepting a preview you have to press the "Comfirm" button of the selected tool. The following 3D tools are at your disposal: \subsection org_mitk_gui_qt_segmentationUserManual3DThresholdTool 3D Threshold tool The Thresholding tool simply applies a 3D threshold to the patient image. All pixels with values equal or above the selected threshold are labeled. You can change the threshold by either moving the slider of setting a certain value in the spinbox. \imageMacro{QmitkSegmentation_3DThresholdTool.png,"3D Threshold tool",10.00} \subsection org_mitk_gui_qt_segmentationUserManual3DULTool 3D Upper/Lower Threshold tool The Upper/Lower Thresholding tool works similar to the simple 3D threshold tool but allows you to define an upper and lower threshold. All pixels with values within this threshold intervall will be labeled \imageMacro{QmitkSegmentation_3DULThresholdTool.png,"3D Upper/Lower Threshold tool",10.00} \subsection org_mitk_gui_qt_segmentationUserManual3DOtsuTool 3D Otsu tool The 3D Otsu tool provides a more sophisticated thresholding algorithm. It allows you to define a number of regions. Based on the image histogram the pixels will then divided into different regions. There more regions you define the longer will the calculation take. \imageMacro{QmitkSegmentation_3DOtsuTool.png,"3D Otsu tool",10.00} \subsection org_mitk_gui_qt_segmentationUserManual3DFMTool 3D Fast Marching tool The 3D Fast Marching tools works similar to the 2D pendant but on the hole image. Depending on you image's size the calculation will take some time. You can interactive set the parameters of the algorithm via the tool GUI. \imageMacro{QmitkSegmentation_3DFMTool.png,"3D Fast Marching tool",10.00} \subsection org_mitk_gui_qt_segmentationUserManual3DRGTool 3D Region Growing tool The 3D Region Growing tool works similar to the 2D pendant. At the beginning you have to place a seedpoint and define a threshold intervall. If you press "Run segmentation" a preview is calculated, if the "3D preview" box is checked you will also see the result in 3D. By moving the "Adapt region growing slider" you can interactively adapt the result to you image. \imageMacro{QmitkSegmentation_3DRGTool.png,"3D Region Growing tool",10.00} +\subsection org_mitk_gui_qt_segmentationUserManual3DWatershedTool 3D Watershed tool This tool provides a watershed based segmentation algorithm. \imageMacro{QmitkSegmentation_3DWatershedTool.png,"3D Watershed tool",10.00} \subsection org_mitk_gui_qt_segmentationUserManualPickingTool Picking tool The Picking tool allows you to select islands within your segmentation. This is especially usefull if e.g. a thresholding delivered your several areas within your image but you are just interested in one special region. \imageMacro{QmitkSegmentation_PickingTool.png,"Picking tool",10.00} \section org_mitk_gui_qt_segmentationUserManualPostprocessing Things you can do with segmentations As mentioned in the introduction, segmentations are never an end in themselves. Consequently, the Segmentation view adds a couple of "post-processing" %actions to the Data Manager. These %actions are accessible through the context-menu of segmentations in Data Manager's list view \imageMacro{QmitkSegmentation_IMGDataManagerContextMenu.png,"Context menu items for segmentations.",10.58}
  • Create polygon %model applies the marching cubes algorithms to the segmentation. This polygon %model can be used for visualization in 3D or other things such as stereolithography (3D printing).
  • Create smoothed polygon %model uses smoothing in addition to the marching cubes algorithms, which creates models that do not follow the exact outlines of the segmentation, but look smoother.
  • Autocrop can save memory. Manual segmentations have the same extent as the patient image, even if the segmentation comprises only a small sub-volume. This invisible and meaningless margin is removed by autocropping.
\section QmitkSegmentation_UserManualSurfaceMasking Surface Masking You can use the surface masking tool to create binary images from a surface which is used used as a mask on an image. This task is demonstrated below: \imageMacro{QmitkSegmentation_FromSurfaceBefore.png,"Load an image and a surface.",16.00} Select the image and the surface in the corresponding drop-down boxes (both are selected automatically if there is just one image and one surface) \imageMacro{QmitkSegmentation_FromSurfaceAfter.png,"Create segmentation from surface",16.00} After clicking "Create segmentation from surface" the newly created binary image is inserted in the DataManager and can be used for further processing \section org_mitk_gui_qt_segmentationUserManualTechnicalDetail Technical Information for Developers For technical specifications see \subpage QmitkSegmentationTechnicalPage and for information on the extensions of the tools system \subpage toolextensions . */ diff --git a/Plugins/org.mitk.gui.qt.segmentation/documentation/UserManual/QmitkSegmentationUtilities.dox b/Plugins/org.mitk.gui.qt.segmentation/documentation/UserManual/QmitkSegmentationUtilities.dox index b60a39db80..65cf6d1b77 100644 --- a/Plugins/org.mitk.gui.qt.segmentation/documentation/UserManual/QmitkSegmentationUtilities.dox +++ b/Plugins/org.mitk.gui.qt.segmentation/documentation/UserManual/QmitkSegmentationUtilities.dox @@ -1,62 +1,62 @@ /** \page org_mitk_views_segmentationutilities The Segmentation Utilities View -\imageMacro{SegmentationUtilities.png,"Icon of the Segmentation Utilities View",5.00} +\imageMacro{segmentation_utilities.svg,"Icon of the Segmentation Utilities View",5.00} \imageMacro{QmitkSegmentationUtilities_Overview.png,"The Segmentation Utilities view",16.00} \tableofcontents \section org_mitk_views_segmentationUtilitiesManualOverview Overview The Segmentation Utilities View allows you to postprocess existing segmentations \section org_mitk_views_segmentationUtilitiesImageSelection Image Selection Usually the data selection in the Segmentation Utilities View is done via drop down box which let you just select the appropriate data. \section org_mitk_views_segmentationUtilitiesBooleanOperations Boolean Operations Boolean operations allow you to create the
  • Union: Combines two existing segmentations
  • Intersection: Keeps just the overlapping areas of two existing segmentations
  • Difference: Subtracts one segmentation from the other
of two segmentations. The selected segmentations must have the same geometry (size, spacing, ...) \imageMacro{QmitkSegmentationUtilities_IMGBooleanOperations.png,"Boolean operations of the SegmentationUtlitiesView",6.00} \section org_mitk_views_segmentationUtilitiesImageMasking Image masking You can mask your grey value image with either an existing segmentation or a surface. The result will be an image containing only the pixels that are cover by the respective mask. \imageMacro{QmitkSegmentationUtilities_IMGImageMasking.png,"Image masking widget of the Segmentation Utilities View",6.00} \section org_mitk_views_segmentationUtilitiesMorphologicalOperators Morphological Operators The morphological operators are applied to a single segmentation image. Based on a given structuring element the underlying segmentation will be modfied. MITK provides a ball and a cross as structuring elements. The follow operators are at your disposal:
  • Dilation: Each labeled pixel within the segmentation will be dilated based on the selected structuring element
  • Erosion: Each labeled pixel within the segmentation will be eroded based on the selected structuring element
  • Opening: A dilation followed by an erosion, used for smoothing edges or eliminating small objects
  • Closing An erosion followed by an dilation, used for filling small holes
  • Fill Holes Fills bigger holes within a segmentation
\imageMacro{QmitkSegmentationUtilities_IMGMorphologicalOperators.png,"Morphological operators widget of the Segmentation Utilities View",6.00} \section org_mitk_views_segmentationUtilitiesSurfaceToImage Surface to binary image This widget lets you fill you meshes into an empty binary image. It is required that a reference grey value image is present. The created binary image will have the same geometrical properties like the reference image \imageMacro{QmitkSegmentationUtilities_IMGSurfaceToImage.png,"Surface to image widget of the Segmentation Utilities View",6.00} **/ diff --git a/Plugins/org.mitk.gui.qt.segmentation/documentation/UserManual/segmentation.svg b/Plugins/org.mitk.gui.qt.segmentation/documentation/UserManual/segmentation.svg new file mode 100644 index 0000000000..54fb2e3178 --- /dev/null +++ b/Plugins/org.mitk.gui.qt.segmentation/documentation/UserManual/segmentation.svg @@ -0,0 +1,2051 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Plugins/org.mitk.gui.qt.segmentation/documentation/UserManual/segmentation_utilities.svg b/Plugins/org.mitk.gui.qt.segmentation/documentation/UserManual/segmentation_utilities.svg new file mode 100644 index 0000000000..5689bc3f3c --- /dev/null +++ b/Plugins/org.mitk.gui.qt.segmentation/documentation/UserManual/segmentation_utilities.svg @@ -0,0 +1,3481 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Plugins/org.mitk.gui.qt.volumevisualization/documentation/UserManual/QmitkVolumeVisualization.dox b/Plugins/org.mitk.gui.qt.volumevisualization/documentation/UserManual/QmitkVolumeVisualization.dox index 45c93fb7c7..49360ff183 100644 --- a/Plugins/org.mitk.gui.qt.volumevisualization/documentation/UserManual/QmitkVolumeVisualization.dox +++ b/Plugins/org.mitk.gui.qt.volumevisualization/documentation/UserManual/QmitkVolumeVisualization.dox @@ -1,154 +1,154 @@ /** \page org_mitk_views_volumevisualization The Volume Visualization Plugin -\imageMacro{QmitkVolumeVisualization_Icon.png,"Icon of the Volume Visualization Plugin",2.00} +\imageMacro{volume_visualization.svg,"Icon of the Volume Visualization Plugin",2.00} \tableofcontents \section QVV_Overview Overview The Volume Visualization Plugin is a basic tool for visualizing three dimensional medical images. MITK provides generic transfer function presets for medical CT data. These functions, that map the gray-value to color and opacity, can be interactively edited. Additionally, there are controls to quickly generate common used transfer function shapes like the threshold and bell curve to help identify a range of grey-values. \imageMacro{QmitkVolumeVisualization_Overview.png,"",16.00} \section QVV_EnableVRPage Enable Volume Rendering \subsection QVV_LoadingImage Loading an image into the application Load an image into the application by
  • dragging a file into the application window.
  • selecting file / load from the menu.
Volume Visualization imposes following restrictions on images:
  • It has to be a 3D-Image Scalar image, that means a normal CT or MRT.
  • 3D+T are supported for rendering, but the histograms are not computed.
  • Also be aware that volume visualization requires a huge amount of memory. Very large images may not work, unless you use the 64bit version.
\subsection QVV_EnableVR Enable Volumerendering \imageMacro{QmitkVolumeVisualization_Checkboxen.png,"",8.21} Select an image in datamanager and click on the checkbox left of "Volumerendering". Please be patient, while the image is prepared for rendering, which can take up to a half minute. \subsection QVV_LODGPU Dropdown menus for the rendering and blend modes Two dropdown menus allow selection of rendering mode (Default, RayCast, GPU) and the blend mode (Composite, Max, Min, Avg, Add). Any Volume Rendering mode requires a lot of computing resources including processor, memory and often also graphics card. The Default selection usually finds the best rendering mode for the available hardware. Alternatively, it is possible to manually specify the selections RayCast and GPU. The RayCast selection is based on CPU computation and therefore typically slow, but allows to render without hardware acceleration. The GPU selection uses computing resources on the graphics card to accelerate volume rendering. It requires a powerful graphics card and OpenGL hardware support for shaders, but achieves much higher frame rates than software-rendering. Blend modes define how the volume voxels intersected by the rendering rays are pooled. The composite mode specifies standard volume rendering, for which each voxel contributes equally with opacity and color. Other blend modes simply visualize the voxel of maximum / minimum intensity and average / add the intentities along the rendering ray. \section QVV_PresetPage Applying premade presets \subsection QVV_Preset Internal presets There are some internal presets given, that can be used with normal CT data (given in Houndsfield units). A large set of medical data has been tested with that presets, but it may not suit on some special cases. Click on the "Preset" tab for using internal or custom presets. \imageMacro{QmitkVolumeVisualization_InternalPresets.png,"",8.30}
  • "CT Generic" is the default transferfunction that is first applied.
  • "CT Black&White" does not use any colors, as it may be distracting on some data.
  • "CT Cardiac" tries to increase detail on CTs from the heart.
  • "CT Bone" emphasizes bones and shows other areas more transparent.
  • "CT Bone (Gradient)" is like "CT Bone", but shows from other organs only the surface by using the gradient.
  • "MR Generic" is the default transferfunction that we use on MRT data (which is not normalized like CT data).
  • "CT Thorax small" tries to increase detail.
  • "CT Thorax large" tries to increase detail.
\subsection QVV_CustomPreset Saving and loading custom presets After creating or editing a transferfunction (see \ref QVV_Editing or \ref QVV_ThresholdBell), the custom transferfunction can be stored and later retrieved on the filesystem. Click "Save" (respectively "Load") button to save (load) the threshold-, color- and gradient function combined in a single .xml file. \section QVV_ThresholdBell Interactively create transferfunctions Beside the possibility to directly edit the transferfunctions (\ref QVV_Editing), a one-click generation of two commonly known shapes is given. Both generators have two parameters, that can be modified by first clicking on the cross and then moving the mouse up/down and left/right. The first parameter "center" (controlled by horizontal movement of the mouse) specifies the gravalue where the center of the shape will be located. The second parameter "width" (controlled by vertical movement of the mouse) specifies the width (or steepness) of the shape. \subsection Threshold Click on the "Threshold" tab to active the threshold function generator. \imageMacro{QmitkVolumeVisualization_Threshold.png,"",8.21} A threshold shape begins with zero and raises to one across the "center" parameter. Lower widths results in steeper threshold functions. \subsection Bell Click on the "Bell" tab to active the threshold function generator. \imageMacro{QmitkVolumeVisualization_Bell.png,"",8.23} A threshold shape begins with zero and raises to one at the "center" parameter and the lowers agains to zero. The "width" parameter correspondens to the width of the bell. \section QVV_Editing Customize transferfunctions in detail \subsection QVV_Navigate Choosing grayvalue interval to edit \imageMacro{QmitkVolumeVisualization_Slider.png,"",8.23} To navigate across the grayvalue range or to zoom in some ranges use the "range"-slider. All three function editors have in common following:
  • By left-clicking a new point is added.
  • By right-clicking a point is deleted.
  • By left-clicking and holding, an exisiting point can be dragged.
  • By pressing arrow keys, the currently selected point is moved.
  • By pressing the "DELETE" key, the currently selected point is deleted.
  • Between points the transferfunctions are linear interpolated.
There are three transferfunctions to customize: \subsection QVV_GO Grayvalue -> Opacity \imageMacro{QmitkVolumeVisualization_Opacity.png,"grayvalues will be mapped to opacity.",8.04} An opacity of 0 means total transparent, an opacity of 1 means total opaque. \subsection QVV_GC Grayvalue -> Color \imageMacro{QmitkVolumeVisualization_Color.png,"grayvalues will be mapped to color.",8.81} The color transferfunction editor also allows by double-clicking a point to change its color. \subsection QVV_GGO Grayvalue and Gradient -> Opacity \imageMacro{QmitkVolumeVisualization_Gradient.png,"",8.85} Here the influence of the gradient is controllable at specific grayvalues. */ diff --git a/Plugins/org.mitk.gui.qt.volumevisualization/documentation/UserManual/volume_visualization.svg b/Plugins/org.mitk.gui.qt.volumevisualization/documentation/UserManual/volume_visualization.svg new file mode 100644 index 0000000000..26682d271e --- /dev/null +++ b/Plugins/org.mitk.gui.qt.volumevisualization/documentation/UserManual/volume_visualization.svg @@ -0,0 +1,65 @@ + + + + + + + + image/svg+xml + + + + + + + + + diff --git a/Wrapping/Common/mitk_swig_classes.i b/Wrapping/Common/mitk_swig_classes.i index 8a5e384924..e531e6fc84 100644 --- a/Wrapping/Common/mitk_swig_classes.i +++ b/Wrapping/Common/mitk_swig_classes.i @@ -1,81 +1,126 @@ // // Defining some Macros that make problems with SWIG as the // corresponding definitions are not included by default. // Luckely, these includes are not necessary for SWIG. // #define ITK_NOEXCEPT #define ITKCommon_EXPORT #define ITK_OVERRIDE #define MITKCORE_EXPORT #define MITKCLCORE_EXPORT #define MITKCLUTILITIES_EXPORT #define ITKCommon_EXPORT +#define MITKMATCHPOINTREGISTRATION_EXPORT +#define MAPDeployment_EXPORT +#define MAPAlgorithms_EXPORT #define ITKCommon_EXPORT #define ITK_FORWARD_EXPORT #define ITK_OVERRIDE #define ITK_NOEXCEPT %include %include %include %include %include %include #define DEPRECATED(func) func #undef ITK_DISALLOW_COPY_AND_ASSIGN #define ITK_DISALLOW_COPY_AND_ASSIGN(TypeName) %pythoncode %{ convertion_list = {} %} SWIG_ADD_MITK_CLASS(Object, itkObject.h, itk) SWIG_ADD_MITK_CLASS(DataObject, itkDataObject.h, itk) SWIG_ADD_MITK_CLASS(TimeGeometry, mitkTimeGeometry.h, mitk) SWIG_ADD_MITK_CLASS(ArbitraryTimeGeometry, mitkArbitraryTimeGeometry.h, mitk) SWIG_ADD_MITK_CLASS(ProportionalTimeGeometry, mitkProportionalTimeGeometry.h, mitk) SWIG_ADD_MITK_CLASS(BaseGeometry, mitkBaseGeometry.h, mitk) SWIG_ADD_MITK_CLASS(Geometry3D, mitkGeometry3D.h, mitk) SWIG_ADD_MITK_CLASS(SlicedGeometry3D, mitkSlicedGeometry3D.h, mitk) SWIG_ADD_MITK_CLASS(PlaneGeometry , mitkPlaneGeometry.h, mitk) SWIG_ADD_NONOBJECT_NOVECTOR_CLASS(BoundingBox, mitkBaseGeometry.h, mitk) SWIG_ADD_NONOBJECT_CLASS(TimeBounds, mitkBaseGeometry.h, mitk) SWIG_ADD_NONOBJECT_CLASS(FixedArrayType, mitkBaseGeometry.h, mitk) SWIG_ADD_NONOBJECT_CLASS(Point2D, mitkPoint.h, mitk) SWIG_ADD_NONOBJECT_CLASS(Point3D, mitkPoint.h, mitk) SWIG_ADD_NONOBJECT_CLASS(Point4D, mitkPoint.h, mitk) SWIG_ADD_NONOBJECT_CLASS(Point2I, mitkPoint.h, mitk) SWIG_ADD_NONOBJECT_CLASS(Point3I, mitkPoint.h, mitk) SWIG_ADD_NONOBJECT_CLASS(Point4I, mitkPoint.h, mitk) SWIG_ADD_NONOBJECT_CLASS(VnlVector, mitkVector.h, mitk) SWIG_ADD_NONOBJECT_CLASS(Vector2D, mitkVector.h, mitk) SWIG_ADD_NONOBJECT_CLASS(Vector3D, mitkVector.h, mitk) SWIG_ADD_NONOBJECT_CLASS(Vector4D, mitkVector.h, mitk) SWIG_ADD_MITK_CLASS(BaseData, mitkBaseData.h, mitk) SWIG_ADD_MITK_CLASS(SlicedData, mitkSlicedData.h, mitk) SWIG_ADD_MITK_CLASS(Image, mitkImage.h, mitk) SWIG_ADD_MITK_CLASS(PointSet, mitkPointSet.h, mitk) +// +// Phenotyping Related Classes +// SWIG_ADD_MITK_CLASS(AbstractGlobalImageFeature, mitkAbstractGlobalImageFeature.h, mitk) SWIG_ADD_MITK_CLASS(GIFImageDescriptionFeatures, mitkGIFImageDescriptionFeatures.h, mitk) SWIG_ADD_MITK_CLASS(GIFFirstOrderStatistics, mitkGIFFirstOrderStatistics.h, mitk) SWIG_ADD_MITK_CLASS(GIFFirstOrderHistogramStatistics, mitkGIFFirstOrderHistogramStatistics.h, mitk) SWIG_ADD_MITK_CLASS(GIFVolumetricStatistics, mitkGIFVolumetricStatistics.h, mitk) SWIG_ADD_MITK_CLASS(GIFVolumetricDensityStatistics, mitkGIFVolumetricDensityStatistics.h, mitk) SWIG_ADD_MITK_CLASS(GIFCooccurenceMatrix2, mitkGIFCooccurenceMatrix2.h, mitk) SWIG_ADD_MITK_CLASS(GIFNeighbouringGreyLevelDependenceFeature, mitkGIFNeighbouringGreyLevelDependenceFeatures.h, mitk) SWIG_ADD_MITK_CLASS(GIFGreyLevelRunLength, mitkGIFGreyLevelRunLength.h, mitk) SWIG_ADD_MITK_CLASS(GIFGreyLevelSizeZone, mitkGIFGreyLevelSizeZone.h, mitk) SWIG_ADD_MITK_CLASS(GIFGreyLevelDistanceZone, mitkGIFGreyLevelDistanceZone.h, mitk) SWIG_ADD_MITK_CLASS(GIFLocalIntensity, mitkGIFLocalIntensity.h, mitk) SWIG_ADD_MITK_CLASS(GIFIntensityVolumeHistogramFeatures, mitkGIFIntensityVolumeHistogramFeatures.h, mitk) SWIG_ADD_MITK_CLASS(GIFNeighbourhoodGreyToneDifferenceFeatures, mitkGIFNeighbourhoodGreyToneDifferenceFeatures.h, mitk) -SWIG_ADD_MITK_CLASS(GIFCurvatureStatistic, mitkGIFCurvatureStatistic.h, mitk) \ No newline at end of file +SWIG_ADD_MITK_CLASS(GIFCurvatureStatistic, mitkGIFCurvatureStatistic.h, mitk) + +// +// MatchPoint Related Classes +// +MITKSWIG_ADD_CLASS(MITKAlgorithmHelper, mitkAlgorithmHelper.h, mitk) +MITKSWIG_ADD_CLASS(RegistrationType, mitkImageMappingHelper.h, mitk::ImageMappingHelper) +MITKSWIG_ADD_CLASS(MITKRegistrationType, mitkImageMappingHelper.h, mitk::ImageMappingHelper) + + +%ignore map::deployment::DLLHandle::New(const LibraryHandleType& libraryHandle, const map::algorithm::UID* pUID, const core::String& libraryFile, const core::String& profileStr); +%ignore map::deployment::DLLHandle::New(const map::algorithm::UID* pUID,const core::String& libraryFilePath,const core::String& profileStr); +%ignore map::deployment::DLLInfo::New(const map::algorithm::UID* pUID,const core::String& libraryFilePath,const core::String& profileStr); +%ignore map::deployment::DLLHandle::New; + +SWIG_ADD_MITK_CLASS_VECTORFREE(DLLDirectoryBrowser, mapDeploymentDLLDirectoryBrowser.h, ::map::deployment) +SWIG_ADD_MITK_CLASS_VECTORFREE(DLLInfo, mapDeploymentDLLInfo.h, ::map::deployment) +SWIG_ADD_MITK_CLASS_VECTORFREE(DLLHandle, mapDeploymentDLLHandle.h, ::map::deployment) +SWIG_ADD_MITK_CLASS_VECTORFREE(UID, mapUID.h, ::map::algorithm) +%{ + namespace algorithm + { + typedef map::algorithm::UID UID; + } + namespace core + { + typedef map::core::String String; + } +%} + +MITKSWIG_ADD_HEADERFILE(mapDeploymentDLLAccess.h) +SWIG_ADD_MITK_CLASS_VECTORFREE(DLLHandle, mapDeploymentDLLHandle.h, ::map::deployment) + +// SWIG_ADD_MITK_CLASS(FastSymmetricForcesDemonsMultiResDefaultRegistrationAlgorithm, mitkFastSymmetricForcesDemonsMultiResDefaultRegistrationAlgorithm.h, mitk) +// SWIG_ADD_MITK_CLASS(LevelSetMotionMultiResDefaultRegistrationAlgorithm, mitkLevelSetMotionMultiResDefaultRegistrationAlgorithm.h, mitk) +// SWIG_ADD_MITK_CLASS(MultiModalAffineDefaultRegistrationAlgorithm, mitkMultiModalAffineDefaultRegistrationAlgorithm.h, mitk) +// SWIG_ADD_MITK_CLASS(MultiModalRigidDefaultRegistrationAlgorithm, mitkMultiModalRigidDefaultRegistrationAlgorithm.h, mitk) +// SWIG_ADD_MITK_CLASS(MultiModalTransDefaultRegistrationAlgorithm, mitkMultiModalTransDefaultRegistrationAlgorithm.h, mitk) +// SWIG_ADD_MITK_CLASS(RigidClosedFormPointsDefaultRegistrationAlgorithm, mitkRigidClosedFormPointsDefaultRegistrationAlgorithm.h, mitk) +// SWIG_ADD_MITK_CLASS(RigidICPDefaultRegistrationAlgorithm, mitkRigidICPDefaultRegistrationAlgorithm.h, mitk) \ No newline at end of file diff --git a/Wrapping/Common/mitk_swig_macros.i b/Wrapping/Common/mitk_swig_macros.i index 5f7c949163..9938aebc28 100644 --- a/Wrapping/Common/mitk_swig_macros.i +++ b/Wrapping/Common/mitk_swig_macros.i @@ -1,196 +1,219 @@ // // This file contains macros for swig. // + + // -// SWIG_ADD_MITK_CLASS is a helper macro in order to do -// all important stuff before an mitk::Class is included. -// Requires the name of the class as it is in c++ as classname -// and the include file, in which the class is defined. -// It is assumed that the class is somehow inherited from -// mitk::BaseData, and supports smartpointers. +// MITKSWIG_ADD_HEADERFILE includes a header-file into SWIG // -%define SWIG_ADD_MITK_CLASS(classname, classinclude, nspace) +%define MITKSWIG_ADD_HEADERFILE( classinclude ) // Include the include file in the generated cpp file %{ #include < ## classinclude ## > - typedef nspace ## :: ## classname classname ## ; - using nspace ## :: ## classname ; %} // Include the given header, where the class definition is found %include < ## classinclude ## > - using nspace ##:: ## classname ; - - - // Declaring that this class is a smart-pointer class, in order to handle - // online upcasting where necessary (for example python) - %feature("smartptr", noblock=1) nspace ##:: ## classname { itk::SmartPointer } +%enddef +// +// MITKSWIG_ADD_CLASS is a helper macro in order to do +// all important stuff in order to wrap an existing +// class +// +%define MITKSWIG_ADD_CLASS(classname, classinclude, nspace) + MITKSWIG_ADD_HEADERFILE( classinclude ) + // Using class name in order to remove ambigiouties + %{ + typedef nspace ## :: ## classname classname ## ; + using nspace ## :: ## classname ; + %} + using nspace ##:: ## classname ; // Typedef is necessary to overcome ambigiouties resulting in the fact that SWIG // ignores namespaces. This can lead to some problems with templates. typedef nspace ## :: ## classname classname ## ; +%enddef - // Initianziation of std. vectors containing pointers to these classes. This allows to use - // vectors of these types as target language arrays. - %template(Vector ## classname ## Pointer) std::vector< nspace ## :: ## classname ## ::Pointer >; - %template(Vector ## classname) std::vector< nspace ## :: ## classname ## ::Self *>; - // Defining the Smartpointer, allows easy access in target language - %template(classname ## Pointer) itk::SmartPointer; +// +// MITKSWIG_AUTOMATED_CASTING is a helper macro in order to +// provide a convinience interface for up/downcasting of +// classes +// +%define MITKSWIG_AUTOMATED_CASTING(classname, classinclude, nspace) // Define a conversion method to convert from and to types %template(ConvertTo ## classname) ConvertTo< nspace ##:: ## classname ## >; - // This extend is necessary to have the automatic cast methods available %extend itk::SmartPointer< nspace ## :: ## classname ## ::Self> { %pythoncode %{ def _GetListOfValidItems(self): return [str(k) for k in self.GetClassHierarchy() if k in convertion_list.keys() ] %} %pythoncode %{ def __getattr__(self, item): if type(item)==str: if (len(item) > 9) and ('ConvertTo' in item): searchString=item[9:] if searchString in self._GetListOfValidItems(): def func_t(): return convertion_list[searchString](self) return func_t %} %pythoncode %{ def __dir__(self): return super().__dir__() + ['ConvertTo'+k for k in self._GetListOfValidItems()] %} } %extend std::vector< nspace ## :: ## classname *>::value_type { %pythoncode %{ def _GetListOfValidItems(self): return [str(k) for k in self.GetClassHierarchy() if k in convertion_list.keys() ] %} %pythoncode %{ def __getattr__(self, item): if type(item)==str: if (len(item) > 9) and ('ConvertTo' in item): searchString=item[9:] if searchString in self._GetListOfValidItems(): def func_t(): return convertion_list[searchString](self) return func_t %} %pythoncode %{ def __dir__(self): return super().__dir__() + ['ConvertTo'+k for k in self._GetListOfValidItems()] %} } %pythoncode %{ convertion_list['classname'] = ConvertTo ## classname %} %enddef +// +// MITKSWIG_SMARTPOINTERVECTOR : Wrapper for Vectors of Smartpointer-Classes +// +%define MITKSWIG_SMARTPOINTERVECTOR(classname, classinclude, nspace) + // Initianziation of std. vectors containing pointers to these classes. This allows to use + // vectors of these types as target language arrays. + %template(Vector ## classname ## Pointer) std::vector< nspace ## :: ## classname ## ::Pointer >; + %template(Vector ## classname) std::vector< nspace ## :: ## classname ## ::Self *>; +%enddef // -// SWIG_ADD_NONOBJECT_CLASS is a helper macro in order to do +// MITKSWIG_POINTERVECTOR : Wrapper for Vectors of Classes +// +%define MITKSWIG_POINTERVECTOR(classname, classinclude, nspace) + // Initianziation of std. vectors containing pointers to these classes. This allows to use + // vectors of these types as target language arrays. + %template(Vector ## classname ## Pointer) std::vector< nspace ## :: ## classname * >; + %template(Vector ## classname) std::vector< nspace ## :: ## classname >; +%enddef + +// +// MITKSWIG_MITKSMARTPOINTER : Wrapper for Vectors of Smartpointer-Classes +// +%define MITKSWIG_MITKSMARTPOINTER(classname, classinclude, nspace) + + // Declaring that this class is a smart-pointer class, in order to handle + // online upcasting where necessary (for example python) + %feature("smartptr", noblock=1) nspace ##:: ## classname { itk::SmartPointer } + + // Defining the Smartpointer, allows easy access in target language + %template(classname ## Pointer) itk::SmartPointer; + +%enddef + + +// +// SWIG_ADD_MITK_CLASS is a helper macro in order to do // all important stuff before an mitk::Class is included. // Requires the name of the class as it is in c++ as classname // and the include file, in which the class is defined. // It is assumed that the class is somehow inherited from // mitk::BaseData, and supports smartpointers. // -%define SWIG_ADD_NONOBJECT_CLASS(classname, classinclude, nspace) - // Include the include file in the generated cpp file - %{ - #include < ## classinclude ## > - typedef nspace ## :: ## classname classname ## ; - using nspace ## :: ## classname ; - %} +%define SWIG_ADD_MITK_CLASS_VECTORFREE(classname, classinclude, nspace) - // Include the given header, where the class definition is found - %include < ## classinclude ## > - using nspace ##:: ## classname ; - - // Typedef is necessary to overcome ambigiouties resulting in the fact that SWIG - // ignores namespaces. This can lead to some problems with templates. - typedef nspace ## :: ## classname classname ## ; + MITKSWIG_ADD_CLASS( classname, classinclude, nspace ) class nspace ## :: ## classname ## ; + class nspace ## :: ## classname ## ::Pointer; - // Initianziation of std. vectors containing pointers to these classes. This allows to use - // vectors of these types as target language arrays. - %template(Vector ## classname ## Pointer) std::vector< nspace ## :: ## classname * >; - %template(Vector ## classname) std::vector< nspace ## :: ## classname >; + MITKSWIG_MITKSMARTPOINTER(classname, classinclude, nspace) + MITKSWIG_AUTOMATED_CASTING(classname, classinclude, nspace) %enddef +// +// SWIG_ADD_MITK_CLASS is a helper macro in order to do +// all important stuff before an mitk::Class is included. +// Requires the name of the class as it is in c++ as classname +// and the include file, in which the class is defined. +// It is assumed that the class is somehow inherited from +// mitk::BaseData, and supports smartpointers. +// +%define SWIG_ADD_MITK_CLASS(classname, classinclude, nspace) + + MITKSWIG_ADD_CLASS( classname, classinclude, nspace ) + + class nspace ## :: ## classname ## ; + class nspace ## :: ## classname ## ::Pointer; + + MITKSWIG_MITKSMARTPOINTER(classname, classinclude, nspace) + + MITKSWIG_SMARTPOINTERVECTOR(classname, classinclude, nspace) + + MITKSWIG_AUTOMATED_CASTING(classname, classinclude, nspace) +%enddef // -// SWIG_ADD_NONOBJECT_TEMPLATECLASS is a helper macro in order to do +// SWIG_ADD_NONOBJECT_CLASS is a helper macro in order to do // all important stuff before an mitk::Class is included. // Requires the name of the class as it is in c++ as classname // and the include file, in which the class is defined. // It is assumed that the class is somehow inherited from // mitk::BaseData, and supports smartpointers. // -%define SWIG_ADD_NONOBJECT_TEMPLATECLASS(classname, classinclude, nspace, tmplstring) - // Include the include file in the generated cpp file - %{ - #include < ## classinclude ## > - typedef nspace ## :: ## classname classname ## ; - using nspace ## :: ## classname ; - %} +%define SWIG_ADD_NONOBJECT_CLASS(classname, classinclude, nspace) - // Include the given header, where the class definition is found - %include < ## classinclude ## > - using nspace ##:: ## classname ; + MITKSWIG_ADD_CLASS( classname, classinclude, nspace ) // Typedef is necessary to overcome ambigiouties resulting in the fact that SWIG // ignores namespaces. This can lead to some problems with templates. typedef nspace ## :: ## classname classname ## ; - %template( classname ) nspace ## :: ## tmplstring ## ; - - // Initianziation of std. vectors containing pointers to these classes. This allows to use - // vectors of these types as target language arrays. - %template(Vector ## classname ## Pointer) std::vector< nspace ## :: ## classname * >; - %template(Vector ## classname) std::vector< nspace ## :: ## classname >; + class nspace ## :: ## classname ## ; + MITKSWIG_POINTERVECTOR(classname, classinclude, nspace) %enddef - - // // SWIG_ADD_NONOBJECT_CLASS is a helper macro in order to do // all important stuff before an mitk::Class is included. // Requires the name of the class as it is in c++ as classname // and the include file, in which the class is defined. // It is assumed that the class is somehow inherited from // mitk::BaseData, and supports smartpointers. // %define SWIG_ADD_NONOBJECT_NOVECTOR_CLASS(classname, classinclude, nspace) - // Include the include file in the generated cpp file - %{ - #include < ## classinclude ## > - typedef nspace ## :: ## classname classname ## ; - using nspace ## :: ## classname ; - %} - // Include the given header, where the class definition is found - %include < ## classinclude ## > - using nspace ##:: ## classname ; + MITKSWIG_ADD_CLASS( classname, classinclude, nspace ) // Typedef is necessary to overcome ambigiouties resulting in the fact that SWIG // ignores namespaces. This can lead to some problems with templates. typedef nspace ## :: ## classname classname ## ; // Initianziation of std. vectors containing pointers to these classes. This allows to use // vectors of these types as target language arrays. %template(Vector ## classname ## Pointer) std::vector< nspace ## :: ## classname * >; %enddef diff --git a/Wrapping/Python/CMakeLists.txt b/Wrapping/Python/CMakeLists.txt index ac708a0b76..086b30dd8b 100644 --- a/Wrapping/Python/CMakeLists.txt +++ b/Wrapping/Python/CMakeLists.txt @@ -1,77 +1,77 @@ # Version 2.8.1 is the minium requirement for this script. # this is lower than the general minimum requirement. #cmake_minimum_required ( VERSION 2.8.1 FATAL_ERROR ) include(mitkTargetLinkLibrariesWithDynamicLookup) project( MITK_Python ) set(CMAKE_SHARED_LINKER_FLAGS "" CACHE INTERNAL "" FORCE) set(CMAKE_MODULE_LINKER_FLAGS "" CACHE INTERNAL "" FORCE) mitk_check_dynamic_lookup(MODULE SHARED MITK_UNDEFINED_SYMBOLS_ALLOWED ) # # Find the necessary libraries etc.. # if ( MITK_UNDEFINED_SYMBOLS_ALLOWED ) set( _QUIET_LIBRARY "QUIET" ) else() set( _QUIET_LIBRARY "REQUIRED" ) endif() find_package ( PythonInterp REQUIRED ) find_package ( PythonLibs ${_QUIET_LIBRARY} ) include_directories ( ${CMAKE_CURRENT_SOURCE_DIR} ) # # Options # option ( MITK_PYTHON_THREADS "Enable threaded python usage by unlocking the GIL." ON ) mark_as_advanced( MITK_PYTHON_THREADS ) option ( MITK_PYTHON_EGG "Add building of python eggs to the dist target." OFF ) mark_as_advanced( MITK_PYTHON_EGG ) option ( MITK_PYTHON_WHEEL "Add building of python wheels to the dist target." ON ) mark_as_advanced( MITK_PYTHON_WHEEL ) # Prepare the SWIG-File, i.e. especially add necessary include folders -mitkSwigPrepareFiles(pyMITK MITK.i "MitkCore;MitkCLCore;MitkCLUtilities;ITKCommon") +mitkSwigPrepareFiles(pyMITK MITK.i "MitkCore;MitkCLCore;MitkCLUtilities;ITKCommon;MitkMatchPointRegistration") # Add additional SWIG Parameters # These parameters depend on the target language set(CMAKE_SWIG_FLAGS ${CMAKE_SWIG_GLOBAL_FLAGS} -features autodoc=1 -keyword ) if( MITK_PYTHON_THREADS ) set(CMAKE_SWIG_FLAGS ${CMAKE_SWIG_FLAGS} -threads) endif() set(CMAKE_SWIG_OUTDIR ${CMAKE_CURRENT_BINARY_DIR}) # Create the actual SWIG project swig_add_module(pyMITK python MITK.i ) -mitkSwigAddLibraryDependencies(pyMITK "MitkCore;MitkCLCore;MitkCLUtilities;ITKCommon") +mitkSwigAddLibraryDependencies(pyMITK "MitkCore;MitkCLCore;MitkCLUtilities;ITKCommon;MitkMatchPointRegistration") mitk_target_link_libraries_with_dynamic_lookup(${SWIG_MODULE_pyMITK_REAL_NAME} ${PYTHON_LIBRARIES}) if(DEFINED SKBUILD) message(WARNING "SKBuild exists") # Currently this installation install(FILES ${CMAKE_CURRENT_BINARY_DIR}/pyMITK.py ${CMAKE_CURRENT_SOURCE_DIR}/Packaging/__init__.py # ${MITK_DOC_FILES} DESTINATION pyMITK COMPONENT Runtime ) install(TARGETS ${SWIG_MODULE_pyMITK_REAL_NAME} RUNTIME DESTINATION pyMITK LIBRARY DESTINATION pyMITK COMPONENT Runtime ) else() message(WARNING "SKBuild missing") include(LegacyPackaging.cmake) endif()