diff --git a/Modules/PhotoacousticsHardware/mitkOphirPyro.cpp b/Modules/PhotoacousticsHardware/mitkOphirPyro.cpp index c9bda56e36..38ee60c7b8 100644 --- a/Modules/PhotoacousticsHardware/mitkOphirPyro.cpp +++ b/Modules/PhotoacousticsHardware/mitkOphirPyro.cpp @@ -1,164 +1,206 @@ /*=================================================================== 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_PulseEnergy.clear(); m_PulseTime.clear(); m_PulseStatus.clear(); + m_TimeStamps.clear(); } mitk::OphirPyro::~OphirPyro() { this->CloseConnection(); MITK_INFO << "[OphirPyro Debug] destroyed that Pyro"; } bool mitk::OphirPyro::StartDataAcquisition() { if (ophirAPI.StartStream(m_DeviceHandle)) m_Streaming = true; 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() +{ + // 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 MakeFolder = "mkdir \"c:/DiPhASTimeStamps/" + currentDate + "\""; + system(MakeFolder.c_str()); + + std::string pathTS = "c:\\DiPhASTimeStamps\\" + currentDate + " Timestamps" + ".csv"; + + std::ofstream timestampFile; + timestampFile.open(pathTS); + + timestampFile << "[index],[timestamp],[PulseEnergy]"; + + for (int index = 0; index < m_TimeStamps.size(); ++index) + { + timestampFile << "\n" << index << "," << m_TimeStamps.at(index) << ","<< m_PulseEnergy.at(index); + } + timestampFile.close(); +} + bool mitk::OphirPyro::StopDataAcquisition() { if (ophirAPI.StopStream(m_DeviceHandle)) m_Streaming = false; + + SaveCsvData(); + return !m_Streaming; } unsigned int mitk::OphirPyro::GetDataFromSensor() { if (m_Streaming) { std::vector newEnergy; std::vector newTimestamp; std::vector newStatus; - MITK_INFO << "DAFUQ"; 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()); + m_TimeStamps.push_back(std::chrono::high_resolution_clock::now().time_since_epoch().count()); + m_PulseTime.insert(m_PulseTime.end(), newTimestamp.begin(), newTimestamp.end()); m_PulseStatus.insert(m_PulseStatus.end(), newStatus.begin(), newStatus.end()); } } catch (std::exception& ex) { MITK_INFO << "this is weird: " << ex.what(); } return noPackages; } return 0; } double mitk::OphirPyro::LookupCurrentPulseEnergy() { if (m_Connected) { if (!m_PulseEnergy.empty()) { MITK_INFO << m_PulseEnergy.size(); return m_PulseEnergy.back(); } else return 0; } } double mitk::OphirPyro::GetNextPulseEnergy() { if (m_Connected) { 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; } } 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 = ophirAPI.ScanUSB(); if (m_SerialNumber != 0) { 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 cef49d0c81..129108cc6d 100644 --- a/Modules/PhotoacousticsHardware/mitkOphirPyro.h +++ b/Modules/PhotoacousticsHardware/mitkOphirPyro.h @@ -1,69 +1,74 @@ /*=================================================================== 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 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(); virtual double LookupCurrentPulseEnergy(); virtual double GetNextPulseEnergy(); virtual double LookupCurrentPulseEnergy(double* timestamp, int* status); virtual double GetNextPulseEnergy(double* timestamp, int* status); protected: OphirPyro(); virtual ~OphirPyro(); + void SaveCsvData(); 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; double m_CurrentWavelength; double m_CurrentEnergyRange; }; } // namespace mitk #endif /* MITKOPHIRPYRO_H_HEADER_INCLUDED */ diff --git a/Modules/US/USHardwareDiPhAS/mitkUSDiPhASImageSource.cpp b/Modules/US/USHardwareDiPhAS/mitkUSDiPhASImageSource.cpp index a2cf0c3093..ce107035bd 100644 --- a/Modules/US/USHardwareDiPhAS/mitkUSDiPhASImageSource.cpp +++ b/Modules/US/USHardwareDiPhAS/mitkUSDiPhASImageSource.cpp @@ -1,460 +1,497 @@ /*=================================================================== 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 // mitk dependencies #include "mitkUSDiPhASDevice.h" #include "mitkUSDiPhASImageSource.h" #include #include "mitkUSDiPhASBModeImageFilter.h" #include "mitkImageCast.h" #include "mitkITKImageImport.h" // itk dependencies #include "itkImage.h" #include "itkResampleImageFilter.h" #include "itkCastImageFilter.h" #include "itkCropImageFilter.h" #include "itkRescaleIntensityImageFilter.h" #include "itkIntensityWindowingImageFilter.h" +#include mitk::USDiPhASImageSource::USDiPhASImageSource(mitk::USDiPhASDevice* device) : m_Image(mitk::Image::New()), m_device(device), startTime(((float)std::clock()) / CLOCKS_PER_SEC), useGUIOutPut(false), m_DataType(DataType::Image_uChar), m_GUIOutput(nullptr), useBModeFilter(false), currentlyRecording(false), m_DataTypeModified(true), m_DataTypeNext(DataType::Image_uChar), m_UseBModeFilterModified(false), - m_UseBModeFilterNext(false) + m_UseBModeFilterNext(false), + m_currenttime(0) { m_ImageMutex = itk::FastMutexLock::New(); m_BufferSize = 100; m_LastWrittenImage = m_BufferSize - 1; m_ImageBuffer.insert(m_ImageBuffer.begin(), m_BufferSize, nullptr); } mitk::USDiPhASImageSource::~USDiPhASImageSource( ) { } void mitk::USDiPhASImageSource::GetNextRawImage( mitk::Image::Pointer& image) { // we get this image pointer from the USDevice and write into it the data we got from the DiPhAS API if (m_DataTypeModified) { SetDataType(m_DataTypeNext); m_DataTypeModified = false; } if (m_UseBModeFilterModified) { SetUseBModeFilter(m_UseBModeFilterNext); m_UseBModeFilterModified = false; } image = &(*m_ImageBuffer[m_LastWrittenImage]); if (image != nullptr) { // do image processing before displaying it if (useBModeFilter && m_DataType == DataType::Beamformed_Short) { // apply BmodeFilter to the image image = ApplyBmodeFilter(image); } } } mitk::Image::Pointer mitk::USDiPhASImageSource::ApplyBmodeFilter2d(mitk::Image::Pointer inputImage) { // we use this seperate ApplyBmodeFilter Method for processing of images that are to be saved later on (see SetRecordingStatus(bool)). // the image needs to be of floating point type for the envelope filter to work; the casting is done automatically by the CastToItkImage typedef itk::Image< float, 2 > itkInputImageType; typedef itk::Image< short, 2 > itkOutputImageType; typedef itk::PhotoacousticBModeImageFilter < itkInputImageType, itkOutputImageType > PhotoacousticBModeImageFilter; PhotoacousticBModeImageFilter::Pointer photoacousticBModeFilter = PhotoacousticBModeImageFilter::New(); itkInputImageType::Pointer itkImage; mitk::CastToItkImage(inputImage, itkImage); photoacousticBModeFilter->SetInput(itkImage); photoacousticBModeFilter->SetDirection(1); return mitk::GrabItkImageMemory(photoacousticBModeFilter->GetOutput()); } mitk::Image::Pointer mitk::USDiPhASImageSource::ApplyBmodeFilter(mitk::Image::Pointer inputImage) { //MITK_INFO << "Applying BMode Filter"; // the image needs to be of floating point type for the envelope filter to work; the casting is done automatically by the CastToItkImage typedef itk::Image< float, 3 > itkFloatImageType; typedef itk::PhotoacousticBModeImageFilter < itkFloatImageType, itkFloatImageType > PhotoacousticBModeImageFilter; PhotoacousticBModeImageFilter::Pointer photoacousticBModeFilter = PhotoacousticBModeImageFilter::New(); typedef itk::ResampleImageFilter < itkFloatImageType, itkFloatImageType > ResampleImageFilter; ResampleImageFilter::Pointer resampleImageFilter = ResampleImageFilter::New(); itkFloatImageType::Pointer itkImage; mitk::CastToItkImage(inputImage, itkImage); photoacousticBModeFilter->SetInput(itkImage); photoacousticBModeFilter->SetDirection(1); itkFloatImageType::Pointer bmode = photoacousticBModeFilter->GetOutput(); itkFloatImageType::SpacingType outputSpacing; itkFloatImageType::SizeType inputSize = itkImage->GetLargestPossibleRegion().GetSize(); itkFloatImageType::SizeType outputSize = inputSize; - outputSize[0] = 128; + outputSize[0] = 256; outputSpacing[0] = itkImage->GetSpacing()[0] * (static_cast(inputSize[0]) / static_cast(outputSize[0])); outputSpacing[1] = outputSpacing[0] / 2; outputSpacing[2] = 0.6; outputSize[1] = inputSize[1] * itkImage->GetSpacing()[1] / outputSpacing[1]; typedef itk::IdentityTransform TransformType; resampleImageFilter->SetInput(bmode); resampleImageFilter->SetSize(outputSize); resampleImageFilter->SetOutputSpacing(outputSpacing); resampleImageFilter->SetTransform(TransformType::New()); resampleImageFilter->UpdateLargestPossibleRegion(); return mitk::GrabItkImageMemory(resampleImageFilter->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) { bool writeImage = ((m_DataType == DataType::Image_uChar) && (imageData != nullptr)) || ((m_DataType == DataType::Beamformed_Short) && (rfDataArrayBeamformed != nullptr)) && !m_Image.IsNull(); if (writeImage) { + //get the timestamp we might later on save + m_currenttime = std::chrono::high_resolution_clock::now().time_since_epoch().count(); + // create a new image and initialize it mitk::Image::Pointer image = mitk::Image::New(); 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); break; } case DataType::Beamformed_Short: { m_ImageDimensions[0] = beamformedLines; m_ImageDimensions[1] = beamformedSamples; m_ImageDimensions[2] = beamformedTotalDatasets; image->Initialize(mitk::MakeScalarPixelType(), 3, m_ImageDimensions); break; } } image->GetGeometry()->SetSpacing(m_ImageSpacing); image->GetGeometry()->Modified(); // write the given buffer into the image switch (m_DataType) { - case DataType::Image_uChar: { + case DataType::Image_uChar: { for (int i = 0; i < imageSetsTotal; i++) { image->SetSlice(&imageData[i*imageHeight*imageWidth], i); } break; } - case DataType::Beamformed_Short: { + case DataType::Beamformed_Short: { short* flipme = new short[beamformedLines*beamformedSamples*beamformedTotalDatasets]; int pixelsPerImage = beamformedLines*beamformedSamples; for (int currentSet = 0; currentSet < beamformedTotalDatasets; currentSet++) { - for (int sample = 0; sample < beamformedSamples; sample++) { for (int 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 } for (int i = 0; i < beamformedTotalDatasets; i++) { image->SetSlice(&flipme[i*beamformedLines*beamformedSamples], i); // set every image to a different slice } delete[] flipme; break; } } // if the user decides to start recording, we feed the vector the generated images if (currentlyRecording) { for (int index = 0; index < image->GetDimension(2); ++index) { if (image->IsSliceSet(index)) { m_recordedImages.push_back(Image::New()); m_recordedImages.back()->Initialize(image->GetPixelType(), 2, image->GetDimensions()); m_recordedImages.back()->SetGeometry(image->GetGeometry()); mitk::ImageReadAccessor inputReadAccessor(image, image->GetSliceData(index)); m_recordedImages.back()->SetSlice(inputReadAccessor.GetData()); } } + // save timestamps for each laser image! + m_timestamps.push_back(m_currenttime); } // [sic!] Thomas: This kills everything, so we commented it out and now it doesnt kill anything.. // if (!useGUIOutPut && m_GUIOutput) { // // Need to do this because the program initializes the GUI twice // // this is probably a bug in UltrasoundSupport, if it's fixed the timing becomes unneccesary // float timePassed = ((float)std::clock()) / CLOCKS_PER_SEC - startTime; // if (timePassed > 10) // { // useGUIOutPut = true; // } // } // if (useGUIOutPut) { // // pass some beamformer state infos to the GUI // getSystemInfo(&BeamformerInfos); // std::ostringstream s; // s << "state info: PRF:" << BeamformerInfos.systemPRF << "Hz, datarate: " << BeamformerInfos.dataTransferRateMBit << "MBit/s"; // m_GUIOutput(QString::fromStdString(s.str())); // } m_ImageBuffer[(m_LastWrittenImage + 1) % m_BufferSize] = image; m_LastWrittenImage = (m_LastWrittenImage + 1) % m_BufferSize; } } 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] = 0.6; 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::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; startTime = ((float)std::clock()) / CLOCKS_PER_SEC; //wait till the callback is available again useGUIOutPut = false; } void mitk::USDiPhASImageSource::SetUseBModeFilter(bool isSet) { useBModeFilter = isSet; } // 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) { - currentlyRecording = true; m_recordedImages.clear(); // we make sure there are no leftovers + m_timestamps.clear(); // also for the timestamps + m_pixelValues.clear(); // aaaand for the pixel values + currentlyRecording = true; } // save images, end recording, and clean up else { currentlyRecording = false; // 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 MakeFolder = "mkdir \"c:/DiPhASImageData/" + currentDate + "\""; system(MakeFolder.c_str()); - // if checked, we add the bmode filter here - for (int index = 0; index < m_recordedImages.size(); ++index) - { - if (m_DataType == 1 && useBModeFilter) - m_recordedImages.at(index) = ApplyBmodeFilter2d(m_recordedImages.at(index)); - } + // initialize file paths and the images Image::Pointer PAImage = Image::New(); Image::Pointer USImage = Image::New(); std::string pathPA = "c:\\DiPhASImageData\\" + currentDate + "\\" + "PAImages" + ".nrrd"; std::string pathUS = "c:\\DiPhASImageData\\" + currentDate + "\\" + "USImages" + ".nrrd"; + std::string pathTS = "c:\\DiPhASImageData\\" + currentDate + "\\" + "Timestamps" + ".csv"; // order the images and save them if (m_device->GetScanMode().beamformingAlgorithm == (int)Beamforming::Interleaved_OA_US) // save a PAImage if we used interleaved mode { OrderImagesInterleaved(PAImage, USImage); mitk::IOUtil::Save(USImage, pathUS); mitk::IOUtil::Save(PAImage, pathPA); + + // read the pixelvalues of the enveloped images at this position + itk::Index<3> pixel = { { m_recordedImages.at(1)->GetDimension(0) / 2, 22, 0 } }; + GetPixelValues(pixel); + + // save the timestamps! + ofstream timestampFile; + + timestampFile.open(pathTS); + timestampFile << "[index],[timestamp],[pixelvalue]"; // write the header + + for (int index = 0; index < m_timestamps.size(); ++index) + { + timestampFile << "\n" << index << "," << m_timestamps.at(index) << "," << m_pixelValues.at(index); + } + timestampFile.close(); } else if (m_device->GetScanMode().beamformingAlgorithm == (int)Beamforming::PlaneWaveCompound) // save no PAImage if we used US only mode { OrderImagesUltrasound(USImage); mitk::IOUtil::Save(USImage, pathUS); } + m_pixelValues.clear(); // clean up the pixel values m_recordedImages.clear(); // clean up the images + m_timestamps.clear(); // clean up the timestamps + } +} + +void mitk::USDiPhASImageSource::GetPixelValues(itk::Index<3> pixel) +{ + unsigned int events = m_device->GetScanMode().transmitEventsCount + 1; // the PA event is not included in the transmitEvents, so we add 1 here + for (int index = 0; index < m_recordedImages.size(); index += events) // omit sound images + { + m_recordedImages.at(index) = ApplyBmodeFilter2d(m_recordedImages.at(index)); + m_pixelValues.push_back(m_recordedImages.at(index).GetPointer()->GetPixelValueByIndex(pixel)); } } void mitk::USDiPhASImageSource::OrderImagesInterleaved(Image::Pointer PAImage, Image::Pointer USImage) { 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 + if (m_DataType == DataType::Beamformed_Short) { width = m_device->GetScanMode().reconstructionLines; height = m_device->GetScanMode().reconstructionSamplesPerLine; } else if (m_DataType == DataType::Image_uChar) { width = m_device->GetScanMode().imageWidth; height = m_device->GetScanMode().imageHeight; } + unsigned int dimLaser[] = { width, height, m_recordedImages.size() / events}; unsigned int dimSound[] = { width, height, m_recordedImages.size() / events * (events-1)}; + PAImage->Initialize(m_recordedImages.back()->GetPixelType(), 3, dimLaser); PAImage->SetGeometry(m_recordedImages.back()->GetGeometry()); USImage->Initialize(m_recordedImages.back()->GetPixelType(), 3, dimSound); USImage->SetGeometry(m_recordedImages.back()->GetGeometry()); + for (int index = 0; index < m_recordedImages.size(); ++index) { mitk::ImageReadAccessor inputReadAccessor(m_recordedImages.at(index)); if (index % events == 0) { PAImage->SetSlice(inputReadAccessor.GetData(), index / events); } else { USImage->SetSlice(inputReadAccessor.GetData(), ((index - (index % events)) / events) + (index % events)-1); } } } void mitk::USDiPhASImageSource::OrderImagesUltrasound(Image::Pointer SoundImage) { unsigned int width = 32; unsigned int height = 32; unsigned int events = m_device->GetScanMode().transmitEventsCount; if (m_DataType == DataType::Beamformed_Short) { width = m_device->GetScanMode().reconstructionLines; height = m_device->GetScanMode().reconstructionSamplesPerLine; } else if (m_DataType == DataType::Image_uChar) { width = m_device->GetScanMode().imageWidth; height = m_device->GetScanMode().imageHeight; } unsigned int dimSound[] = { width, height, m_recordedImages.size()}; SoundImage->Initialize(m_recordedImages.back()->GetPixelType(), 3, dimSound); SoundImage->SetGeometry(m_recordedImages.back()->GetGeometry()); for (int index = 0; index < m_recordedImages.size(); ++index) { mitk::ImageReadAccessor inputReadAccessor(m_recordedImages.at(index)); SoundImage->SetSlice(inputReadAccessor.GetData(), index); } } \ No newline at end of file diff --git a/Modules/US/USHardwareDiPhAS/mitkUSDiPhASImageSource.h b/Modules/US/USHardwareDiPhAS/mitkUSDiPhASImageSource.h index 7e6d26f400..271e0c1566 100644 --- a/Modules/US/USHardwareDiPhAS/mitkUSDiPhASImageSource.h +++ b/Modules/US/USHardwareDiPhAS/mitkUSDiPhASImageSource.h @@ -1,149 +1,158 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #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 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 mitk::USDiPhASDeviceCustomControls::DataType DataType; /** * Implementation of the superclass method. Returns the pointer * to the mitk::Image filled by DiPhAS API callback. */ virtual void GetNextRawImage( mitk::Image::Pointer& ); /** * 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 ModifyDataType(DataType DataT); void ModifyUseBModeFilter(bool isSet); /** * 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); 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_timestamps; + std::vector m_pixelValues; + long long m_currenttime; + bool currentlyRecording; + std::vector m_ImageBuffer; int m_LastWrittenImage; int m_BufferSize; unsigned int m_ImageDimensions[3]; mitk::Vector3D m_ImageSpacing; mitk::Image::Pointer ApplyBmodeFilter(mitk::Image::Pointer inputImage); mitk::Image::Pointer ApplyBmodeFilter2d(mitk::Image::Pointer inputImage); void OrderImagesInterleaved(Image::Pointer LaserImage, Image::Pointer SoundImage); void OrderImagesUltrasound(Image::Pointer SoundImage); + void GetPixelValues(itk::Index<3> pixel); + /** This image holds the image to be displayed right now*/ mitk::Image::Pointer m_Image; 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. */ float startTime; bool useGUIOutPut; BeamformerStateInfoNative BeamformerInfos; bool useBModeFilter; - bool currentlyRecording; + bool m_DataTypeModified; DataType m_DataTypeNext; bool m_UseBModeFilterModified; bool m_UseBModeFilterNext; DataType m_DataType; itk::FastMutexLock::Pointer m_ImageMutex; }; } // namespace mitk #endif // MITKUSDiPhASImageSource_H diff --git a/Plugins/org.mitk.gui.qt.lasercontrol/src/internal/QmitkLaserControl.cpp b/Plugins/org.mitk.gui.qt.lasercontrol/src/internal/QmitkLaserControl.cpp index c8b1364dff..68396e29fd 100644 --- a/Plugins/org.mitk.gui.qt.lasercontrol/src/internal/QmitkLaserControl.cpp +++ b/Plugins/org.mitk.gui.qt.lasercontrol/src/internal/QmitkLaserControl.cpp @@ -1,367 +1,367 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ // Blueberry #include #include // Qmitk #include "QmitkLaserControl.h" // Qt #include #include const std::string OPOLaserControl::VIEW_ID = "org.mitk.views.lasercontrol"; void OPOLaserControl::SetFocus() { } void OPOLaserControl::CreateQtPartControl(QWidget *parent) { // create GUI widgets from the Qt Designer's .ui file m_Controls.setupUi(parent); connect(m_Controls.buttonInitLaser, SIGNAL(clicked()), this, SLOT(InitLaser())); connect(m_Controls.buttonTune, SIGNAL(clicked()), this, SLOT(TuneWavelength())); connect(m_Controls.buttonFastTuning, SIGNAL(clicked()), this, SLOT(StartFastTuning())); connect(m_Controls.buttonFlashlamp, SIGNAL(clicked()), this, SLOT(ToggleFlashlamp())); connect(m_Controls.buttonQSwitch, SIGNAL(clicked()), this, SLOT(ToggleQSwitch())); connect(m_Controls.sliderWavelength, SIGNAL(valueChanged(int)), this, SLOT(SyncWavelengthSetBySlider())); connect(m_Controls.spinBoxWavelength, SIGNAL(valueChanged(double)), this, SLOT(SyncWavelengthSetBySpinBox())); connect(&m_ShutterWatcher, SIGNAL(finished()), this, SLOT(EnableLaser())); m_SyncFromSpinBox = true; m_SyncFromSlider = true; m_PumpLaserConnected = false; m_OPOConnected = false; m_PyroConnected = false; } void OPOLaserControl::EnableLaser() { m_Controls.buttonQSwitch->setEnabled(true); m_Controls.buttonQSwitch->setText("Start Laser"); this->GetState(); } void OPOLaserControl::SyncWavelengthSetBySlider() { if (m_SyncFromSlider) { m_SyncFromSpinBox = false; m_Controls.spinBoxWavelength->setValue(m_Controls.sliderWavelength->value() / 10); } else m_SyncFromSlider = true; } void OPOLaserControl::SyncWavelengthSetBySpinBox() { if (m_SyncFromSpinBox) { m_SyncFromSlider = false; m_Controls.sliderWavelength->setValue(m_Controls.spinBoxWavelength->value() * 10); } else m_SyncFromSpinBox = true; } void OPOLaserControl::InitLaser() { m_Controls.buttonInitLaser->setEnabled(false); m_Controls.buttonInitLaser->setText("working ..."); QThread::sleep(1); if (!m_PumpLaserConnected) { m_PumpLaserController = mitk::QuantelLaser::New(); if (m_PumpLaserController->OpenConnection("OpotekPhocusMobile")) { m_Controls.buttonFlashlamp->setEnabled(true); m_Controls.buttonQSwitch->setEnabled(false); m_Controls.buttonInitLaser->setText("Reset and Release Laser"); std::string answer(""); std::string triggerCommand("TRIG "); if (m_Controls.checkBoxTriggerExternally->isChecked()) { triggerCommand.append("EE"); // set both Triggers external m_PumpLaserController->SendAndReceiveLine(&triggerCommand, &answer); MITK_INFO << answer; } else { triggerCommand.append("II"); // set both Triggers internal m_PumpLaserController->SendAndReceiveLine(&triggerCommand, &answer); MITK_INFO << answer; std::string energyCommand("QDLY 30"); m_PumpLaserController->SendAndReceiveLine(&energyCommand, &answer); MITK_INFO << answer; } m_PumpLaserConnected = true; this->GetState(); } else { QMessageBox::warning(NULL, "Laser Control", "Opotek Pump Laser Initialization Failed."); m_Controls.buttonInitLaser->setText("Init Laser"); m_Controls.buttonInitLaser->setEnabled(true); return; } } else { // destroy and free if (m_PumpLaserController->CloseConnection()) { m_Controls.buttonFlashlamp->setEnabled(false); m_Controls.buttonQSwitch->setEnabled(false); m_Controls.buttonInitLaser->setText("Init Laser"); m_PumpLaserConnected = false; } else { QMessageBox::warning(NULL, "Laser Control", "Opotek Pump Laser Release Failed."); m_Controls.buttonInitLaser->setText("Reset and Release Laser"); } } if (!m_OPOConnected) { m_OPOMotor = mitk::GalilMotor::New(); if (m_OPOMotor->OpenConnection("OpotekPhocusMobile")) { m_Controls.buttonTune->setEnabled(true); m_Controls.buttonFastTuning->setEnabled(true); m_Controls.sliderWavelength->setMinimum(m_OPOMotor->GetMinWavelength() * 10); m_Controls.sliderWavelength->setMaximum(m_OPOMotor->GetMaxWavelength() * 10); m_Controls.spinBoxWavelength->setMinimum(m_OPOMotor->GetMinWavelength()); m_Controls.spinBoxWavelength->setMaximum(m_OPOMotor->GetMaxWavelength()); m_Controls.sliderWavelength->setValue(m_OPOMotor->GetCurrentWavelength() * 10); m_Controls.spinBoxWavelength->setValue(m_OPOMotor->GetCurrentWavelength()); m_OPOConnected = true; // not always right FIXME } else { QMessageBox::warning(NULL, "Laser Control", "OPO Initialization Failed."); m_Controls.buttonInitLaser->setText("Init Laser"); } } else { // destroy and free if (m_OPOMotor->CloseConnection()) { m_Controls.buttonTune->setEnabled(false); m_Controls.buttonFastTuning->setEnabled(false); m_Controls.buttonInitLaser->setText("Init Laser"); m_OPOConnected = false; } else { QMessageBox::warning(NULL, "Laser Control", "OPO Release Failed."); m_Controls.buttonInitLaser->setText("Reset and Release Laser"); } } m_Controls.buttonInitLaser->setEnabled(true); this->GetState(); try { 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_CurrentPulseEnergy = 0; m_PyroConnected = true; QFuture future = QtConcurrent::run(this, &OPOLaserControl::ShowEnergy); m_EnergyWatcher.setFuture(future); } else { m_PyroConnected = false; } } catch (...) { MITK_INFO << " While trying to connect to the Pyro an exception was caught (this almost always happens on the first try after reboot - try again!)"; m_PyroConnected = false; } } void OPOLaserControl::TuneWavelength() { if (m_Controls.checkBoxCalibration->isChecked()) { m_OPOMotor->TuneToWavelength(m_Controls.spinBoxPosition->value(), true); } else { m_OPOMotor->TuneToWavelength(m_Controls.spinBoxWavelength->value(), false); } QString wavelengthText = QString::number(m_OPOMotor->GetCurrentWavelength()); wavelengthText.append("nm"); m_Controls.labelWavelength->setText(wavelengthText); } void OPOLaserControl::StartFastTuning() { std::vector listOfWavelengths; double tmpWavelength = 0; int currentRow = 0; bool success = false; do { if (currentRow != 0) listOfWavelengths.push_back(tmpWavelength); if (m_Controls.tableFastTuningWavelengths->item(0, currentRow)) { QString test = m_Controls.tableFastTuningWavelengths->item(0, currentRow)->text(); tmpWavelength = test.toDouble(&success); currentRow++; } else tmpWavelength = 0; if (success == 0) tmpWavelength = 0; } while (tmpWavelength<950.1 && tmpWavelength>689.9); m_OPOMotor->FastTuneWavelengths(listOfWavelengths); } void OPOLaserControl::ShutterCountDown() { m_Controls.buttonFlashlamp->setText("Stop Lamp"); m_Controls.buttonQSwitch->setEnabled(false); m_Controls.buttonQSwitch->setText("10s ..."); std::this_thread::sleep_for(std::chrono::seconds(1)); m_Controls.buttonQSwitch->setText("9s ..."); std::this_thread::sleep_for(std::chrono::seconds(1)); m_Controls.buttonQSwitch->setText("8s ..."); std::this_thread::sleep_for(std::chrono::seconds(1)); m_Controls.buttonQSwitch->setText("7s ..."); std::this_thread::sleep_for(std::chrono::seconds(1)); m_Controls.buttonQSwitch->setText("6s ..."); std::this_thread::sleep_for(std::chrono::seconds(1)); m_Controls.buttonQSwitch->setText("5s ..."); std::this_thread::sleep_for(std::chrono::seconds(1)); m_Controls.buttonQSwitch->setText("4s ..."); std::this_thread::sleep_for(std::chrono::seconds(1)); m_Controls.buttonQSwitch->setText("3s ..."); std::this_thread::sleep_for(std::chrono::seconds(1)); m_Controls.buttonQSwitch->setText("2s ..."); std::this_thread::sleep_for(std::chrono::seconds(1)); m_Controls.buttonQSwitch->setText("1s ..."); std::this_thread::sleep_for(std::chrono::seconds(1)); return; } void OPOLaserControl::ShowEnergy() { forever { - std::this_thread::sleep_for(std::chrono::milliseconds(200)); + std::this_thread::sleep_for(std::chrono::milliseconds(5)); if (!m_PyroConnected) { MITK_INFO << "[Pyro Debug] StopDataAcquisition: " << m_Pyro->StopDataAcquisition(); MITK_INFO << "[Pyro Debug] CloseConnection: " << m_Pyro->CloseConnection(); m_CurrentPulseEnergy = 0; return; } m_Pyro->GetDataFromSensor(); m_CurrentPulseEnergy = 60000 * m_Pyro->LookupCurrentPulseEnergy(); m_Controls.labelEnergy->setText(std::to_string(m_CurrentPulseEnergy).append(" mJ").c_str()); } } void OPOLaserControl::ToggleFlashlamp() { m_Controls.buttonFlashlamp->setText("..."); if (!m_PumpLaserController->IsFlashing()) { if (m_PumpLaserController->StartFlashing()) { QFuture future = QtConcurrent::run(this, &OPOLaserControl::ShutterCountDown); m_ShutterWatcher.setFuture(future); } else m_Controls.buttonFlashlamp->setText("Start Lamp"); } else { if (m_PumpLaserController->StopFlashing()) { m_Controls.buttonFlashlamp->setText("Start Lamp"); m_Controls.buttonQSwitch->setText("Start Laser"); m_Controls.buttonQSwitch->setEnabled(false); } else m_Controls.buttonFlashlamp->setText("Stop Lamp"); } this->GetState(); } void OPOLaserControl::ToggleQSwitch() { m_Controls.buttonQSwitch->setText("..."); if (!m_PumpLaserController->IsEmitting()) { if (m_PumpLaserController->StartQswitching()) m_Controls.buttonQSwitch->setText("Stop Laser"); else m_Controls.buttonQSwitch->setText("Start Laser"); } else { if (m_PumpLaserController->StopQswitching()) m_Controls.buttonQSwitch->setText("Start Laser"); else m_Controls.buttonQSwitch->setText("Stop Laser"); } this->GetState(); } void OPOLaserControl::OnSelectionChanged(berry::IWorkbenchPart::Pointer /*source*/, const QList& nodes) { } void OPOLaserControl::GetState() { mitk::QuantelLaser::LaserState pumpLaserState = m_PumpLaserController->GetState(); if (pumpLaserState == mitk::QuantelLaser::STATE0) m_Controls.labelStatus->setText("PL0: Boot Fault."); else if (pumpLaserState == mitk::QuantelLaser::STATE1) m_Controls.labelStatus->setText("PL1: Warm Up."); else if (pumpLaserState == mitk::QuantelLaser::STATE2) m_Controls.labelStatus->setText("PL2: Laser Ready."); else if (pumpLaserState == mitk::QuantelLaser::STATE3) m_Controls.labelStatus->setText("PL3: Flashing. Pulse Disabled."); else if (pumpLaserState == mitk::QuantelLaser::STATE4) m_Controls.labelStatus->setText("PL4: Flashing. Shutter Closed."); else if (pumpLaserState == mitk::QuantelLaser::STATE5) m_Controls.labelStatus->setText("PL5: Flashing. Shutter Open."); else if (pumpLaserState == mitk::QuantelLaser::STATE6) m_Controls.labelStatus->setText("PL6: Flashing. Pulse Enabled."); else if (pumpLaserState == mitk::QuantelLaser::STATE7) m_Controls.labelStatus->setText("PL7: Undefined State."); else if (pumpLaserState == mitk::QuantelLaser::UNCONNECTED) m_Controls.labelStatus->setText("PL Not Connected."); } \ No newline at end of file diff --git a/Plugins/org.mitk.gui.qt.ultrasound/src/internal/UltrasoundSupport.cpp b/Plugins/org.mitk.gui.qt.ultrasound/src/internal/UltrasoundSupport.cpp index adb9971765..9c16e84402 100644 --- a/Plugins/org.mitk.gui.qt.ultrasound/src/internal/UltrasoundSupport.cpp +++ b/Plugins/org.mitk.gui.qt.ultrasound/src/internal/UltrasoundSupport.cpp @@ -1,638 +1,640 @@ /*=================================================================== 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 //Mitk #include #include #include #include #include #include #include // Qmitk #include "UltrasoundSupport.h" // Qt #include #include #include // Ultrasound #include "mitkUSDevice.h" #include "QmitkUSAbstractCustomWidget.h" #include #include #include "usServiceReference.h" #include "internal/org_mitk_gui_qt_ultrasound_Activator.h" const std::string UltrasoundSupport::VIEW_ID = "org.mitk.views.ultrasoundsupport"; void UltrasoundSupport::SetFocus() { } void UltrasoundSupport::CreateQtPartControl(QWidget *parent) { //initialize timers m_UpdateTimer = new QTimer(this); m_RenderingTimer2d = new QTimer(this); m_RenderingTimer3d = new QTimer(this); // create GUI widgets from the Qt Designer's .ui file m_Controls.setupUi(parent); //load persistence data before connecting slots (so no slots are called in this phase...) LoadUISettings(); //connect signals and slots... connect(m_Controls.m_DeviceManagerWidget, SIGNAL(NewDeviceButtonClicked()), this, SLOT(OnClickedAddNewDevice())); // Change Widget Visibilities connect(m_Controls.m_DeviceManagerWidget, SIGNAL(NewDeviceButtonClicked()), this->m_Controls.m_NewVideoDeviceWidget, SLOT(CreateNewDevice())); // Init NewDeviceWidget connect(m_Controls.m_ActiveVideoDevices, SIGNAL(ServiceSelectionChanged(us::ServiceReferenceU)), this, SLOT(OnChangedActiveDevice())); connect(m_Controls.m_RunImageTimer, SIGNAL(clicked()), this, SLOT(OnChangedActiveDevice())); connect(m_Controls.m_ShowImageStream, SIGNAL(clicked()), this, SLOT(OnChangedActiveDevice())); connect(m_Controls.m_NewVideoDeviceWidget, SIGNAL(Finished()), this, SLOT(OnNewDeviceWidgetDone())); // After NewDeviceWidget finished editing connect(m_Controls.m_FrameRatePipeline, SIGNAL(valueChanged(int)), this, SLOT(OnChangedFramerateLimit())); connect(m_Controls.m_FrameRate2d, SIGNAL(valueChanged(int)), this, SLOT(OnChangedFramerateLimit())); connect(m_Controls.m_FrameRate3d, SIGNAL(valueChanged(int)), this, SLOT(OnChangedFramerateLimit())); connect(m_Controls.m_FreezeButton, SIGNAL(clicked()), this, SLOT(OnClickedFreezeButton())); connect(m_UpdateTimer, SIGNAL(timeout()), this, SLOT(UpdateImage())); connect(m_RenderingTimer2d, SIGNAL(timeout()), this, SLOT(RenderImage2d())); connect(m_RenderingTimer3d, SIGNAL(timeout()), this, SLOT(RenderImage3d())); connect(m_Controls.m_Update2DView, SIGNAL(clicked()), this, SLOT(StartTimers())); connect(m_Controls.m_Update3DView, SIGNAL(clicked()), this, SLOT(StartTimers())); connect(m_Controls.m_DeviceManagerWidget, SIGNAL(EditDeviceButtonClicked(mitk::USDevice::Pointer)), this, SLOT(OnClickedEditDevice())); //Change Widget Visibilities connect(m_Controls.m_DeviceManagerWidget, SIGNAL(EditDeviceButtonClicked(mitk::USDevice::Pointer)), this->m_Controls.m_NewVideoDeviceWidget, SLOT(EditDevice(mitk::USDevice::Pointer))); // Initializations m_Controls.m_NewVideoDeviceWidget->setVisible(false); std::string filter = "(&(" + us::ServiceConstants::OBJECTCLASS() + "=" + "org.mitk.services.UltrasoundDevice)(" + mitk::USDevice::GetPropertyKeys().US_PROPKEY_ISACTIVE + "=true))"; m_Controls.m_ActiveVideoDevices->Initialize( mitk::USDevice::GetPropertyKeys().US_PROPKEY_LABEL, filter); m_Controls.m_ActiveVideoDevices->SetAutomaticallySelectFirstEntry(true); m_FrameCounterPipeline = 0; m_FrameCounter2d = 0; m_FrameCounter3d = 0; m_Controls.tabWidget->setTabEnabled(1, false); } #include void UltrasoundSupport::InitNewNode() { m_Node.push_back(nullptr); auto& Node = m_Node.back(); Node = mitk::DataNode::New(); Node->SetName("No Data received yet ..."); //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); Node->SetData(dummyImage); m_OldGeometry = dynamic_cast(dummyImage->GetGeometry()); UpdateColormaps(); this->GetDataStorage()->Add(Node); } void UltrasoundSupport::DestroyLastNode() { auto& Node = m_Node.back(); this->GetDataStorage()->Remove(Node); Node->ReleaseData(); m_Node.pop_back(); UpdateColormaps(); } void UltrasoundSupport::UpdateColormaps() { // we update here both the colormaps of the nodes, as well as the // level window for the current dynamic range mitk::LevelWindow levelWindow; if (m_Node.size() > 1) { for (int index = 0; index < m_AmountOfOutputs - 1; ++index) { SetColormap(m_Node.at(index), mitk::LookupTable::LookupTableType::GRAYSCALE); m_Node.at(index)->GetLevelWindow(levelWindow); if (!m_Image->IsEmpty()) levelWindow.SetAuto(m_Image, true, true); m_Node.at(index)->SetLevelWindow(levelWindow); } SetColormap(m_Node.back(), mitk::LookupTable::LookupTableType::JET_TRANSPARENT); m_Node.back()->GetLevelWindow(levelWindow); levelWindow.SetWindowBounds(10, 150, true); m_Node.back()->SetLevelWindow(levelWindow); } else if (m_Node.size() == 1) { SetColormap(m_Node.back(), mitk::LookupTable::LookupTableType::GRAYSCALE); m_Node.back()->GetLevelWindow(levelWindow); if (!m_Image->IsEmpty()) levelWindow.SetAuto(m_Image, true, true); m_Node.back()->SetLevelWindow(levelWindow); } } void UltrasoundSupport::SetColormap(mitk::DataNode::Pointer node, mitk::LookupTable::LookupTableType type) { mitk::LookupTable::Pointer lookupTable = mitk::LookupTable::New(); mitk::LookupTableProperty::Pointer lookupTableProperty = mitk::LookupTableProperty::New(); lookupTable->SetType(type); lookupTableProperty->SetLookupTable(lookupTable); node->SetProperty("LookupTable", lookupTableProperty); mitk::RenderingModeProperty::Pointer renderingMode = dynamic_cast(node->GetProperty("Image Rendering.Mode")); renderingMode->SetValue(mitk::RenderingModeProperty::LOOKUPTABLE_LEVELWINDOW_COLOR); } void UltrasoundSupport::OnClickedAddNewDevice() { m_Controls.m_NewVideoDeviceWidget->setVisible(true); m_Controls.m_DeviceManagerWidget->setVisible(false); m_Controls.m_Headline->setText("Add New Video Device:"); m_Controls.m_WidgetActiveDevices->setVisible(false); } void UltrasoundSupport::OnClickedEditDevice() { m_Controls.m_NewVideoDeviceWidget->setVisible(true); m_Controls.m_DeviceManagerWidget->setVisible(false); m_Controls.m_WidgetActiveDevices->setVisible(false); m_Controls.m_Headline->setText("Edit Video Device:"); } void UltrasoundSupport::UpdateAmountOfOutputs() { // Update the amount of Nodes; there should be one Node for every slide that is set. Note that we must check whether the slices are set, // just using the m_Image->dimension(3) will produce nulltpointers on slices of the image that were not set bool isSet = true; m_AmountOfOutputs = 0; while (isSet) { isSet = m_Image->IsSliceSet(m_AmountOfOutputs); if (isSet) ++m_AmountOfOutputs; } // correct the amount of Nodes to display data while (m_Node.size() < m_AmountOfOutputs) { InitNewNode(); } while (m_Node.size() > m_AmountOfOutputs) { DestroyLastNode(); } // correct the amount of image outputs that we feed the nodes with while (m_curOutput.size() < m_AmountOfOutputs) { m_curOutput.push_back(mitk::Image::New()); // initialize the slice images as 2d images with the size of m_Images unsigned int* dimOld = m_Image->GetDimensions(); unsigned int dim[2] = { dimOld[0], dimOld[1] }; m_curOutput.back()->Initialize(m_Image->GetPixelType(), 2, dim); } while (m_curOutput.size() > m_AmountOfOutputs) { m_curOutput.pop_back(); } } void UltrasoundSupport::UpdateImage() { if(m_Controls.m_ShowImageStream->isChecked()) { m_Device->Modified(); m_Device->Update(); // Update device m_Image = m_Device->GetOutput(); // get the Image data to display UpdateAmountOfOutputs(); // create as many Nodes and Outputs as there are slices in m_Image if (m_AmountOfOutputs == 0) return; // if there is no image to be displayed, skip the rest of this method for (int index = 0; index < m_AmountOfOutputs; ++index) { if (m_curOutput.at(index)->GetDimension(0) != m_Image->GetDimension(0) || m_curOutput.at(index)->GetDimension(1) != m_Image->GetDimension(1) || m_curOutput.at(index)->GetDimension(2) != m_Image->GetDimension(2) || m_curOutput.at(index)->GetPixelType() != m_Image->GetPixelType()) { unsigned int* dimOld = m_Image->GetDimensions(); unsigned int dim[2] = { dimOld[0], dimOld[1]}; m_curOutput.at(index)->Initialize(m_Image->GetPixelType(), 2, dim); // if we switched image resolution or type the outputs must be reinitialized! } if (!m_Image->IsEmpty()) { mitk::ImageReadAccessor inputReadAccessor(m_Image, m_Image->GetSliceData(m_AmountOfOutputs-index-1,0,0,nullptr,mitk::Image::ReferenceMemory)); // just reference the slices, to get a small performance gain m_curOutput.at(index)->SetSlice(inputReadAccessor.GetData()); m_curOutput.at(index)->GetGeometry()->SetIndexToWorldTransform(m_Image->GetSlicedGeometry()->GetIndexToWorldTransform()); // Update the image Output with seperate slices } if (m_curOutput.at(index)->IsEmpty()) { m_Node.at(index)->SetName("No Data received yet ..."); // create a noise image for correct initialization of level window, etc. mitk::Image::Pointer randomImage = mitk::ImageGenerator::GenerateRandomImage(32, 32, 1, 1, 1, 1, 1, 255, 0); m_Node.at(index)->SetData(randomImage); m_curOutput.at(index)->SetGeometry(randomImage->GetGeometry()); } else { char name[30]; sprintf(name, "US Viewing Stream - Image %d", index); m_Node.at(index)->SetName(name); m_Node.at(index)->SetData(m_curOutput.at(index)); // set the name of the Output } } // if the geometry changed: reinitialize the ultrasound image. we use the m_curOutput.at(0) to readjust the geometry if ((m_OldGeometry.IsNotNull()) && (m_curOutput.at(0)->GetGeometry() != NULL) && (!mitk::Equal(m_OldGeometry.GetPointer(), m_curOutput.at(0)->GetGeometry(), 0.0001, false)) ) { mitk::IRenderWindowPart* renderWindow = this->GetRenderWindowPart(); if ((renderWindow != NULL) && (m_curOutput.at(0)->GetTimeGeometry()->IsValid()) && (m_Controls.m_ShowImageStream->isChecked())) { renderWindow->GetRenderingManager()->InitializeViews( m_curOutput.at(0)->GetGeometry(), mitk::RenderingManager::REQUEST_UPDATE_ALL, true); renderWindow->GetRenderingManager()->RequestUpdateAll(); } m_CurrentImageWidth = m_curOutput.at(0)->GetDimension(0); m_CurrentImageHeight = m_curOutput.at(0)->GetDimension(1); m_OldGeometry = dynamic_cast(m_curOutput.at(0)->GetGeometry()); } } //Update frame counter m_FrameCounterPipeline++; if (m_FrameCounterPipeline >= 10) { // compute framerate of pipeline update int nMilliseconds = m_Clock.restart(); int fps = 10000.0f / (nMilliseconds); m_FPSPipeline = fps; m_FrameCounterPipeline = 0; // display lowest framerate in UI int lowestFPS = m_FPSPipeline; if (m_Controls.m_Update2DView->isChecked() && (m_FPS2d < lowestFPS)) { lowestFPS = m_FPS2d; } if (m_Controls.m_Update3DView->isChecked() && (m_FPS3d < lowestFPS)) { lowestFPS = m_FPS3d; } m_Controls.m_FramerateLabel->setText("Current Framerate: " + QString::number(lowestFPS) + " FPS"); } } void UltrasoundSupport::RenderImage2d() { if (!m_Controls.m_Update2DView->isChecked()) return; mitk::IRenderWindowPart* renderWindow = this->GetRenderWindowPart(); renderWindow->GetRenderingManager()->RequestUpdate(mitk::BaseRenderer::GetInstance(mitk::BaseRenderer::GetRenderWindowByName("stdmulti.widget1"))->GetRenderWindow()); //this->RequestRenderWindowUpdate(mitk::RenderingManager::REQUEST_UPDATE_2DWINDOWS); m_FrameCounter2d++; if (m_FrameCounter2d >= 10) { // compute framerate of 2d render window update int nMilliseconds = m_Clock2d.restart(); int fps = 10000.0f / (nMilliseconds); m_FPS2d = fps; m_FrameCounter2d = 0; } } void UltrasoundSupport::RenderImage3d() { if (!m_Controls.m_Update3DView->isChecked()) return; this->RequestRenderWindowUpdate(mitk::RenderingManager::REQUEST_UPDATE_3DWINDOWS); m_FrameCounter3d++; if (m_FrameCounter3d >= 10) { // compute framerate of 2d render window update int nMilliseconds = m_Clock3d.restart(); int fps = 10000.0f / (nMilliseconds); m_FPS3d = fps; m_FrameCounter3d = 0; } } void UltrasoundSupport::OnChangedFramerateLimit() { StopTimers(); int intervalPipeline = (1000 / m_Controls.m_FrameRatePipeline->value()); int interval2D = (1000 / m_Controls.m_FrameRate2d->value()); int interval3D = (1000 / m_Controls.m_FrameRate3d->value()); SetTimerIntervals(intervalPipeline, interval2D, interval3D); StartTimers(); } void UltrasoundSupport::OnClickedFreezeButton() { if (m_Device.IsNull()) { MITK_WARN("UltrasoundSupport") << "Freeze button clicked though no device is selected."; return; } if (m_Device->GetIsFreezed()) { m_Device->SetIsFreezed(false); m_Controls.m_FreezeButton->setText("Freeze"); } else { m_Device->SetIsFreezed(true); m_Controls.m_FreezeButton->setText("Start Viewing Again"); } } void UltrasoundSupport::OnChangedActiveDevice() { //clean up, delete nodes and stop timer StopTimers(); this->RemoveControlWidgets(); for (auto& Node : m_Node) { this->GetDataStorage()->Remove(Node); Node->ReleaseData(); } m_Node.clear(); //get current device, abort if it is invalid m_Device = m_Controls.m_ActiveVideoDevices->GetSelectedService(); if (m_Device.IsNull()) { m_Controls.tabWidget->setTabEnabled(1, false); return; } //create the widgets for this device and enable the widget tab this->CreateControlWidgets(); m_Controls.tabWidget->setTabEnabled(1, true); //start timer if (m_Controls.m_RunImageTimer->isChecked()) { int intervalPipeline = (1000 / m_Controls.m_FrameRatePipeline->value()); int interval2D = (1000 / m_Controls.m_FrameRate2d->value()); int interval3D = (1000 / m_Controls.m_FrameRate3d->value()); SetTimerIntervals(intervalPipeline, interval2D, interval3D); StartTimers(); m_Controls.m_TimerWidget->setEnabled(true); } else { m_Controls.m_TimerWidget->setEnabled(false); } } void UltrasoundSupport::OnNewDeviceWidgetDone() { m_Controls.m_NewVideoDeviceWidget->setVisible(false); m_Controls.m_DeviceManagerWidget->setVisible(true); m_Controls.m_Headline->setText("Ultrasound Devices:"); m_Controls.m_WidgetActiveDevices->setVisible(true); } void UltrasoundSupport::CreateControlWidgets() { m_ControlProbesWidget = new QmitkUSControlsProbesWidget(m_Device->GetControlInterfaceProbes(), m_Controls.m_ToolBoxControlWidgets); m_Controls.probesWidgetContainer->addWidget(m_ControlProbesWidget); // create b mode widget for current device m_ControlBModeWidget = new QmitkUSControlsBModeWidget(m_Device->GetControlInterfaceBMode(), m_Controls.m_ToolBoxControlWidgets); - m_Controls.m_ToolBoxControlWidgets->addItem(m_ControlBModeWidget, "B Mode Controls"); - if (!m_Device->GetControlInterfaceBMode()) + + if (m_Device->GetControlInterfaceBMode()) { - m_Controls.m_ToolBoxControlWidgets->setItemEnabled(m_Controls.m_ToolBoxControlWidgets->count() - 1, false); + m_Controls.m_ToolBoxControlWidgets->addItem(m_ControlBModeWidget, "B Mode Controls"); + //m_Controls.m_ToolBoxControlWidgets->setItemEnabled(m_Controls.m_ToolBoxControlWidgets->count() - 1, false); } // create doppler widget for current device m_ControlDopplerWidget = new QmitkUSControlsDopplerWidget(m_Device->GetControlInterfaceDoppler(), m_Controls.m_ToolBoxControlWidgets); - m_Controls.m_ToolBoxControlWidgets->addItem(m_ControlDopplerWidget, "Doppler Controls"); - if (!m_Device->GetControlInterfaceDoppler()) + + if (m_Device->GetControlInterfaceDoppler()) { - m_Controls.m_ToolBoxControlWidgets->setItemEnabled(m_Controls.m_ToolBoxControlWidgets->count() - 1, false); + m_Controls.m_ToolBoxControlWidgets->addItem(m_ControlDopplerWidget, "Doppler Controls"); + //m_Controls.m_ToolBoxControlWidgets->setItemEnabled(m_Controls.m_ToolBoxControlWidgets->count() - 1, false); } ctkPluginContext* pluginContext = mitk::PluginActivator::GetContext(); if (pluginContext) { std::string filter = "(ork.mitk.services.UltrasoundCustomWidget.deviceClass=" + m_Device->GetDeviceClass() + ")"; QString interfaceName = QString::fromStdString(us_service_interface_iid()); m_CustomWidgetServiceReference = pluginContext->getServiceReferences(interfaceName, QString::fromStdString(filter)); if (m_CustomWidgetServiceReference.size() > 0) { m_ControlCustomWidget = pluginContext->getService (m_CustomWidgetServiceReference.at(0))->CloneForQt(m_Controls.tab2); m_ControlCustomWidget->SetDevice(m_Device); m_Controls.m_ToolBoxControlWidgets->addItem(m_ControlCustomWidget, "Custom Controls"); } else { m_Controls.m_ToolBoxControlWidgets->addItem(new QWidget(m_Controls.m_ToolBoxControlWidgets), "Custom Controls"); m_Controls.m_ToolBoxControlWidgets->setItemEnabled(m_Controls.m_ToolBoxControlWidgets->count() - 1, false); } } // select first enabled control widget for (int n = 0; n < m_Controls.m_ToolBoxControlWidgets->count(); ++n) { if (m_Controls.m_ToolBoxControlWidgets->isItemEnabled(n)) { m_Controls.m_ToolBoxControlWidgets->setCurrentIndex(n); break; } } } void UltrasoundSupport::RemoveControlWidgets() { if (!m_ControlProbesWidget) { return; } //widgets do not exist... nothing to do // remove all control widgets from the tool box widget while (m_Controls.m_ToolBoxControlWidgets->count() > 0) { m_Controls.m_ToolBoxControlWidgets->removeItem(0); } // remove probes widget (which is not part of the tool box widget) m_Controls.probesWidgetContainer->removeWidget(m_ControlProbesWidget); delete m_ControlProbesWidget; m_ControlProbesWidget = 0; delete m_ControlBModeWidget; m_ControlBModeWidget = 0; delete m_ControlDopplerWidget; m_ControlDopplerWidget = 0; // delete custom widget if it is present if (m_ControlCustomWidget) { ctkPluginContext* pluginContext = mitk::PluginActivator::GetContext(); delete m_ControlCustomWidget; m_ControlCustomWidget = 0; if (m_CustomWidgetServiceReference.size() > 0) { pluginContext->ungetService(m_CustomWidgetServiceReference.at(0)); } } } void UltrasoundSupport::OnDeciveServiceEvent(const ctkServiceEvent event) { if (m_Device.IsNull() || event.getType() != us::ServiceEvent::MODIFIED) { return; } ctkServiceReference service = event.getServiceReference(); if (m_Device->GetManufacturer() != service.getProperty(QString::fromStdString(mitk::USDevice::GetPropertyKeys().US_PROPKEY_MANUFACTURER)).toString().toStdString() && m_Device->GetName() != service.getProperty(QString::fromStdString(mitk::USDevice::GetPropertyKeys().US_PROPKEY_NAME)).toString().toStdString()) { return; } if (!m_Device->GetIsActive() && m_UpdateTimer->isActive()) { StopTimers(); } if (m_CurrentDynamicRange != service.getProperty(QString::fromStdString(mitk::USDevice::GetPropertyKeys().US_PROPKEY_BMODE_DYNAMIC_RANGE)).toDouble()) { m_CurrentDynamicRange = service.getProperty(QString::fromStdString(mitk::USDevice::GetPropertyKeys().US_PROPKEY_BMODE_DYNAMIC_RANGE)).toDouble(); // update level window for the current dynamic range mitk::LevelWindow levelWindow; for (auto& Node : m_Node) { Node->GetLevelWindow(levelWindow); levelWindow.SetAuto(m_Image, true, true); levelWindow.SetWindowBounds(55, 125,true); Node->SetLevelWindow(levelWindow); } } } UltrasoundSupport::UltrasoundSupport() : m_ControlCustomWidget(0), m_ControlBModeWidget(0), m_ControlProbesWidget(0), m_ImageAlreadySetToNode(false), m_CurrentImageWidth(0), m_CurrentImageHeight(0), m_AmountOfOutputs(0) { ctkPluginContext* pluginContext = mitk::PluginActivator::GetContext(); if (pluginContext) { // to be notified about service event of an USDevice pluginContext->connectServiceListener(this, "OnDeciveServiceEvent", QString::fromStdString("(" + us::ServiceConstants::OBJECTCLASS() + "=" + us_service_interface_iid() + ")")); } } UltrasoundSupport::~UltrasoundSupport() { try { StopTimers(); // Get all active devicesand deactivate them to prevent freeze std::vector devices = this->m_Controls.m_ActiveVideoDevices->GetAllServices(); for (int i = 0; i < devices.size(); i++) { mitk::USDevice::Pointer device = devices[i]; if (device.IsNotNull() && device->GetIsActive()) { device->Deactivate(); device->Disconnect(); } } StoreUISettings(); } catch (std::exception &e) { MITK_ERROR << "Exception during call of destructor! Message: " << e.what(); } } void UltrasoundSupport::StoreUISettings() { QSettings settings; settings.beginGroup(QString::fromStdString(VIEW_ID)); settings.setValue("DisplayImage", QVariant(m_Controls.m_ShowImageStream->isChecked())); settings.setValue("RunImageTimer", QVariant(m_Controls.m_RunImageTimer->isChecked())); settings.setValue("Update2DView", QVariant(m_Controls.m_Update2DView->isChecked())); settings.setValue("Update3DView", QVariant(m_Controls.m_Update3DView->isChecked())); settings.setValue("UpdateRatePipeline", QVariant(m_Controls.m_FrameRatePipeline->value())); settings.setValue("UpdateRate2d", QVariant(m_Controls.m_FrameRate2d->value())); settings.setValue("UpdateRate3d", QVariant(m_Controls.m_FrameRate3d->value())); settings.endGroup(); } void UltrasoundSupport::LoadUISettings() { QSettings settings; settings.beginGroup(QString::fromStdString(VIEW_ID)); m_Controls.m_ShowImageStream->setChecked(settings.value("DisplayImage", true).toBool()); m_Controls.m_RunImageTimer->setChecked(settings.value("RunImageTimer", true).toBool()); m_Controls.m_Update2DView->setChecked(settings.value("Update2DView", true).toBool()); m_Controls.m_Update3DView->setChecked(settings.value("Update3DView", true).toBool()); m_Controls.m_FrameRatePipeline->setValue(settings.value("UpdateRatePipeline", 50).toInt()); m_Controls.m_FrameRate2d->setValue(settings.value("UpdateRate2d", 20).toInt()); m_Controls.m_FrameRate3d->setValue(settings.value("UpdateRate3d", 5).toInt()); settings.endGroup(); } void UltrasoundSupport::StartTimers() { m_UpdateTimer->start(); if (m_Controls.m_Update2DView->isChecked()) { m_RenderingTimer2d->start(); } if (m_Controls.m_Update3DView->isChecked()) { m_RenderingTimer3d->start(); } } void UltrasoundSupport::StopTimers() { m_UpdateTimer->stop(); m_RenderingTimer2d->stop(); m_RenderingTimer3d->stop(); } void UltrasoundSupport::SetTimerIntervals(int intervalPipeline, int interval2D, int interval3D) { m_UpdateTimer->setInterval(intervalPipeline); m_RenderingTimer2d->setInterval(interval2D); m_RenderingTimer3d->setInterval(interval3D); }