diff --git a/Modules/PhotoacousticsHardware/mitkOphirPyro.cpp b/Modules/PhotoacousticsHardware/mitkOphirPyro.cpp index c4d76224e1..f075bc276c 100644 --- a/Modules/PhotoacousticsHardware/mitkOphirPyro.cpp +++ b/Modules/PhotoacousticsHardware/mitkOphirPyro.cpp @@ -1,337 +1,337 @@ /*=================================================================== 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() +void mitk::OphirPyro::SaveCsvData(std::string filename) { - // get the time and date, put them into a nice string and create a folder for the images - time_t time = std::time(nullptr); - time_t* timeptr = &time; - std::string currentDate = std::ctime(timeptr); - replaceAll(currentDate, ":", "-"); - currentDate.pop_back(); - - std::string pathTS = "c:\\ImageData\\" + currentDate + " pyro-ts" + ".csv"; + 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() +void mitk::OphirPyro::SaveData(std::string filename) { - SaveCsvData(); + SaveCsvData(filename); } bool mitk::OphirPyro::StopDataAcquisition() { if (ophirAPI.StopStream(m_DeviceHandle)) m_Streaming = false; - SaveCsvData(); + //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; iGetDataFromSensor(); 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/PhotoacousticsHardware/mitkOphirPyro.h b/Modules/PhotoacousticsHardware/mitkOphirPyro.h index b1d9f886d6..aa34f061fa 100644 --- a/Modules/PhotoacousticsHardware/mitkOphirPyro.h +++ b/Modules/PhotoacousticsHardware/mitkOphirPyro.h @@ -1,94 +1,94 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef MITKOPHIRPYRO_H_HEADER_INCLUDED #define MITKOPHIRPYRO_H_HEADER_INCLUDED #include "itkObject.h" #include "mitkCommon.h" #include "vector" #include "MitkPhotoacousticsHardwareExports.h" #include "OphirPyroWrapper.h" #include #include #include #include #include #include #include #include #include namespace mitk { class MITKPHOTOACOUSTICSHARDWARE_EXPORT OphirPyro : public itk::Object { public: mitkClassMacroItkParent(mitk::OphirPyro, itk::Object); itkFactorylessNewMacro(Self); virtual bool OpenConnection(); virtual bool CloseConnection(); virtual bool StartDataAcquisition(); virtual bool StopDataAcquisition(); unsigned int GetDataFromSensor(); void GetDataFromSensorThread(); - void SaveData(); + void SaveData(std::string filename); virtual double LookupCurrentPulseEnergy(); virtual double GetNextPulseEnergy(); virtual double LookupCurrentPulseEnergy(double* timestamp, int* status); virtual double GetNextPulseEnergy(double* timestamp, int* status); virtual double GetClosestEnergyInmJ(long long ImageTimeStamp, double interval=20); virtual double GetNextEnergyInmJ(long long ImageTimeStamp, double interval = 20); virtual void SetSyncDelay(long long FirstImageTimeStamp); virtual bool IsSyncDelaySet(); protected: OphirPyro(); virtual ~OphirPyro(); - void SaveCsvData(); + void SaveCsvData(std::string filename); OphirPyroWrapper ophirAPI; char* m_SerialNumber; int m_DeviceHandle; bool m_Connected; bool m_Streaming; std::vector m_PulseEnergy; std::vector m_PulseTime; std::vector m_PulseStatus; std::vector m_TimeStamps; std::vector m_PulseEnergySaved; std::vector m_PulseTimeSaved; std::vector m_PulseStatusSaved; std::vector m_TimeStampsSaved; double m_CurrentWavelength; double m_CurrentEnergyRange; long long m_ImagePyroDelay; float m_EnergyMultiplicator; std::thread m_GetDataThread; }; } // namespace mitk #endif /* MITKOPHIRPYRO_H_HEADER_INCLUDED */ diff --git a/Modules/US/USControlInterfaces/mitkUSDiPhASDeviceCustomControls.cpp b/Modules/US/USControlInterfaces/mitkUSDiPhASDeviceCustomControls.cpp index e78a56df9a..09948886cd 100644 --- a/Modules/US/USControlInterfaces/mitkUSDiPhASDeviceCustomControls.cpp +++ b/Modules/US/USControlInterfaces/mitkUSDiPhASDeviceCustomControls.cpp @@ -1,206 +1,203 @@ /*=================================================================== 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 "mitkUSDiPhASDeviceCustomControls.h" mitk::USDiPhASDeviceCustomControls::USDiPhASDeviceCustomControls(itk::SmartPointer device) : mitk::USAbstractControlInterface(device.GetPointer()), m_IsActive(false), silentUpdate(false) { } mitk::USDiPhASDeviceCustomControls::~USDiPhASDeviceCustomControls() { } void mitk::USDiPhASDeviceCustomControls::SetIsActive(bool isActive) { m_IsActive = isActive; } bool mitk::USDiPhASDeviceCustomControls::GetIsActive() { return m_IsActive; } -void mitk::USDiPhASDeviceCustomControls::passGUIOut(std::function /*callback*/) {} - void mitk::USDiPhASDeviceCustomControls::SetSilentUpdate(bool silent) { silentUpdate = silent; } bool mitk::USDiPhASDeviceCustomControls::GetSilentUpdate() { return silentUpdate; } +void mitk::USDiPhASDeviceCustomControls::SetSavingName(std::string name) +{ + this->OnSetSavingName(name); +} + //Set Functions void mitk::USDiPhASDeviceCustomControls::SetCompensateEnergy(bool compensate) { this->OnSetCompensateEnergy(compensate); } void mitk::USDiPhASDeviceCustomControls::SetUseBModeFilter(bool isSet) { this->OnSetUseBModeFilter(isSet); } void mitk::USDiPhASDeviceCustomControls::SetRecord(bool record) { this->OnSetRecord(record); } void mitk::USDiPhASDeviceCustomControls::SetVerticalSpacing(float mm) { this->OnSetVerticalSpacing(mm); } void mitk::USDiPhASDeviceCustomControls::SetScatteringCoefficient(float coeff) { this->OnSetScatteringCoefficient(coeff); } void mitk::USDiPhASDeviceCustomControls::SetCompensateScattering(bool compensate) { this->OnSetCompensateScattering(compensate); } void mitk::USDiPhASDeviceCustomControls::SetSavingSettings(SavingSettings settings) { this->OnSetSavingSettings(settings); } //Transmit void mitk::USDiPhASDeviceCustomControls::SetTransmitPhaseLength(double us) { this->OnSetTransmitPhaseLength(us); } void mitk::USDiPhASDeviceCustomControls::SetExcitationFrequency(double MHz) { this->OnSetExcitationFrequency(MHz); } void mitk::USDiPhASDeviceCustomControls::SetTransmitEvents(int events) { this->OnSetTransmitEvents(events); } void mitk::USDiPhASDeviceCustomControls::SetVoltage(int voltage) { this->OnSetVoltage(voltage); } void mitk::USDiPhASDeviceCustomControls::SetMode(bool interleaved) { this->OnSetMode(interleaved); } //Receive void mitk::USDiPhASDeviceCustomControls::SetScanDepth(double mm) { this->OnSetScanDepth(mm); } void mitk::USDiPhASDeviceCustomControls::SetAveragingCount(int count) { this->OnSetAveragingCount(count); } -void mitk::USDiPhASDeviceCustomControls::SetTGCMin(int min) -{ - this->OnSetTGCMin(min); -} - -void mitk::USDiPhASDeviceCustomControls::SetTGCMax(int max) +void mitk::USDiPhASDeviceCustomControls::SetTGC(int* tgc) { - this->OnSetTGCMax(max); - + this->OnSetTGC(tgc); } void mitk::USDiPhASDeviceCustomControls::SetDataType(DataType type) { this->OnSetDataType(type); } //Beamforming void mitk::USDiPhASDeviceCustomControls::SetPitch(double mm) { this->OnSetPitch(mm); } void mitk::USDiPhASDeviceCustomControls::SetReconstructedSamples(int samples) { this->OnSetReconstructedSamples(samples); } void mitk::USDiPhASDeviceCustomControls::SetReconstructedLines(int lines) { this->OnSetReconstructedLines(lines); } void mitk::USDiPhASDeviceCustomControls::SetSpeedOfSound(int mps) { this->OnSetSpeedOfSound(mps); } //Bandpass void mitk::USDiPhASDeviceCustomControls::SetBandpassEnabled(bool bandpass) { this->OnSetBandpassEnabled(bandpass); } void mitk::USDiPhASDeviceCustomControls::SetLowCut(double MHz) { this->OnSetLowCut(MHz); } void mitk::USDiPhASDeviceCustomControls::SetHighCut(double MHz) { this->OnSetHighCut(MHz); } //OnSetDummies void mitk::USDiPhASDeviceCustomControls::OnSetCompensateEnergy(bool /*compensate*/) {} void mitk::USDiPhASDeviceCustomControls::OnSetUseBModeFilter(bool /*isSet*/) {} void mitk::USDiPhASDeviceCustomControls::OnSetRecord(bool /*record*/) {} void mitk::USDiPhASDeviceCustomControls::OnSetVerticalSpacing(float /*mm*/) {} void mitk::USDiPhASDeviceCustomControls::OnSetScatteringCoefficient(float /*coeff*/) {} void mitk::USDiPhASDeviceCustomControls::OnSetCompensateScattering(bool /*compensate*/) {} void mitk::USDiPhASDeviceCustomControls::OnSetSavingSettings(SavingSettings /*settings*/) {} +void mitk::USDiPhASDeviceCustomControls::OnSetSavingName(std::string name) {} //Transmit void mitk::USDiPhASDeviceCustomControls::OnSetTransmitPhaseLength(double /*ms*/) {} void mitk::USDiPhASDeviceCustomControls::OnSetExcitationFrequency(double /*MHz*/) {} void mitk::USDiPhASDeviceCustomControls::OnSetTransmitEvents(int /*events*/) {} void mitk::USDiPhASDeviceCustomControls::OnSetVoltage(int /*voltage*/) {} void mitk::USDiPhASDeviceCustomControls::OnSetMode(bool /*interleaved*/) {} //Receive void mitk::USDiPhASDeviceCustomControls::OnSetScanDepth(double /*mm*/) {} void mitk::USDiPhASDeviceCustomControls::OnSetAveragingCount(int /*count*/) {} -void mitk::USDiPhASDeviceCustomControls::OnSetTGCMin(int /*min*/) {} -void mitk::USDiPhASDeviceCustomControls::OnSetTGCMax(int /*max*/) {} +void mitk::USDiPhASDeviceCustomControls::OnSetTGC(int* /*tgc*/) {} void mitk::USDiPhASDeviceCustomControls::OnSetDataType(DataType /*type*/) {} //Beamforming void mitk::USDiPhASDeviceCustomControls::OnSetPitch(double /*mm*/) {} void mitk::USDiPhASDeviceCustomControls::OnSetReconstructedSamples(int /*samples*/) {} void mitk::USDiPhASDeviceCustomControls::OnSetReconstructedLines(int /*lines*/) {} void mitk::USDiPhASDeviceCustomControls::OnSetSpeedOfSound(int /*mps*/) {} //Bandpass void mitk::USDiPhASDeviceCustomControls::OnSetBandpassEnabled(bool /*bandpass*/) {} void mitk::USDiPhASDeviceCustomControls::OnSetLowCut(double /*MHz*/) {} void mitk::USDiPhASDeviceCustomControls::OnSetHighCut(double /*MHz*/) {} diff --git a/Modules/US/USControlInterfaces/mitkUSDiPhASDeviceCustomControls.h b/Modules/US/USControlInterfaces/mitkUSDiPhASDeviceCustomControls.h index 1843d4f7b4..17d575a51f 100644 --- a/Modules/US/USControlInterfaces/mitkUSDiPhASDeviceCustomControls.h +++ b/Modules/US/USControlInterfaces/mitkUSDiPhASDeviceCustomControls.h @@ -1,144 +1,143 @@ /*=================================================================== 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 MITKUSDiPhASDeviceCustomControls_H_HEADER_INCLUDED_ #define MITKUSDiPhASDeviceCustomControls_H_HEADER_INCLUDED_ #include "mitkUSAbstractControlInterface.h" #include "mitkUSImageVideoSource.h" #include "mitkUSDevice.h" #include #include #include namespace mitk { /** * \brief Custom controls for mitk::USDiPhASDevice. */ class MITKUS_EXPORT USDiPhASDeviceCustomControls : public USAbstractControlInterface { public: mitkClassMacro(USDiPhASDeviceCustomControls, USAbstractControlInterface); mitkNewMacro1Param(Self, itk::SmartPointer); /** * Activate or deactivate the custom controls. This is just for handling * widget visibility in a GUI for example. */ void SetIsActive( bool isActive ) override; enum DataType { Image_uChar, Beamformed_Short }; struct SavingSettings { bool saveRaw; bool saveBeamformed; }; /** * \return if this custom controls are currently activated */ bool GetIsActive( ) override; virtual void SetCompensateEnergy(bool compensate); virtual void SetUseBModeFilter(bool isSet); virtual void SetVerticalSpacing(float mm); virtual void SetRecord(bool record); virtual void SetScatteringCoefficient(float coeff); virtual void SetCompensateScattering(bool compensate); virtual void SetSavingSettings(SavingSettings settings); + virtual void SetSavingName(std::string name); //Transmit virtual void SetTransmitPhaseLength(double us); virtual void SetExcitationFrequency(double MHz); virtual void SetTransmitEvents(int events); virtual void SetVoltage(int voltage); virtual void SetMode(bool interleaved); //Receive virtual void SetScanDepth(double mm); virtual void SetAveragingCount(int count); - virtual void SetTGCMin(int min); - virtual void SetTGCMax(int max); + virtual void SetTGC(int* tgc); virtual void SetDataType(DataType type); //Beamforming virtual void SetPitch(double mm); virtual void SetReconstructedSamples(int samples); virtual void SetReconstructedLines(int lines); virtual void SetSpeedOfSound(int mps); //Bandpass virtual void SetBandpassEnabled(bool bandpass); virtual void SetLowCut(double MHz); virtual void SetHighCut(double MHz); - virtual void passGUIOut(std::function /*callback*/); virtual void SetSilentUpdate(bool silent); virtual bool GetSilentUpdate(); protected: /** * Class needs an mitk::USDevice object for beeing constructed. */ USDiPhASDeviceCustomControls( itk::SmartPointer device ); ~USDiPhASDeviceCustomControls( ) override; bool m_IsActive; USImageVideoSource::Pointer m_ImageSource; bool silentUpdate; /** virtual handlers implemented in Device Controls */ virtual void OnSetCompensateEnergy(bool /*compensate*/); virtual void OnSetSavingSettings(SavingSettings /*settings*/); virtual void OnSetUseBModeFilter(bool /*isSet*/); virtual void OnSetRecord(bool /*record*/); virtual void OnSetVerticalSpacing(float /*mm*/); virtual void OnSetScatteringCoefficient(float /*coeff*/); virtual void OnSetCompensateScattering(bool /*compensate*/); + virtual void OnSetSavingName(std::string name); //Transmit virtual void OnSetTransmitPhaseLength(double /*us*/); virtual void OnSetExcitationFrequency(double /*MHz*/); virtual void OnSetTransmitEvents(int /*events*/); virtual void OnSetVoltage(int /*voltage*/); virtual void OnSetMode(bool /*interleaved*/); //Receive virtual void OnSetScanDepth(double /*mm*/); virtual void OnSetAveragingCount(int /*count*/); - virtual void OnSetTGCMin(int /*min*/); - virtual void OnSetTGCMax(int /*max*/); + virtual void OnSetTGC(int* /*tgc*/); virtual void OnSetDataType(DataType /*type*/); //Beamforming virtual void OnSetPitch(double /*mm*/); virtual void OnSetReconstructedSamples(int /*samples*/); virtual void OnSetReconstructedLines(int /*lines*/); virtual void OnSetSpeedOfSound(int /*mps*/); //Bandpass virtual void OnSetBandpassEnabled(bool /*bandpass*/); virtual void OnSetLowCut(double /*MHz*/); virtual void OnSetHighCut(double /*MHz*/); }; } // namespace mitk #endif // MITKUSDiPhASDeviceCustomControls_H_HEADER_INCLUDED_ diff --git a/Modules/US/USHardwareDiPhAS/mitkUSDiPhASCustomControls.cpp b/Modules/US/USHardwareDiPhAS/mitkUSDiPhASCustomControls.cpp index adab35c481..19f4dbb583 100644 --- a/Modules/US/USHardwareDiPhAS/mitkUSDiPhASCustomControls.cpp +++ b/Modules/US/USHardwareDiPhAS/mitkUSDiPhASCustomControls.cpp @@ -1,228 +1,215 @@ /*=================================================================== 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 "mitkUSDiPhASCustomControls.h" #include mitk::USDiPhASCustomControls::USDiPhASCustomControls(USDiPhASDevice* device) : mitk::USDiPhASDeviceCustomControls(device), m_IsActive(false), m_device(device), currentBeamformingAlgorithm((int)Beamforming::PlaneWaveCompound) { } mitk::USDiPhASCustomControls::~USDiPhASCustomControls() { } void mitk::USDiPhASCustomControls::SetIsActive(bool isActive) { m_IsActive = isActive; } bool mitk::USDiPhASCustomControls::GetIsActive() { return m_IsActive; } - -void mitk::USDiPhASCustomControls::passGUIOut(std::function callback) -{ - mitk::USDiPhASImageSource* imageSource = dynamic_cast(m_device->GetUSImageSource().GetPointer()); - callback("initializing"); - imageSource->SetGUIOutput(callback); -} - // OnSet methods void mitk::USDiPhASCustomControls::OnSetCompensateEnergy(bool compensate) { mitk::USDiPhASImageSource* imageSource = dynamic_cast(m_device->GetUSImageSource().GetPointer()); imageSource->ModifyEnergyCompensation(compensate); } void mitk::USDiPhASCustomControls::OnSetUseBModeFilter(bool isSet) { mitk::USDiPhASImageSource* imageSource = dynamic_cast(m_device->GetUSImageSource().GetPointer()); imageSource->ModifyUseBModeFilter(isSet); } void mitk::USDiPhASCustomControls::OnSetRecord(bool record) { mitk::USDiPhASImageSource* imageSource = dynamic_cast(m_device->GetUSImageSource().GetPointer()); imageSource->SetRecordingStatus(record); } void mitk::USDiPhASCustomControls::OnSetVerticalSpacing(float mm) { mitk::USDiPhASImageSource* imageSource = dynamic_cast(m_device->GetUSImageSource().GetPointer()); imageSource->SetVerticalSpacing(mm); } void mitk::USDiPhASCustomControls::OnSetScatteringCoefficient(float coeff) { mitk::USDiPhASImageSource* imageSource = dynamic_cast(m_device->GetUSImageSource().GetPointer()); imageSource->ModifyScatteringCoefficient(coeff); } void mitk::USDiPhASCustomControls::OnSetCompensateScattering(bool compensate) { mitk::USDiPhASImageSource* imageSource = dynamic_cast(m_device->GetUSImageSource().GetPointer()); imageSource->ModifyCompensateForScattering(compensate); } void mitk::USDiPhASCustomControls::OnSetSavingSettings(SavingSettings settings) { mitk::USDiPhASImageSource* imageSource = dynamic_cast(m_device->GetUSImageSource().GetPointer()); imageSource->SetSavingSettings(settings); } +void mitk::USDiPhASCustomControls::OnSetSavingName(std::string name) +{ + mitk::USDiPhASImageSource* imageSource = dynamic_cast(m_device->GetUSImageSource().GetPointer()); + imageSource->SetSavingName(name); +} + //Transmit void mitk::USDiPhASCustomControls::OnSetTransmitPhaseLength(double us) { m_device->GetScanMode().transmitPhaseLengthSeconds = us/1000000; m_device->UpdateScanmode(); } void mitk::USDiPhASCustomControls::OnSetExcitationFrequency(double MHz) { m_device->SetBursts(round(((120 / MHz) - 2) / 2)); m_device->UpdateScanmode(); // b = (c/f - 2) * 1/2, where c is the internal clock, f the wanted frequency, b the burst count } void mitk::USDiPhASCustomControls::OnSetTransmitEvents(int events) { m_device->GetScanMode().transmitEventsCount = events; m_device->UpdateScanmode(); } void mitk::USDiPhASCustomControls::OnSetVoltage(int voltage) { m_device->GetScanMode().voltageV = voltage; m_device->UpdateScanmode(); } void mitk::USDiPhASCustomControls::OnSetMode(bool interleaved) { m_device->SetInterleaved(interleaved); m_device->UpdateScanmode(); } //Receive void mitk::USDiPhASCustomControls::OnSetScanDepth(double mm) { auto& scanMode = m_device->GetScanMode(); float time = 2 * (0.001 * (mm)) / scanMode.averageSpeedOfSound; float timeInMicroSeconds = floor(time *1e6); // this is necessary because sub-microsecond accuracy causes undefined behaviour m_device->GetScanMode().receivePhaseLengthSeconds = timeInMicroSeconds*1e-6; m_device->UpdateScanmode(); } void mitk::USDiPhASCustomControls::OnSetAveragingCount(int count) { m_device->GetScanMode().averagingCount = count; m_device->UpdateScanmode(); } -void mitk::USDiPhASCustomControls::OnSetTGCMin(int min) -{ - auto& scanMode = m_device->GetScanMode(); - char range = scanMode.tgcdB[7] - min; - for (int tgc = 0; tgc < 7; ++tgc) - scanMode.tgcdB[tgc] = round(tgc*range / 7 + min); - - m_device->UpdateScanmode(); -} - -void mitk::USDiPhASCustomControls::OnSetTGCMax(int max) +void mitk::USDiPhASCustomControls::OnSetTGC(int* tgc) { auto& scanMode = m_device->GetScanMode(); - char range = max - scanMode.tgcdB[0]; - for (int tgc = 1; tgc < 8; ++tgc) - scanMode.tgcdB[tgc] = round(tgc*range / 7 + scanMode.tgcdB[0]); + for (int i = 0; i < 8; ++i) + scanMode.tgcdB[i] = tgc[i]; m_device->UpdateScanmode(); } void mitk::USDiPhASCustomControls::OnSetDataType(DataType type) { auto& scanMode = m_device->GetScanMode(); auto imageSource = dynamic_cast(m_device->GetUSImageSource().GetPointer()); switch (type) { case DataType::Image_uChar : { scanMode.transferBeamformedData = false; scanMode.transferImageData = true; m_device->UpdateScanmode(); imageSource->ModifyDataType(DataType::Image_uChar); break; } case DataType::Beamformed_Short : { scanMode.transferBeamformedData = true; scanMode.transferImageData = false; m_device->UpdateScanmode(); imageSource->ModifyDataType(DataType::Beamformed_Short); break; } default: MITK_INFO << "Unknown Data Type requested"; break; } } // 0= image; 1= beamformed //Beamforming void mitk::USDiPhASCustomControls::OnSetPitch(double mm) { m_device->GetScanMode().reconstructedLinePitchMmOrAngleDegree = mm; m_device->UpdateScanmode(); } void mitk::USDiPhASCustomControls::OnSetReconstructedSamples(int samples) { m_device->GetScanMode().reconstructionSamplesPerLine = samples; m_device->UpdateScanmode(); } void mitk::USDiPhASCustomControls::OnSetReconstructedLines(int lines) { m_device->GetScanMode().reconstructionLines = lines; m_device->UpdateScanmode(); } void mitk::USDiPhASCustomControls::OnSetSpeedOfSound(int mps) { m_device->GetScanMode().averageSpeedOfSound = mps; m_device->SetInterleaved(m_device->IsInterleaved()); //update transmit parameters m_device->UpdateScanmode(); } //Bandpass void mitk::USDiPhASCustomControls::OnSetBandpassEnabled(bool bandpass) { m_device->GetScanMode().bandpassApply = bandpass; m_device->UpdateScanmode(); } void mitk::USDiPhASCustomControls::OnSetLowCut(double MHz) { m_device->GetScanMode().bandpassFrequencyLowHz = MHz*1000*1000; m_device->UpdateScanmode(); } void mitk::USDiPhASCustomControls::OnSetHighCut(double MHz) { m_device->GetScanMode().bandpassFrequencyHighHz = MHz*1000*1000; m_device->UpdateScanmode(); } \ No newline at end of file diff --git a/Modules/US/USHardwareDiPhAS/mitkUSDiPhASCustomControls.h b/Modules/US/USHardwareDiPhAS/mitkUSDiPhASCustomControls.h index 5d3aea0c58..e9d8e6fb01 100644 --- a/Modules/US/USHardwareDiPhAS/mitkUSDiPhASCustomControls.h +++ b/Modules/US/USHardwareDiPhAS/mitkUSDiPhASCustomControls.h @@ -1,106 +1,104 @@ /*=================================================================== 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 MITKUSDiPhASCustomControls_H_HEADER_INCLUDED_ #define MITKUSDiPhASCustomControls_H_HEADER_INCLUDED_ #include "mitkUSDiPhASDeviceCustomControls.h" #include "mitkUSDevice.h" #include "mitkUSDiPhASDevice.h" #include "Framework.IBMT.US.CWrapper.h" #include namespace mitk { /** * \brief Custom controls for mitk::USDiPhASDevice. */ class USDiPhASDevice; class USDiPhASCustomControls : public USDiPhASDeviceCustomControls { public: mitkClassMacro(USDiPhASCustomControls, USAbstractControlInterface); mitkNewMacro1Param(Self, mitk::USDiPhASDevice*); typedef USDiPhASDeviceCustomControls::DataType DataType; typedef USDiPhASDeviceCustomControls::SavingSettings SavingSettings; /** * Activate or deactivate the custom controls. This is just for handling * widget visibility in a GUI for example. */ virtual void SetIsActive( bool isActive ) override; /** * \return if this custom controls are currently activated */ virtual bool GetIsActive( ) override; - virtual void passGUIOut(std::function callback) override; - BeamformingParametersPlaneWaveCompound parametersPW; BeamformingParametersInterleaved_OA_US parametersOSUS; protected: /** * Class needs an mitk::USDiPhASDevice object for beeing constructed. * This object's ScanMode will be manipulated by the custom controls methods. */ USDiPhASCustomControls(USDiPhASDevice* device); virtual ~USDiPhASCustomControls( ); bool m_IsActive; USImageVideoSource::Pointer m_ImageSource; USDiPhASDevice* m_device; int currentBeamformingAlgorithm; /** handlers for value changes */ virtual void OnSetCompensateEnergy(bool compensate) override; virtual void OnSetUseBModeFilter(bool isSet) override; virtual void OnSetRecord(bool record) override; virtual void OnSetVerticalSpacing(float mm) override; virtual void OnSetScatteringCoefficient(float coeff) override; virtual void OnSetCompensateScattering(bool compensate) override; virtual void OnSetSavingSettings(SavingSettings settings) override; + virtual void OnSetSavingName(std::string name) override; //Transmit virtual void OnSetTransmitPhaseLength(double us) override; virtual void OnSetExcitationFrequency(double MHz) override; virtual void OnSetTransmitEvents(int events) override; virtual void OnSetVoltage(int voltage) override; virtual void OnSetMode(bool interleaved) override; //Receive virtual void OnSetScanDepth(double mm) override; virtual void OnSetAveragingCount(int count) override; - virtual void OnSetTGCMin(int min) override; - virtual void OnSetTGCMax(int max) override; + virtual void OnSetTGC(int* tgc) override; virtual void OnSetDataType(DataType type) override; //Beamforming virtual void OnSetPitch(double mm) override; virtual void OnSetReconstructedSamples(int samples) override; virtual void OnSetReconstructedLines(int lines) override; virtual void OnSetSpeedOfSound(int mps) override; //Bandpass virtual void OnSetBandpassEnabled(bool bandpass) override; virtual void OnSetLowCut(double MHz) override; virtual void OnSetHighCut(double MHz) override; }; } // namespace mitk #endif // MITKUSDiPhASCustomControls_H_HEADER_INCLUDED_ \ No newline at end of file diff --git a/Modules/US/USHardwareDiPhAS/mitkUSDiPhASDevice.cpp b/Modules/US/USHardwareDiPhAS/mitkUSDiPhASDevice.cpp index 5cb68f1dcd..bdd8d914aa 100644 --- a/Modules/US/USHardwareDiPhAS/mitkUSDiPhASDevice.cpp +++ b/Modules/US/USHardwareDiPhAS/mitkUSDiPhASDevice.cpp @@ -1,307 +1,308 @@ /*=================================================================== 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 "mitkUSDiPhASDevice.h" #include "mitkUSDiPhASCustomControls.h" mitk::USDiPhASDevice::USDiPhASDevice(std::string manufacturer, std::string model) : mitk::USDevice(manufacturer, model), m_ControlsProbes(mitk::USDiPhASProbesControls::New(this)), m_ImageSource(mitk::USDiPhASImageSource::New(this)), m_ControlInterfaceCustom(mitk::USDiPhASCustomControls::New(this)), m_IsRunning(false), m_BurstHalfwaveClockCount(7), m_Interleaved(true) { m_NumberOfOutputs = 2; this->SetNumberOfIndexedOutputs(m_NumberOfOutputs); SetNthOutput(0, this->MakeOutput(0)); SetNthOutput(1, this->MakeOutput(1)); } mitk::USDiPhASDevice::~USDiPhASDevice() { + } //Gets std::string mitk::USDiPhASDevice::GetDeviceClass() { return "org.mitk.modules.us.USDiPhASDevice"; } mitk::USControlInterfaceProbes::Pointer mitk::USDiPhASDevice::GetControlInterfaceProbes() { return m_ControlsProbes.GetPointer(); }; mitk::USAbstractControlInterface::Pointer mitk::USDiPhASDevice::GetControlInterfaceCustom() { return m_ControlInterfaceCustom.GetPointer(); } mitk::USImageSource::Pointer mitk::USDiPhASDevice::GetUSImageSource() { return m_ImageSource.GetPointer(); } ScanModeNative& mitk::USDiPhASDevice::GetScanMode() { return m_ScanMode; } // Setup and Cleanup bool mitk::USDiPhASDevice::OnInitialization() { return true; } //---------------------------------------------------------------------------------------------------------------------------- /* ugly wrapper stuff - find better solution so it isn't necessary to create a global pointer to USDiPhASDevice... * passing a lambda function would be nicer - sadly something goes wrong when passing the adress of a lambda function: * the API produces access violations. Passing the Lambda function itself would be preferable, but that's not possible */ mitk::USDiPhASDevice* w_device; mitk::USDiPhASImageSource* w_ISource; void WrapperMessageCallback(const char* message) { w_device->MessageCallback(message); } void WrapperImageDataCallback( short* rfDataChannelData, int channelDatalinesPerDataset, int channelDataSamplesPerChannel, int channelDataTotalDatasets, short* rfDataArrayBeamformed, int beamformedLines, int beamformedSamples, int beamformedTotalDatasets, unsigned char* imageData, int imageWidth, int imageHeight, int imagePixelFormat, int imageSetsTotal, double timeStamp) { w_ISource->ImageDataCallback( rfDataChannelData, channelDatalinesPerDataset, channelDataSamplesPerChannel, channelDataTotalDatasets, rfDataArrayBeamformed, beamformedLines, beamformedSamples, beamformedTotalDatasets, imageData, imageWidth, imageHeight, imagePixelFormat, imageSetsTotal, timeStamp); } //---------------------------------------------------------------------------------------------------------------------------- bool mitk::USDiPhASDevice::OnConnection() { w_device = this; w_ISource = m_ImageSource; // Need those pointers for the forwarders to call member functions; createBeamformer expects non-member function pointers. createBeamformer((StringMessageCallback)&WrapperMessageCallback, (NewDataCallback)&WrapperImageDataCallback); InitializeScanMode(); initBeamformer(); //start the hardware connection m_ImageSource->UpdateImageGeometry(); //make sure the image geometry is initialized! // pass the new scanmode to the device: setupScan(this->m_ScanMode); return true; } bool mitk::USDiPhASDevice::OnDisconnection() { //close the beamformer so hardware is disconnected closeBeamformer(); return true; } bool mitk::USDiPhASDevice::OnActivation() { // probe controls are available now m_ControlsProbes->SetIsActive(true); if (m_ControlsProbes->GetProbesCount() < 1) { MITK_WARN("USDevice")("USDiPhASDevice") << "No probe found."; return false; } m_ControlsProbes->SelectProbe(0); // toggle the beamformer of the API if(!m_IsRunning) m_IsRunning=toggleFreeze(); return true; } bool mitk::USDiPhASDevice::OnDeactivation() { if(m_IsRunning) m_IsRunning=toggleFreeze(); return true; } void mitk::USDiPhASDevice::OnFreeze(bool freeze) { if(m_IsRunning==freeze) m_IsRunning=toggleFreeze(); // toggleFreeze() returns true if it starts running the beamformer, otherwise false } void mitk::USDiPhASDevice::UpdateScanmode() { OnFreeze(true); SetInterleaved(m_Interleaved); // update the beamforming parameters... UpdateTransmitEvents(); if (!(dynamic_cast(this->m_ControlInterfaceCustom.GetPointer())->GetSilentUpdate())) { setupScan(this->m_ScanMode); m_ImageSource->UpdateImageGeometry(); } OnFreeze(false); } void mitk::USDiPhASDevice::UpdateTransmitEvents() { int numChannels = m_ScanMode.reconstructionLines; // transmitEventsCount defines only the number of acoustic measurements (angles); there will be one event added to the start for OA measurement m_ScanMode.TransmitEvents = new TransmitEventNative[m_ScanMode.transmitEventsCount]; for (int ev = 0; ev < m_ScanMode.transmitEventsCount; ++ev) { m_ScanMode.TransmitEvents[ev].transmitEventDelays = new float[numChannels]; m_ScanMode.TransmitEvents[ev].BurstHalfwaveClockCountPerChannel = new int[numChannels]; m_ScanMode.TransmitEvents[ev].BurstCountPerChannel = new int[numChannels]; m_ScanMode.TransmitEvents[ev].BurstUseNegativePolarityPerChannel = new bool[numChannels]; m_ScanMode.TransmitEvents[ev].ChannelMultiplexerSetups = nullptr; float tiltStrength = ((m_ScanMode.transmitEventsCount - 1) / 2 - ev) * 20e-9f; for (int i = 0; i < numChannels; ++i) { m_ScanMode.TransmitEvents[ev].BurstHalfwaveClockCountPerChannel[i] = m_BurstHalfwaveClockCount; // 120 MHz / (2 * (predefinedBurstHalfwaveClockCount + 1)) --> 7.5 MHz m_ScanMode.TransmitEvents[ev].BurstCountPerChannel[i] = 1; // Burst with 1 cycle m_ScanMode.TransmitEvents[ev].BurstUseNegativePolarityPerChannel[i] = true; m_ScanMode.TransmitEvents[ev].transmitEventDelays[i] = 2e-6f + (i - numChannels / 2) * tiltStrength; } } m_ScanMode.transmitSequenceCount = 1; m_ScanMode.transmitSequences = new SequenceNative[m_ScanMode.transmitSequenceCount]; m_ScanMode.transmitSequences[0].startEvent = 0; m_ScanMode.transmitSequences[0].endEvent = m_ScanMode.transmitEventsCount; } void mitk::USDiPhASDevice::InitializeScanMode() { // create a scanmode to be used for measurements: m_ScanMode.scanModeName = "InterleavedMode"; // configure a linear transducer m_ScanMode.transducerName = "L5-10"; m_ScanMode.transducerCurvedRadiusMeter = 0; m_ScanMode.transducerElementCount = 128; m_ScanMode.transducerFrequencyHz = 7500000; m_ScanMode.transducerPitchMeter = 0.0003f; m_ScanMode.transducerType = 1; // configure the receive paramters: m_ScanMode.receivePhaseLengthSeconds = 185e-6f; // about 15 cm imaging depth m_ScanMode.tgcdB = new unsigned char[8]; for (int tgc = 0; tgc < 8; ++tgc) m_ScanMode.tgcdB[tgc] = tgc * 2 + 10; m_ScanMode.accumulation = 1; m_ScanMode.bandpassApply = false; m_ScanMode.averagingCount = 1; // configure general processing: m_ScanMode.transferChannelData = true; // configure reconstruction processing: m_ScanMode.averageSpeedOfSound = 1540; m_ScanMode.computeBeamforming = true; // setup beamforming parameters: SetInterleaved(true); m_ScanMode.reconstructedLinePitchMmOrAngleDegree = 0.3f; m_ScanMode.reconstructionLines = 128; m_ScanMode.reconstructionSamplesPerLine = 2048; m_ScanMode.transferBeamformedData = true; // configure the transmit sequence(s): m_ScanMode.transmitEventsCount = 1; m_ScanMode.transmitPhaseLengthSeconds = 1e-6f; m_ScanMode.voltageV = 75; UpdateTransmitEvents(); // configure bandpass: m_ScanMode.bandpassApply = false; m_ScanMode.bandpassFrequencyLowHz = 1e6f; m_ScanMode.bandpassFrequencyHighHz = 20e6f; // configure image generation: m_ScanMode.imageWidth = 512; m_ScanMode.imageHeight = 512; m_ScanMode.imageMultiplier = 1; m_ScanMode.imageLeveling = 0; m_ScanMode.transferImageData = false; // Trigger setup: m_ScanMode.triggerSetup.enabled = true; m_ScanMode.triggerSetup.constantPulseRepetitionRateHz = 20; m_ScanMode.triggerSetup.triggerWidthMicroseconds = 15; - m_ScanMode.triggerSetup.delayTrigger2Microseconds = 300; + m_ScanMode.triggerSetup.delayTrigger2Microseconds = 290; } // callback for the DiPhAS API void mitk::USDiPhASDevice::MessageCallback(const char* message) { MITK_INFO << "DiPhAS API: " << message << '\n'; } void mitk::USDiPhASDevice::SetBursts(int bursts) { m_BurstHalfwaveClockCount = bursts; } bool mitk::USDiPhASDevice::IsInterleaved() { return m_Interleaved; } void mitk::USDiPhASDevice::SetInterleaved(bool interleaved) { m_Interleaved = interleaved; if (interleaved) { m_ScanMode.scanModeName = "Interleaved Beamforming Mode"; m_CurrentBeamformingAlgorithm = Beamforming::Interleaved_OA_US; paramsInterleaved.SpeedOfSoundMeterPerSecond = m_ScanMode.averageSpeedOfSound; paramsInterleaved.angleSkipFactor = 1; paramsInterleaved.OptoacousticDelay = 0.0000003; // 300ns paramsInterleaved.filter = Filter::None; m_ScanMode.beamformingAlgorithmParameters = ¶msInterleaved; } else { m_ScanMode.scanModeName = "Plane Wave Beamforming Mode"; m_CurrentBeamformingAlgorithm = Beamforming::PlaneWaveCompound; paramsPlaneWave.SpeedOfSoundMeterPerSecond = m_ScanMode.averageSpeedOfSound; paramsPlaneWave.angleSkipFactor = 0; paramsPlaneWave.usePhaseCoherence = 0; m_ScanMode.beamformingAlgorithmParameters = ¶msPlaneWave; } m_ScanMode.beamformingAlgorithm = m_CurrentBeamformingAlgorithm; } \ No newline at end of file diff --git a/Modules/US/USHardwareDiPhAS/mitkUSDiPhASImageSource.cpp b/Modules/US/USHardwareDiPhAS/mitkUSDiPhASImageSource.cpp index 789a181a45..5b565556d4 100644 --- a/Modules/US/USHardwareDiPhAS/mitkUSDiPhASImageSource.cpp +++ b/Modules/US/USHardwareDiPhAS/mitkUSDiPhASImageSource.cpp @@ -1,931 +1,1035 @@ /*=================================================================== 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 "mitkUSDiPhASDevice.h" #include "mitkUSDiPhASImageSource.h" #include #include "mitkUSDiPhASBModeImageFilter.h" #include "ITKUltrasound/itkBModeImageFilter.h" #include "mitkImageCast.h" #include "mitkITKImageImport.h" +#include // itk dependencies #include "itkImage.h" #include "itkResampleImageFilter.h" #include "itkCastImageFilter.h" #include "itkCropImageFilter.h" #include "itkRescaleIntensityImageFilter.h" #include "itkIntensityWindowingImageFilter.h" #include #include "itkMultiplyImageFilter.h" mitk::USDiPhASImageSource::USDiPhASImageSource(mitk::USDiPhASDevice* device) : m_Device(device), m_StartTime(((float)std::clock()) / CLOCKS_PER_SEC), - m_UseGUIOutPut(false), m_DataType(DataType::Image_uChar), - m_GUIOutput(nullptr), m_UseBModeFilter(false), m_CurrentlyRecording(false), m_DataTypeModified(true), m_DataTypeNext(DataType::Image_uChar), - m_CurrentImageTimestamp(0), + m_RecordedImageCounter(0), m_PyroConnected(false), - m_ImageTimestampBuffer(), 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_CompensateEnergyModified(false), + m_BatchSize(24), // why? because magic number, thats why! + m_SavingName("-replace"), + m_BufferBatches(12), // we like magic numbers + m_LastImage(0) { - m_BufferSize = 100; - m_ImageTimestampBuffer.insert(m_ImageTimestampBuffer.begin(), m_BufferSize, 0); - m_LastWrittenImage = m_BufferSize - 1; - m_ImageBuffer.insert(m_ImageBuffer.begin(), m_BufferSize, nullptr); + + // 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 (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::GetNextRawImage(std::vector& imageVector) +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; } +} - if (imageVector.size() != 2) - { - imageVector.resize(2); - } +void mitk::USDiPhASImageSource::ResizeFluenceImage(mitk::Vector3D spacing, unsigned int* dimensions) +{ + auto curResizeImage = ApplyResampling(m_FluenceCompOriginal.at(m_ScatteringCoefficient), spacing, dimensions); - // make sure image is nullptr - mitk::Image::Pointer image = nullptr; - float ImageEnergyValue = 0; + 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 (int i = 100; i > 90 && ImageEnergyValue <= 0; --i) + for (unsigned int i = 0; i < lowerBound && i < imageSize; ++i) { - if (m_ImageTimestampBuffer[(m_LastWrittenImage + i) % 100] != 0) - { - ImageEnergyValue = m_Pyro->GetClosestEnergyInmJ(m_ImageTimestampBuffer[(m_LastWrittenImage + i) % 100]); - if (ImageEnergyValue > 0) { - image = &(*m_ImageBuffer[(m_LastWrittenImage + i) % 100]); - } - } + rawOutputData[i] = 0; // everything than cannot be compensated shall be treated as garbage, here the upper 0.15mm } - // if we did not get any usable Energy value, compensate using this default value - if (image == nullptr) + for (unsigned int i = lowerBound; i < upperBound && i < imageSize; ++i) { - image = &(*m_ImageBuffer[m_LastWrittenImage]); - ImageEnergyValue = 40; - if (image == nullptr) - return; + rawOutputData[i] = 1 / rawScatteringData[i - lowerBound]; } - - // do image processing before displaying it - if (image.IsNotNull()) + for (unsigned int i = upperBound; i < imageSize; ++i) { - itkFloatImageType::Pointer itkImage; - - mitk::CastToItkImage(image, itkImage); - image = mitk::GrabItkImageMemory(itkImage); //thereby using float images - image = CutOffTop(image, 165); - - // now apply filters to the image, if the options have been selected. - if ((m_CompensateForScattering || m_UseBModeFilter) && m_DataType == DataType::Beamformed_Short) - { - if (m_Device->GetScanMode().beamformingAlgorithm == Beamforming::PlaneWaveCompound) // this is for ultrasound only mode - { - if (m_UseBModeFilter) - { - image = ApplyBmodeFilter(image, true); - if (m_VerticalSpacing) - image = ResampleOutputVertical(image, m_VerticalSpacing); - } - } - - else - { - Image::Pointer imagePA = Image::New(); - unsigned int dim[] = { image->GetDimension(0),image->GetDimension(1),1}; - imagePA->Initialize(image->GetPixelType(), 3, dim); - imagePA->SetGeometry(image->GetGeometry()); + rawOutputData[i] = 0; // everything than cannot be compensated shall be treated as garbage + } - Image::Pointer imageUS = Image::New(); - imageUS->Initialize(image->GetPixelType(), 3, dim); - imageUS->SetGeometry(image->GetGeometry()); + unsigned int dim[] = { dimensions[0], dimensions[1], 1 }; + curResizeImage->Initialize(mitk::MakeScalarPixelType(), 3, dim); + curResizeImage->SetSpacing(spacing); + curResizeImage->SetSlice(rawOutputData); - ImageReadAccessor inputReadAccessorCopyPA(image, image->GetSliceData(0)); - imagePA->SetSlice(inputReadAccessorCopyPA.GetData(), 0); - ImageReadAccessor inputReadAccessorCopyUS(image, image->GetSliceData(1)); - imageUS->SetSlice(inputReadAccessorCopyUS.GetData(), 0); + mitk::CastToItkImage(curResizeImage, m_FluenceCompResizedItk.at(m_ScatteringCoefficient)); + m_FluenceCompResized.at(m_ScatteringCoefficient) = mitk::GrabItkImageMemory(m_FluenceCompResizedItk.at(m_ScatteringCoefficient)); - // first, seperate the PA image from the USImages + MITK_INFO << "Resized a fluence image."; - // then, we compensate the PAImage using our ImageEnergyValue - if(m_CompensateEnergy) - imagePA = MultiplyImage(imagePA, 1/ImageEnergyValue); // TODO: add the correct prefactor here!!!! + delete[] rawOutputData; +} - // now we apply the BModeFilter - if (m_UseBModeFilter) - { - imageUS = ApplyBmodeFilter(imageUS, true); // the US Images get a logarithmic filter - imagePA = ApplyBmodeFilter(imagePA, false); - } +void mitk::USDiPhASImageSource::GetNextRawImage(std::vector& imageVector) +{ + CheckModifiedVariables(); + if (imageVector.size() != 2) + { + imageVector.resize(2); + } - ImageReadAccessor inputReadAccessorPA(imagePA, imagePA->GetSliceData(0)); - image->SetSlice(inputReadAccessorPA.GetData(), 0); - ImageReadAccessor inputReadAccessorUS(imageUS, imageUS->GetSliceData(0)); - image->SetSlice(inputReadAccessorUS.GetData(), 1); - if (m_VerticalSpacing) - { - image = ResampleOutputVertical(image, m_VerticalSpacing); - } + if (m_LastImage == -1) + return; - // and finally the scattering corrections - if (m_CompensateForScattering) - { - auto curResizeImage = m_FluenceCompResized.at(m_ScatteringCoefficient); // just for convenience + // make sure image is nullptr + mitk::Image::Pointer imageUS = nullptr; + mitk::Image::Pointer imagePA = nullptr; + float ImageEnergyValue = 0; - // update the fluence reference images! - bool doResampling = image->GetDimension(0) != curResizeImage->GetDimension(0) || image->GetDimension(1) != curResizeImage->GetDimension(1) - || image->GetGeometry()->GetSpacing()[0] != curResizeImage->GetGeometry()->GetSpacing()[0] || image->GetGeometry()->GetSpacing()[1] != curResizeImage->GetGeometry()->GetSpacing()[1]; - if (doResampling) - { - curResizeImage = ApplyResampling(m_FluenceCompOriginal.at(m_ScatteringCoefficient), image->GetGeometry()->GetSpacing(), image->GetDimensions()); + imagePA = m_ImagesBuffer[m_LastBatch][m_LastImage].first; + imageUS = m_ImagesBuffer[m_LastBatch][m_LastImage].second; + ImageEnergyValue = 1; - double* rawOutputData = new double[image->GetDimension(0)*image->GetDimension(1)]; - double* rawScatteringData = (double*)curResizeImage->GetData(); - int sizeRawScatteringData = curResizeImage->GetDimension(0) * curResizeImage->GetDimension(1); - int imageSize = image->GetDimension(0)*image->GetDimension(1); + if (imagePA == nullptr || imageUS == nullptr || !imagePA->IsInitialized() || !imageUS->IsInitialized()) + return; - //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; - int lowerBound = std::round(upperCutoffmm / image->GetGeometry()->GetSpacing()[1])*image->GetDimension(0); - int upperBound = lowerBound + sizeRawScatteringData; + // 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 - for (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 (int i = lowerBound; i < upperBound && i < imageSize; ++i) - { - rawOutputData[i] = 1 / rawScatteringData[i-lowerBound]; - } - for (int i = upperBound; i < imageSize; ++i) - { - rawOutputData[i] = 0; // everything than cannot be compensated shall be treated as garbage - } + imagePA = CutOffTop(imagePA, 165); + if (m_CompensateEnergy && ImageEnergyValue != 1) + imagePA = MultiplyImage(imagePA, 1 / ImageEnergyValue); // TODO: add the correct prefactor here - unsigned int dim[] = { image->GetDimension(0), image->GetDimension(1), 1 }; - curResizeImage->Initialize(mitk::MakeScalarPixelType(), 3, dim); - curResizeImage->SetGeometry(image->GetGeometry()); - curResizeImage->SetSlice(rawOutputData,0); + if (m_UseBModeFilter) + imagePA = ApplyBmodeFilter(imagePA, false); - delete[] rawOutputData; + if (m_VerticalSpacing) + imagePA = ResampleOutputVertical(imagePA, m_VerticalSpacing); - mitk::CastToItkImage(curResizeImage, m_FluenceCompResizedItk.at(m_ScatteringCoefficient)); - m_FluenceCompResized.at(m_ScatteringCoefficient) = mitk::GrabItkImageMemory(m_FluenceCompResizedItk.at(m_ScatteringCoefficient)); + 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()); - MITK_INFO << "Resized a fluence image."; - } - // actually apply the scattering compensation - imagePA = ApplyScatteringCompensation(imagePA, m_ScatteringCoefficient); - ImageReadAccessor inputReadAccessorPA(imagePA, imagePA->GetSliceData(0)); - image->SetSlice(inputReadAccessorPA.GetData(), 0); - } - } + imagePA = ApplyScatteringCompensation(imagePA, m_ScatteringCoefficient); } + } + if(imageUS.IsNotNull() && m_DataType == DataType::Beamformed_Short) + { + itkFloatImageType::Pointer itkImage; - //TODO: completely rewrite this mess + mitk::CastToItkImage(imageUS, itkImage); + imageUS = mitk::GrabItkImageMemory(itkImage); //thereby using float images + imageUS = CutOffTop(imageUS, 165); - imageVector[0] = Image::New(); - unsigned int dim[] = { image->GetDimension(0),image->GetDimension(1),1 }; - imageVector[0]->Initialize(image->GetPixelType(), 3, dim); - imageVector[0]->SetGeometry(image->GetGeometry()); + if (m_UseBModeFilter) + imageUS = ApplyBmodeFilter(imageUS, true); // the US Images get a logarithmic filter - imageVector[1] = Image::New(); - imageVector[1]->Initialize(image->GetPixelType(), 3, dim); - imageVector[1]->SetGeometry(image->GetGeometry()); + if (m_VerticalSpacing) + imageUS = ResampleOutputVertical(imageUS, m_VerticalSpacing); - ImageReadAccessor inputReadAccessorCopyPA(image, image->GetSliceData(0)); - imageVector[0]->SetSlice(inputReadAccessorCopyPA.GetData(), 0); - ImageReadAccessor inputReadAccessorCopyUS(image, image->GetSliceData(1)); - imageVector[1]->SetSlice(inputReadAccessorCopyUS.GetData(), 0); } + imageVector[0] = imagePA; + imageVector[1] = imageUS; } 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 < itkFloatImageType, itkFloatImageType > BModeFilterType; BModeFilterType::Pointer bModeFilter = BModeFilterType::New(); // LogFilter typedef itk::PhotoacousticBModeImageFilter < itkFloatImageType, itkFloatImageType > 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 < itkFloatImageType, itkFloatImageType > 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 < itkFloatImageType, itkFloatImageType > 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 < itkFloatImageType, itkFloatImageType > 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[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) + 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) { - //get the timestamp we might save later on - m_CurrentImageTimestamp = std::chrono::high_resolution_clock::now().time_since_epoch().count(); + int currentBatch = -1; + int currentImage = -1; - // create a new image and initialize it - mitk::Image::Pointer image = mitk::Image::New(); + long long currentImageTimestamp = std::chrono::high_resolution_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 * std::pow(10, 6)) + { + MITK_ERROR << "Lost about " << (float)std::abs(currentImageTimestamp - m_ImageTimestampsBuffer[m_LastBatch][m_LastImage]) / (5.f*std::pow(10, 7)) - 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: { - m_ImageDimensions[0] = imageWidth; - m_ImageDimensions[1] = imageHeight; - m_ImageDimensions[2] = imageSetsTotal; - image->Initialize(mitk::MakeScalarPixelType(), 3, m_ImageDimensions); + 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 (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: { - m_ImageDimensions[0] = beamformedLines; - m_ImageDimensions[1] = beamformedSamples; - m_ImageDimensions[2] = beamformedTotalDatasets; - image->Initialize(mitk::MakeScalarPixelType(), 3, m_ImageDimensions); + 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 (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; } } - image->GetGeometry()->SetSpacing(m_ImageSpacing); - image->GetGeometry()->Modified(); - + // write the given buffer into the image switch (m_DataType) { case DataType::Image_uChar: { - for (unsigned char i = 0; i < imageSetsTotal; i++) { - image->SetSlice(&imageData[i*imageHeight*imageWidth], i); + 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: { - short* flipme = new short[beamformedLines*beamformedSamples*beamformedTotalDatasets]; - int pixelsPerImage = beamformedLines*beamformedSamples; + mitk::ImageWriteAccessor dataPAAccess(imagePA); + mitk::ImageWriteAccessor dataUSAccess(imageUS); - for (unsigned char currentSet = 0; currentSet < beamformedTotalDatasets; currentSet++) + 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 sample = 0; sample < beamformedSamples; sample++) + { + for (unsigned short line = 0; line < beamformedLines; line++) { - for (unsigned short line = 0; line < beamformedLines; line++) - { - flipme[sample*beamformedLines + line + pixelsPerImage*currentSet] - = rfDataArrayBeamformed[line*beamformedSamples + sample + pixelsPerImage*currentSet]; - } - } // the beamformed pa image is flipped by 90 degrees; we need to flip it manually + 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]; + } + } } - - for (unsigned char i = 0; i < beamformedTotalDatasets; i++) { - image->SetSlice(&flipme[i*beamformedLines*beamformedSamples], i); - // set every image to a different slice + 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]; + } + } } - delete[] flipme; break; } } if (m_SavingSettings.saveRaw && m_CurrentlyRecording && rfDataChannelData != nullptr) { - unsigned int dim[3]; - dim[0] = channelDataChannelsPerDataset; - dim[1] = channelDataSamplesPerChannel; - dim[2] = 1; - short offset = m_Device->GetScanMode().accumulation * 2048; - - short* noOffset = new short[channelDataChannelsPerDataset*channelDataSamplesPerChannel*channelDataTotalDatasets]; - for (unsigned char set = 0; set < 1; ++set)// channelDataTotalDatasets; ++set) // we ignore the raw US images for now + 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()) { - for (unsigned short sam = 0; sam < channelDataSamplesPerChannel; ++sam) - { - for (unsigned short chan = 0; chan < channelDataChannelsPerDataset; ++chan) - { - noOffset[set*channelDataSamplesPerChannel*channelDataChannelsPerDataset + sam * channelDataChannelsPerDataset + chan] = - rfDataChannelData[set*channelDataSamplesPerChannel*channelDataChannelsPerDataset + sam * channelDataChannelsPerDataset + chan] - offset; // this offset in the raw Images is given by the API... - } - } - } + 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; - // save the raw images when recording - for (unsigned char i = 0; i < 1; ++i)// channelDataTotalDatasets; ++i) // we ignore the raw US images for now - { - mitk::Image::Pointer rawImage = mitk::Image::New(); - rawImage->Initialize(mitk::MakeScalarPixelType(), 3, dim); + unsigned int imageDimensionsUS[3]; + imageDimensionsUS[0] = channelDataChannelsPerDataset; + imageDimensionsUS[1] = channelDataSamplesPerChannel; + imageDimensionsUS[2] = channelDataTotalDatasets - 1; - rawImage->SetSlice(&noOffset[i*channelDataChannelsPerDataset*channelDataSamplesPerChannel]); + short* imageData = new short[channelDataChannelsPerDataset*channelDataSamplesPerChannel]; // * (channelDataTotalDatasets - 1)]; - float& recordTime = m_Device->GetScanMode().receivePhaseLengthSeconds; - int& speedOfSound = m_Device->GetScanMode().averageSpeedOfSound; + 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); - mitk::Vector3D rawSpacing; - rawSpacing[0] = m_Device->GetScanMode().transducerPitchMeter * 1000; // save in mm - rawSpacing[1] = recordTime / channelDataSamplesPerChannel * 1000000; // save in us - rawSpacing[2] = 1; + m_RawImagesBuffer[b][i].first->SetSpacing(rawSpacing); + m_RawImagesBuffer[b][i].second->SetSpacing(rawSpacing); - rawImage->GetGeometry()->SetSpacing(rawSpacing); - rawImage->GetGeometry()->Modified(); + 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); + } + } - m_RawRecordedImages.push_back(rawImage); + delete[] imageData; + + rawImagePA = m_RawImagesBuffer[currentBatch][currentImage].first; + rawImageUS = m_RawImagesBuffer[currentBatch][currentImage].second; } - delete[] noOffset; - } + short offset = m_Device->GetScanMode().accumulation * 2048; - itk::Index<3> pixel = { { - (itk::Index<3>::IndexValueType)(image->GetDimension(0) / 2), - (itk::Index<3>::IndexValueType)(22.0/532.0*m_Device->GetScanMode().reconstructionSamplesPerLine), - 0 } }; //22/532*2048 = 84 - if (!m_Pyro->IsSyncDelaySet() &&(image->GetPixelValueByIndex(pixel) < -30)) // #MagicNumber - { - MITK_INFO << "Setting SyncDelay now"; - m_Pyro->SetSyncDelay(m_CurrentImageTimestamp); - } + mitk::ImageWriteAccessor dataPAAccess(rawImagePA); + mitk::ImageWriteAccessor dataUSAccess(rawImageUS); - m_ImageTimestampBuffer[(m_LastWrittenImage + 1) % m_BufferSize] = m_CurrentImageTimestamp; - m_ImageBuffer[(m_LastWrittenImage + 1) % m_BufferSize] = image; - m_LastWrittenImage = (m_LastWrittenImage + 1) % m_BufferSize; + short* dataPA = (short*)dataPAAccess.GetData(); + short* dataUS = (short*)dataUSAccess.GetData(); - // if the user decides to start recording, we feed the vector the generated images - if (m_CurrentlyRecording) { - for (unsigned char index = 0; index < image->GetDimension(2); ++index) + size_t pixelsPerImage = channelDataSamplesPerChannel*channelDataChannelsPerDataset; + + if (m_Device->GetScanMode().beamformingAlgorithm == (int)Beamforming::Interleaved_OA_US) { - if (image->IsSliceSet(index)) + for (unsigned short sam = 0; sam < std::ceil((float)channelDataSamplesPerChannel / 2.f); ++sam) { - m_RecordedImages.push_back(Image::New()); - unsigned int dim[] = { image ->GetDimension(0), image->GetDimension(1), 1}; - m_RecordedImages.back()->Initialize(image->GetPixelType(), 3, dim); - m_RecordedImages.back()->SetGeometry(image->GetGeometry()); - - mitk::ImageReadAccessor inputReadAccessor(image, image->GetSliceData(index)); - m_RecordedImages.back()->SetSlice(inputReadAccessor.GetData(),0); + 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_ImageTimestampRecord.push_back(m_CurrentImageTimestamp); - // save timestamps for each laser image! } + + 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::SetGUIOutput(std::function out) -{ - USDiPhASImageSource::m_GUIOutput = out; - m_StartTime = ((float)std::clock()) / CLOCKS_PER_SEC; //wait till the callback is available again - m_UseGUIOutPut = false; -} - 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) { - m_RecordedImages.clear(); - m_RawRecordedImages.clear(); // we make sure there are no leftovers - m_ImageTimestampRecord.clear(); // also for the timestamps - m_PixelValues.clear(); // aaaand for the pixel values - 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 images, end recording, and clean up - else - { - m_CurrentlyRecording = false; - - m_Device->GetScanMode().transferChannelData = false; // make sure raw Channel Data is not transferred anymore! - m_Device->UpdateScanmode(); - // get the time and date, put them into a nice string and create a folder for the images + //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(); - //std::string MakeFolder = "mkdir \"c:/DiPhASImageData/" + currentDate + "\""; - //system(MakeFolder.c_str()); - - // initialize file paths and the images - Image::Pointer PAImage = Image::New(); - Image::Pointer USImage = Image::New(); - std::string pathPA = "c:\\ImageData\\" + currentDate + "-" + "PAbeamformed" + ".nrrd"; - std::string pathUS = "c:\\ImageData\\" + currentDate + "-" + "USImages" + ".nrrd"; - std::string pathTS = "c:\\ImageData\\" + currentDate + "-" + "ts" + ".csv"; - std::string pathS = "c:\\ImageData\\" + currentDate + "-" + "Settings" + ".txt"; - - // idon't forget the raw Images (if chosen to be saved) - Image::Pointer PAImageRaw = Image::New(); - Image::Pointer USImageRaw = Image::New(); - std::string pathPARaw = "c:\\ImageData\\" + currentDate + "-" + "PAraw" + ".nrrd"; - std::string pathUSRaw = "c:\\ImageData\\" + currentDate + "-" + "USImagesRaw" + ".nrrd"; - - if (m_Device->GetScanMode().beamformingAlgorithm == (int)Beamforming::Interleaved_OA_US) // save a PAImage if we used interleaved mode - { - // first, save the data, so the pyro does not aquire more unneccessary timestamps - m_Pyro->SaveData(); - - // now order the images and save them - // the beamformed ones... - if (m_SavingSettings.saveBeamformed) - { - OrderImagesInterleaved(PAImage, USImage, m_RecordedImages, false); - mitk::IOUtil::Save(USImage, pathUS); - mitk::IOUtil::Save(PAImage, pathPA); - } - - // ...and the raw images - if (m_SavingSettings.saveRaw) - { - OrderImagesInterleaved(PAImageRaw, USImageRaw, m_RawRecordedImages, true); - // mitk::IOUtil::Save(USImageRaw, pathUSRaw); - mitk::IOUtil::Save(PAImageRaw, pathPARaw); - } - // read the pixelvalues of the enveloped images at this position - - itk::Index<3> pixel = { { - (itk::Index<3>::IndexValueType)(m_RecordedImages.at(0)->GetDimension(0) / 2), - (itk::Index<3>::IndexValueType)(22.0 / 532.0*m_Device->GetScanMode().reconstructionSamplesPerLine), - 0 } }; //22/532*2048 = 84 - - GetPixelValues(pixel, m_PixelValues); // write the Pixelvalues to m_PixelValues - - // save the timestamps! - ofstream timestampFile; + if (m_SavingName == "-replace") + { + m_SavingName = "c:\\ImageData\\" + currentDate; + } - timestampFile.open(pathTS); - timestampFile << ",timestamp,pixelvalue"; // write the header + std::string pathS = m_SavingName + ".settings" + ".txt"; - for (int index = 0; index < m_ImageTimestampRecord.size(); ++index) - { - timestampFile << "\n" << index << "," << m_ImageTimestampRecord.at(index) << "," << m_PixelValues.at(index); - } - timestampFile.close(); + ofstream settingsFile; - //save the settings! + settingsFile.open(pathS); + auto& sM = m_Device->GetScanMode(); - ofstream settingsFile; + 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.open(pathS); - auto& sM = m_Device->GetScanMode(); + settingsFile << "[Beamforming Parameters]\n"; + settingsFile << "Reconstructed Lines = " << sM.reconstructionLines << "\n"; + settingsFile << "Samples per Line = " << sM.reconstructionSamplesPerLine << "\n"; - 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; - settingsFile.close(); - } - else if (m_Device->GetScanMode().beamformingAlgorithm == (int)Beamforming::PlaneWaveCompound) // save no PAImage if we used US only mode + // wait for all saving Threads to finish + for (size_t i = 0; i < m_SaveThreads.size(); ++i) { - OrderImagesUltrasound(USImage, m_RecordedImages); - mitk::IOUtil::Save(USImage, pathUS); + m_SaveThreads[i].join(); } - m_PixelValues.clear(); - m_RawRecordedImages.clear(); // clean up the pixel values - m_RecordedImages.clear(); // clean up the images - m_ImageTimestampRecord.clear(); // clean up the timestamps - } -} + // make sure raw Channel Data is not transferred anymore + m_Device->GetScanMode().transferChannelData = false; + m_Device->UpdateScanmode(); -void mitk::USDiPhASImageSource::GetPixelValues(itk::Index<3> pixel, std::vector& values) -{ - unsigned int events = 2; - for (int index = 0; index < m_RecordedImages.size(); index += events) // omit sound images - { - Image::Pointer image = m_RecordedImages.at(index); - image = ApplyBmodeFilter(image); - values.push_back(image.GetPointer()->GetPixelValueByIndex(pixel)); + // 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) +void mitk::USDiPhASImageSource::OrderImagesInterleaved(Image::Pointer PAImage, Image::Pointer USImage, std::vector> recordedList, bool raw) { unsigned int width = 32; - unsigned int height = 32; - unsigned int events = m_Device->GetScanMode().transmitEventsCount + 1; // the PA event is not included in the transmitEvents, so we add 1 here + unsigned int heightPA = 32; + unsigned int heightUS = 32; + unsigned int events = m_Device->GetScanMode().transmitEventsCount; if (!raw) - events = 2; // the beamformed image array contains only the resulting image of multiple events + events = 1; // the beamformed image array contains only the resulting image of multiple events if (raw) { - width = recordedList.at(0)->GetDimension(0); - height = recordedList.at(0)->GetDimension(1); + 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; - height = m_Device->GetScanMode().reconstructionSamplesPerLine; + heightPA = m_Device->GetScanMode().reconstructionSamplesPerLine; + heightUS = m_Device->GetScanMode().reconstructionSamplesPerLine; } else if (m_DataType == DataType::Image_uChar) { width = m_Device->GetScanMode().imageWidth; - height = m_Device->GetScanMode().imageHeight; + heightPA = m_Device->GetScanMode().imageHeight; + heightUS = m_Device->GetScanMode().imageHeight; } - unsigned int dimLaser[] = { (unsigned int)width, (unsigned int)height, (unsigned int)(recordedList.size() / events)}; - unsigned int dimSound[] = { (unsigned int)width, (unsigned int)height, (unsigned int)(recordedList.size() / events * (events-1))}; + unsigned int dimPA[] = { width, heightPA, (unsigned int)recordedList.size()}; + unsigned int dimUS[] = { width, heightUS, (unsigned int)(recordedList.size() * events)}; - PAImage->Initialize(recordedList.back()->GetPixelType(), 3, dimLaser); - PAImage->SetGeometry(recordedList.back()->GetGeometry()); - USImage->Initialize(recordedList.back()->GetPixelType(), 3, dimSound); - USImage->SetGeometry(recordedList.back()->GetGeometry()); + 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 inputReadAccessor(recordedList.at(index)); + mitk::ImageReadAccessor inputReadAccessorPA(recordedList.at(index).first); + PAImage->SetSlice(inputReadAccessorPA.GetData(), index); - if (index % events == 0) - { - PAImage->SetSlice(inputReadAccessor.GetData(), index / events); - } - else + if (!raw) { - if(!raw) - USImage->SetSlice(inputReadAccessor.GetData(), ((index - (index % events)) / events) + (index % events)-1); + 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) +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[] = { (unsigned int)width, (unsigned int)height, (unsigned int)recordedList.size()}; + unsigned int dimSound[] = { width, height, (unsigned int)(recordedList.size() * events) }; - USImage->Initialize(recordedList.back()->GetPixelType(), 3, dimSound); - USImage->SetGeometry(recordedList.back()->GetGeometry()); + USImage->Initialize(recordedList.back().second->GetPixelType(), 3, dimSound); + USImage->SetSpacing(recordedList.back().second->GetGeometry()->GetSpacing()); for (int index = 0; index < recordedList.size(); ++index) { - mitk::ImageReadAccessor inputReadAccessor(recordedList.at(index)); - USImage->SetSlice(inputReadAccessor.GetData(), 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"; // 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 diff --git a/Modules/US/USHardwareDiPhAS/mitkUSDiPhASImageSource.h b/Modules/US/USHardwareDiPhAS/mitkUSDiPhASImageSource.h index 54ac576ffd..d40dd8d4a4 100644 --- a/Modules/US/USHardwareDiPhAS/mitkUSDiPhASImageSource.h +++ b/Modules/US/USHardwareDiPhAS/mitkUSDiPhASImageSource.h @@ -1,193 +1,193 @@ /*=================================================================== 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 MITKUSDiPhASImageSource_H_HEADER_INCLUDED_ #define MITKUSDiPhASImageSource_H_HEADER_INCLUDED_ #include "mitkUSImageSource.h" #include "mitkUSDiPhASCustomControls.h" #include "Framework.IBMT.US.CWrapper.h" #include "mitkImageReadAccessor.h" #include "itkFastMutexLock.h" #include #include #include #include #include #include #include #include +#include namespace mitk { class USDiPhASDevice; /** * \brief Implementation of mitk::USImageSource for DiPhAS API devices. * The method mitk::USImageSource::GetNextRawImage() is implemented for * getting images from the DiPhAS API. * * The image data is given to this class from the DiPhAS API by calling * a callback method that writes the image data to an mitk::image */ class USDiPhASImageSource : public USImageSource { public: mitkClassMacro(USDiPhASImageSource, USImageSource); mitkNewMacro1Param(Self, mitk::USDiPhASDevice*); itkCloneMacro(Self); typedef itk::Image< float, 3 > itkFloatImageType; typedef mitk::USDiPhASDeviceCustomControls::DataType DataType; typedef mitk::USDiPhASDeviceCustomControls::SavingSettings SavingSettings; /** * Implementation of the superclass method. Returns the pointer * to the mitk::Image filled by DiPhAS API callback. */ virtual void GetNextRawImage( std::vector& ) override; /** * The API calls this function to pass the image data to the * user; here the m_Image is updated */ 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& imagePixelFormat, int& imageSetsTotal, double& timeStamp); - void SetGUIOutput(std::function out); - /** This starts or ends the recording session*/ void SetRecordingStatus(bool record); void SetSavingSettings(SavingSettings settings); void SetVerticalSpacing(float mm); + void SetSavingName(std::string name); void ModifyDataType(DataType dataT); void ModifyUseBModeFilter(bool isSet); void ModifyScatteringCoefficient(int coeff); void ModifyCompensateForScattering(bool useIt); void ModifyEnergyCompensation(bool compensate); /** * Sets the spacing used in the image based on the informations of the ScanMode in USDiPhAS Device */ void UpdateImageGeometry(); protected: void SetDataType(DataType dataT); void SetUseBModeFilter(bool isSet); + void CheckModifiedVariables(); + void ResizeFluenceImage(mitk::Vector3D spacing, unsigned int* dimensions); + USDiPhASImageSource(mitk::USDiPhASDevice* device); virtual ~USDiPhASImageSource( ); /** This vector holds all the images we record, if recording is set to active. */ - std::vector m_RecordedImages; - std::vector m_RawRecordedImages; - std::vector m_ImageTimestampRecord; - std::vector m_ImageTimestampBuffer; - long long m_CurrentImageTimestamp; + std::vector>> m_ImagesBuffer; // first image US, second image PA + std::vector>> m_RawImagesBuffer; + std::vector> m_ImageTimestampsBuffer; // stores all timestamps since recording started + + std::string m_SavingName; + std::vector m_SaveThreads; + void saveDataThread(size_t batch, unsigned long long counter, std::string currentSaveName); + bool m_CurrentlyRecording; + mitk::OphirPyro::Pointer m_Pyro; bool m_PyroConnected; - std::vector m_FluenceCompOriginal; - std::vector m_FluenceCompResized; - std::vector::Pointer> m_FluenceCompResizedItk; + std::vector m_FluenceCompOriginal; + std::vector m_FluenceCompResized; + std::vector::Pointer> m_FluenceCompResizedItk; - std::vector m_ImageBuffer; - int m_LastWrittenImage; - int m_BufferSize; + int m_LastBatch; + int m_LastImage; + size_t m_RecordedImageCounter; + int m_BufferBatches; + int m_BatchSize; - unsigned int m_ImageDimensions[3]; mitk::Vector3D m_ImageSpacing; mitk::Image::Pointer ApplyBmodeFilter(mitk::Image::Pointer image, bool useLogFilter = false); mitk::Image::Pointer CutOffTop(mitk::Image::Pointer image, int cutOffSize = 165); mitk::Image::Pointer ResampleOutputVertical(mitk::Image::Pointer image, float verticalSpacing = 0.1); mitk::Image::Pointer ApplyScatteringCompensation(mitk::Image::Pointer inputImage, int scatteringCoefficient); mitk::Image::Pointer ApplyResampling(mitk::Image::Pointer inputImage, mitk::Vector3D outputSpacing, unsigned int outputSize[3]); mitk::Image::Pointer MultiplyImage(mitk::Image::Pointer inputImage, double value); - void OrderImagesInterleaved(Image::Pointer PAImage, Image::Pointer USImage, std::vector recordedList, bool raw); - void OrderImagesUltrasound(Image::Pointer USImage, std::vector recordedList); - - void GetPixelValues(itk::Index<3> pixel, std::vector& values); - float GetPixelValue(itk::Index<3> pixel); - std::vector m_PixelValues; + void OrderImagesInterleaved(Image::Pointer PAImage, Image::Pointer USImage, std::vector> recordedList, bool raw); + void OrderImagesUltrasound(Image::Pointer USImage, std::vector> recordedList); mitk::USDiPhASDevice* m_Device; - /** This is a callback to pass text data to the GUI. */ - std::function m_GUIOutput; - /** * Variables for management of current state. */ SavingSettings m_SavingSettings; float m_StartTime; - bool m_UseGUIOutPut; BeamformerStateInfoNative m_BeamformerInfos; bool m_UseBModeFilter; bool m_DataTypeModified; DataType m_DataTypeNext; bool m_UseBModeFilterModified; bool m_UseBModeFilterNext; float m_VerticalSpacing; float m_VerticalSpacingNext; bool m_VerticalSpacingModified; int m_ScatteringCoefficient; int m_ScatteringCoefficientNext; bool m_ScatteringCoefficientModified; bool m_CompensateForScattering; bool m_CompensateForScatteringNext; bool m_CompensateForScatteringModified; bool m_CompensateEnergy; bool m_CompensateEnergyNext; bool m_CompensateEnergyModified; DataType m_DataType; }; } // namespace mitk #endif // MITKUSDiPhASImageSource_H diff --git a/Modules/USUI/Qmitk/QmitkUSControlsCustomDiPhASDeviceWidget.cpp b/Modules/USUI/Qmitk/QmitkUSControlsCustomDiPhASDeviceWidget.cpp index 71b04c33ad..ac8c62cdad 100644 --- a/Modules/USUI/Qmitk/QmitkUSControlsCustomDiPhASDeviceWidget.cpp +++ b/Modules/USUI/Qmitk/QmitkUSControlsCustomDiPhASDeviceWidget.cpp @@ -1,419 +1,470 @@ /*=================================================================== 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 "QmitkUSControlsCustomDiPhASDeviceWidget.h" #include "ui_QmitkUSControlsCustomDiPhASDeviceWidget.h" #include #include -QmitkUSControlsCustomDiPhASDeviceWidget::QmitkUSControlsCustomDiPhASDeviceWidget() - : ui(new Ui::QmitkUSControlsCustomDiPhASDeviceWidget) +QmitkUSControlsCustomDiPhASDeviceWidget::QmitkUSControlsCustomDiPhASDeviceWidget(QWidget *parent) + : QmitkUSAbstractCustomWidget(parent), ui(new Ui::QmitkUSControlsCustomDiPhASDeviceWidget) { } -QmitkUSControlsCustomDiPhASDeviceWidget::QmitkUSControlsCustomDiPhASDeviceWidget(QWidget *parent) - : QmitkUSAbstractCustomWidget(parent), ui(new Ui::QmitkUSControlsCustomDiPhASDeviceWidget) +QmitkUSControlsCustomDiPhASDeviceWidget::QmitkUSControlsCustomDiPhASDeviceWidget() + : ui(new Ui::QmitkUSControlsCustomDiPhASDeviceWidget) { } QmitkUSControlsCustomDiPhASDeviceWidget::~QmitkUSControlsCustomDiPhASDeviceWidget() { + m_ControlInterface = dynamic_cast + (this->GetDevice()->GetControlInterfaceCustom().GetPointer()); + delete ui; } std::string QmitkUSControlsCustomDiPhASDeviceWidget::GetDeviceClass() const { return "org.mitk.modules.us.USDiPhASDevice"; } QmitkUSAbstractCustomWidget* QmitkUSControlsCustomDiPhASDeviceWidget::Clone(QWidget* parent) const { QmitkUSAbstractCustomWidget* clonedWidget = new QmitkUSControlsCustomDiPhASDeviceWidget(parent); clonedWidget->SetDevice(this->GetDevice()); return clonedWidget; } void QmitkUSControlsCustomDiPhASDeviceWidget::OnDeviceSet() { m_ControlInterface = dynamic_cast (this->GetDevice()->GetControlInterfaceCustom().GetPointer()); - if ( m_ControlInterface.IsNotNull() ) - { - m_ControlInterface->passGUIOut([this](QString str)->void{ - if (this->ui) { - this->ui->CurrentState->setText(str); - } }); - } - else + if ( m_ControlInterface.IsNull() ) { MITK_WARN("QmitkUSAbstractCustomWidget")("QmitkUSControlsCustomDiPhASDeviceWidget") << "Did not get a custom device control interface."; } //now pass the default values m_OldReconstructionLines = 0; m_ControlInterface->SetSilentUpdate(true); // don't update the scanmode everytime OnTransmitPhaseLengthChanged(); OnExcitationFrequencyChanged(); OnTransmitEventsChanged(); OnVoltageChanged(); OnScanDepthChanged(); // HERE OnAveragingCountChanged(); - OnTGCMinChanged(); - OnTGCMaxChanged(); + OnTGCChanged(); OnDataTypeChanged(); OnPitchChanged(); OnReconstructedSamplesChanged(); OnReconstructedLinesChanged(); OnSpeedOfSoundChanged(); OnBandpassEnabledChanged(); OnLowCutChanged(); OnHighCutChanged(); OnUseBModeFilterChanged(); // HERE OnVerticalSpacingChanged(); OnScatteringCoefficientChanged(); OnCompensateScatteringChanged(); OnChangedSavingSettings(); OnCompensateEnergyChanged(); + OnSaveNameChanged(); m_ControlInterface->SetSilentUpdate(false); // on the last update pass the scanmode and geometry! OnModeChanged(); // HERE } void QmitkUSControlsCustomDiPhASDeviceWidget::Initialize() { ui->setupUi(this); connect(ui->CompensateEnergy, SIGNAL(stateChanged(int)), this, SLOT(OnCompensateEnergyChanged())); connect(ui->UseBModeFilter, SIGNAL(stateChanged(int)), this, SLOT(OnUseBModeFilterChanged())); connect(ui->StartStopRecord, SIGNAL(clicked()), this, SLOT(OnRecordChanged())); connect(ui->ScatteringCoefficient, SIGNAL(valueChanged(int)), this, SLOT(OnScatteringCoefficientChanged())); connect(ui->CompensateScattering, SIGNAL(stateChanged(int)), this, SLOT(OnCompensateScatteringChanged())); connect(ui->VerticalSpacing, SIGNAL(valueChanged(double)), this, SLOT(OnVerticalSpacingChanged())); connect(ui->SaveBeamformed, SIGNAL(stateChanged(int)), this, SLOT(OnChangedSavingSettings())); connect(ui->SaveRaw, SIGNAL(stateChanged(int)), this, SLOT(OnChangedSavingSettings())); //transmit connect(ui->TransmitPhaseLength, SIGNAL(valueChanged(double)), this, SLOT(OnTransmitPhaseLengthChanged())); connect(ui->ExcitationFrequency, SIGNAL(valueChanged(double)), this, SLOT(OnExcitationFrequencyChanged())); connect(ui->TransmitEvents, SIGNAL(valueChanged(int)), this, SLOT(OnTransmitEventsChanged())); connect(ui->Voltage, SIGNAL(valueChanged(int)), this, SLOT(OnVoltageChanged())); connect(ui->Mode, SIGNAL(currentTextChanged(QString)), this, SLOT(OnModeChanged())); //Receive connect(ui->ScanDepth, SIGNAL(valueChanged(double)), this, SLOT(OnScanDepthChanged())); connect(ui->AveragingCount, SIGNAL(valueChanged(int)), this, SLOT(OnAveragingCountChanged())); - connect(ui->TimeGainCompensationMinSlider, SIGNAL(valueChanged(int)), this, SLOT(OnTGCMinChanged())); - connect(ui->TimeGainCompensationMaxSlider, SIGNAL(valueChanged(int)), this, SLOT(OnTGCMaxChanged())); + connect(ui->TimeGainCompensation0Slider, SIGNAL(valueChanged(int)), this, SLOT(OnTGCChanged())); + connect(ui->TimeGainCompensation1Slider, SIGNAL(valueChanged(int)), this, SLOT(OnTGCChanged())); + connect(ui->TimeGainCompensation2Slider, SIGNAL(valueChanged(int)), this, SLOT(OnTGCChanged())); + connect(ui->TimeGainCompensation3Slider, SIGNAL(valueChanged(int)), this, SLOT(OnTGCChanged())); + connect(ui->TimeGainCompensation4Slider, SIGNAL(valueChanged(int)), this, SLOT(OnTGCChanged())); + connect(ui->TimeGainCompensation5Slider, SIGNAL(valueChanged(int)), this, SLOT(OnTGCChanged())); + connect(ui->TimeGainCompensation6Slider, SIGNAL(valueChanged(int)), this, SLOT(OnTGCChanged())); + connect(ui->TimeGainCompensation7Slider, SIGNAL(valueChanged(int)), this, SLOT(OnTGCChanged())); connect(ui->DataType, SIGNAL(currentTextChanged(QString)), this, SLOT(OnDataTypeChanged())); //Beamforming connect(ui->PitchOfTransducer, SIGNAL(valueChanged(double)), this, SLOT(OnPitchChanged())); connect(ui->ReconstructedSamplesPerLine, SIGNAL(valueChanged(int)), this, SLOT(OnReconstructedSamplesChanged())); connect(ui->ReconstructedLines, SIGNAL(valueChanged(int)), this, SLOT(OnReconstructedLinesChanged())); connect(ui->SpeedOfSound, SIGNAL(valueChanged(int)), this, SLOT(OnSpeedOfSoundChanged())); //Bandpass connect(ui->BandpassEnabled, SIGNAL(currentTextChanged(QString)), this, SLOT(OnBandpassEnabledChanged())); connect(ui->LowCut, SIGNAL(valueChanged(double)), this, SLOT(OnLowCutChanged())); connect(ui->HighCut, SIGNAL(valueChanged(double)), this, SLOT(OnHighCutChanged())); + connect(ui->RecordingName, SIGNAL(textChanged()), this, SLOT(OnSaveNameChanged())); } //slots void QmitkUSControlsCustomDiPhASDeviceWidget::OnCompensateEnergyChanged() { if (m_ControlInterface.IsNull()) { return; } bool CompensateEnergy = ui->CompensateEnergy->isChecked(); m_ControlInterface->SetCompensateEnergy(CompensateEnergy); } void QmitkUSControlsCustomDiPhASDeviceWidget::OnUseBModeFilterChanged() { if (m_ControlInterface.IsNull()) { return; } bool UseBModeFilter = ui->UseBModeFilter->isChecked(); m_ControlInterface->SetUseBModeFilter(UseBModeFilter); } +void QmitkUSControlsCustomDiPhASDeviceWidget::OnSaveNameChanged() +{ + if (m_ControlInterface.IsNull()) { return; } + m_ControlInterface->SetSavingName(ui->RecordingName->toPlainText().toStdString()); +} + void QmitkUSControlsCustomDiPhASDeviceWidget::OnRecordChanged() { if (m_ControlInterface.IsNull()) { return; } if (ui->StartStopRecord->text() == "Start Recording") { ui->StartStopRecord->setText("Stop Recording"); ui->UseBModeFilter->setEnabled(false); ui->ScatteringCoefficient->setEnabled(false); ui->CompensateScattering->setEnabled(false); ui->VerticalSpacing->setEnabled(false); ui->SaveBeamformed->setEnabled(false); ui->SaveRaw->setEnabled(false); ui->TransmitPhaseLength->setEnabled(false); ui->ExcitationFrequency->setEnabled(false); ui->TransmitEvents->setEnabled(false); ui->Voltage->setEnabled(false); ui->Mode->setEnabled(false); ui->ScanDepth->setEnabled(false); ui->AveragingCount->setEnabled(false); - ui->TimeGainCompensationMinSlider->setEnabled(false); - ui->TimeGainCompensationMaxSlider->setEnabled(false); + ui->TimeGainCompensation0Slider->setEnabled(false); + ui->TimeGainCompensation1Slider->setEnabled(false); + ui->TimeGainCompensation2Slider->setEnabled(false); + ui->TimeGainCompensation3Slider->setEnabled(false); + ui->TimeGainCompensation4Slider->setEnabled(false); + ui->TimeGainCompensation5Slider->setEnabled(false); + ui->TimeGainCompensation6Slider->setEnabled(false); + ui->TimeGainCompensation7Slider->setEnabled(false); ui->DataType->setEnabled(false); ui->PitchOfTransducer->setEnabled(false); ui->ReconstructedSamplesPerLine->setEnabled(false); ui->ReconstructedLines->setEnabled(false); ui->SpeedOfSound->setEnabled(false); ui->BandpassEnabled->setEnabled(false); ui->LowCut->setEnabled(false); ui->HighCut->setEnabled(false); ui->CompensateEnergy->setEnabled(false); + ui->RecordingName->setEnabled(false); m_ControlInterface->SetRecord(true); } else { + ui->StartStopRecord->setText("Start Recording"); ui->UseBModeFilter->setEnabled(true); ui->CompensateScattering->setEnabled(true); if(ui->CompensateScattering->isChecked()) ui->ScatteringCoefficient->setEnabled(true); ui->VerticalSpacing->setEnabled(true); ui->SaveBeamformed->setEnabled(true); ui->SaveRaw->setEnabled(true); ui->TransmitPhaseLength->setEnabled(true); ui->ExcitationFrequency->setEnabled(true); ui->TransmitEvents->setEnabled(true); ui->Voltage->setEnabled(true); ui->Mode->setEnabled(true); ui->ScanDepth->setEnabled(true); ui->AveragingCount->setEnabled(true); - ui->TimeGainCompensationMinSlider->setEnabled(true); - ui->TimeGainCompensationMaxSlider->setEnabled(true); + ui->TimeGainCompensation0Slider->setEnabled(true); + ui->TimeGainCompensation1Slider->setEnabled(true); + ui->TimeGainCompensation2Slider->setEnabled(true); + ui->TimeGainCompensation3Slider->setEnabled(true); + ui->TimeGainCompensation4Slider->setEnabled(true); + ui->TimeGainCompensation5Slider->setEnabled(true); + ui->TimeGainCompensation6Slider->setEnabled(true); + ui->TimeGainCompensation7Slider->setEnabled(true); ui->DataType->setEnabled(true); ui->PitchOfTransducer->setEnabled(true); ui->ReconstructedSamplesPerLine->setEnabled(true); ui->ReconstructedLines->setEnabled(true); ui->SpeedOfSound->setEnabled(true); ui->BandpassEnabled->setEnabled(true); ui->LowCut->setEnabled(true); ui->HighCut->setEnabled(true); ui->CompensateEnergy->setEnabled(true); + ui->RecordingName->setEnabled(true); m_ControlInterface->SetRecord(false); + + size_t index_ = ui->RecordingName->toPlainText().toStdString().find_last_of('_'); + if (std::string::npos == index_) + ui->RecordingName->setPlainText(ui->RecordingName->toPlainText() + "_1"); + else + { + std::string oldName = ui->RecordingName->toPlainText().toStdString(); + int number = std::stoi(oldName.substr(index_ + 1, oldName.length() - index_)) + 1; + std::string newName = oldName.substr(0, index_ + 1) + std::to_string(number); + ui->RecordingName->setPlainText(QString::fromStdString(newName)); + } } } void QmitkUSControlsCustomDiPhASDeviceWidget::OnVerticalSpacingChanged() { if (m_ControlInterface.IsNull()) { return; } m_ControlInterface->SetVerticalSpacing(ui->VerticalSpacing->value()); } void QmitkUSControlsCustomDiPhASDeviceWidget::OnScatteringCoefficientChanged() { if (m_ControlInterface.IsNull()) { return; } m_ControlInterface->SetScatteringCoefficient(ui->ScatteringCoefficient->value()); } void QmitkUSControlsCustomDiPhASDeviceWidget::OnCompensateScatteringChanged() { if (m_ControlInterface.IsNull()) { return; } if (ui->CompensateScattering->isChecked()) ui->ScatteringCoefficient->setEnabled(true); else ui->ScatteringCoefficient->setEnabled(false); m_ControlInterface->SetCompensateScattering(ui->CompensateScattering->isChecked()); } void QmitkUSControlsCustomDiPhASDeviceWidget::OnChangedSavingSettings() { if (m_ControlInterface.IsNull()) { return; } mitk::USDiPhASDeviceCustomControls::SavingSettings settings; settings.saveBeamformed = ui->SaveBeamformed->isChecked(); settings.saveRaw = ui->SaveRaw->isChecked(); m_ControlInterface->SetSavingSettings(settings); } //Transmit void QmitkUSControlsCustomDiPhASDeviceWidget::OnTransmitPhaseLengthChanged() { if (m_ControlInterface.IsNull()) { return; } m_ControlInterface->SetTransmitPhaseLength(ui->TransmitPhaseLength->value()); } void QmitkUSControlsCustomDiPhASDeviceWidget::OnExcitationFrequencyChanged() { if (m_ControlInterface.IsNull()) { return; } m_ControlInterface->SetExcitationFrequency(ui->ExcitationFrequency->value()); } void QmitkUSControlsCustomDiPhASDeviceWidget::OnTransmitEventsChanged() { if (m_ControlInterface.IsNull()) { return; } - + if (ui->TransmitEvents->value()%2 == 0 || ui->TransmitEvents->value() > 11) + ui->TransmitEvents->setValue(1); m_ControlInterface->SetTransmitEvents(ui->TransmitEvents->value()); } void QmitkUSControlsCustomDiPhASDeviceWidget::OnVoltageChanged() { if (m_ControlInterface.IsNull()) { return; } m_ControlInterface->SetVoltage(ui->Voltage->value()); } void QmitkUSControlsCustomDiPhASDeviceWidget::OnModeChanged() { if (m_ControlInterface.IsNull()) { return; } QString Mode = ui->Mode->currentText(); bool silent = m_ControlInterface->GetSilentUpdate(); m_ControlInterface->SetSilentUpdate(true); if (Mode == "Ultrasound only") { m_ControlInterface->SetMode(false); - ui->TransmitEvents->setValue(1); } else if (Mode == "Interleaved") { m_ControlInterface->SetMode(true); - ui->TransmitEvents->setValue(1); } if (!silent) { m_ControlInterface->SetSilentUpdate(false); } OnTransmitEventsChanged(); } //Receive void QmitkUSControlsCustomDiPhASDeviceWidget::OnScanDepthChanged() { if (m_ControlInterface.IsNull()) { return; } m_ControlInterface->SetScanDepth(ui->ScanDepth->value()); } void QmitkUSControlsCustomDiPhASDeviceWidget::OnAveragingCountChanged() { if (m_ControlInterface.IsNull()) { return; } m_ControlInterface->SetAveragingCount(ui->AveragingCount->value()); } -void QmitkUSControlsCustomDiPhASDeviceWidget::OnTGCMinChanged() +void QmitkUSControlsCustomDiPhASDeviceWidget::OnTGCChanged() { if (m_ControlInterface.IsNull()) { return; } - int tgcMin = ui->TimeGainCompensationMinSlider->value(); - int tgcMax = ui->TimeGainCompensationMaxSlider->value(); - if (tgcMin > tgcMax) { - ui->TimeGainCompensationMinSlider->setValue(tgcMax); - MITK_INFO << "User tried to set tgcMin>tgcMax."; - } - QString text("TGC min = " + QString::fromStdString(std::to_string(ui->TimeGainCompensationMinSlider->value()))); - ui->TimeGainCompensationMinLabel->setText(text); - m_ControlInterface->SetTGCMin(ui->TimeGainCompensationMinSlider->value()); -} -void QmitkUSControlsCustomDiPhASDeviceWidget::OnTGCMaxChanged() -{ - if (m_ControlInterface.IsNull()) { return; } - - int tgcMin = ui->TimeGainCompensationMinSlider->value(); - int tgcMax = ui->TimeGainCompensationMaxSlider->value(); - if (tgcMin > tgcMax) { - ui->TimeGainCompensationMaxSlider->setValue(tgcMin); - MITK_INFO << "User tried to set tgcMin>tgcMax."; - } - QString text("TGC max = "+QString::fromStdString(std::to_string(ui->TimeGainCompensationMaxSlider->value()))); - ui->TimeGainCompensationMaxLabel->setText(text); - m_ControlInterface->SetTGCMax(ui->TimeGainCompensationMaxSlider->value()); + int tgc[8]; + + tgc[0] = ui->TimeGainCompensation0Slider->value(); + tgc[1] = ui->TimeGainCompensation1Slider->value(); + tgc[2] = ui->TimeGainCompensation2Slider->value(); + tgc[3] = ui->TimeGainCompensation3Slider->value(); + tgc[4] = ui->TimeGainCompensation4Slider->value(); + tgc[5] = ui->TimeGainCompensation5Slider->value(); + tgc[6] = ui->TimeGainCompensation6Slider->value(); + tgc[7] = ui->TimeGainCompensation7Slider->value(); + + QString label[8]; + + label[0] = QString::fromStdString(std::to_string(ui->TimeGainCompensation0Slider->value())); + label[1] = QString::fromStdString(std::to_string(ui->TimeGainCompensation1Slider->value())); + label[2] = QString::fromStdString(std::to_string(ui->TimeGainCompensation2Slider->value())); + label[3] = QString::fromStdString(std::to_string(ui->TimeGainCompensation3Slider->value())); + label[4] = QString::fromStdString(std::to_string(ui->TimeGainCompensation4Slider->value())); + label[5] = QString::fromStdString(std::to_string(ui->TimeGainCompensation5Slider->value())); + label[6] = QString::fromStdString(std::to_string(ui->TimeGainCompensation6Slider->value())); + label[7] = QString::fromStdString(std::to_string(ui->TimeGainCompensation7Slider->value())); + ui->TimeGainCompensation0Label->setText(label[0]); + ui->TimeGainCompensation1Label->setText(label[1]); + ui->TimeGainCompensation2Label->setText(label[2]); + ui->TimeGainCompensation3Label->setText(label[3]); + ui->TimeGainCompensation4Label->setText(label[4]); + ui->TimeGainCompensation5Label->setText(label[5]); + ui->TimeGainCompensation6Label->setText(label[6]); + ui->TimeGainCompensation7Label->setText(label[7]); + + ui->BandpassLabel->hide(); + ui->BandpassParameters->setEnabled(false); + ui->Mode->hide(); + ui->ModeLabel->hide(); + m_ControlInterface->SetTGC(tgc); } void QmitkUSControlsCustomDiPhASDeviceWidget::OnDataTypeChanged() { if (m_ControlInterface.IsNull()) { return; } QString DataType = ui->DataType->currentText(); if (DataType == "Image Data") { m_ControlInterface->SetDataType(mitk::USDiPhASDeviceCustomControls::DataType::Image_uChar); } else if (DataType == "Beamformed Data") { m_ControlInterface->SetDataType(mitk::USDiPhASDeviceCustomControls::DataType::Beamformed_Short); } } //Beamforming void QmitkUSControlsCustomDiPhASDeviceWidget::OnPitchChanged() { if (m_ControlInterface.IsNull()) { return; } m_ControlInterface->SetPitch(ui->PitchOfTransducer->value()); } void QmitkUSControlsCustomDiPhASDeviceWidget::OnReconstructedSamplesChanged() { if (m_ControlInterface.IsNull()) { return; } + if(ui->ReconstructedSamplesPerLine->value()%256 != 0) + ui->ReconstructedSamplesPerLine->setValue(2048); m_ControlInterface->SetReconstructedSamples(ui->ReconstructedSamplesPerLine->value()); } void QmitkUSControlsCustomDiPhASDeviceWidget::OnReconstructedLinesChanged() { if (m_ControlInterface.IsNull()) { return; } if (m_OldReconstructionLines == 0) m_OldReconstructionLines = ui->ReconstructedLines->value(); + if(ui->ReconstructedLines->value()%128 != 0) + ui->ReconstructedLines->setValue(128); + m_ControlInterface->SetReconstructedLines(ui->ReconstructedLines->value()); ui->PitchOfTransducer->setValue(ui->PitchOfTransducer->value()*((double)m_OldReconstructionLines / (double)ui->ReconstructedLines->value())); m_OldReconstructionLines = ui->ReconstructedLines->value(); } void QmitkUSControlsCustomDiPhASDeviceWidget::OnSpeedOfSoundChanged() { if (m_ControlInterface.IsNull()) { return; } m_ControlInterface->SetSpeedOfSound(ui->SpeedOfSound->value()); } //Bandpass void QmitkUSControlsCustomDiPhASDeviceWidget::OnBandpassEnabledChanged() { if (m_ControlInterface.IsNull()) { return; } if (ui->BandpassEnabled->currentText() == "On") { m_ControlInterface->SetBandpassEnabled(true); } else { m_ControlInterface->SetBandpassEnabled(false); } } void QmitkUSControlsCustomDiPhASDeviceWidget::OnLowCutChanged() { if (m_ControlInterface.IsNull()) { return; } unsigned int Low = ui->LowCut->value(); unsigned int High = ui->HighCut->value(); if (Low > High) { ui->LowCut->setValue(High); MITK_INFO << "User tried to set LowCut>HighCut."; } m_ControlInterface->SetLowCut(ui->LowCut->value()); } void QmitkUSControlsCustomDiPhASDeviceWidget::OnHighCutChanged() { if (m_ControlInterface.IsNull()) { return; } unsigned int Low = ui->LowCut->value(); unsigned int High = ui->HighCut->value(); if (Low > High) { ui->HighCut->setValue(Low); MITK_INFO << "User tried to set LowCut>HighCut."; } m_ControlInterface->SetHighCut(ui->HighCut->value()); } diff --git a/Modules/USUI/Qmitk/QmitkUSControlsCustomDiPhASDeviceWidget.h b/Modules/USUI/Qmitk/QmitkUSControlsCustomDiPhASDeviceWidget.h index cfc6955fdb..8f47fc6ffc 100644 --- a/Modules/USUI/Qmitk/QmitkUSControlsCustomDiPhASDeviceWidget.h +++ b/Modules/USUI/Qmitk/QmitkUSControlsCustomDiPhASDeviceWidget.h @@ -1,117 +1,116 @@ - /*=================================================================== 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 QmitkUSControlsCustomDiPhASDeviceWidget_H #define QmitkUSControlsCustomDiPhASDeviceWidget_H #include "QmitkUSAbstractCustomWidget.h" #include "mitkUSDiPhASDeviceCustomControls.h" #include "mitkUSDevice.h" #include namespace Ui { class QmitkUSControlsCustomDiPhASDeviceWidget; } /** \brief Widget for custom controls of mitk::USDiPhASDevice. * This class handles the itk::USDiPhASDeviceCustomControls of video device * objects. */ class QmitkUSControlsCustomDiPhASDeviceWidget : public QmitkUSAbstractCustomWidget { Q_OBJECT private slots: virtual void OnCompensateEnergyChanged(); virtual void OnUseBModeFilterChanged(); virtual void OnVerticalSpacingChanged(); virtual void OnRecordChanged(); virtual void OnScatteringCoefficientChanged(); virtual void OnCompensateScatteringChanged(); virtual void OnChangedSavingSettings(); + virtual void OnSaveNameChanged(); //Transmit virtual void OnTransmitPhaseLengthChanged(); virtual void OnExcitationFrequencyChanged(); virtual void OnTransmitEventsChanged(); virtual void OnVoltageChanged(); virtual void OnModeChanged(); //Receive virtual void OnScanDepthChanged(); virtual void OnAveragingCountChanged(); - virtual void OnTGCMinChanged(); - virtual void OnTGCMaxChanged(); + virtual void OnTGCChanged(); virtual void OnDataTypeChanged(); //Beamforming virtual void OnPitchChanged(); virtual void OnReconstructedSamplesChanged(); virtual void OnReconstructedLinesChanged(); virtual void OnSpeedOfSoundChanged(); //Bandpass virtual void OnBandpassEnabledChanged(); virtual void OnLowCutChanged(); virtual void OnHighCutChanged(); public: /** * Constructs widget object. All gui control elements will be disabled until * QmitkUSAbstractCustomWidget::SetDevice() was called. */ QmitkUSControlsCustomDiPhASDeviceWidget(); ~QmitkUSControlsCustomDiPhASDeviceWidget(); /** * Getter for the device class of mitk:USDiPhASDevice. */ std::string GetDeviceClass() const override; /** * Creates new QmitkUSAbstractCustomWidget with the same mitk::USDiPhASDevice * and the same mitk::USDiPhASDeviceCustomControls which were set on the * original object. * * This method is just for being calles by the factory. Use * QmitkUSAbstractCustomWidget::CloneForQt() instead, if you want a clone of * an object. */ QmitkUSAbstractCustomWidget* Clone(QWidget* parent = nullptr) const override; /** * Gets control interface from the device which was currently set. Control * elements are according to current crop area of the device. If custom * control interface is null, the control elements stay disabled. */ - void OnDeviceSet() override; + virtual void OnDeviceSet() override; - void Initialize() override; + virtual void Initialize() override; protected: - void BlockSignalAndSetValue(QSpinBox* target, int value); + //void BlockSignalAndSetValue(QSpinBox* target, int value); private: QmitkUSControlsCustomDiPhASDeviceWidget(QWidget *parent); Ui::QmitkUSControlsCustomDiPhASDeviceWidget* ui; mitk::USDiPhASDeviceCustomControls::Pointer m_ControlInterface; int m_OldReconstructionLines; }; #endif // QmitkUSControlsCustomDiPhASDeviceWidget_H \ No newline at end of file diff --git a/Modules/USUI/Qmitk/QmitkUSControlsCustomDiPhASDeviceWidget.ui b/Modules/USUI/Qmitk/QmitkUSControlsCustomDiPhASDeviceWidget.ui index f45bef1372..2e114f54da 100644 --- a/Modules/USUI/Qmitk/QmitkUSControlsCustomDiPhASDeviceWidget.ui +++ b/Modules/USUI/Qmitk/QmitkUSControlsCustomDiPhASDeviceWidget.ui @@ -1,663 +1,797 @@ QmitkUSControlsCustomDiPhASDeviceWidget 0 0 351 - 903 + 993 Form - - - - - 0 - 25 - - - - - 16777215 - 25 - - - - QFrame::WinPanel - - - QFrame::Raised - - - Started - - - Qt::AlignCenter - - - <html><head/><body><p><span style=" font-weight:600;">Record Images</span></p></body></html> - Save Beamformed + Save PA Beamformed + + + false - Save Raw + Save PA Raw + + + true - - - - 100 - 25 - - - - Start Recording - - + + + + + + 250 + 50 + + + + E:/ImageData/recordingName + + + + + + + + 100 + 25 + + + + Start Recording + + + + Qt::Horizontal 40 20 - + - <html><head/><body><p><span style=" font-weight:600;">Receive Parameters</span></p></body></html> + <html><head/><body><p><span style=" font-weight:600;">Beamforming Parameters</span></p></body></html> - + - - - - 0 - 0 - + + + 256 - - - 0 - 0 - + + 4096 - - - 16777215 - 16777215 - + + 256 + + + 2048 + + + + + + + 3 + + + 0.005000000000000 - 42 + 10.000000000000000 - - 10 + + 0.005000000000000 - - Qt::Horizontal + + 0.150000000000000 - - + + + + Qt::PreventContextMenu + + + 128 + - 42 + 1024 + + + 128 - 20 + 256 - - Qt::Horizontal + + + + + + Samples per Line - - 0 + + + + + + Reconstructed Lines - - + + + + 1000 + + + 1000000 + + + 5 + + + 1540 + + + + + - Scan Depth [mm] + Speed of Sound [m/s] + + + + Pitch of Transducer [mm] + + + + + + + + + <html><head/><body><p><span style=" font-weight:600;">Receive Parameters</span></p></body></html> + + + + + 1 100 1 + + + + Scan Depth [mm] + + + Averaging Count - + + + + TGC Max + + + + + + + 42 + + + 42 + + + Qt::Horizontal + + + 0 + + + + + + + TGC Min + + + + + + + 42 + + + 42 + + + Qt::Horizontal + + + + false Beamformed Data Image Data + + + + DataType + + + 3.000000000000000 200.000000000000000 50.000000000000000 + + + + 42 + + + 38 + + + Qt::Horizontal + + + + + + + TextLabel + + + + + + + 42 + + + 34 + + + Qt::Horizontal + + + + + + + 42 + + + 42 + + + Qt::Horizontal + + + + + + + 42 + + + 40 + + + Qt::Horizontal + + + + + + + 42 + + + 36 + + + Qt::Horizontal + + + + + + + TextLabel + + + - + - DataType + TextLabel - - + + - TGC Min + TextLabel - - + + - TGC Max + TextLabel + + + + + + + TextLabel + + + + + + + + 0 + 0 + + + + + 0 + 0 + + + + + 16777215 + 16777215 + + + + 42 + + + 32 + + + Qt::Horizontal + + + + + + + <html><head/><body><p><span style=" font-weight:600;">Time Gain Compensation</span></p></body></html> + + true + Compensate Fluence For Scattering - false + true 5 25 15 false Avg. μs' [1/cm] - - - - <html><head/><body><p><span style=" font-weight:600;">Beamforming Parameters</span></p></body></html> - - - - - - - - - 256 - - - 4096 - - - 256 - - - 2048 - - - - - - - 0.010000000000000 - - - 0.050000000000000 - - - 0.300000000000000 - - - - - - - Qt::PreventContextMenu - - - 128 - - - 1024 - - - 128 - - - 128 - - - - - - - Samples per Line - - - - - - - Reconstructed Lines - - - - - - - 1000 - - - 1000000 - - - 5 - - - 1480 - - - - - - - Speed of Sound [m/s] - - - - - - - Pitch of Transducer [mm] - - - - - <html><head/><body><p><span style=" font-weight:600;">Display Parameters</span></p></body></html> Envelope Filter true + + false + Compensate Energy Values 0.010000000000000 1.200000000000000 0.050000000000000 - 0.300000000000000 + 0.100000000000000 Vertical Spacing <html><head/><body><p><span style=" font-weight:600;">Transmit Parameters</span></p></body></html> 1.000000000000000 15.000000000000000 0.100000000000000 7.500000000000000 1 11 2 - 1 + 3 Transmit Events true false 1 1.000000000000000 10000.000000000000000 4.000000000000000 Transmit Phase Length [us] Excitation Frequency [MHz] false Interleaved Ultrasound only false - 6 + 0 75 - 70 + 10 Voltage [V] Mode false <html><head/><body><p><span style=" font-weight:600;">Bandpass Parameters</span></p></body></html> false High Cut [MHz] false Low Cut [MHz] false Bandpass Enabled false false 5.000000000000000 false Off On Qt::Vertical 20 40 diff --git a/Plugins/org.mitk.gui.qt.photoacoustics.pausviewer/src/QmitkPAUSViewerView.cpp b/Plugins/org.mitk.gui.qt.photoacoustics.pausviewer/src/QmitkPAUSViewerView.cpp index 213a25e502..33fec65be1 100644 --- a/Plugins/org.mitk.gui.qt.photoacoustics.pausviewer/src/QmitkPAUSViewerView.cpp +++ b/Plugins/org.mitk.gui.qt.photoacoustics.pausviewer/src/QmitkPAUSViewerView.cpp @@ -1,159 +1,208 @@ /*=================================================================== 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 #include "mitkScaleLegendAnnotation.h" #include "mitkLayoutAnnotationRenderer.h" #include "mitkManualPlacementAnnotationRenderer.h" #include "mitkTextAnnotation2D.h" #include "QmitkPAUSViewerView.h" +#include +#include + const std::string QmitkPAUSViewerView::VIEW_ID = "org.mitk.views.photoacoustics.pausviewer"; -QmitkPAUSViewerView::QmitkPAUSViewerView() : m_PADataStorage(mitk::StandaloneDataStorage::New()), m_USDataStorage(mitk::StandaloneDataStorage::New()), m_UltrasoundReference(nullptr) +QmitkPAUSViewerView::QmitkPAUSViewerView() : m_PADataStorage(mitk::StandaloneDataStorage::New()), m_USDataStorage(mitk::StandaloneDataStorage::New()) { } QmitkPAUSViewerView::~QmitkPAUSViewerView() { - if(m_UltrasoundReference != nullptr) - *m_UltrasoundReference = nullptr; + this->GetDataStorage()->AddNodeEvent.RemoveListener(mitk::MessageDelegate1(this, &QmitkPAUSViewerView::ScanDataStorage)); + this->GetDataStorage()->AddNodeEvent.RemoveListener(mitk::MessageDelegate1(this, &QmitkPAUSViewerView::RemovedNodeFromStorage)); + this->GetDataStorage()->ChangedNodeEvent.RemoveListener(mitk::MessageDelegate1(this, &QmitkPAUSViewerView::ScanDataStorage)); } void QmitkPAUSViewerView::InitWindows() { AddOverlays(); } void QmitkPAUSViewerView::SetFocus() { } void QmitkPAUSViewerView::OnSelectionChanged(berry::IWorkbenchPart::Pointer /*source*/, const QList& /*nodes*/) { } void QmitkPAUSViewerView::CreateQtPartControl(QWidget *parent) { + MITK_INFO << "in here"; m_Controls = new Ui::QmitkPAUSViewerViewControls; m_Controls->setupUi(parent); - m_Controls->m_PARenderWindow->GetRenderer()->SetDataStorage(m_PADataStorage); - m_Controls->m_USRenderWindow->GetRenderer()->SetDataStorage(m_USDataStorage); + SetPADataStorage(m_PADataStorage); + SetUSDataStorage(m_USDataStorage); + + //create a dummy image (gray values 0..255) for correct initialization of level window, etc. + mitk::Image::Pointer dummyImage = mitk::ImageGenerator::GenerateRandomImage(100, 100, 1, 1, 1, 1, 1, 255, 0); + + m_USData = mitk::DataNode::New(); + std::stringstream USnodeName; + USnodeName << "US Image Stream"; + m_USData->SetName(USnodeName.str()); + m_USData->SetData(dummyImage); + m_USDataStorage->Add(m_USData); + + m_PAData = mitk::DataNode::New(); + std::stringstream PAnodeName; + PAnodeName << "PA Image Stream"; + m_PAData->SetName(PAnodeName.str()); + m_PAData->SetData(dummyImage); + m_PADataStorage->Add(m_PAData); + + InitWindows(); + + this->GetDataStorage()->AddNodeEvent.AddListener(mitk::MessageDelegate1(this, &QmitkPAUSViewerView::ScanDataStorage)); + this->GetDataStorage()->RemoveNodeEvent.AddListener(mitk::MessageDelegate1(this, &QmitkPAUSViewerView::RemovedNodeFromStorage)); + this->GetDataStorage()->ChangedNodeEvent.AddListener(mitk::MessageDelegate1(this, &QmitkPAUSViewerView::ScanDataStorage)); + + ScanDataStorage(nullptr); } void QmitkPAUSViewerView::SetPADataStorage(mitk::StandaloneDataStorage::Pointer paStore) { if (m_Controls == nullptr) return; m_PADataStorage = paStore; m_Controls->m_PARenderWindow->GetRenderer()->SetDataStorage(m_PADataStorage); m_Controls->m_PALevelWindow->SetDataStorage(m_PADataStorage); } void QmitkPAUSViewerView::SetUSDataStorage(mitk::StandaloneDataStorage::Pointer usStore) { if (m_Controls == nullptr) return; m_USDataStorage = usStore; m_Controls->m_USRenderWindow->GetRenderer()->SetDataStorage(m_USDataStorage); m_Controls->m_USLevelWindow->SetDataStorage(m_USDataStorage); } vtkRenderWindow* QmitkPAUSViewerView::GetPARenderWindow() { if (m_Controls == nullptr) return nullptr; return m_Controls->m_PARenderWindow->GetRenderWindow(); } vtkRenderWindow* QmitkPAUSViewerView::GetUSRenderWindow() { if (m_Controls == nullptr) return nullptr; return m_Controls->m_USRenderWindow->GetRenderWindow(); } void QmitkPAUSViewerView::AddOverlays() { //if (m_PARenderer == nullptr || /*m_PAOverlayController == nullptr||*/ m_USRenderer == nullptr /*|| m_USOverlayController == nullptr*/) //{ - m_PARenderer = mitk::BaseRenderer::GetInstance(GetPARenderWindow()); - m_USRenderer = mitk::BaseRenderer::GetInstance(GetUSRenderWindow()); + m_PARenderer = mitk::BaseRenderer::GetInstance(GetPARenderWindow()); + m_USRenderer = mitk::BaseRenderer::GetInstance(GetUSRenderWindow()); //} - MITK_INFO << "1111111111111111111111"; mitk::ScaleLegendAnnotation::Pointer scaleAnnotation = mitk::ScaleLegendAnnotation::New(); //scaleAnnotation->SetLeftAxisVisibility(true); //scaleAnnotation->SetRightAxisVisibility(false); //scaleAnnotation->SetRightAxisVisibility(false); //scaleAnnotation->SetTopAxisVisibility(false); //scaleAnnotation->SetCornerOffsetFactor(0); - MITK_INFO << "1111111111111111111111"; // Add Overlays //![TextAnnotation2D] // Create a textAnnotation2D mitk::TextAnnotation2D::Pointer textAnnotation = mitk::TextAnnotation2D::New(); textAnnotation->SetText("Test!"); // set UTF-8 encoded text to render textAnnotation->SetFontSize(40); textAnnotation->SetColor(1, 0, 0); // Set text color to red textAnnotation->SetOpacity(0.5); - MITK_INFO << "1111111111111111111111"; // The position of the Annotation can be set to a fixed coordinate on the display. mitk::Point2D pos; pos[0] = 10; pos[1] = 20; textAnnotation->SetPosition2D(pos); - MITK_INFO << "1111111111111111111111"; std::string rendererID = m_PARenderer->GetName(); // The LayoutAnnotationRenderer can place the TextAnnotation2D at some defined corner positions mitk::LayoutAnnotationRenderer::AddAnnotation( textAnnotation, rendererID, mitk::LayoutAnnotationRenderer::TopLeft, 5, 5, 1); mitk::LayoutAnnotationRenderer::AddAnnotation( textAnnotation, m_PARenderer.GetPointer(), mitk::LayoutAnnotationRenderer::TopLeft, 5, 5, 1); mitk::ManualPlacementAnnotationRenderer::AddAnnotation( textAnnotation, m_PARenderer.GetPointer()); - MITK_INFO << "1111111111111111111111"; mitk::LayoutAnnotationRenderer::AddAnnotation(scaleAnnotation.GetPointer(), m_PARenderer->GetName(), mitk::LayoutAnnotationRenderer::TopLeft, 5, 5, 1); mitk::LayoutAnnotationRenderer::AddAnnotation(scaleAnnotation.GetPointer(), m_USRenderer, mitk::LayoutAnnotationRenderer::TopLeft, 5, 5, 1); - MITK_INFO << "1111111111111111111111"; - } void QmitkPAUSViewerView::RemoveOverlays() { - // m_PAOverlayManager->RemoveAllOverlays(); + // m_PAOverlayManager->RemoveAllOverlays(); } -void QmitkPAUSViewerView::SetUltrasoundReference(QmitkPAUSViewerView** ultrasoundReference) +void QmitkPAUSViewerView::ScanDataStorage(const mitk::DataNode* dataNode) { - m_UltrasoundReference = ultrasoundReference; + auto storage = this->GetDataStorage(); + auto nodeUS = storage->GetNamedNode("US Viewing Stream - Image 1"); + auto nodePA = storage->GetNamedNode("US Viewing Stream - Image 0"); + + if (nodeUS != nullptr) + { + m_USData->SetData(dynamic_cast(nodeUS->GetData())); + } + if (nodePA != nullptr) + { + m_PAData->SetData(dynamic_cast(nodePA->GetData())); + } + auto renderingManager = mitk::RenderingManager::GetInstance(); + renderingManager->RequestUpdate(GetPARenderWindow()); + renderingManager->RequestUpdate(GetUSRenderWindow()); } + +void QmitkPAUSViewerView::RemovedNodeFromStorage(const mitk::DataNode* dataNode) +{ + auto nodeUS = m_USDataStorage->GetNamedNode(dataNode->GetName()); + auto nodePA = m_PADataStorage->GetNamedNode(dataNode->GetName()); + + //create a dummy image (gray values 0..255) for correct initialization of level window, etc. + mitk::Image::Pointer dummyImage = mitk::ImageGenerator::GenerateRandomImage(100, 100, 1, 1, 1, 1, 1, 255, 0); + + m_USData->SetData(dummyImage); + m_PAData->SetData(dummyImage); +} \ No newline at end of file diff --git a/Plugins/org.mitk.gui.qt.photoacoustics.pausviewer/src/QmitkPAUSViewerView.h b/Plugins/org.mitk.gui.qt.photoacoustics.pausviewer/src/QmitkPAUSViewerView.h index f9c4d43a96..097ab998bc 100644 --- a/Plugins/org.mitk.gui.qt.photoacoustics.pausviewer/src/QmitkPAUSViewerView.h +++ b/Plugins/org.mitk.gui.qt.photoacoustics.pausviewer/src/QmitkPAUSViewerView.h @@ -1,74 +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 QMITKPAUSVIEWERVIEW_H_INCLUDED #define QMITKPAUSVIEWERVIEW_H_INCLUDED #include #include #include #include #include "QmitkRenderWindow.h" #include "ui_QmitkPAUSViewerViewControls.h" #include "org_mitk_gui_qt_photoacoustics_pausviewer_Export.h" #include "mitkCommon.h" class PHOTOACOUSTICS_PAUSVIEWER_EXPORTS QmitkPAUSViewerView : 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; QmitkPAUSViewerView(); virtual ~QmitkPAUSViewerView(); virtual void CreateQtPartControl(QWidget *parent) override; void InitWindows(); void SetPADataStorage(mitk::StandaloneDataStorage::Pointer paStore); void SetUSDataStorage(mitk::StandaloneDataStorage::Pointer usStore); - vtkRenderWindow* GetPARenderWindow(); - vtkRenderWindow* GetUSRenderWindow(); - - void SetUltrasoundReference(QmitkPAUSViewerView** ultrasoundReference); - protected: void AddOverlays(); void RemoveOverlays(); mitk::StandaloneDataStorage::Pointer m_PADataStorage; mitk::StandaloneDataStorage::Pointer m_USDataStorage; + mitk::DataNode::Pointer m_PAData; + mitk::DataNode::Pointer m_USData; mitk::BaseRenderer::Pointer m_PARenderer; mitk::BaseRenderer::Pointer m_USRenderer; virtual void SetFocus() override; virtual void OnSelectionChanged(berry::IWorkbenchPart::Pointer, const QList&) override; Ui::QmitkPAUSViewerViewControls* m_Controls; - QmitkPAUSViewerView** m_UltrasoundReference; + + void ScanDataStorage(const mitk::DataNode* dataNode); + void RemovedNodeFromStorage(const mitk::DataNode* dataNode); + + vtkRenderWindow* GetPARenderWindow(); + vtkRenderWindow* GetUSRenderWindow(); }; #endif // QMITKPAUSVIEWERVIEW_H_INCLUDED