diff --git a/Modules/PhotoacousticsHardware/mitkOphirPyro.cpp b/Modules/PhotoacousticsHardware/mitkOphirPyro.cpp index f4e65e32e4..648fce8b47 100644 --- a/Modules/PhotoacousticsHardware/mitkOphirPyro.cpp +++ b/Modules/PhotoacousticsHardware/mitkOphirPyro.cpp @@ -1,339 +1,339 @@ /*=================================================================== 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 "mitkOphirPyro.h" #include #include #include #include mitk::OphirPyro::OphirPyro() : m_CurrentWavelength(0), m_DeviceHandle(0), m_Connected(false), m_Streaming(false), m_SerialNumber(nullptr), m_GetDataThread(), m_ImagePyroDelay(0), m_EnergyMultiplicator(60000) { m_PulseEnergy.clear(); m_PulseTime.clear(); m_PulseStatus.clear(); m_TimeStamps.clear(); } mitk::OphirPyro::~OphirPyro() { if (m_Connected) { this->CloseConnection(); if (m_GetDataThread.joinable()) { m_GetDataThread.join(); MITK_INFO << "[OphirPyro Debug] joined data thread"; } } MITK_INFO << "[OphirPyro Debug] destroying that Pyro"; /* cleanup thread */ } bool mitk::OphirPyro::StartDataAcquisition() { if (ophirAPI.StartStream(m_DeviceHandle)) { m_Streaming = true; m_GetDataThread = std::thread(&mitk::OphirPyro::GetDataFromSensorThread, this); } return m_Streaming; } // this is just a little function to set the filenames below right inline void replaceAll(std::string& str, const std::string& from, const std::string& to) { if (from.empty()) return; size_t start_pos = 0; while ((start_pos = str.find(from, start_pos)) != std::string::npos) { str.replace(start_pos, from.length(), to); start_pos += to.length(); // In case 'to' contains 'from', like replacing 'x' with 'yx' } } void mitk::OphirPyro::SaveCsvData(std::string filename) { std::string pathTS = filename; std::ofstream timestampFile; timestampFile.open(pathTS); timestampFile << ",timestamp,PulseEnergy,PulseTime"; int currentSize = m_TimeStamps.size(); for (int index = 0; index < currentSize; ++index) { timestampFile << "\n" << index << "," << m_TimeStamps.at(index) << ","<< m_PulseEnergySaved.at(index) << "," << (long)m_PulseTimeSaved.at(index); } timestampFile.close(); } void mitk::OphirPyro::SaveData(std::string filename) { SaveCsvData(filename); } bool mitk::OphirPyro::StopDataAcquisition() { if (ophirAPI.StopStream(m_DeviceHandle)) m_Streaming = false; //SaveCsvData(); MITK_INFO << "[OphirPyro Debug] m_Streaming = "<< m_Streaming; std::this_thread::sleep_for(std::chrono::milliseconds(50)); if (m_GetDataThread.joinable()) { m_GetDataThread.join(); } return !m_Streaming; } unsigned int mitk::OphirPyro::GetDataFromSensor() { if (m_Streaming) { std::vector newEnergy; std::vector newTimestamp; std::vector newStatus; unsigned int noPackages = 0; try { noPackages = ophirAPI.GetData(m_DeviceHandle, &newEnergy, &newTimestamp, &newStatus); if (noPackages > 0) { m_PulseEnergy.insert(m_PulseEnergy.end(), newEnergy.begin(), newEnergy.end()); for (unsigned int i=0; i( - std::chrono::steady_clock::now().time_since_epoch() ).count()); + std::chrono::system_clock::now().time_since_epoch()).count()); m_PulseTime.insert(m_PulseTime.end(), newTimestamp.begin(), newTimestamp.end()); m_PulseStatus.insert(m_PulseStatus.end(), newStatus.begin(), newStatus.end()); m_PulseEnergySaved.insert(m_PulseEnergySaved.end(), newEnergy.begin(), newEnergy.end()); m_PulseTimeSaved.insert(m_PulseTimeSaved.end(), newTimestamp.begin(), newTimestamp.end()); m_PulseStatusSaved.insert(m_PulseStatusSaved.end(), newStatus.begin(), newStatus.end()); } } catch (std::exception& ex) { MITK_INFO << "this is weird: " << ex.what(); } return noPackages; } return 0; } void mitk::OphirPyro::GetDataFromSensorThread() { while(m_Connected && m_Streaming) { this->GetDataFromSensor(); std::this_thread::sleep_for(std::chrono::milliseconds(50)); } return; } double mitk::OphirPyro::LookupCurrentPulseEnergy() { if (m_Connected && !m_PulseEnergy.empty()) { MITK_INFO << m_PulseEnergy.size(); return m_PulseEnergy.back(); } return 0; } double mitk::OphirPyro::GetClosestEnergyInmJ(long long ImageTimeStamp, double interval) { if (m_PulseTime.size() == 0) return 0; long long searchTime = (ImageTimeStamp/1000000) - m_ImagePyroDelay; // conversion from ns to ms //MITK_INFO << "searchTime = " << searchTime; int foundIndex = -1; long long shortestDifference = 250*interval; // search the list for a fitting energy value time for (int index = 0; index < m_PulseTime.size();++index) { long long newDifference = abs(((int)m_PulseTime[index]) - searchTime); //MITK_INFO << "newDifference[" << index << "] = " << newDifference; if (newDifference < shortestDifference) { shortestDifference = newDifference; foundIndex = index; //MITK_INFO << "foundIndex = " << foundIndex; } } if (abs(shortestDifference) < interval) { // delete all elements before the one found m_PulseEnergy.erase(m_PulseEnergy.begin(), m_PulseEnergy.begin() + foundIndex); m_PulseTime.erase(m_PulseTime.begin(), m_PulseTime.begin() + foundIndex); m_PulseStatus.erase(m_PulseStatus.begin(), m_PulseStatus.begin() + foundIndex); // multipy with m_EnergyMultiplicator, because the Pyro gives just a fraction of the actual Laser Energy return (GetNextPulseEnergy()*m_EnergyMultiplicator); } //MITK_INFO << "No matching energy value for image found in interval of " << interval << "ms. sd: " << shortestDifference; return -1; } double mitk::OphirPyro::GetNextEnergyInmJ(long long ImageTimeStamp, double interval) { if (m_Connected && !(m_PulseTime.size() > 0)) return 0; long long searchTime = (ImageTimeStamp / 1000000) - m_ImagePyroDelay; // conversion from ns to ms if (abs(searchTime - m_PulseTime.front()) < interval) { return (GetNextPulseEnergy()*m_EnergyMultiplicator); // multipy with m_EnergyMultiplicator, because the Pyro gives just a fraction of the actual Laser Energy } MITK_INFO << "Image aquisition and energy measurement ran out of sync"; return -1; } void mitk::OphirPyro::SetSyncDelay(long long FirstImageTimeStamp) { unsigned int i = 0; while (!m_PulseTime.size()) { std::this_thread::sleep_for(std::chrono::milliseconds(1)); ++i; if (i > 100) { MITK_ERROR << "Pyro was not active for synchronization. Please turn on the laser before setting tgc higher (WIP)!"; return; } } m_ImagePyroDelay = (FirstImageTimeStamp / 1000000) - m_PulseTime.at(0); MITK_INFO << "m_ImagePyroDelay = " << m_ImagePyroDelay; return; } bool mitk::OphirPyro::IsSyncDelaySet() { return (m_ImagePyroDelay != 0); } double mitk::OphirPyro::GetNextPulseEnergy() { if (m_Connected && m_PulseEnergy.size()>=1) { double out = m_PulseEnergy.front(); m_PulseEnergy.erase(m_PulseEnergy.begin()); m_PulseTime.erase(m_PulseTime.begin()); m_PulseStatus.erase(m_PulseStatus.begin()); return out; } return 0; } double mitk::OphirPyro::LookupCurrentPulseEnergy(double* timestamp, int* status) { if (m_Connected) { *timestamp = m_PulseTime.back(); *status = m_PulseStatus.back(); return m_PulseEnergy.back(); } return 0; } double mitk::OphirPyro::GetNextPulseEnergy(double* timestamp, int* status) { if (m_Connected) { double out = m_PulseEnergy.front(); *timestamp = m_PulseTime.front(); *status = m_PulseStatus.front(); m_PulseEnergy.erase(m_PulseEnergy.begin()); m_PulseTime.erase(m_PulseTime.begin()); m_PulseStatus.erase(m_PulseStatus.begin()); return out; } return 0; } bool mitk::OphirPyro::OpenConnection() { if (!m_Connected) { char* m_SerialNumber; try { MITK_INFO << "Scanning for Ophir connection"; m_SerialNumber = ophirAPI.ScanUSB(); } catch (...) { MITK_INFO << "Scanning failed, trying again in 2 seconds..."; std::this_thread::sleep_for(std::chrono::seconds(2)); MITK_INFO << "Scanning for Ophir connection"; m_SerialNumber = ophirAPI.ScanUSB(); } if (m_SerialNumber != 0) { try { MITK_INFO << "Opening Ophir connection"; m_DeviceHandle = ophirAPI.OpenDevice(m_SerialNumber); } catch (...) { MITK_INFO << "Ophir connection failed, trying again in 2 seconds..."; std::this_thread::sleep_for(std::chrono::seconds(2)); MITK_INFO << "Opening Ophir connection"; m_DeviceHandle = ophirAPI.OpenDevice(m_SerialNumber); } if (m_DeviceHandle != 0) { m_Connected = true; return true; } } } return false; } bool mitk::OphirPyro::CloseConnection() { if (m_Connected) { bool closed = ophirAPI.CloseDevice(m_DeviceHandle); if (closed) m_DeviceHandle = 0; m_Connected = !closed; return closed; } return false; } \ No newline at end of file diff --git a/Modules/US/USHardwareDiPhAS/mitkUSDiPhASImageSource.cpp b/Modules/US/USHardwareDiPhAS/mitkUSDiPhASImageSource.cpp index 54f7373c27..a8285f8689 100644 --- a/Modules/US/USHardwareDiPhAS/mitkUSDiPhASImageSource.cpp +++ b/Modules/US/USHardwareDiPhAS/mitkUSDiPhASImageSource.cpp @@ -1,1129 +1,1129 @@ /*=================================================================== 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. ===================================================================*/ // std dependencies #include #include #include // mitk dependencies #include "ITKUltrasound/itkBModeImageFilter.h" #include "mitkITKImageImport.h" #include "mitkImageCast.h" #include "mitkUSDiPhASBModeImageFilter.h" #include "mitkUSDiPhASDevice.h" #include "mitkUSDiPhASImageSource.h" #include #include #include // itk dependencies #include "itkCastImageFilter.h" #include "itkCropImageFilter.h" #include "itkImage.h" #include "itkIntensityWindowingImageFilter.h" #include "itkMultiplyImageFilter.h" #include "itkResampleImageFilter.h" #include "itkRescaleIntensityImageFilter.h" #include mitk::USDiPhASImageSource::USDiPhASImageSource(mitk::USDiPhASDevice *device) : m_Device(device), m_StartTime(((float)std::clock()) / CLOCKS_PER_SEC), m_DataType(DataType::Image_uChar), m_UseBModeFilter(false), m_CurrentlyRecording(false), m_DataTypeModified(true), m_DataTypeNext(DataType::Image_uChar), m_RecordedImageCounter(0), m_PyroConnected(false), m_VerticalSpacing(0), m_UseBModeFilterModified(false), m_UseBModeFilterNext(false), m_ScatteringCoefficientModified(false), m_CompensateForScatteringModified(false), m_VerticalSpacingModified(false), m_ScatteringCoefficient(15), m_CompensateForScattering(false), m_CompensateEnergy(false), m_CompensateEnergyNext(false), m_CompensateEnergyModified(false), m_BatchSize(24), // why? because magic number, thats why! m_SavingName("-replace"), m_BufferBatches(12), // we like magic numbers m_LastImage(0) { // fill the Recording Buffers with empty images m_ImagesBuffer.insert( m_ImagesBuffer.begin(), m_BufferBatches, std::vector>()); m_RawImagesBuffer.insert( m_RawImagesBuffer.begin(), m_BufferBatches, std::vector>()); m_ImageTimestampsBuffer.insert(m_ImageTimestampsBuffer.begin(), m_BufferBatches, std::vector()); for (int i = 0; i < m_BufferBatches; ++i) { m_ImagesBuffer[i].insert( m_ImagesBuffer[i].begin(), m_BatchSize, std::pair(mitk::Image::New(), mitk::Image::New())); m_RawImagesBuffer[i].insert( m_RawImagesBuffer[i].begin(), m_BatchSize, std::pair(mitk::Image::New(), mitk::Image::New())); m_ImageTimestampsBuffer[i].insert(m_ImageTimestampsBuffer[i].begin(), m_BatchSize, 0); } m_LastBatch = 0; m_LastImage = -1; us::ModuleResource resourceFile; std::string name; m_FluenceCompOriginal.insert(m_FluenceCompOriginal.begin(), 5, Image::New()); for (int i = 5; i <= 25; ++i) { name = "c:\\HomogeneousScatteringAssumptions\\Scattering" + std::to_string(i) + ".nrrd"; m_FluenceCompOriginal.push_back(mitk::IOUtil::Load(name)); } m_FluenceCompResized.insert(m_FluenceCompResized.begin(), 26, Image::New()); m_FluenceCompResizedItk.insert(m_FluenceCompResizedItk.begin(), 26, itk::Image::New()); } mitk::USDiPhASImageSource::~USDiPhASImageSource() { // close the pyro MITK_INFO("Pyro Debug") << "StopDataAcquisition: " << m_Pyro->StopDataAcquisition(); MITK_INFO("Pyro Debug") << "CloseConnection: " << m_Pyro->CloseConnection(); m_PyroConnected = false; m_Pyro = nullptr; } void mitk::USDiPhASImageSource::CheckModifiedVariables() { // modify all settings that have been changed here, so we don't get multithreading issues if (m_DataTypeModified) { SetDataType(m_DataTypeNext); m_DataTypeModified = false; UpdateImageGeometry(); } if (m_UseBModeFilterModified) { SetUseBModeFilter(m_UseBModeFilterNext); m_UseBModeFilterModified = false; } if (m_VerticalSpacingModified) { m_VerticalSpacing = m_VerticalSpacingNext; m_VerticalSpacingModified = false; } if (m_ScatteringCoefficientModified) { m_ScatteringCoefficient = m_ScatteringCoefficientNext; m_ScatteringCoefficientModified = false; } if (m_CompensateForScatteringModified) { m_CompensateForScattering = m_CompensateForScatteringNext; m_CompensateForScatteringModified = false; } if (m_CompensateEnergyModified) { m_CompensateEnergy = m_CompensateEnergyNext; m_CompensateEnergyModified = false; } } void mitk::USDiPhASImageSource::ResizeFluenceImage(mitk::Vector3D spacing, unsigned int *dimensions) { auto curResizeImage = ApplyResampling(m_FluenceCompOriginal.at(m_ScatteringCoefficient), spacing, dimensions); unsigned int imageSize = dimensions[0] * dimensions[1]; double *rawOutputData = new double[imageSize]; double *rawScatteringData = (double *)curResizeImage->GetData(); unsigned int sizeRawScatteringData = curResizeImage->GetDimension(0) * curResizeImage->GetDimension(1); // everything above 1.5mm is still inside the transducer; therefore the fluence compensation image has to be // positioned a little lower float upperCutoffmm = 1.5; unsigned int lowerBound = std::round(upperCutoffmm / spacing[1]) * dimensions[0]; unsigned int upperBound = lowerBound + sizeRawScatteringData; for (unsigned int i = 0; i < lowerBound && i < imageSize; ++i) { rawOutputData[i] = 0; // everything than cannot be compensated shall be treated as garbage, here the upper 0.15mm } for (unsigned int i = lowerBound; i < upperBound && i < imageSize; ++i) { rawOutputData[i] = 1 / rawScatteringData[i - lowerBound]; } for (unsigned int i = upperBound; i < imageSize; ++i) { rawOutputData[i] = 0; // everything than cannot be compensated shall be treated as garbage } unsigned int dim[] = {dimensions[0], dimensions[1], 1}; curResizeImage->Initialize(mitk::MakeScalarPixelType(), 3, dim); curResizeImage->SetSpacing(spacing); curResizeImage->SetSlice(rawOutputData); mitk::CastToItkImage(curResizeImage, m_FluenceCompResizedItk.at(m_ScatteringCoefficient)); m_FluenceCompResized.at(m_ScatteringCoefficient) = mitk::GrabItkImageMemory(m_FluenceCompResizedItk.at(m_ScatteringCoefficient)); MITK_INFO << "Resized a fluence image."; delete[] rawOutputData; } void mitk::USDiPhASImageSource::GetNextRawImage(std::vector &imageVector) { CheckModifiedVariables(); if (imageVector.size() != 2) { imageVector.resize(2); } if (m_LastImage == -1) return; // make sure image is nullptr mitk::Image::Pointer imageUS = nullptr; mitk::Image::Pointer imagePA = nullptr; float ImageEnergyValue = 0; imagePA = m_ImagesBuffer[m_LastBatch][m_LastImage].first; imageUS = m_ImagesBuffer[m_LastBatch][m_LastImage].second; ImageEnergyValue = 1; if (imagePA == nullptr || imageUS == nullptr || !imagePA->IsInitialized() || !imageUS->IsInitialized()) return; // do image processing before displaying it if (imagePA.IsNotNull() && m_DataType == DataType::Beamformed_Short) { itkFloatImageType::Pointer itkImage; mitk::CastToItkImage(imagePA, itkImage); imagePA = mitk::GrabItkImageMemory(itkImage); // thereby using float images imagePA = CutOffTop(imagePA, 165); if (m_CompensateEnergy && ImageEnergyValue != 1) imagePA = MultiplyImage(imagePA, 1 / ImageEnergyValue); // TODO: add the correct prefactor here if (m_UseBModeFilter) imagePA = ApplyBmodeFilter(imagePA, false); if (m_VerticalSpacing) imagePA = ResampleOutputVertical(imagePA, m_VerticalSpacing); if (m_CompensateForScattering) { auto curResizeImage = m_FluenceCompResized.at(m_ScatteringCoefficient); bool doResampling = imagePA->GetDimension(0) != curResizeImage->GetDimension(0) || imagePA->GetDimension(1) != curResizeImage->GetDimension(1) || imagePA->GetGeometry()->GetSpacing()[0] != curResizeImage->GetGeometry()->GetSpacing()[0] || imagePA->GetGeometry()->GetSpacing()[1] != curResizeImage->GetGeometry()->GetSpacing()[1]; if (doResampling) ResizeFluenceImage(imagePA->GetGeometry()->GetSpacing(), imagePA->GetDimensions()); imagePA = ApplyScatteringCompensation(imagePA, m_ScatteringCoefficient); } } if (imageUS.IsNotNull() && m_DataType == DataType::Beamformed_Short) { itkFloatImageType::Pointer itkImage; mitk::CastToItkImage(imageUS, itkImage); imageUS = mitk::GrabItkImageMemory(itkImage); // thereby using float images imageUS = CutOffTop(imageUS, 165); if (m_UseBModeFilter) imageUS = ApplyBmodeFilter(imageUS, true); // the US Images get a logarithmic filter if (m_VerticalSpacing) imageUS = ResampleOutputVertical(imageUS, m_VerticalSpacing); } itkUCharImageType::Pointer itkImage = nullptr; if (imageUS != nullptr && imageUS->IsInitialized()) { //NormalizeImage(imageUS, 10); //itkImage = nullptr; //mitk::CastToItkImage(imageUS, itkImage); //imageUS = mitk::GrabItkImageMemory(itkImage); } if (imagePA != nullptr && imagePA->IsInitialized()) { NormalizeImage(imagePA, 10); itkImage = nullptr; mitk::CastToItkImage(imagePA, itkImage); imagePA = mitk::GrabItkImageMemory(itkImage); } imageVector[0] = imagePA; imageVector[1] = imageUS; } void mitk::USDiPhASImageSource::NormalizeImage(mitk::Image::Pointer image, double rescale) { auto imageStat = image->GetStatistics(); float max = imageStat->GetScalarValueMax(); float min = imageStat->GetScalarValueMin(); float offset = (max + min) / 2.; float scale = std::max(abs(min - offset), abs(max - offset)); mitk::ImageWriteAccessor write(image); float *data = (float *)write.GetData(); for (size_t i = 0; i < image->GetDimension(0) * image->GetDimension(1) * image->GetDimension(2); ++i) { if (data[i] > 255) data[i] = 255; else if (data[i] < 0) data[i] = 0; } } mitk::Image::Pointer mitk::USDiPhASImageSource::ApplyBmodeFilter(mitk::Image::Pointer image, bool useLogFilter) { // we use this seperate ApplyBmodeFilter Method for processing of two-dimensional images // the image needs to be of floating point type for the envelope filter to work; the casting is done automatically by // the CastToItkImage typedef itk::BModeImageFilter BModeFilterType; BModeFilterType::Pointer bModeFilter = BModeFilterType::New(); // LogFilter typedef itk::PhotoacousticBModeImageFilter PhotoacousticBModeImageFilter; PhotoacousticBModeImageFilter::Pointer photoacousticBModeFilter = PhotoacousticBModeImageFilter::New(); // No LogFilter itkFloatImageType::Pointer itkImage; itkFloatImageType::Pointer bmode; mitk::CastToItkImage(image, itkImage); if (useLogFilter) { bModeFilter->SetInput(itkImage); bModeFilter->SetDirection(1); bmode = bModeFilter->GetOutput(); } else { photoacousticBModeFilter->SetInput(itkImage); photoacousticBModeFilter->SetDirection(1); bmode = photoacousticBModeFilter->GetOutput(); } return mitk::GrabItkImageMemory(bmode); } mitk::Image::Pointer mitk::USDiPhASImageSource::CutOffTop(mitk::Image::Pointer image, int cutOffSize) { typedef itk::CropImageFilter CutImageFilter; itkFloatImageType::SizeType cropSize; itkFloatImageType::Pointer itkImage; mitk::CastToItkImage(image, itkImage); cropSize[0] = 0; if (itkImage->GetLargestPossibleRegion().GetSize()[1] == 2048) cropSize[1] = cutOffSize; else cropSize[1] = 0; cropSize[2] = 0; CutImageFilter::Pointer cutOffFilter = CutImageFilter::New(); cutOffFilter->SetInput(itkImage); cutOffFilter->SetLowerBoundaryCropSize(cropSize); cutOffFilter->UpdateLargestPossibleRegion(); return mitk::GrabItkImageMemory(cutOffFilter->GetOutput()); } mitk::Image::Pointer mitk::USDiPhASImageSource::ResampleOutputVertical(mitk::Image::Pointer image, float verticalSpacing) { typedef itk::ResampleImageFilter ResampleImageFilter; ResampleImageFilter::Pointer resampleImageFilter = ResampleImageFilter::New(); itkFloatImageType::Pointer itkImage; mitk::CastToItkImage(image, itkImage); itkFloatImageType::SpacingType outputSpacing; itkFloatImageType::SizeType inputSize = itkImage->GetLargestPossibleRegion().GetSize(); itkFloatImageType::SizeType outputSize = inputSize; outputSpacing[0] = itkImage->GetSpacing()[0] * (static_cast(inputSize[0]) / static_cast(outputSize[0])); outputSpacing[1] = verticalSpacing; outputSpacing[2] = itkImage->GetSpacing()[2]; outputSize[1] = inputSize[1] * itkImage->GetSpacing()[1] / outputSpacing[1]; typedef itk::IdentityTransform TransformType; resampleImageFilter->SetInput(itkImage); resampleImageFilter->SetSize(outputSize); resampleImageFilter->SetOutputSpacing(outputSpacing); resampleImageFilter->SetTransform(TransformType::New()); resampleImageFilter->UpdateLargestPossibleRegion(); return mitk::GrabItkImageMemory(resampleImageFilter->GetOutput()); } mitk::Image::Pointer mitk::USDiPhASImageSource::ApplyScatteringCompensation(mitk::Image::Pointer inputImage, int scattering) { typedef itk::MultiplyImageFilter MultiplyImageFilterType; itkFloatImageType::Pointer itkImage; mitk::CastToItkImage(inputImage, itkImage); MultiplyImageFilterType::Pointer multiplyFilter = MultiplyImageFilterType::New(); multiplyFilter->SetInput1(itkImage); multiplyFilter->SetInput2(m_FluenceCompResizedItk.at(m_ScatteringCoefficient)); return mitk::GrabItkImageMemory(multiplyFilter->GetOutput()); } mitk::Image::Pointer mitk::USDiPhASImageSource::ApplyResampling(mitk::Image::Pointer inputImage, mitk::Vector3D outputSpacing, unsigned int outputSize[3]) { typedef itk::ResampleImageFilter ResampleImageFilter; ResampleImageFilter::Pointer resampleImageFilter = ResampleImageFilter::New(); itkFloatImageType::Pointer itkImage; mitk::CastToItkImage(inputImage, itkImage); itkFloatImageType::SpacingType outputSpacingItk; itkFloatImageType::SizeType inputSizeItk = itkImage->GetLargestPossibleRegion().GetSize(); itkFloatImageType::SizeType outputSizeItk = inputSizeItk; itkFloatImageType::SpacingType inputSpacing = itkImage->GetSpacing(); outputSizeItk[0] = outputSize[0]; outputSizeItk[1] = 10 * (inputSpacing[1] * inputSizeItk[1]) / (outputSpacing[1]); outputSizeItk[2] = 1; outputSpacingItk[0] = 0.996 * inputSpacing[0] * (static_cast(inputSizeItk[0]) / static_cast( outputSizeItk[0])); // TODO: find out why the spacing is not correct, so we need that factor; ?!?! outputSpacingItk[1] = inputSpacing[1] * (static_cast(inputSizeItk[1]) / static_cast(outputSizeItk[1])); outputSpacingItk[2] = outputSpacing[2]; typedef itk::IdentityTransform TransformType; resampleImageFilter->SetInput(itkImage); resampleImageFilter->SetSize(outputSizeItk); resampleImageFilter->SetOutputSpacing(outputSpacingItk); resampleImageFilter->SetTransform(TransformType::New()); resampleImageFilter->UpdateLargestPossibleRegion(); return mitk::GrabItkImageMemory(resampleImageFilter->GetOutput()); } mitk::Image::Pointer mitk::USDiPhASImageSource::MultiplyImage(mitk::Image::Pointer inputImage, double value) { typedef itk::MultiplyImageFilter MultiplyImageFilterType; itkFloatImageType::Pointer itkImage; mitk::CastToItkImage(inputImage, itkImage); MultiplyImageFilterType::Pointer multiplyFilter = MultiplyImageFilterType::New(); multiplyFilter->SetInput1(itkImage); multiplyFilter->SetConstant(value); return mitk::GrabItkImageMemory(multiplyFilter->GetOutput()); } void mitk::USDiPhASImageSource::ImageDataCallback(short *rfDataChannelData, int &channelDataChannelsPerDataset, int &channelDataSamplesPerChannel, int &channelDataTotalDatasets, short *rfDataArrayBeamformed, int &beamformedLines, int &beamformedSamples, int &beamformedTotalDatasets, unsigned char *imageData, int &imageWidth, int &imageHeight, int &imageBytesPerPixel, int &imageSetsTotal, double &timeStamp) { if (m_DataTypeModified) return; if (!m_PyroConnected) { m_Pyro = mitk::OphirPyro::New(); MITK_INFO << "[Pyro Debug] OpenConnection: " << m_Pyro->OpenConnection(); MITK_INFO << "[Pyro Debug] StartDataAcquisition: " << m_Pyro->StartDataAcquisition(); m_PyroConnected = true; } bool writeImage = ((m_DataType == DataType::Image_uChar) && (imageData != nullptr)) || ((m_DataType == DataType::Beamformed_Short) && (rfDataArrayBeamformed != nullptr)); if (writeImage) { int currentBatch = -1; int currentImage = -1; long long currentImageTimestamp = std::chrono::duration_cast( std::chrono::system_clock::now().time_since_epoch()).count(); // check whether we lost any images if (m_CurrentlyRecording && m_ImageTimestampsBuffer[m_LastBatch][m_LastImage] != 0 && std::abs(currentImageTimestamp - m_ImageTimestampsBuffer[m_LastBatch][m_LastImage]) > 53) { MITK_ERROR << "Lost about " << (float)std::abs(currentImageTimestamp - m_ImageTimestampsBuffer[m_LastBatch][m_LastImage]) / (50.f) - - 1 + 1. << " images"; } if ((m_LastImage + 1) % m_BatchSize == 0) { if (m_CurrentlyRecording) { // save the completed batch m_SaveThreads.push_back( std::thread(&USDiPhASImageSource::saveDataThread, this, m_LastBatch, m_RecordedImageCounter, m_SavingName)); ++m_RecordedImageCounter; } // switch to next one currentBatch = (m_LastBatch + 1) % m_BufferBatches; currentImage = 0; } else { currentBatch = m_LastBatch; currentImage = (m_LastImage + 1); } // create new images and initialize them mitk::Image::Pointer imageUS = m_ImagesBuffer[currentBatch][currentImage].second; mitk::Image::Pointer imagePA = m_ImagesBuffer[currentBatch][currentImage].first; switch (m_DataType) { case DataType::Image_uChar: { if (!imagePA->IsInitialized() || imagePA->GetDimension(0) != imageWidth || imagePA->GetDimension(1) != imageHeight || std::abs(imagePA->GetGeometry()->GetSpacing()[0] - m_ImageSpacing[0]) > std::numeric_limits::epsilon() || std::abs(imagePA->GetGeometry()->GetSpacing()[1] - m_ImageSpacing[1]) > std::numeric_limits::epsilon()) { MITK_INFO << "Update buffers for image data"; unsigned int imageDimensions[3]; imageDimensions[0] = imageWidth; imageDimensions[1] = imageHeight; imageDimensions[2] = 1; short *imageData = new short[beamformedLines * beamformedSamples]; for (int b = 0; b < m_BufferBatches; ++b) { for (int i = 0; i < m_BatchSize; ++i) { m_ImagesBuffer[b][i] = std::pair(mitk::Image::New(), mitk::Image::New()); m_ImagesBuffer[b][i].first->Initialize(mitk::MakeScalarPixelType(), 3, imageDimensions); m_ImagesBuffer[b][i].second->Initialize(mitk::MakeScalarPixelType(), 3, imageDimensions); m_ImagesBuffer[b][i].first->SetSpacing(m_ImageSpacing); m_ImagesBuffer[b][i].second->SetSpacing(m_ImageSpacing); m_ImagesBuffer[b][i].first->SetImportVolume(imageData, 0, 0, mitk::Image::RtlCopyMemory); m_ImagesBuffer[b][i].second->SetImportVolume(imageData, 0, 0, mitk::Image::RtlCopyMemory); } } delete[] imageData; imageUS = m_ImagesBuffer[currentBatch][currentImage].second; imagePA = m_ImagesBuffer[currentBatch][currentImage].first; } break; } case DataType::Beamformed_Short: { if (!imagePA->IsInitialized() || imagePA->GetDimension(0) != beamformedLines || imagePA->GetDimension(1) != beamformedSamples || std::abs(imagePA->GetGeometry()->GetSpacing()[0] - m_ImageSpacing[0]) > std::numeric_limits::epsilon() || std::abs(imagePA->GetGeometry()->GetSpacing()[1] - m_ImageSpacing[1]) > std::numeric_limits::epsilon()) { MITK_INFO << "Update buffers for beamformed image data"; unsigned int imageDimensions[3]; imageDimensions[0] = beamformedLines; imageDimensions[1] = beamformedSamples; imageDimensions[2] = 1; short *imageData = new short[beamformedLines * beamformedSamples]; for (int b = 0; b < m_BufferBatches; ++b) { for (int i = 0; i < m_BatchSize; ++i) { m_ImagesBuffer[b][i] = std::pair(mitk::Image::New(), mitk::Image::New()); m_ImagesBuffer[b][i].first->Initialize(mitk::MakeScalarPixelType(), 3, imageDimensions); m_ImagesBuffer[b][i].second->Initialize(mitk::MakeScalarPixelType(), 3, imageDimensions); m_ImagesBuffer[b][i].first->SetSpacing(m_ImageSpacing); m_ImagesBuffer[b][i].second->SetSpacing(m_ImageSpacing); m_ImagesBuffer[b][i].first->SetImportVolume(imageData, 0, 0, mitk::Image::RtlCopyMemory); m_ImagesBuffer[b][i].second->SetImportVolume(imageData, 0, 0, mitk::Image::RtlCopyMemory); } } delete[] imageData; imageUS = m_ImagesBuffer[currentBatch][currentImage].second; imagePA = m_ImagesBuffer[currentBatch][currentImage].first; } break; } } // write the given buffer into the image switch (m_DataType) { case DataType::Image_uChar: { if (m_Device->GetScanMode().beamformingAlgorithm == (int)Beamforming::Interleaved_OA_US) { imagePA->SetSlice(imageData); imageUS->SetVolume(&imageData[imageHeight * imageWidth]); } else { imageUS->SetVolume(imageData); } break; } case DataType::Beamformed_Short: { mitk::ImageWriteAccessor dataPAAccess(imagePA); mitk::ImageWriteAccessor dataUSAccess(imageUS); short *dataPA = (short *)dataPAAccess.GetData(); short *dataUS = (short *)dataUSAccess.GetData(); size_t pixelsPerImage = beamformedLines * beamformedSamples; if (m_Device->GetScanMode().beamformingAlgorithm == (int)Beamforming::Interleaved_OA_US) { for (unsigned short sample = 0; sample < beamformedSamples; sample++) { for (unsigned short line = 0; line < beamformedLines; line++) { dataPA[sample * beamformedLines + line] = rfDataArrayBeamformed[line * beamformedSamples + sample]; } } for (unsigned short sample = 0; sample < beamformedSamples; sample++) { for (unsigned short line = 0; line < beamformedLines; line++) { dataUS[sample * beamformedLines + line] = rfDataArrayBeamformed[pixelsPerImage + line * beamformedSamples + sample]; } } } else { for (unsigned short sample = 0; sample < beamformedSamples; sample++) { for (unsigned short line = 0; line < beamformedLines; line++) { dataUS[sample * beamformedLines + line] = rfDataArrayBeamformed[line * beamformedSamples + sample]; } } } break; } } if (m_SavingSettings.saveRaw && m_CurrentlyRecording && rfDataChannelData != nullptr) { mitk::Image::Pointer rawImageUS = m_RawImagesBuffer[currentBatch][currentImage].second; mitk::Image::Pointer rawImagePA = m_RawImagesBuffer[currentBatch][currentImage].first; mitk::Vector3D rawSpacing; rawSpacing[0] = m_Device->GetScanMode().transducerPitchMeter * 1e3; // save in mm rawSpacing[1] = m_Device->GetScanMode().receivePhaseLengthSeconds / channelDataSamplesPerChannel * 1e6; // save in us rawSpacing[2] = 1; if (!rawImagePA->IsInitialized() || rawImagePA->GetDimension(0) != channelDataChannelsPerDataset || rawImagePA->GetDimension(1) != std::ceil((float)channelDataSamplesPerChannel / 2.f) || rawImageUS->GetDimension(1) != channelDataSamplesPerChannel || rawImageUS->GetDimension(2) != channelDataTotalDatasets - 1 || std::abs(rawImagePA->GetGeometry()->GetSpacing()[0] - rawSpacing[0]) > std::numeric_limits::epsilon() || std::abs(rawImagePA->GetGeometry()->GetSpacing()[1] - rawSpacing[1]) > std::numeric_limits::epsilon()) { MITK_INFO << "Update buffers for raw image data"; unsigned int imageDimensionsPA[3]; imageDimensionsPA[0] = channelDataChannelsPerDataset; imageDimensionsPA[1] = std::ceil((float)channelDataSamplesPerChannel / 2.f); imageDimensionsPA[2] = 1; unsigned int imageDimensionsUS[3]; imageDimensionsUS[0] = channelDataChannelsPerDataset; imageDimensionsUS[1] = channelDataSamplesPerChannel; imageDimensionsUS[2] = channelDataTotalDatasets - 1; short *imageData = new short[channelDataChannelsPerDataset * channelDataSamplesPerChannel]; // * (channelDataTotalDatasets - 1)]; for (int b = 0; b < m_BufferBatches; ++b) { for (int i = 0; i < m_BatchSize; ++i) { m_RawImagesBuffer[b][i] = std::pair(mitk::Image::New(), mitk::Image::New()); m_RawImagesBuffer[b][i].first->Initialize(mitk::MakeScalarPixelType(), 3, imageDimensionsPA); m_RawImagesBuffer[b][i].second->Initialize(mitk::MakeScalarPixelType(), 3, imageDimensionsUS); m_RawImagesBuffer[b][i].first->SetSpacing(rawSpacing); m_RawImagesBuffer[b][i].second->SetSpacing(rawSpacing); m_RawImagesBuffer[b][i].first->SetImportVolume(imageData, 0, 0, mitk::Image::RtlCopyMemory); // currently US raw Data is not acquired // m_RawImagesBuffer[b][i].second->SetImportVolume(imageData, 0, 0, mitk::Image::RtlCopyMemory); } } delete[] imageData; rawImagePA = m_RawImagesBuffer[currentBatch][currentImage].first; rawImageUS = m_RawImagesBuffer[currentBatch][currentImage].second; } short offset = m_Device->GetScanMode().accumulation * 2048; mitk::ImageWriteAccessor dataPAAccess(rawImagePA); mitk::ImageWriteAccessor dataUSAccess(rawImageUS); short *dataPA = (short *)dataPAAccess.GetData(); short *dataUS = (short *)dataUSAccess.GetData(); size_t pixelsPerImage = channelDataSamplesPerChannel * channelDataChannelsPerDataset; if (m_Device->GetScanMode().beamformingAlgorithm == (int)Beamforming::Interleaved_OA_US) { for (unsigned short sam = 0; sam < std::ceil((float)channelDataSamplesPerChannel / 2.f); ++sam) { for (unsigned short chan = 0; chan < channelDataChannelsPerDataset; ++chan) { dataPA[sam * channelDataChannelsPerDataset + chan] = rfDataChannelData[sam * channelDataChannelsPerDataset + chan] - offset; // this offset in the raw Images is given by the API... } } // currently not used! /*for (unsigned short sam = 0; sam < channelDataSamplesPerChannel; ++sam) { for (unsigned short chan = 0; chan < channelDataChannelsPerDataset; ++chan) { dataUS[sam * channelDataChannelsPerDataset + chan] = rfDataChannelData[pixelsPerImage + sam * channelDataChannelsPerDataset + chan] - offset; // this offset in the raw Images is given by the API... } }*/ } else { // currently not used! /*for (unsigned short sam = 0; sam < channelDataSamplesPerChannel; ++sam) { for (unsigned short chan = 0; chan < channelDataChannelsPerDataset; ++chan) { dataUS[sam * channelDataChannelsPerDataset + chan] = rfDataChannelData[sam * channelDataChannelsPerDataset + chan] - offset; // this offset in the raw Images is given by the API... } }*/ } } m_LastBatch = currentBatch; m_LastImage = currentImage; m_ImageTimestampsBuffer[currentBatch][currentImage] = currentImageTimestamp; } } void mitk::USDiPhASImageSource::UpdateImageGeometry() { MITK_INFO << "Retreaving Image Geometry Information for Spacing..."; float &recordTime = m_Device->GetScanMode().receivePhaseLengthSeconds; int &speedOfSound = m_Device->GetScanMode().averageSpeedOfSound; float &pitch = m_Device->GetScanMode().reconstructedLinePitchMmOrAngleDegree; int &reconstructionLines = m_Device->GetScanMode().reconstructionLines; switch (m_DataType) { case DataType::Image_uChar: { int &imageWidth = m_Device->GetScanMode().imageWidth; int &imageHeight = m_Device->GetScanMode().imageHeight; m_ImageSpacing[0] = pitch * reconstructionLines / imageWidth; m_ImageSpacing[1] = recordTime * speedOfSound / 2 * 1000 / imageHeight; break; } case DataType::Beamformed_Short: { int &imageWidth = reconstructionLines; int &imageHeight = m_Device->GetScanMode().reconstructionSamplesPerLine; m_ImageSpacing[0] = pitch; m_ImageSpacing[1] = recordTime * speedOfSound / 2 * 1000 / imageHeight; break; } } m_ImageSpacing[2] = 1; MITK_INFO << "Retreaving Image Geometry Information for Spacing " << m_ImageSpacing[0] << " ... " << m_ImageSpacing[1] << " ... " << m_ImageSpacing[2] << " ...[DONE]"; } void mitk::USDiPhASImageSource::ModifyDataType(DataType dataT) { m_DataTypeModified = true; m_DataTypeNext = dataT; } void mitk::USDiPhASImageSource::ModifyUseBModeFilter(bool isSet) { m_UseBModeFilterModified = true; m_UseBModeFilterNext = isSet; } void mitk::USDiPhASImageSource::ModifyScatteringCoefficient(int coeff) { m_ScatteringCoefficientNext = coeff; m_ScatteringCoefficientModified = true; } void mitk::USDiPhASImageSource::ModifyCompensateForScattering(bool useIt) { m_CompensateForScatteringNext = useIt; m_CompensateForScatteringModified = true; } void mitk::USDiPhASImageSource::ModifyEnergyCompensation(bool compensate) { m_CompensateEnergyNext = compensate; m_CompensateEnergyModified = true; } void mitk::USDiPhASImageSource::SetDataType(DataType dataT) { if (dataT != m_DataType) { m_DataType = dataT; MITK_INFO << "Setting new DataType..." << dataT; switch (m_DataType) { case DataType::Image_uChar: MITK_INFO << "height: " << m_Device->GetScanMode().imageHeight << " width: " << m_Device->GetScanMode().imageWidth; break; case DataType::Beamformed_Short: MITK_INFO << "samples: " << m_Device->GetScanMode().reconstructionSamplesPerLine << " lines: " << m_Device->GetScanMode().reconstructionLines; break; } } } void mitk::USDiPhASImageSource::SetUseBModeFilter(bool isSet) { m_UseBModeFilter = isSet; } void mitk::USDiPhASImageSource::SetVerticalSpacing(float mm) { m_VerticalSpacingNext = mm; m_VerticalSpacingModified = true; } void mitk::USDiPhASImageSource::SetSavingSettings(SavingSettings settings) { m_SavingSettings = settings; } void mitk::USDiPhASImageSource::SetSavingName(std::string name) { if (name == "") { m_SavingName = "-replace"; } else { m_SavingName = name; } } // this is just a little function to set the filenames below right inline void replaceAll(std::string &str, const std::string &from, const std::string &to) { if (from.empty()) return; size_t start_pos = 0; while ((start_pos = str.find(from, start_pos)) != std::string::npos) { str.replace(start_pos, from.length(), to); start_pos += to.length(); // In case 'to' contains 'from', like replacing 'x' with 'yx' } } void mitk::USDiPhASImageSource::SetRecordingStatus(bool record) { // start the recording process if (record) { if (m_SavingSettings.saveRaw) { m_Device->GetScanMode().transferChannelData = true; m_Device->UpdateScanmode(); // set the raw Data to be transfered } m_RecordedImageCounter = 0; // tell the callback to start recording images m_CurrentlyRecording = true; // save the settings! // get the time and date time_t time = std::time(nullptr); time_t *timeptr = &time; std::string currentDate = std::ctime(timeptr); replaceAll(currentDate, ":", "-"); currentDate.pop_back(); if (m_SavingName == "-replace") { m_SavingName = "c:\\ImageData\\" + currentDate; } std::string pathS = m_SavingName + ".settings" + ".txt"; ofstream settingsFile; settingsFile.open(pathS); auto &sM = m_Device->GetScanMode(); settingsFile << "[General Parameters]\n"; settingsFile << "Scan Depth [mm] = " << sM.receivePhaseLengthSeconds * sM.averageSpeedOfSound / 2 * 1000 << "\n"; settingsFile << "Speed of Sound [m/s] = " << sM.averageSpeedOfSound << "\n"; settingsFile << "Excitation Frequency [MHz] = " << sM.transducerFrequencyHz / 1000000 << "\n"; settingsFile << "Voltage [V] = " << sM.voltageV << "\n"; settingsFile << "TGC min = " << (int)sM.tgcdB[0] << " max = " << (int)sM.tgcdB[7] << "\n"; settingsFile << "[Beamforming Parameters]\n"; settingsFile << "Reconstructed Lines = " << sM.reconstructionLines << "\n"; settingsFile << "Samples per Line = " << sM.reconstructionSamplesPerLine << "\n"; settingsFile.close(); } // save images, end recording, and clean up else { m_CurrentlyRecording = false; // wait for all saving Threads to finish for (size_t i = 0; i < m_SaveThreads.size(); ++i) { m_SaveThreads[i].join(); } // make sure raw Channel Data is not transferred anymore m_Device->GetScanMode().transferChannelData = false; m_Device->UpdateScanmode(); // clear the save threads m_SaveThreads.clear(); // clean up all threads m_SavingName = "-replace"; } } void mitk::USDiPhASImageSource::OrderImagesInterleaved( Image::Pointer PAImage, Image::Pointer USImage, std::vector> recordedList, bool raw) { unsigned int width = 32; unsigned int heightPA = 32; unsigned int heightUS = 32; unsigned int events = m_Device->GetScanMode().transmitEventsCount; if (!raw) events = 1; // the beamformed image array contains only the resulting image of multiple events if (raw) { width = recordedList.at(0).first->GetDimension(0); heightPA = recordedList.at(0).first->GetDimension(1); heightUS = recordedList.at(0).second->GetDimension(1); } else if (m_DataType == DataType::Beamformed_Short) { width = m_Device->GetScanMode().reconstructionLines; heightPA = m_Device->GetScanMode().reconstructionSamplesPerLine; heightUS = m_Device->GetScanMode().reconstructionSamplesPerLine; } else if (m_DataType == DataType::Image_uChar) { width = m_Device->GetScanMode().imageWidth; heightPA = m_Device->GetScanMode().imageHeight; heightUS = m_Device->GetScanMode().imageHeight; } unsigned int dimPA[] = {width, heightPA, (unsigned int)recordedList.size()}; unsigned int dimUS[] = {width, heightUS, (unsigned int)(recordedList.size() * events)}; PAImage->Initialize(recordedList.back().first->GetPixelType(), 3, dimPA); PAImage->SetSpacing(recordedList.back().first->GetGeometry()->GetSpacing()); USImage->Initialize(recordedList.back().first->GetPixelType(), 3, dimUS); USImage->SetSpacing(recordedList.back().first->GetGeometry()->GetSpacing()); for (int index = 0; index < recordedList.size(); ++index) { mitk::ImageReadAccessor inputReadAccessorPA(recordedList.at(index).first); PAImage->SetSlice(inputReadAccessorPA.GetData(), index); if (!raw) { for (unsigned int i = 0; i < events; ++i) { mitk::ImageReadAccessor inputReadAccessorUS(recordedList.at(index).second, recordedList.at(index).second->GetSliceData(i)); USImage->SetSlice(inputReadAccessorUS.GetData(), index + i); } } } } void mitk::USDiPhASImageSource::OrderImagesUltrasound( Image::Pointer USImage, std::vector> recordedList) { unsigned int width = 32; unsigned int height = 32; unsigned int events = m_Device->GetScanMode().transmitEventsCount; if (m_DataType == DataType::Beamformed_Short) { width = (unsigned int)m_Device->GetScanMode().reconstructionLines; height = (unsigned int)m_Device->GetScanMode().reconstructionSamplesPerLine; } else if (m_DataType == DataType::Image_uChar) { width = (unsigned int)m_Device->GetScanMode().imageWidth; height = (unsigned int)m_Device->GetScanMode().imageHeight; } unsigned int dimSound[] = {width, height, (unsigned int)(recordedList.size() * events)}; USImage->Initialize(recordedList.back().second->GetPixelType(), 3, dimSound); USImage->SetSpacing(recordedList.back().second->GetGeometry()->GetSpacing()); for (int index = 0; index < recordedList.size(); ++index) { for (unsigned int i = 0; i < events; ++i) { mitk::ImageReadAccessor inputReadAccessorUS(recordedList.at(index).second, recordedList.at(index).second->GetSliceData(i)); USImage->SetSlice(inputReadAccessorUS.GetData(), index + i); } } } void mitk::USDiPhASImageSource::saveDataThread(size_t batch, unsigned long long counter, std::string currentSaveName) { char postfix[25]; sprintf(postfix, "%07llu", counter); // initialize file paths and the images Image::Pointer PAImage = Image::New(); Image::Pointer USImage = Image::New(); std::string pathPA = currentSaveName + ".PA.bf." + postfix + ".nrrd"; std::string pathUS = currentSaveName + ".US.bf." + postfix + ".nrrd"; std::string pathTS = currentSaveName + ".ts." + postfix + ".csv"; // raw Images (if chosen to be saved) Image::Pointer PAImageRaw = Image::New(); Image::Pointer USImageRaw = Image::New(); std::string pathPARaw = currentSaveName + ".PA.rf." + postfix + ".nrrd"; std::string pathUSRaw = currentSaveName + ".US.rf." + postfix + ".nrrd"; if (m_Device->GetScanMode().beamformingAlgorithm == (int)Beamforming::Interleaved_OA_US) // save a PAImage if we used interleaved mode { // first, save the pyro data m_Pyro->SaveData(currentSaveName + ".pyro." + postfix + ".csv"); // now order the images and save them // the beamformed ones... OrderImagesInterleaved(PAImage, USImage, m_ImagesBuffer[batch], false); mitk::IOUtil::Save(USImage, pathUS); if (m_SavingSettings.saveBeamformed) { mitk::IOUtil::Save(PAImage, pathPA); } // ...and the raw images if (m_SavingSettings.saveRaw) { OrderImagesInterleaved(PAImageRaw, USImageRaw, m_RawImagesBuffer[batch], true); // mitk::IOUtil::Save(USImageRaw, pathUSRaw); mitk::IOUtil::Save(PAImageRaw, pathPARaw); } // save the timestamps! ofstream timestampFile; timestampFile.open(pathTS); timestampFile << ",timestamp[ms]"; // write the header for (size_t index = 0; index < m_ImageTimestampsBuffer[batch].size(); ++index) { timestampFile << "\n" << index << "," << m_ImageTimestampsBuffer[batch][index]; } timestampFile.close(); } else if (m_Device->GetScanMode().beamformingAlgorithm == (int)Beamforming::PlaneWaveCompound) // save no PAImage if we used US only mode { OrderImagesUltrasound(USImage, m_ImagesBuffer[batch]); mitk::IOUtil::Save(USImage, pathUS); } MITK_INFO << "The saving Thread for batch " << batch << " has finished."; } \ No newline at end of file