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..4c1becb275 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 { - m_inputImage = mitk::IOUtil::Load(inputFilename); + if (verbose) std::cout << "Loading input image..." << std::endl; + m_inputImage = mitk::IOUtil::LoadImage(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..decd6880be --- /dev/null +++ b/Modules/PhotoacousticsLib/MitkPAPhantomGenerator/PAPhantomGenerator.cpp @@ -0,0 +1,216 @@ +/*=================================================================== + +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 CreatePhantom_08_03_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(false); + returnParameters->SetMinNumberOfVessels(5); + returnParameters->SetMaxNumberOfVessels(5); + returnParameters->SetMinVesselAbsorption(10); + returnParameters->SetMaxVesselAbsorption(10); + 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.0075); + returnParameters->SetXDim(560); + returnParameters->SetYDim(800); + returnParameters->SetZDim(720); + 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); + double absorptions[10] = {1, 3, 5, 7, 9, 11, 13, 15, 17, 19}; + unsigned int iterationNumber = 0; + + for(;iterationNumber<10; iterationNumber++) + { + + auto parameters = CreatePhantom_08_03_18_Parameters(); + parameters->SetMinVesselAbsorption(absorptions[iterationNumber]); + parameters->SetMaxVesselAbsorption(absorptions[iterationNumber]); + MITK_INFO(input.verbose) << "Generating tissue.."; + auto resultTissue = PhantomTissueGenerator::GeneratePhantomData(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 + + "_" + std::to_string(iterationNumber) + ".nrrd"; + mitk::IOUtil::Save(resultTissue->ConvertToMitkImage(), savePath); + std::string outputPath = input.saveFolderPath + "output/Phantom_" + input.identifyer + + "_" + std::to_string(iterationNumber) + "/"; + + if (!itksys::SystemTools::FileIsDirectory(outputPath)) + { + itksys::SystemTools::MakeDirectory(outputPath); + } + + outputPath = outputPath + "Fluence_Phantom_" + input.identifyer + "_" + std::to_string(iterationNumber); + + MITK_INFO(input.verbose) << "Simulating fluence.."; + + int result = -4; + if(!input.probePath.empty()) + result = std::system(std::string(input.exePath + " -i " + savePath + " -o " + + (outputPath + ".nrrd") + + " -yo " + "0" + " -p " + input.probePath + + " -n 50000").c_str()); + else + result = std::system(std::string(input.exePath + " -i " + savePath + " -o " + + (outputPath + ".nrrd") + + " -yo " + "0" + " -n 50000").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..49aaae7a6b 100644 --- a/Modules/PhotoacousticsLib/include/mitkPATissueGeneratorParameters.h +++ b/Modules/PhotoacousticsLib/include/mitkPATissueGeneratorParameters.h @@ -1,214 +1,214 @@ /*=================================================================== 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(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(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; 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; - 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..9a6f9c9b59 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); + 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..36fab0d9d0 100644 --- a/Modules/PhotoacousticsLib/src/Domain/Volume/mitkPAInSilicoTissueVolume.cpp +++ b/Modules/PhotoacousticsLib/src/Domain/Volume/mitkPAInSilicoTissueVolume.cpp @@ -1,325 +1,388 @@ /*=================================================================== 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; 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..130fc86494 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) { #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; } diff --git a/Modules/PhotoacousticsLib/src/Generator/mitkPAPhantomTissueGenerator.cpp b/Modules/PhotoacousticsLib/src/Generator/mitkPAPhantomTissueGenerator.cpp new file mode 100644 index 0000000000..5b264c08ae --- /dev/null +++ b/Modules/PhotoacousticsLib/src/Generator/mitkPAPhantomTissueGenerator.cpp @@ -0,0 +1,158 @@ +/*=================================================================== + +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.6125; + 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..42d72b557b 100644 --- a/Modules/PhotoacousticsLib/src/Generator/mitkPATissueGenerator.cpp +++ b/Modules/PhotoacousticsLib/src/Generator/mitkPATissueGenerator.cpp @@ -1,160 +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 "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); + + const double DIRECTION_VECTOR_INITIAL_VARIANCE = 0.2; + + 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); switch (borderType) { case 0: 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(), 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..da6c526443 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::LoadImage(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::LoadImage(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..af4359d8c2 100644 --- a/Modules/PhotoacousticsLib/src/Utils/mitkPATissueGeneratorParameters.cpp +++ b/Modules/PhotoacousticsLib/src/Utils/mitkPATissueGeneratorParameters.cpp @@ -1,79 +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. ===================================================================*/ #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_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/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/mitkPhotoacousticNoiseGeneratorTest.cpp b/Modules/PhotoacousticsLib/test/mitkPhotoacousticNoiseGeneratorTest.cpp index 04a988a528..30a43b9292 100644 --- a/Modules/PhotoacousticsLib/test/mitkPhotoacousticNoiseGeneratorTest.cpp +++ b/Modules/PhotoacousticsLib/test/mitkPhotoacousticNoiseGeneratorTest.cpp @@ -1,71 +1,71 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include #include #include #include "mitkPAVolume.h" #include "mitkPANoiseGenerator.h" class mitkPhotoacousticNoiseGeneratorTestSuite : public mitk::TestFixture { CPPUNIT_TEST_SUITE(mitkPhotoacousticNoiseGeneratorTestSuite); MITK_TEST(testNoiseGenerator); CPPUNIT_TEST_SUITE_END(); private: public: mitk::pa::Volume::Pointer m_Volume; void setUp() override { } void testNoiseGenerator() { int size = 1000 * 100 * 100; auto* volume = new double[size]; for (int i = 0; i < size; i++) { volume[i] = 1; } - m_Volume = mitk::pa::Volume::New(volume, 1000, 100, 100); + m_Volume = mitk::pa::Volume::New(volume, 1000, 100, 100, 1); mitk::pa::NoiseGenerator::ApplyNoiseModel(m_Volume, 0.75, 0.1); int negativecounter = 0; for (int i = 0; i < size; i++) { if (m_Volume->GetData()[i] <= 0) { negativecounter++; } } CPPUNIT_ASSERT_EQUAL_MESSAGE("More than one negative: " + std::to_string(negativecounter) + " (" + std::to_string((((double)negativecounter) / size) * 100) + "%)", negativecounter, 0); } void tearDown() override { } }; MITK_TEST_SUITE_REGISTRATION(mitkPhotoacousticNoiseGenerator) diff --git a/Modules/PhotoacousticsLib/test/mitkPhotoacousticTissueGeneratorTest.cpp b/Modules/PhotoacousticsLib/test/mitkPhotoacousticTissueGeneratorTest.cpp index 8199cdc0bf..0627bfb4b7 100644 --- a/Modules/PhotoacousticsLib/test/mitkPhotoacousticTissueGeneratorTest.cpp +++ b/Modules/PhotoacousticsLib/test/mitkPhotoacousticTissueGeneratorTest.cpp @@ -1,83 +1,85 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include #include #include "mitkPATissueGenerator.h" class mitkPhotoacousticTissueGeneratorTestSuite : public mitk::TestFixture { CPPUNIT_TEST_SUITE(mitkPhotoacousticTissueGeneratorTestSuite); MITK_TEST(testCallWithEmptyParameters); MITK_TEST(testCallWithWorkingParameters); CPPUNIT_TEST_SUITE_END(); private: public: void setUp() override { } mitk::pa::TissueGeneratorParameters::Pointer createRandomTestVolumeParameters() { auto returnParameters = mitk::pa::TissueGeneratorParameters::New(); returnParameters->SetXDim(rand() % 50 + 1); returnParameters->SetYDim(rand() % 50 + 1); returnParameters->SetZDim(rand() % 50 + 1); - returnParameters->SetBackgroundAbsorption(rand() % 100 / 10.0); + double absorb = rand() % 100 / 10.0; + returnParameters->SetMinBackgroundAbsorption(absorb); + returnParameters->SetMaxBackgroundAbsorption(absorb); returnParameters->SetBackgroundScattering(rand() % 100 / 10.0); returnParameters->SetBackgroundAnisotropy(rand() % 100 / 10.0); int min = rand() % 10; returnParameters->SetMinNumberOfVessels(min); returnParameters->SetMaxNumberOfVessels(min + (rand() % 10)); returnParameters->SetCalculateNewVesselPositionCallback( &mitk::pa::VesselMeanderStrategy::CalculateRandomlyDivergingPosition); returnParameters->SetMinVesselZOrigin(rand() % 3 + 1); returnParameters->SetMaxVesselZOrigin(rand() % 3 + 1); int minRad = rand() % 100; returnParameters->SetMinVesselRadiusInMillimeters(minRad); returnParameters->SetMaxVesselRadiusInMillimeters(minRad + (rand() % 100)); returnParameters->SetVoxelSpacingInCentimeters(1); return returnParameters; } void testCallWithEmptyParameters() { auto parameters = mitk::pa::TissueGeneratorParameters::New(); auto volume = mitk::pa::InSilicoTissueGenerator::GenerateInSilicoData(parameters); CPPUNIT_ASSERT(volume.IsNotNull()); } void testCallWithWorkingParameters() { for (int i = 0; i < 20; i++) { auto parameters = createRandomTestVolumeParameters(); auto volume = mitk::pa::InSilicoTissueGenerator::GenerateInSilicoData(parameters); CPPUNIT_ASSERT(volume.IsNotNull()); } } void tearDown() override { } }; MITK_TEST_SUITE_REGISTRATION(mitkPhotoacousticTissueGenerator) diff --git a/Modules/PhotoacousticsLib/test/mitkPhotoacousticVesselTest.cpp b/Modules/PhotoacousticsLib/test/mitkPhotoacousticVesselTest.cpp index 17b06e9e21..c1e3949514 100644 --- a/Modules/PhotoacousticsLib/test/mitkPhotoacousticVesselTest.cpp +++ b/Modules/PhotoacousticsLib/test/mitkPhotoacousticVesselTest.cpp @@ -1,169 +1,242 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include #include #include #include "mitkPAInSilicoTissueVolume.h" #include "mitkPAVector.h" #include "mitkPAVessel.h" #include "mitkIOUtil.h" class mitkPhotoacousticVesselTestSuite : public mitk::TestFixture { CPPUNIT_TEST_SUITE(mitkPhotoacousticVesselTestSuite); MITK_TEST(testEmptyInitializationProperties); MITK_TEST(testWalkInStraightLine); MITK_TEST(testBifurcate); + MITK_TEST(testPartialVolume); CPPUNIT_TEST_SUITE_END(); private: mitk::pa::Vessel::Pointer m_TestVessel; mitk::pa::Vessel::CalculateNewVesselPositionCallback m_StraightLine; mitk::pa::Vessel::CalculateNewVesselPositionCallback m_Diverging; mitk::pa::InSilicoTissueVolume::Pointer m_TestInSilicoVolume; mitk::pa::TissueGeneratorParameters::Pointer m_TestVolumeParameters; public: void setUp() override { auto params = mitk::pa::VesselProperties::New(); m_TestVessel = mitk::pa::Vessel::New(params); m_StraightLine = &mitk::pa::VesselMeanderStrategy::CalculateNewPositionInStraightLine; m_Diverging = &mitk::pa::VesselMeanderStrategy::CalculateRandomlyDivergingPosition; m_TestVolumeParameters = createTestVolumeParameters(); - m_TestInSilicoVolume = mitk::pa::InSilicoTissueVolume::New(m_TestVolumeParameters); + auto rng = std::mt19937(); + m_TestInSilicoVolume = mitk::pa::InSilicoTissueVolume::New(m_TestVolumeParameters, &rng); } mitk::pa::TissueGeneratorParameters::Pointer createTestVolumeParameters() { auto returnParameters = mitk::pa::TissueGeneratorParameters::New(); returnParameters->SetXDim(10); returnParameters->SetYDim(10); returnParameters->SetZDim(10); - returnParameters->SetBackgroundAbsorption(0); + returnParameters->SetMinBackgroundAbsorption(0); + returnParameters->SetMaxBackgroundAbsorption(0); returnParameters->SetBackgroundScattering(0); returnParameters->SetBackgroundAnisotropy(0); return returnParameters; } void testEmptyInitializationProperties() { CPPUNIT_ASSERT(m_TestVessel->CanBifurcate() == false); CPPUNIT_ASSERT(m_TestVessel->IsFinished() == true); } - void testWalkInStraightLine() + void testPartialVolume() { auto testPosition = mitk::pa::Vector::New(); testPosition->SetElement(0, 0); testPosition->SetElement(1, 4); testPosition->SetElement(2, 4); auto testDirection = mitk::pa::Vector::New(); testDirection->SetElement(0, 1); testDirection->SetElement(1, 0); testDirection->SetElement(2, 0); auto params = mitk::pa::VesselProperties::New(); + params->SetDoPartialVolume(true); params->SetRadiusInVoxel(1); params->SetBifurcationFrequency(100); params->SetAbsorptionCoefficient(10); params->SetScatteringCoefficient(10); params->SetAnisotopyCoefficient(10); params->SetPositionVector(testPosition); params->SetDirectionVector(testDirection); m_TestVessel = mitk::pa::Vessel::New(params); CPPUNIT_ASSERT(m_TestVessel->CanBifurcate() == false); CPPUNIT_ASSERT(m_TestVessel->IsFinished() == false); CPPUNIT_ASSERT(std::abs(m_TestInSilicoVolume->GetAbsorptionVolume()->GetData(0, 4, 4)) <= mitk::eps); m_TestVessel->ExpandVessel(m_TestInSilicoVolume, m_StraightLine, 0, nullptr); - CPPUNIT_ASSERT(std::abs(m_TestInSilicoVolume->GetAbsorptionVolume()->GetData(0, 4, 4) - 10) <= mitk::eps); - CPPUNIT_ASSERT(std::abs(m_TestInSilicoVolume->GetAbsorptionVolume()->GetData(0, 5, 4) - 10) <= mitk::eps); - CPPUNIT_ASSERT(std::abs(m_TestInSilicoVolume->GetAbsorptionVolume()->GetData(0, 6, 4)) <= mitk::eps); + CPPUNIT_ASSERT_MESSAGE(std::to_string(m_TestInSilicoVolume->GetAbsorptionVolume()->GetData(0, 4, 4)), + abs(m_TestInSilicoVolume->GetAbsorptionVolume()->GetData(0, 4, 4) - 10) <= mitk::eps); + CPPUNIT_ASSERT_MESSAGE(std::to_string(m_TestInSilicoVolume->GetAbsorptionVolume()->GetData(0, 4, 5)), + abs(m_TestInSilicoVolume->GetAbsorptionVolume()->GetData(0, 4, 5) - 10) <= mitk::eps); + CPPUNIT_ASSERT_MESSAGE(std::to_string(m_TestInSilicoVolume->GetAbsorptionVolume()->GetData(0, 4, 3)), + abs(m_TestInSilicoVolume->GetAbsorptionVolume()->GetData(0, 4, 3) - 10) <= mitk::eps); + CPPUNIT_ASSERT_MESSAGE(std::to_string(m_TestInSilicoVolume->GetAbsorptionVolume()->GetData(0, 5, 4)), + abs(m_TestInSilicoVolume->GetAbsorptionVolume()->GetData(0, 5, 4) - 10) <= mitk::eps); + CPPUNIT_ASSERT_MESSAGE(std::to_string(m_TestInSilicoVolume->GetAbsorptionVolume()->GetData(0, 3, 4)), + abs(m_TestInSilicoVolume->GetAbsorptionVolume()->GetData(0, 3, 4) - 10) <= mitk::eps); + CPPUNIT_ASSERT_MESSAGE(std::to_string(m_TestInSilicoVolume->GetAbsorptionVolume()->GetData(0, 5, 5)), + abs(m_TestInSilicoVolume->GetAbsorptionVolume()->GetData(0, 5, 5) - 5.85786438) <= 1e-6); + CPPUNIT_ASSERT_MESSAGE(std::to_string(m_TestInSilicoVolume->GetAbsorptionVolume()->GetData(0, 3, 3)), + abs(m_TestInSilicoVolume->GetAbsorptionVolume()->GetData(0, 3, 3) - 5.85786438) <= 1e-6); + CPPUNIT_ASSERT_MESSAGE(std::to_string(m_TestInSilicoVolume->GetAbsorptionVolume()->GetData(0, 5, 3)), + abs(m_TestInSilicoVolume->GetAbsorptionVolume()->GetData(0, 5, 3) - 5.85786438) <= 1e-6); + CPPUNIT_ASSERT_MESSAGE(std::to_string(m_TestInSilicoVolume->GetAbsorptionVolume()->GetData(0, 3, 5)), + abs(m_TestInSilicoVolume->GetAbsorptionVolume()->GetData(0, 3, 5) - 5.85786438) <= 1e-6); + } - CPPUNIT_ASSERT(std::abs(m_TestInSilicoVolume->GetAbsorptionVolume()->GetData(0, 4, 4) - 10) <= mitk::eps); - CPPUNIT_ASSERT(std::abs(m_TestInSilicoVolume->GetAbsorptionVolume()->GetData(0, 4, 5) - 10) <= mitk::eps); - CPPUNIT_ASSERT(std::abs(m_TestInSilicoVolume->GetAbsorptionVolume()->GetData(0, 4, 6)) <= mitk::eps); + void testWalkInStraightLine() + { + auto testPosition = mitk::pa::Vector::New(); + testPosition->SetElement(0, 0); + testPosition->SetElement(1, 4); + testPosition->SetElement(2, 4); + auto testDirection = mitk::pa::Vector::New(); + testDirection->SetElement(0, 1); + testDirection->SetElement(1, 0); + testDirection->SetElement(2, 0); + auto params = mitk::pa::VesselProperties::New(); + params->SetDoPartialVolume(false); + params->SetRadiusInVoxel(1); + params->SetBifurcationFrequency(100); + params->SetAbsorptionCoefficient(10); + params->SetScatteringCoefficient(10); + params->SetAnisotopyCoefficient(10); + params->SetPositionVector(testPosition); + params->SetDirectionVector(testDirection); + m_TestVessel = mitk::pa::Vessel::New(params); + + CPPUNIT_ASSERT(m_TestVessel->CanBifurcate() == false); + CPPUNIT_ASSERT(m_TestVessel->IsFinished() == false); - CPPUNIT_ASSERT(std::abs(m_TestInSilicoVolume->GetAbsorptionVolume()->GetData(0, 4, 4) - 10) <= mitk::eps); - CPPUNIT_ASSERT(std::abs(m_TestInSilicoVolume->GetAbsorptionVolume()->GetData(1, 4, 4) - 10) <= mitk::eps); - CPPUNIT_ASSERT(std::abs(m_TestInSilicoVolume->GetAbsorptionVolume()->GetData(2, 4, 4)) <= mitk::eps); + CPPUNIT_ASSERT_MESSAGE(std::to_string(m_TestInSilicoVolume->GetAbsorptionVolume()->GetData(0, 4, 4)), + abs(m_TestInSilicoVolume->GetAbsorptionVolume()->GetData(0, 4, 4)) <= mitk::eps); m_TestVessel->ExpandVessel(m_TestInSilicoVolume, m_StraightLine, 0, nullptr); - CPPUNIT_ASSERT(std::abs(m_TestInSilicoVolume->GetAbsorptionVolume()->GetData(1, 4, 4) - 10) <= mitk::eps); - CPPUNIT_ASSERT(std::abs(m_TestInSilicoVolume->GetAbsorptionVolume()->GetData(1, 5, 4) - 10) <= mitk::eps); - CPPUNIT_ASSERT(std::abs(m_TestInSilicoVolume->GetAbsorptionVolume()->GetData(1, 6, 4)) <= mitk::eps); + mitk::IOUtil::Save(m_TestInSilicoVolume->GetAbsorptionVolume()->AsMitkImage(), "C:/Users/groehl/Desktop/test.nrrd"); + + CPPUNIT_ASSERT_MESSAGE(std::to_string(m_TestInSilicoVolume->GetAbsorptionVolume()->GetData(0, 4, 4)), + abs(m_TestInSilicoVolume->GetAbsorptionVolume()->GetData(0, 4, 4) - 10) <= mitk::eps); + CPPUNIT_ASSERT_MESSAGE(std::to_string(m_TestInSilicoVolume->GetAbsorptionVolume()->GetData(0, 5, 4)), + abs(m_TestInSilicoVolume->GetAbsorptionVolume()->GetData(0, 5, 4)) <= mitk::eps); + CPPUNIT_ASSERT_MESSAGE(std::to_string(m_TestInSilicoVolume->GetAbsorptionVolume()->GetData(0, 6, 4)), + abs(m_TestInSilicoVolume->GetAbsorptionVolume()->GetData(0, 6, 4)) <= mitk::eps); + + CPPUNIT_ASSERT_MESSAGE(std::to_string(m_TestInSilicoVolume->GetAbsorptionVolume()->GetData(0, 4, 4)), + abs(m_TestInSilicoVolume->GetAbsorptionVolume()->GetData(0, 4, 4) - 10) <= mitk::eps); + CPPUNIT_ASSERT_MESSAGE(std::to_string(m_TestInSilicoVolume->GetAbsorptionVolume()->GetData(0, 4, 5)), + abs(m_TestInSilicoVolume->GetAbsorptionVolume()->GetData(0, 4, 5)) <= mitk::eps); + CPPUNIT_ASSERT_MESSAGE(std::to_string(m_TestInSilicoVolume->GetAbsorptionVolume()->GetData(0, 4, 6)), + abs(m_TestInSilicoVolume->GetAbsorptionVolume()->GetData(0, 4, 6)) <= mitk::eps); + + CPPUNIT_ASSERT_MESSAGE(std::to_string(m_TestInSilicoVolume->GetAbsorptionVolume()->GetData(0, 4, 4)), + abs(m_TestInSilicoVolume->GetAbsorptionVolume()->GetData(0, 4, 4) - 10) <= mitk::eps); + CPPUNIT_ASSERT_MESSAGE(std::to_string(m_TestInSilicoVolume->GetAbsorptionVolume()->GetData(1, 4, 4)), + abs(m_TestInSilicoVolume->GetAbsorptionVolume()->GetData(1, 4, 4) - 10) <= mitk::eps); + CPPUNIT_ASSERT_MESSAGE(std::to_string(m_TestInSilicoVolume->GetAbsorptionVolume()->GetData(2, 4, 4)), + abs(m_TestInSilicoVolume->GetAbsorptionVolume()->GetData(2, 4, 4)) <= mitk::eps); - CPPUNIT_ASSERT(std::abs(m_TestInSilicoVolume->GetAbsorptionVolume()->GetData(1, 4, 4) - 10) <= mitk::eps); - CPPUNIT_ASSERT(std::abs(m_TestInSilicoVolume->GetAbsorptionVolume()->GetData(1, 4, 5) - 10) <= mitk::eps); - CPPUNIT_ASSERT(std::abs(m_TestInSilicoVolume->GetAbsorptionVolume()->GetData(1, 4, 6)) <= mitk::eps); + m_TestVessel->ExpandVessel(m_TestInSilicoVolume, m_StraightLine, 0, nullptr); - CPPUNIT_ASSERT(std::abs(m_TestInSilicoVolume->GetAbsorptionVolume()->GetData(1, 4, 4) - 10) <= mitk::eps); - CPPUNIT_ASSERT(std::abs(m_TestInSilicoVolume->GetAbsorptionVolume()->GetData(2, 4, 4) - 10) <= mitk::eps); - CPPUNIT_ASSERT(std::abs(m_TestInSilicoVolume->GetAbsorptionVolume()->GetData(3, 4, 4)) <= mitk::eps); + CPPUNIT_ASSERT_MESSAGE(std::to_string(m_TestInSilicoVolume->GetAbsorptionVolume()->GetData(1, 4, 4)), + abs(m_TestInSilicoVolume->GetAbsorptionVolume()->GetData(1, 4, 4) - 10) <= mitk::eps); + CPPUNIT_ASSERT_MESSAGE(std::to_string(m_TestInSilicoVolume->GetAbsorptionVolume()->GetData(1, 5, 4)), + abs(m_TestInSilicoVolume->GetAbsorptionVolume()->GetData(1, 5, 4)) <= mitk::eps); + CPPUNIT_ASSERT_MESSAGE(std::to_string(m_TestInSilicoVolume->GetAbsorptionVolume()->GetData(1, 6, 4)), + abs(m_TestInSilicoVolume->GetAbsorptionVolume()->GetData(1, 6, 4)) <= mitk::eps); + + CPPUNIT_ASSERT_MESSAGE(std::to_string(m_TestInSilicoVolume->GetAbsorptionVolume()->GetData(1, 4, 4)), + abs(m_TestInSilicoVolume->GetAbsorptionVolume()->GetData(1, 4, 4) - 10) <= mitk::eps); + CPPUNIT_ASSERT_MESSAGE(std::to_string(m_TestInSilicoVolume->GetAbsorptionVolume()->GetData(1, 4, 5)), + abs(m_TestInSilicoVolume->GetAbsorptionVolume()->GetData(1, 4, 5)) <= mitk::eps); + CPPUNIT_ASSERT_MESSAGE(std::to_string(m_TestInSilicoVolume->GetAbsorptionVolume()->GetData(1, 4, 6)), + abs(m_TestInSilicoVolume->GetAbsorptionVolume()->GetData(1, 4, 6)) <= mitk::eps); + + CPPUNIT_ASSERT_MESSAGE(std::to_string(m_TestInSilicoVolume->GetAbsorptionVolume()->GetData(1, 4, 4)), + abs(m_TestInSilicoVolume->GetAbsorptionVolume()->GetData(1, 4, 4) - 10) <= mitk::eps); + CPPUNIT_ASSERT_MESSAGE(std::to_string(m_TestInSilicoVolume->GetAbsorptionVolume()->GetData(2, 4, 4)), + abs(m_TestInSilicoVolume->GetAbsorptionVolume()->GetData(2, 4, 4) - 10) <= mitk::eps); + CPPUNIT_ASSERT_MESSAGE(std::to_string(m_TestInSilicoVolume->GetAbsorptionVolume()->GetData(3, 4, 4)), + abs(m_TestInSilicoVolume->GetAbsorptionVolume()->GetData(3, 4, 4)) <= mitk::eps); } void testBifurcate() { auto testPosition = mitk::pa::Vector::New(); testPosition->SetElement(0, 0); testPosition->SetElement(1, 4); testPosition->SetElement(2, 4); auto testDirection = mitk::pa::Vector::New(); testDirection->SetElement(0, 1); testDirection->SetElement(1, 0); testDirection->SetElement(2, 0); auto params = mitk::pa::VesselProperties::New(); params->SetRadiusInVoxel(1); params->SetBifurcationFrequency(1); params->SetAbsorptionCoefficient(10); params->SetScatteringCoefficient(10); params->SetAnisotopyCoefficient(10); params->SetPositionVector(testPosition); params->SetDirectionVector(testDirection); m_TestVessel = mitk::pa::Vessel::New(params); CPPUNIT_ASSERT(m_TestVessel->CanBifurcate() == false); CPPUNIT_ASSERT(m_TestVessel->IsFinished() == false); CPPUNIT_ASSERT(std::abs(m_TestInSilicoVolume->GetAbsorptionVolume()->GetData(0, 4, 4)) <= mitk::eps); m_TestVessel->ExpandVessel(m_TestInSilicoVolume, m_StraightLine, 0, nullptr); m_TestVessel->ExpandVessel(m_TestInSilicoVolume, m_StraightLine, 0, nullptr); CPPUNIT_ASSERT(m_TestVessel->CanBifurcate() == true); std::mt19937 rng; auto bifurcationVessel = m_TestVessel->Bifurcate(&rng); CPPUNIT_ASSERT(m_TestVessel->CanBifurcate() == false); CPPUNIT_ASSERT(bifurcationVessel->CanBifurcate() == false); } void tearDown() override { } }; MITK_TEST_SUITE_REGISTRATION(mitkPhotoacousticVessel) diff --git a/Modules/PhotoacousticsLib/test/mitkPhotoacousticVesselTreeTest.cpp b/Modules/PhotoacousticsLib/test/mitkPhotoacousticVesselTreeTest.cpp index a78d6a7bb9..1a022017f3 100644 --- a/Modules/PhotoacousticsLib/test/mitkPhotoacousticVesselTreeTest.cpp +++ b/Modules/PhotoacousticsLib/test/mitkPhotoacousticVesselTreeTest.cpp @@ -1,106 +1,108 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include #include #include #include "mitkPAVesselTree.h" #include "mitkPAInSilicoTissueVolume.h" using namespace mitk::pa; class mitkPhotoacousticVesselTreeTestSuite : public mitk::TestFixture { CPPUNIT_TEST_SUITE(mitkPhotoacousticVesselTreeTestSuite); MITK_TEST(testVesselTreeInitialBehavior); MITK_TEST(testCallStepMethod); CPPUNIT_TEST_SUITE_END(); private: public: VesselTree::Pointer m_Tree; VesselProperties::Pointer m_VesselProperties; Vessel::CalculateNewVesselPositionCallback m_StraightLine; InSilicoTissueVolume::Pointer m_TestInSilicoVolume; void setUp() override { m_VesselProperties = VesselProperties::New(); m_Tree = VesselTree::New(m_VesselProperties); m_StraightLine = &VesselMeanderStrategy::CalculateNewPositionInStraightLine; - m_TestInSilicoVolume = InSilicoTissueVolume::New(createTestVolumeParameters()); + auto rng = std::mt19937(); + m_TestInSilicoVolume = InSilicoTissueVolume::New(createTestVolumeParameters(), &rng); } TissueGeneratorParameters::Pointer createTestVolumeParameters() { auto returnParameters = TissueGeneratorParameters::New(); returnParameters->SetXDim(10); returnParameters->SetYDim(10); returnParameters->SetZDim(10); - returnParameters->SetBackgroundAbsorption(0); + returnParameters->SetMinBackgroundAbsorption(0); + returnParameters->SetMaxBackgroundAbsorption(0); returnParameters->SetBackgroundScattering(0); returnParameters->SetBackgroundAnisotropy(0); return returnParameters; } void testVesselTreeInitialBehavior() { CPPUNIT_ASSERT(m_Tree->IsFinished() == true); } void testCallStepMethod() { //really bad test atm.. The only thing that is tested is that the method can be called without a crash. //But hey - it is something :P m_VesselProperties->SetRadiusInVoxel(2); std::mt19937 rng; rng.seed(1); m_Tree = VesselTree::New(m_VesselProperties); m_Tree->Step(m_TestInSilicoVolume, m_StraightLine, 0, &rng); rng.seed(1); auto secondTree = VesselTree::New(m_VesselProperties); secondTree->Step(m_TestInSilicoVolume, m_StraightLine, 0, &rng); CPPUNIT_ASSERT(Equal(m_Tree, secondTree, 1e-6, true)); secondTree->Step(m_TestInSilicoVolume, m_StraightLine, 0, &rng); CPPUNIT_ASSERT(!Equal(m_Tree, secondTree, 1e-6, true)); int i = 0; for (; i < 1000; i++) { secondTree->Step(m_TestInSilicoVolume, m_StraightLine, 0, &rng); if (secondTree->IsFinished()) break; } CPPUNIT_ASSERT(i < 999); } void tearDown() override { } }; MITK_TEST_SUITE_REGISTRATION(mitkPhotoacousticVesselTree) diff --git a/Modules/PhotoacousticsLib/test/mitkPhotoacousticVolumeTest.cpp b/Modules/PhotoacousticsLib/test/mitkPhotoacousticVolumeTest.cpp index 0af0c79d24..b3e874aa1c 100644 --- a/Modules/PhotoacousticsLib/test/mitkPhotoacousticVolumeTest.cpp +++ b/Modules/PhotoacousticsLib/test/mitkPhotoacousticVolumeTest.cpp @@ -1,347 +1,385 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include #include #include #include "mitkPAInSilicoTissueVolume.h" #include "mitkPATissueGeneratorParameters.h" class mitkPhotoacousticVolumeTestSuite : public mitk::TestFixture { CPPUNIT_TEST_SUITE(mitkPhotoacousticVolumeTestSuite); + MITK_TEST(TestUniformDistributionIsUniform); MITK_TEST(TestInitializedTissueContainsOnlyZeros); MITK_TEST(TestConvertedMitkImageContainsOnlyZerosOrAir); MITK_TEST(TestTissueVolumeContainsCorrectAbsorptionNumber); MITK_TEST(TestTissueVolumeContainsCorrectScatteringNumber); MITK_TEST(TestTissueVolumeContainsCorrectAnisotropyNumber); MITK_TEST(testSecondConstructor); MITK_TEST(testCompleteAirVoxelInclusion); MITK_TEST(testHalfAirVoxelInclusion); MITK_TEST(testCompleteAirAndSkinVoxelInclusion); MITK_TEST(testRandomizeCoefficients); CPPUNIT_TEST_SUITE_END(); private: mitk::pa::InSilicoTissueVolume::Pointer m_PhotoacousticVolume; mitk::pa::TissueGeneratorParameters::Pointer m_TissueGeneratorParameters; public: void setUp() override { m_TissueGeneratorParameters = mitk::pa::TissueGeneratorParameters::New(); - m_PhotoacousticVolume = mitk::pa::InSilicoTissueVolume::New(m_TissueGeneratorParameters); + auto rng = std::mt19937(); + m_PhotoacousticVolume = mitk::pa::InSilicoTissueVolume::New(m_TissueGeneratorParameters, &rng); + } + + void TestUniformDistributionIsUniform() + { + + int dims = 30; + m_TissueGeneratorParameters->SetXDim(dims); + m_TissueGeneratorParameters->SetYDim(dims); + m_TissueGeneratorParameters->SetZDim(dims); + m_TissueGeneratorParameters->SetAirThicknessInMillimeters(0); + + m_TissueGeneratorParameters->SetMinBackgroundAbsorption(0.001); + m_TissueGeneratorParameters->SetMaxBackgroundAbsorption(0.2); + + auto rng = std::mt19937(); + m_PhotoacousticVolume = mitk::pa::InSilicoTissueVolume::New(m_TissueGeneratorParameters, &rng); + + for (int x = 0; x < dims; x++) + { + for (int y = 0; y < dims; y++) + { + for (int z = 0; z < dims; z++) + { + CPPUNIT_ASSERT_EQUAL_MESSAGE("Every absorption should be in bounds.", + m_PhotoacousticVolume->GetAbsorptionVolume()->GetData(x, y, z) >= 0.001 && + m_PhotoacousticVolume->GetAbsorptionVolume()->GetData(x, y, z) <= 0.2, true); + } + } + } + } void TestInitializedTissueContainsOnlyZeros() { int dims = 30; m_TissueGeneratorParameters->SetXDim(dims); m_TissueGeneratorParameters->SetYDim(dims); m_TissueGeneratorParameters->SetZDim(dims); m_TissueGeneratorParameters->SetAirThicknessInMillimeters(0); - m_TissueGeneratorParameters->SetBackgroundAbsorption(0); - m_PhotoacousticVolume = mitk::pa::InSilicoTissueVolume::New(m_TissueGeneratorParameters); + m_TissueGeneratorParameters->SetMinBackgroundAbsorption(0); + m_TissueGeneratorParameters->SetMaxBackgroundAbsorption(0); + auto rng = std::mt19937(); + m_PhotoacousticVolume = mitk::pa::InSilicoTissueVolume::New(m_TissueGeneratorParameters, &rng); for (int x = 0; x < dims; x++) { for (int y = 0; y < dims; y++) { for (int z = 0; z < dims; z++) { CPPUNIT_ASSERT_EQUAL_MESSAGE("Every field should be initialized with 0.", std::abs(m_PhotoacousticVolume->GetAbsorptionVolume()->GetData(x, y, z)) < mitk::eps, true); } } } } void TestConvertedMitkImageContainsOnlyZerosOrAir() { int dims = 30; m_TissueGeneratorParameters->SetXDim(dims); m_TissueGeneratorParameters->SetYDim(dims); m_TissueGeneratorParameters->SetZDim(dims); - m_PhotoacousticVolume = mitk::pa::InSilicoTissueVolume::New(m_TissueGeneratorParameters); + auto rng = std::mt19937(); + m_PhotoacousticVolume = mitk::pa::InSilicoTissueVolume::New(m_TissueGeneratorParameters, &rng); mitk::Image::Pointer testImage = m_PhotoacousticVolume->ConvertToMitkImage(); mitk::ImageReadAccessor imgMemAcc(testImage); auto* imagePointer = (double*)imgMemAcc.GetData(); for (int index = 0; index < dims*dims*dims; index++, imagePointer++) { CPPUNIT_ASSERT_EQUAL_MESSAGE("Every voxel in image should be 0.1 or 0.0001.", true, std::abs(*imagePointer - 0.1) <= mitk::eps || std::abs(*imagePointer - 0.0001) <= mitk::eps); } } void TestTissueVolumeContainsCorrectAbsorptionNumber() { int dims = 2; m_TissueGeneratorParameters->SetXDim(dims); m_TissueGeneratorParameters->SetYDim(dims); m_TissueGeneratorParameters->SetZDim(dims); - m_PhotoacousticVolume = mitk::pa::InSilicoTissueVolume::New(m_TissueGeneratorParameters); + auto rng = std::mt19937(); + m_PhotoacousticVolume = mitk::pa::InSilicoTissueVolume::New(m_TissueGeneratorParameters, &rng); m_PhotoacousticVolume->SetVolumeValues(0, 0, 0, 0, 0, 0); m_PhotoacousticVolume->SetVolumeValues(0, 0, 1, 1, 0, 0); m_PhotoacousticVolume->SetVolumeValues(0, 1, 0, 2, 0, 0); m_PhotoacousticVolume->SetVolumeValues(0, 1, 1, 3, 0, 0); m_PhotoacousticVolume->SetVolumeValues(1, 0, 0, 4, 0, 0); m_PhotoacousticVolume->SetVolumeValues(1, 0, 1, 5, 0, 0); m_PhotoacousticVolume->SetVolumeValues(1, 1, 0, 6, 0, 0); m_PhotoacousticVolume->SetVolumeValues(1, 1, 1, 7, 0, 0); CPPUNIT_ASSERT_EQUAL_MESSAGE("Should be correct value", 0.0, m_PhotoacousticVolume->GetAbsorptionVolume()->GetData(0, 0, 0)); CPPUNIT_ASSERT_EQUAL_MESSAGE("Should be correct value", 1.0, m_PhotoacousticVolume->GetAbsorptionVolume()->GetData(0, 0, 1)); CPPUNIT_ASSERT_EQUAL_MESSAGE("Should be correct value", 2.0, m_PhotoacousticVolume->GetAbsorptionVolume()->GetData(0, 1, 0)); CPPUNIT_ASSERT_EQUAL_MESSAGE("Should be correct value", 3.0, m_PhotoacousticVolume->GetAbsorptionVolume()->GetData(0, 1, 1)); CPPUNIT_ASSERT_EQUAL_MESSAGE("Should be correct value", 4.0, m_PhotoacousticVolume->GetAbsorptionVolume()->GetData(1, 0, 0)); CPPUNIT_ASSERT_EQUAL_MESSAGE("Should be correct value", 5.0, m_PhotoacousticVolume->GetAbsorptionVolume()->GetData(1, 0, 1)); CPPUNIT_ASSERT_EQUAL_MESSAGE("Should be correct value", 6.0, m_PhotoacousticVolume->GetAbsorptionVolume()->GetData(1, 1, 0)); CPPUNIT_ASSERT_EQUAL_MESSAGE("Should be correct value", 7.0, m_PhotoacousticVolume->GetAbsorptionVolume()->GetData(1, 1, 1)); } void TestTissueVolumeContainsCorrectScatteringNumber() { int dims = 2; m_TissueGeneratorParameters->SetXDim(dims); m_TissueGeneratorParameters->SetYDim(dims); m_TissueGeneratorParameters->SetZDim(dims); - m_PhotoacousticVolume = mitk::pa::InSilicoTissueVolume::New(m_TissueGeneratorParameters); + auto rng = std::mt19937(); + m_PhotoacousticVolume = mitk::pa::InSilicoTissueVolume::New(m_TissueGeneratorParameters, &rng); m_PhotoacousticVolume->SetVolumeValues(0, 0, 0, 0, 0, 0); m_PhotoacousticVolume->SetVolumeValues(0, 0, 1, 0, 1, 0); m_PhotoacousticVolume->SetVolumeValues(0, 1, 0, 0, 2, 0); m_PhotoacousticVolume->SetVolumeValues(0, 1, 1, 0, 3, 0); m_PhotoacousticVolume->SetVolumeValues(1, 0, 0, 0, 4, 0); m_PhotoacousticVolume->SetVolumeValues(1, 0, 1, 0, 5, 0); m_PhotoacousticVolume->SetVolumeValues(1, 1, 0, 0, 6, 0); m_PhotoacousticVolume->SetVolumeValues(1, 1, 1, 0, 7, 0); CPPUNIT_ASSERT_EQUAL_MESSAGE("Should be correct value", 0.0, m_PhotoacousticVolume->GetScatteringVolume()->GetData(0, 0, 0)); CPPUNIT_ASSERT_EQUAL_MESSAGE("Should be correct value", 1.0, m_PhotoacousticVolume->GetScatteringVolume()->GetData(0, 0, 1)); CPPUNIT_ASSERT_EQUAL_MESSAGE("Should be correct value", 2.0, m_PhotoacousticVolume->GetScatteringVolume()->GetData(0, 1, 0)); CPPUNIT_ASSERT_EQUAL_MESSAGE("Should be correct value", 3.0, m_PhotoacousticVolume->GetScatteringVolume()->GetData(0, 1, 1)); CPPUNIT_ASSERT_EQUAL_MESSAGE("Should be correct value", 4.0, m_PhotoacousticVolume->GetScatteringVolume()->GetData(1, 0, 0)); CPPUNIT_ASSERT_EQUAL_MESSAGE("Should be correct value", 5.0, m_PhotoacousticVolume->GetScatteringVolume()->GetData(1, 0, 1)); CPPUNIT_ASSERT_EQUAL_MESSAGE("Should be correct value", 6.0, m_PhotoacousticVolume->GetScatteringVolume()->GetData(1, 1, 0)); CPPUNIT_ASSERT_EQUAL_MESSAGE("Should be correct value", 7.0, m_PhotoacousticVolume->GetScatteringVolume()->GetData(1, 1, 1)); } void TestTissueVolumeContainsCorrectAnisotropyNumber() { int dims = 2; m_TissueGeneratorParameters->SetXDim(dims); m_TissueGeneratorParameters->SetYDim(dims); m_TissueGeneratorParameters->SetZDim(dims); - m_PhotoacousticVolume = mitk::pa::InSilicoTissueVolume::New(m_TissueGeneratorParameters); + auto rng = std::mt19937(); + m_PhotoacousticVolume = mitk::pa::InSilicoTissueVolume::New(m_TissueGeneratorParameters, &rng); m_PhotoacousticVolume->SetVolumeValues(0, 0, 0, 0, 0, 0); m_PhotoacousticVolume->SetVolumeValues(0, 0, 1, 0, 0, 1); m_PhotoacousticVolume->SetVolumeValues(0, 1, 0, 0, 0, 2); m_PhotoacousticVolume->SetVolumeValues(0, 1, 1, 0, 0, 3); m_PhotoacousticVolume->SetVolumeValues(1, 0, 0, 0, 0, 4); m_PhotoacousticVolume->SetVolumeValues(1, 0, 1, 0, 0, 5); m_PhotoacousticVolume->SetVolumeValues(1, 1, 0, 0, 0, 6); m_PhotoacousticVolume->SetVolumeValues(1, 1, 1, 0, 0, 7); CPPUNIT_ASSERT_EQUAL_MESSAGE("Should be correct value", 0.0, m_PhotoacousticVolume->GetAnisotropyVolume()->GetData(0, 0, 0)); CPPUNIT_ASSERT_EQUAL_MESSAGE("Should be correct value", 1.0, m_PhotoacousticVolume->GetAnisotropyVolume()->GetData(0, 0, 1)); CPPUNIT_ASSERT_EQUAL_MESSAGE("Should be correct value", 2.0, m_PhotoacousticVolume->GetAnisotropyVolume()->GetData(0, 1, 0)); CPPUNIT_ASSERT_EQUAL_MESSAGE("Should be correct value", 3.0, m_PhotoacousticVolume->GetAnisotropyVolume()->GetData(0, 1, 1)); CPPUNIT_ASSERT_EQUAL_MESSAGE("Should be correct value", 4.0, m_PhotoacousticVolume->GetAnisotropyVolume()->GetData(1, 0, 0)); CPPUNIT_ASSERT_EQUAL_MESSAGE("Should be correct value", 5.0, m_PhotoacousticVolume->GetAnisotropyVolume()->GetData(1, 0, 1)); CPPUNIT_ASSERT_EQUAL_MESSAGE("Should be correct value", 6.0, m_PhotoacousticVolume->GetAnisotropyVolume()->GetData(1, 1, 0)); CPPUNIT_ASSERT_EQUAL_MESSAGE("Should be correct value", 7.0, m_PhotoacousticVolume->GetAnisotropyVolume()->GetData(1, 1, 1)); } mitk::pa::Volume::Pointer createTestVolume(double value) { auto* data = new double[27]; for (int i = 0; i < 27; ++i) data[i] = value; - return mitk::pa::Volume::New(data, 3, 3, 3); + return mitk::pa::Volume::New(data, 3, 3, 3, 1); } void assertEqual(mitk::pa::Volume::Pointer first, mitk::pa::Volume::Pointer second) { CPPUNIT_ASSERT(first->GetXDim() == second->GetXDim()); CPPUNIT_ASSERT(first->GetYDim() == second->GetYDim()); CPPUNIT_ASSERT(first->GetZDim() == second->GetZDim()); for (unsigned int x = 0; x < first->GetXDim(); ++x) for (unsigned int y = 0; y < first->GetYDim(); ++y) for (unsigned int z = 0; z < first->GetZDim(); ++z) CPPUNIT_ASSERT(std::abs(first->GetData(x, y, z) - second->GetData(x, y, z)) < mitk::eps); } void testSecondConstructor() { mitk::pa::Volume::Pointer absorption = createTestVolume(1); mitk::pa::Volume::Pointer scattering = createTestVolume(2); mitk::pa::Volume::Pointer anisotropy = createTestVolume(3); mitk::pa::Volume::Pointer segmentation = createTestVolume(4); mitk::PropertyList::Pointer properties = mitk::PropertyList::New(); m_PhotoacousticVolume = mitk::pa::InSilicoTissueVolume::New(absorption, scattering, anisotropy, segmentation, m_TissueGeneratorParameters, properties); assertEqual(m_PhotoacousticVolume->GetAbsorptionVolume(), absorption); assertEqual(m_PhotoacousticVolume->GetScatteringVolume(), scattering); assertEqual(m_PhotoacousticVolume->GetAnisotropyVolume(), anisotropy); assertEqual(m_PhotoacousticVolume->GetSegmentationVolume(), segmentation); } void testCompleteAirVoxelInclusion() { mitk::pa::Volume::Pointer absorption = createTestVolume(1); mitk::pa::Volume::Pointer scattering = createTestVolume(2); mitk::pa::Volume::Pointer anisotropy = createTestVolume(3); mitk::pa::Volume::Pointer segmentation = createTestVolume(4); mitk::PropertyList::Pointer properties = mitk::PropertyList::New(); m_TissueGeneratorParameters->SetXDim(3); m_TissueGeneratorParameters->SetYDim(3); m_TissueGeneratorParameters->SetZDim(3); m_TissueGeneratorParameters->SetAirThicknessInMillimeters(10); m_TissueGeneratorParameters->SetSkinThicknessInMillimeters(0); m_TissueGeneratorParameters->SetAirAbsorption(2); m_TissueGeneratorParameters->SetAirScattering(4); m_TissueGeneratorParameters->SetAirAnisotropy(6); m_PhotoacousticVolume = mitk::pa::InSilicoTissueVolume::New(absorption, scattering, anisotropy, segmentation, m_TissueGeneratorParameters, properties); m_PhotoacousticVolume->FinalizeVolume(); CPPUNIT_ASSERT(std::abs(m_PhotoacousticVolume->GetAbsorptionVolume()->GetData(0, 0, 0) - 2) < mitk::eps); CPPUNIT_ASSERT(std::abs(m_PhotoacousticVolume->GetAbsorptionVolume()->GetData(1, 1, 1) - 1) < mitk::eps); CPPUNIT_ASSERT(std::abs(m_PhotoacousticVolume->GetAbsorptionVolume()->GetData(1, 1, 2) - 1) < mitk::eps); CPPUNIT_ASSERT(std::abs(m_PhotoacousticVolume->GetScatteringVolume()->GetData(0, 0, 0) - 4) < mitk::eps); CPPUNIT_ASSERT(std::abs(m_PhotoacousticVolume->GetScatteringVolume()->GetData(1, 1, 1) - 2) < mitk::eps); CPPUNIT_ASSERT(std::abs(m_PhotoacousticVolume->GetScatteringVolume()->GetData(1, 1, 2) - 2) < mitk::eps); CPPUNIT_ASSERT(std::abs(m_PhotoacousticVolume->GetAnisotropyVolume()->GetData(0, 0, 0) - 6) < mitk::eps); CPPUNIT_ASSERT(std::abs(m_PhotoacousticVolume->GetAnisotropyVolume()->GetData(1, 1, 1) - 3) < mitk::eps); CPPUNIT_ASSERT(std::abs(m_PhotoacousticVolume->GetAnisotropyVolume()->GetData(1, 1, 2) - 3) < mitk::eps); } void testRandomizeCoefficients() { mitk::pa::Volume::Pointer absorption = createTestVolume(1); mitk::pa::Volume::Pointer scattering = createTestVolume(1); mitk::pa::Volume::Pointer anisotropy = createTestVolume(1); mitk::pa::Volume::Pointer segmentation = createTestVolume(4); mitk::PropertyList::Pointer properties = mitk::PropertyList::New(); m_TissueGeneratorParameters->SetXDim(3); m_TissueGeneratorParameters->SetYDim(3); m_TissueGeneratorParameters->SetZDim(3); m_TissueGeneratorParameters->SetRandomizePhysicalProperties(true); m_TissueGeneratorParameters->SetRandomizePhysicalPropertiesPercentage(1); m_TissueGeneratorParameters->SetRngSeed(17); m_TissueGeneratorParameters->SetUseRngSeed(true); m_PhotoacousticVolume = mitk::pa::InSilicoTissueVolume::New(absorption, scattering, anisotropy, segmentation, m_TissueGeneratorParameters, properties); m_PhotoacousticVolume->FinalizeVolume(); CPPUNIT_ASSERT(std::abs(m_PhotoacousticVolume->GetAbsorptionVolume()->GetData(0, 0, 0) - 1) < 0.1); CPPUNIT_ASSERT(std::abs(m_PhotoacousticVolume->GetAbsorptionVolume()->GetData(1, 1, 1) - 1) < 0.1); CPPUNIT_ASSERT(std::abs(m_PhotoacousticVolume->GetAbsorptionVolume()->GetData(1, 1, 2) - 1) < 0.1); CPPUNIT_ASSERT(std::abs(m_PhotoacousticVolume->GetScatteringVolume()->GetData(0, 0, 0) - 1) < 0.1); CPPUNIT_ASSERT(std::abs(m_PhotoacousticVolume->GetScatteringVolume()->GetData(1, 1, 1) - 1) < 0.1); CPPUNIT_ASSERT(std::abs(m_PhotoacousticVolume->GetScatteringVolume()->GetData(1, 1, 2) - 1) < 0.1); CPPUNIT_ASSERT(std::abs(m_PhotoacousticVolume->GetAnisotropyVolume()->GetData(0, 0, 0) - 1) < 0.1); CPPUNIT_ASSERT(std::abs(m_PhotoacousticVolume->GetAnisotropyVolume()->GetData(1, 1, 1) - 1) < 0.1); CPPUNIT_ASSERT(std::abs(m_PhotoacousticVolume->GetAnisotropyVolume()->GetData(1, 1, 2) - 1) < 0.1); } void testCompleteAirAndSkinVoxelInclusion() { mitk::pa::Volume::Pointer absorption = createTestVolume(1); mitk::pa::Volume::Pointer scattering = createTestVolume(2); mitk::pa::Volume::Pointer anisotropy = createTestVolume(3); mitk::pa::Volume::Pointer segmentation = createTestVolume(4); mitk::PropertyList::Pointer properties = mitk::PropertyList::New(); m_TissueGeneratorParameters->SetXDim(3); m_TissueGeneratorParameters->SetYDim(3); m_TissueGeneratorParameters->SetZDim(3); m_TissueGeneratorParameters->SetAirThicknessInMillimeters(10); m_TissueGeneratorParameters->SetSkinThicknessInMillimeters(10); m_TissueGeneratorParameters->SetAirAbsorption(2); m_TissueGeneratorParameters->SetAirScattering(4); m_TissueGeneratorParameters->SetAirAnisotropy(6); m_TissueGeneratorParameters->SetSkinAbsorption(4); m_TissueGeneratorParameters->SetSkinScattering(8); m_TissueGeneratorParameters->SetSkinAnisotropy(12); m_PhotoacousticVolume = mitk::pa::InSilicoTissueVolume::New(absorption, scattering, anisotropy, segmentation, m_TissueGeneratorParameters, properties); m_PhotoacousticVolume->FinalizeVolume(); CPPUNIT_ASSERT(std::abs(m_PhotoacousticVolume->GetAbsorptionVolume()->GetData(0, 0, 0) - 2) < mitk::eps); CPPUNIT_ASSERT(std::abs(m_PhotoacousticVolume->GetAbsorptionVolume()->GetData(1, 1, 1) - 4) < mitk::eps); CPPUNIT_ASSERT(std::abs(m_PhotoacousticVolume->GetAbsorptionVolume()->GetData(1, 1, 2) - 1) < mitk::eps); CPPUNIT_ASSERT(std::abs(m_PhotoacousticVolume->GetScatteringVolume()->GetData(0, 0, 0) - 4) < mitk::eps); CPPUNIT_ASSERT(std::abs(m_PhotoacousticVolume->GetScatteringVolume()->GetData(1, 1, 1) - 8) < mitk::eps); CPPUNIT_ASSERT(std::abs(m_PhotoacousticVolume->GetScatteringVolume()->GetData(1, 1, 2) - 2) < mitk::eps); CPPUNIT_ASSERT(std::abs(m_PhotoacousticVolume->GetAnisotropyVolume()->GetData(0, 0, 0) - 6) < mitk::eps); CPPUNIT_ASSERT(std::abs(m_PhotoacousticVolume->GetAnisotropyVolume()->GetData(1, 1, 1) - 12) < mitk::eps); CPPUNIT_ASSERT(std::abs(m_PhotoacousticVolume->GetAnisotropyVolume()->GetData(1, 1, 2) - 3) < mitk::eps); } void testHalfAirVoxelInclusion() { mitk::pa::Volume::Pointer absorption = createTestVolume(1); mitk::pa::Volume::Pointer scattering = createTestVolume(2); mitk::pa::Volume::Pointer anisotropy = createTestVolume(3); mitk::pa::Volume::Pointer segmentation = createTestVolume(4); mitk::PropertyList::Pointer properties = mitk::PropertyList::New(); m_TissueGeneratorParameters->SetXDim(3); m_TissueGeneratorParameters->SetYDim(3); m_TissueGeneratorParameters->SetZDim(3); m_TissueGeneratorParameters->SetAirThicknessInMillimeters(15); m_TissueGeneratorParameters->SetSkinThicknessInMillimeters(0); m_TissueGeneratorParameters->SetAirAbsorption(2); m_TissueGeneratorParameters->SetAirScattering(4); m_TissueGeneratorParameters->SetAirAnisotropy(6); m_PhotoacousticVolume = mitk::pa::InSilicoTissueVolume::New(absorption, scattering, anisotropy, segmentation, m_TissueGeneratorParameters, properties); m_PhotoacousticVolume->FinalizeVolume(); CPPUNIT_ASSERT(std::abs(m_PhotoacousticVolume->GetAbsorptionVolume()->GetData(0, 0, 0) - 2) < mitk::eps); CPPUNIT_ASSERT(std::abs(m_PhotoacousticVolume->GetAbsorptionVolume()->GetData(1, 1, 1) - 1.5) < mitk::eps); CPPUNIT_ASSERT(std::abs(m_PhotoacousticVolume->GetAbsorptionVolume()->GetData(1, 1, 2) - 1) < mitk::eps); CPPUNIT_ASSERT(std::abs(m_PhotoacousticVolume->GetScatteringVolume()->GetData(0, 0, 0) - 4) < mitk::eps); CPPUNIT_ASSERT(std::abs(m_PhotoacousticVolume->GetScatteringVolume()->GetData(1, 1, 1) - 3) < mitk::eps); CPPUNIT_ASSERT(std::abs(m_PhotoacousticVolume->GetScatteringVolume()->GetData(1, 1, 2) - 2) < mitk::eps); CPPUNIT_ASSERT(std::abs(m_PhotoacousticVolume->GetAnisotropyVolume()->GetData(0, 0, 0) - 6) < mitk::eps); CPPUNIT_ASSERT(std::abs(m_PhotoacousticVolume->GetAnisotropyVolume()->GetData(1, 1, 1) - 4.5) < mitk::eps); CPPUNIT_ASSERT(std::abs(m_PhotoacousticVolume->GetAnisotropyVolume()->GetData(1, 1, 2) - 3) < mitk::eps); } void tearDown() override { m_PhotoacousticVolume = nullptr; } }; MITK_TEST_SUITE_REGISTRATION(mitkPhotoacousticVolume) diff --git a/Modules/PhotoacousticsLib/test/mitkSimulationBatchGeneratorTest.cpp b/Modules/PhotoacousticsLib/test/mitkSimulationBatchGeneratorTest.cpp index 2732c64bd5..10fdbbde73 100644 --- a/Modules/PhotoacousticsLib/test/mitkSimulationBatchGeneratorTest.cpp +++ b/Modules/PhotoacousticsLib/test/mitkSimulationBatchGeneratorTest.cpp @@ -1,90 +1,90 @@ ///*=================================================================== //The Medical Imaging Interaction Toolkit (MITK) //Copyright (c) German Cancer Research Center, //Division of Medical and Biological Informatics. //All rights reserved. //This software is distributed WITHOUT ANY WARRANTY; without //even the implied warranty of MERCHANTABILITY or FITNESS FOR //A PARTICULAR PURPOSE. //See LICENSE.txt or http://www.mitk.org for details. //===================================================================*/ #include #include #include #include #include class mitkSimulationBatchGeneratorTestSuite : public mitk::TestFixture { CPPUNIT_TEST_SUITE(mitkSimulationBatchGeneratorTestSuite); MITK_TEST(testGenerateBatchFileString); MITK_TEST(testGenerateBatchFileAndSaveFile); CPPUNIT_TEST_SUITE_END(); private: const std::string TEST_FOLDER_PATH = "testFiles/"; mitk::pa::SimulationBatchGeneratorParameters::Pointer m_Parameters; mitk::pa::Volume::Pointer m_Test3DVolume; public: void setUp() override { m_Parameters = mitk::pa::SimulationBatchGeneratorParameters::New(); m_Parameters->SetBinaryPath("binary"); m_Parameters->SetNrrdFilePath(TEST_FOLDER_PATH); m_Parameters->SetNumberOfPhotons(100); m_Parameters->SetTissueName("tissueName"); m_Parameters->SetVolumeIndex(0); m_Parameters->SetYOffsetLowerThresholdInCentimeters(-1); m_Parameters->SetYOffsetUpperThresholdInCentimeters(1); m_Parameters->SetYOffsetStepInCentimeters(0.5); m_Test3DVolume = createTest3DVolume(5); itk::FileTools::CreateDirectory(TEST_FOLDER_PATH); CPPUNIT_ASSERT(itksys::SystemTools::FileIsDirectory(TEST_FOLDER_PATH)); } mitk::pa::Volume::Pointer createTest3DVolume(double value) { unsigned int xDim = 10; unsigned int yDim = 10; unsigned int zDim = 10; unsigned int length = xDim * yDim * zDim; auto* data = new double[length]; for (unsigned int i = 0; i < length; i++) data[i] = value; - return mitk::pa::Volume::New(data, xDim, yDim, zDim); + return mitk::pa::Volume::New(data, xDim, yDim, zDim, 1); } void testGenerateBatchFileString() { std::string batchGenerationString = mitk::pa::SimulationBatchGenerator::CreateBatchSimulationString(m_Parameters); CPPUNIT_ASSERT(!batchGenerationString.empty()); } void testGenerateBatchFileAndSaveFile() { mitk::pa::SimulationBatchGenerator::WriteBatchFileAndSaveTissueVolume(m_Parameters, m_Test3DVolume->AsMitkImage()); CPPUNIT_ASSERT(itksys::SystemTools::FileExists(TEST_FOLDER_PATH + m_Parameters->GetTissueName() + "000.nrrd")); CPPUNIT_ASSERT(itksys::SystemTools::FileExists(TEST_FOLDER_PATH + "simulate_all.sh") || itksys::SystemTools::FileExists(TEST_FOLDER_PATH + "simulate_all.bat")); CPPUNIT_ASSERT(itksys::SystemTools::FileExists(TEST_FOLDER_PATH + m_Parameters->GetTissueName() + "000") && itksys::SystemTools::FileIsDirectory(TEST_FOLDER_PATH + m_Parameters->GetTissueName() + "000")); } void tearDown() override { m_Parameters = nullptr; CPPUNIT_ASSERT_MESSAGE("Resource leak of test files onto hard drive..", itksys::SystemTools::RemoveADirectory(TEST_FOLDER_PATH) == true); } }; MITK_TEST_SUITE_REGISTRATION(mitkSimulationBatchGenerator) diff --git a/Modules/PhotoacousticsLib/test/mitkSlicedVolumeGeneratorTest.cpp b/Modules/PhotoacousticsLib/test/mitkSlicedVolumeGeneratorTest.cpp index c4bfc1fa09..aab23e0aa2 100644 --- a/Modules/PhotoacousticsLib/test/mitkSlicedVolumeGeneratorTest.cpp +++ b/Modules/PhotoacousticsLib/test/mitkSlicedVolumeGeneratorTest.cpp @@ -1,187 +1,188 @@ ///*=================================================================== //The Medical Imaging Interaction Toolkit (MITK) //Copyright (c) German Cancer Research Center, //Division of Medical and Biological Informatics. //All rights reserved. //This software is distributed WITHOUT ANY WARRANTY; without //even the implied warranty of MERCHANTABILITY or FITNESS FOR //A PARTICULAR PURPOSE. //See LICENSE.txt or http://www.mitk.org for details. //===================================================================*/ #include #include #include class mitkSlicedVolumeGeneratorTestSuite : public mitk::TestFixture { CPPUNIT_TEST_SUITE(mitkSlicedVolumeGeneratorTestSuite); MITK_TEST(testConstructorDestructor); MITK_TEST(testGetSlicedFluenceVolume); MITK_TEST(testGetSlicedFluenceVolumeInverse); MITK_TEST(testGetSlicedFluenceVolumeWithPrecorrection); MITK_TEST(testGetSlicedFluenceVolumeWithPrecorrectionInverse); MITK_TEST(testGetSlicedSignalVolume); MITK_TEST(testGetSlicedAbsorptionVolume); CPPUNIT_TEST_SUITE_END(); private: mitk::pa::ComposedVolume::Pointer m_ComposedVolume; mitk::pa::TissueGeneratorParameters::Pointer m_DefaultParameters; mitk::pa::InSilicoTissueVolume::Pointer m_InSilicoTissueVolume; mitk::pa::SlicedVolumeGenerator::Pointer m_SlicedVolumeGenerator; mitk::pa::Volume::Pointer m_PrecorrectionVolume; public: void setUp() override { m_SlicedVolumeGenerator = nullptr; m_DefaultParameters = mitk::pa::TissueGeneratorParameters::New(); m_DefaultParameters->SetXDim(3); m_DefaultParameters->SetYDim(3); m_DefaultParameters->SetZDim(3); - m_InSilicoTissueVolume = mitk::pa::InSilicoTissueVolume::New(m_DefaultParameters); + auto rng = std::mt19937(); + m_InSilicoTissueVolume = mitk::pa::InSilicoTissueVolume::New(m_DefaultParameters, &rng); m_ComposedVolume = mitk::pa::ComposedVolume::New(m_InSilicoTissueVolume); m_ComposedVolume->AddSlice(CreateValidationPair(-1, 1)); m_ComposedVolume->AddSlice(CreateValidationPair(0, 3)); m_ComposedVolume->AddSlice(CreateValidationPair(1, 6)); m_PrecorrectionVolume = CreatePrecorrectionVolume(); } mitk::pa::Volume::Pointer CreatePrecorrectionVolume() { auto* data = new double[27]; for (int i = 0; i < 27; ++i) data[i] = 0.5; - return mitk::pa::Volume::New(data, 3, 3, 3); + return mitk::pa::Volume::New(data, 3, 3, 3, 1); } void FillYSliceWith(mitk::pa::Volume::Pointer fluenceVolume, double ySlice, double value) { for (unsigned int x = 0; x < fluenceVolume->GetXDim(); ++x) for (unsigned int z = 0; z < fluenceVolume->GetZDim(); ++z) { fluenceVolume->SetData(value, x, ySlice, z); } } mitk::pa::FluenceYOffsetPair::Pointer CreateValidationPair(double yOffset, int start) { auto* data = new double[27]; - mitk::pa::Volume::Pointer fluenceVolume = mitk::pa::Volume::New(data, 3, 3, 3); + mitk::pa::Volume::Pointer fluenceVolume = mitk::pa::Volume::New(data, 3, 3, 3, 1); FillYSliceWith(fluenceVolume, 0, start + 0); FillYSliceWith(fluenceVolume, 1, start + 1); FillYSliceWith(fluenceVolume, 2, start + 2); return mitk::pa::FluenceYOffsetPair::New(fluenceVolume, yOffset); } void AssertYSliceValue(mitk::pa::Volume::Pointer fluenceVolume, double ySlice, double value) { for (unsigned int x = 0; x < fluenceVolume->GetXDim(); ++x) for (unsigned int z = 0; z < fluenceVolume->GetZDim(); ++z) { std::string msg = "Expected: " + std::to_string(value) + " actual: " + std::to_string(fluenceVolume->GetData(x, ySlice, z)); CPPUNIT_ASSERT_MESSAGE(msg, std::abs(fluenceVolume->GetData(x, ySlice, z) - value) < mitk::eps); } } void testConstructorDestructor() { m_SlicedVolumeGenerator = mitk::pa::SlicedVolumeGenerator::New(0, false, nullptr, false); CPPUNIT_ASSERT(m_SlicedVolumeGenerator.IsNotNull()); } void testGetSlicedFluenceVolume() { m_SlicedVolumeGenerator = mitk::pa::SlicedVolumeGenerator::New(1, false, nullptr, false); mitk::pa::Volume::Pointer slicedFluence = m_SlicedVolumeGenerator->GetSlicedFluenceImageFromComposedVolume(m_ComposedVolume); CPPUNIT_ASSERT(slicedFluence->GetXDim() == 3); CPPUNIT_ASSERT(slicedFluence->GetYDim() == 3); CPPUNIT_ASSERT(slicedFluence->GetZDim() == 3); AssertYSliceValue(slicedFluence, 0, 1); AssertYSliceValue(slicedFluence, 1, 4); AssertYSliceValue(slicedFluence, 2, 8); } void testGetSlicedFluenceVolumeInverse() { m_SlicedVolumeGenerator = mitk::pa::SlicedVolumeGenerator::New(1, false, nullptr, true); mitk::pa::Volume::Pointer slicedFluence = m_SlicedVolumeGenerator->GetSlicedFluenceImageFromComposedVolume(m_ComposedVolume); CPPUNIT_ASSERT(slicedFluence->GetXDim() == 3); CPPUNIT_ASSERT(slicedFluence->GetYDim() == 3); CPPUNIT_ASSERT(slicedFluence->GetZDim() == 3); AssertYSliceValue(slicedFluence, 0, 1); AssertYSliceValue(slicedFluence, 1, 1.0 / 4.0); AssertYSliceValue(slicedFluence, 2, 1.0 / 8.0); } void testGetSlicedFluenceVolumeWithPrecorrection() { m_SlicedVolumeGenerator = mitk::pa::SlicedVolumeGenerator::New(1, true, m_PrecorrectionVolume, false); mitk::pa::Volume::Pointer slicedFluence = m_SlicedVolumeGenerator->GetSlicedFluenceImageFromComposedVolume(m_ComposedVolume); CPPUNIT_ASSERT(slicedFluence->GetXDim() == 3); CPPUNIT_ASSERT(slicedFluence->GetYDim() == 3); CPPUNIT_ASSERT(slicedFluence->GetZDim() == 3); AssertYSliceValue(slicedFluence, 0, 2); AssertYSliceValue(slicedFluence, 1, 8); AssertYSliceValue(slicedFluence, 2, 16); } void testGetSlicedFluenceVolumeWithPrecorrectionInverse() { m_SlicedVolumeGenerator = mitk::pa::SlicedVolumeGenerator::New(1, true, m_PrecorrectionVolume, true); mitk::pa::Volume::Pointer slicedFluence = m_SlicedVolumeGenerator->GetSlicedFluenceImageFromComposedVolume(m_ComposedVolume); CPPUNIT_ASSERT(slicedFluence->GetXDim() == 3); CPPUNIT_ASSERT(slicedFluence->GetYDim() == 3); CPPUNIT_ASSERT(slicedFluence->GetZDim() == 3); AssertYSliceValue(slicedFluence, 0, 1.0 / 2); AssertYSliceValue(slicedFluence, 1, 1.0 / 8); AssertYSliceValue(slicedFluence, 2, 1.0 / 16); } void testGetSlicedSignalVolume() { m_SlicedVolumeGenerator = mitk::pa::SlicedVolumeGenerator::New(1, false, nullptr, false); mitk::pa::Volume::Pointer slicedFluence = m_SlicedVolumeGenerator->GetSlicedSignalImageFromComposedVolume(m_ComposedVolume); CPPUNIT_ASSERT(slicedFluence->GetXDim() == 3); CPPUNIT_ASSERT(slicedFluence->GetYDim() == 3); CPPUNIT_ASSERT(slicedFluence->GetZDim() == 3); - AssertYSliceValue(slicedFluence, 0, 1 * m_DefaultParameters->GetBackgroundAbsorption()); - AssertYSliceValue(slicedFluence, 1, 4 * m_DefaultParameters->GetBackgroundAbsorption()); - AssertYSliceValue(slicedFluence, 2, 8 * m_DefaultParameters->GetBackgroundAbsorption()); + AssertYSliceValue(slicedFluence, 0, 1 * m_DefaultParameters->GetMinBackgroundAbsorption()); + AssertYSliceValue(slicedFluence, 1, 4 * m_DefaultParameters->GetMinBackgroundAbsorption()); + AssertYSliceValue(slicedFluence, 2, 8 * m_DefaultParameters->GetMinBackgroundAbsorption()); } void testGetSlicedAbsorptionVolume() { m_SlicedVolumeGenerator = mitk::pa::SlicedVolumeGenerator::New(1, false, nullptr, false); mitk::pa::Volume::Pointer slicedFluence = m_SlicedVolumeGenerator->GetSlicedGroundTruthImageFromComposedVolume(m_ComposedVolume); CPPUNIT_ASSERT(slicedFluence->GetXDim() == 3); CPPUNIT_ASSERT(slicedFluence->GetYDim() == 3); CPPUNIT_ASSERT(slicedFluence->GetZDim() == 3); - AssertYSliceValue(slicedFluence, 0, m_DefaultParameters->GetBackgroundAbsorption()); - AssertYSliceValue(slicedFluence, 1, m_DefaultParameters->GetBackgroundAbsorption()); - AssertYSliceValue(slicedFluence, 2, m_DefaultParameters->GetBackgroundAbsorption()); + AssertYSliceValue(slicedFluence, 0, m_DefaultParameters->GetMinBackgroundAbsorption()); + AssertYSliceValue(slicedFluence, 1, m_DefaultParameters->GetMinBackgroundAbsorption()); + AssertYSliceValue(slicedFluence, 2, m_DefaultParameters->GetMinBackgroundAbsorption()); } void tearDown() override { m_SlicedVolumeGenerator = nullptr; } }; MITK_TEST_SUITE_REGISTRATION(mitkSlicedVolumeGenerator) diff --git a/Plugins/org.mitk.gui.qt.photoacoustics.simulation/src/internal/PASimulator.cpp b/Plugins/org.mitk.gui.qt.photoacoustics.simulation/src/internal/PASimulator.cpp index e1b432613a..c35200cc00 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,255 @@ /*=================================================================== 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->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