diff --git a/Modules/IGTBase/include/mitkIGTMimeTypes.h b/Modules/IGTBase/include/mitkIGTMimeTypes.h index cef22a1141..52b5f38d47 100644 --- a/Modules/IGTBase/include/mitkIGTMimeTypes.h +++ b/Modules/IGTBase/include/mitkIGTMimeTypes.h @@ -1,34 +1,35 @@ /*=================================================================== 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 MITKIOMIMETYPE_H_HEADER_INCLUDED_ #define MITKIOMIMETYPE_H_HEADER_INCLUDED_ #include #include #include namespace mitk { class MITKIGTBASE_EXPORT IGTMimeTypes { public: static CustomMimeType NAVIGATIONDATASETXML_MIMETYPE(); static CustomMimeType NAVIGATIONDATASETCSV_MIMETYPE(); + static CustomMimeType USDEVICEINFORMATIONXML_MIMETYPE(); }; } #endif // MITKIOMIMETYPE_H_HEADER_INCLUDED_ \ No newline at end of file diff --git a/Modules/IGTBase/src/mitkIGTMimeTypes.cpp b/Modules/IGTBase/src/mitkIGTMimeTypes.cpp index f342ad8bea..a9db7c8f96 100644 --- a/Modules/IGTBase/src/mitkIGTMimeTypes.cpp +++ b/Modules/IGTBase/src/mitkIGTMimeTypes.cpp @@ -1,38 +1,48 @@ /*=================================================================== 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. ===================================================================*/ // MITK #include "mitkIGTMimeTypes.h" mitk::CustomMimeType mitk::IGTMimeTypes::NAVIGATIONDATASETXML_MIMETYPE() { mitk::CustomMimeType mimeType(IOMimeTypes::DEFAULT_BASE_NAME() + ".NavigationDataSet.xml"); std::string category = "NavigationDataSet"; mimeType.SetComment("NavigationDataSet (XML)"); mimeType.SetCategory(category); mimeType.AddExtension("xml"); return mimeType; } mitk::CustomMimeType mitk::IGTMimeTypes::NAVIGATIONDATASETCSV_MIMETYPE() { mitk::CustomMimeType mimeType(IOMimeTypes::DEFAULT_BASE_NAME() + ".NavigationDataSet.csv"); std::string category = "NavigationDataSet"; mimeType.SetComment("NavigationDataSet (csv)"); mimeType.SetCategory(category); mimeType.AddExtension("csv"); return mimeType; +} + +mitk::CustomMimeType mitk::IGTMimeTypes::USDEVICEINFORMATIONXML_MIMETYPE() +{ + mitk::CustomMimeType mimeType(IOMimeTypes::DEFAULT_BASE_NAME() + ".USDeviceInformation.xml"); + std::string category = "USDeviceInformation"; + mimeType.SetComment("USDeviceInformation (XML)"); + mimeType.SetCategory(category); + mimeType.AddExtension("xml"); + return mimeType; } \ No newline at end of file diff --git a/Modules/US/USModel/mitkUSDevice.cpp b/Modules/US/USModel/mitkUSDevice.cpp index 029fdada96..8e80410b82 100644 --- a/Modules/US/USModel/mitkUSDevice.cpp +++ b/Modules/US/USModel/mitkUSDevice.cpp @@ -1,670 +1,692 @@ /*=================================================================== 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 "mitkUSDevice.h" #include "mitkImageReadAccessor.h" // US Control Interfaces #include "mitkUSControlInterfaceProbes.h" #include "mitkUSControlInterfaceBMode.h" #include "mitkUSControlInterfaceDoppler.h" // Microservices #include #include #include #include mitk::USDevice::PropertyKeys mitk::USDevice::GetPropertyKeys() { static mitk::USDevice::PropertyKeys propertyKeys; return propertyKeys; } mitk::USDevice::USImageCropArea mitk::USDevice::GetCropArea() { MITK_INFO << "Return Crop Area L:" << m_CropArea.cropLeft << " R:" << m_CropArea.cropRight << " T:" << m_CropArea.cropTop << " B:" << m_CropArea.cropBottom; return m_CropArea; } mitk::USDevice::USDevice(std::string manufacturer, std::string model) : mitk::ImageSource(), m_IsFreezed(false), m_DeviceState(State_NoState), m_NumberOfOutputs(1), m_Manufacturer(manufacturer), m_Name(model), m_SpawnAcquireThread(true), m_MultiThreader(itk::MultiThreader::New()), m_ImageMutex(itk::FastMutexLock::New()), m_ThreadID(-1), m_UnregisteringStarted(false) { USImageCropArea empty; empty.cropBottom = 0; empty.cropTop = 0; empty.cropLeft = 0; empty.cropRight = 0; this->m_CropArea = empty; // set number of outputs this->SetNumberOfIndexedOutputs(m_NumberOfOutputs); // create a new output mitk::Image::Pointer newOutput = mitk::Image::New(); this->SetNthOutput(0, newOutput); } mitk::USDevice::USDevice(mitk::USImageMetadata::Pointer metadata) : mitk::ImageSource(), m_IsFreezed(false), m_DeviceState(State_NoState), m_SpawnAcquireThread(true), m_MultiThreader(itk::MultiThreader::New()), m_ImageMutex(itk::FastMutexLock::New()), m_ThreadID(-1), m_UnregisteringStarted(false) { m_Manufacturer = metadata->GetDeviceManufacturer(); m_Name = metadata->GetDeviceModel(); m_Comment = metadata->GetDeviceComment(); USImageCropArea empty; empty.cropBottom = 0; empty.cropTop = 0; empty.cropLeft = 0; empty.cropRight = 0; this->m_CropArea = empty; // set number of outputs this->SetNumberOfIndexedOutputs(1); // create a new output mitk::Image::Pointer newOutput = mitk::Image::New(); this->SetNthOutput(0, newOutput); } mitk::USDevice::~USDevice() { if (m_ThreadID >= 0) { m_MultiThreader->TerminateThread(m_ThreadID); } // make sure that the us device is not registered at the micro service // anymore after it is destructed this->UnregisterOnService(); } mitk::USAbstractControlInterface::Pointer mitk::USDevice::GetControlInterfaceCustom() { MITK_INFO << "Custom control interface does not exist for this object."; return nullptr; } mitk::USControlInterfaceBMode::Pointer mitk::USDevice::GetControlInterfaceBMode() { MITK_INFO << "Control interface BMode does not exist for this object."; return nullptr; } mitk::USControlInterfaceProbes::Pointer mitk::USDevice::GetControlInterfaceProbes() { MITK_INFO << "Control interface Probes does not exist for this object."; return nullptr; } mitk::USControlInterfaceDoppler::Pointer mitk::USDevice::GetControlInterfaceDoppler() { MITK_INFO << "Control interface Doppler does not exist for this object."; return nullptr; } void mitk::USDevice::SetManufacturer(std::string manufacturer) { m_Manufacturer = manufacturer; if (m_DeviceState >= State_Initialized) { this->UpdateServiceProperty( mitk::USDevice::GetPropertyKeys().US_PROPKEY_MANUFACTURER, manufacturer); } } void mitk::USDevice::SetName(std::string name) { m_Name = name; if (m_DeviceState >= State_Initialized) { this->UpdateServiceProperty( mitk::USDevice::GetPropertyKeys().US_PROPKEY_NAME, name); } } void mitk::USDevice::SetComment(std::string comment) { m_Comment = comment; if (m_DeviceState >= State_Initialized) { this->UpdateServiceProperty( mitk::USDevice::GetPropertyKeys().US_PROPKEY_COMMENT, comment); } } us::ServiceProperties mitk::USDevice::ConstructServiceProperties() { mitk::USDevice::PropertyKeys propertyKeys = mitk::USDevice::GetPropertyKeys(); us::ServiceProperties props; props[propertyKeys.US_PROPKEY_ISCONNECTED] = this->GetIsConnected() ? "true" : "false"; props[propertyKeys.US_PROPKEY_ISACTIVE] = this->GetIsActive() ? "true" : "false"; props[propertyKeys.US_PROPKEY_LABEL] = this->GetServicePropertyLabel(); // get identifier of selected probe if there is one selected mitk::USControlInterfaceProbes::Pointer probesControls = this->GetControlInterfaceProbes(); if (probesControls.IsNotNull() && probesControls->GetIsActive()) { mitk::USProbe::Pointer probe = probesControls->GetSelectedProbe(); if (probe.IsNotNull()) { props[propertyKeys.US_PROPKEY_PROBES_SELECTED] = probe->GetName(); } } props[propertyKeys.US_PROPKEY_CLASS] = GetDeviceClass(); props[propertyKeys.US_PROPKEY_MANUFACTURER] = m_Manufacturer; props[propertyKeys.US_PROPKEY_NAME] = m_Name; props[propertyKeys.US_PROPKEY_COMMENT] = m_Comment; m_ServiceProperties = props; return props; } void mitk::USDevice::UnregisterOnService() { // unregister on micro service if (m_ServiceRegistration && !m_UnregisteringStarted) { // make sure that unregister is not started a second // time due to a callback during unregister for example m_UnregisteringStarted = true; m_ServiceRegistration.Unregister(); m_ServiceRegistration = 0; } } bool mitk::USDevice::Initialize() { if (!this->OnInitialization()) { return false; } m_DeviceState = State_Initialized; // Get Context and Module us::ModuleContext* context = us::GetModuleContext(); us::ServiceProperties props = this->ConstructServiceProperties(); m_ServiceRegistration = context->RegisterService(this, props); return true; } bool mitk::USDevice::Connect() { MITK_DEBUG << "mitk::USDevice::Connect() called"; if (this->GetIsConnected()) { MITK_INFO("mitkUSDevice") << "Tried to connect an ultrasound device that " "was already connected. Ignoring call..."; return true; } if (!this->GetIsInitialized()) { MITK_ERROR("mitkUSDevice") << "Cannot connect device if it is not in initialized state."; return false; } // Prepare connection, fail if this fails. if (!this->OnConnection()) { return false; } // Update state m_DeviceState = State_Connected; this->UpdateServiceProperty( mitk::USDevice::GetPropertyKeys().US_PROPKEY_ISCONNECTED, true); return true; } void mitk::USDevice::ConnectAsynchron() { this->m_MultiThreader->SpawnThread(this->ConnectThread, this); } bool mitk::USDevice::Disconnect() { if (!GetIsConnected()) { MITK_WARN << "Tried to disconnect an ultrasound device that was not " "connected. Ignoring call..."; return false; } // Prepare connection, fail if this fails. if (!this->OnDisconnection()) return false; // Update state m_DeviceState = State_Initialized; this->UpdateServiceProperty( mitk::USDevice::GetPropertyKeys().US_PROPKEY_ISCONNECTED, false); return true; } bool mitk::USDevice::Activate() { if (!this->GetIsConnected()) { MITK_INFO("mitkUSDevice") << "Cannot activate device if it is not in connected state."; return true; } if (OnActivation()) { m_DeviceState = State_Activated; m_FreezeBarrier = itk::ConditionVariable::New(); // spawn thread for aquire images if us device is active if (m_SpawnAcquireThread) { this->m_ThreadID = this->m_MultiThreader->SpawnThread(this->Acquire, this); } this->UpdateServiceProperty( mitk::USDevice::GetPropertyKeys().US_PROPKEY_ISACTIVE, true); this->UpdateServiceProperty( mitk::USDevice::GetPropertyKeys().US_PROPKEY_LABEL, this->GetServicePropertyLabel()); // initialize the b mode control properties of the micro service mitk::USControlInterfaceBMode::Pointer bmodeControls = this->GetControlInterfaceBMode(); if (bmodeControls.IsNotNull()) { bmodeControls->Initialize(); } } this->ProvideViaOIGTL(); return m_DeviceState == State_Activated; } void mitk::USDevice::ProvideViaOIGTL() { // create a new OpenIGTLink Server if (m_IGTLServer.IsNull()) m_IGTLServer = mitk::IGTLServer::New(true); m_IGTLServer->SetName(this->GetName()); // create a new OpenIGTLink Device source if (m_IGTLMessageProvider.IsNull()) m_IGTLMessageProvider = mitk::IGTLMessageProvider::New(); // set the OpenIGTLink server as the source for the device source m_IGTLMessageProvider->SetIGTLDevice(m_IGTLServer); // register the provider so that it can be configured with the IGTL manager // plugin. This could be hardcoded but now I already have the fancy plugin. m_IGTLMessageProvider->RegisterAsMicroservice(); m_ImageToIGTLMsgFilter = mitk::ImageToIGTLMessageFilter::New(); m_ImageToIGTLMsgFilter->ConnectTo(this); // set the name of this filter to identify it easier m_ImageToIGTLMsgFilter->SetName(this->GetName()); // register this filter as micro service. The message provider looks for // provided IGTLMessageSources, once it found this microservice and someone // requested this data type then the provider will connect with this filter // automatically. m_ImageToIGTLMsgFilter->RegisterAsMicroservice(); } void mitk::USDevice::Deactivate() { if (!this->GetIsActive()) { MITK_WARN("mitkUSDevice") - << "Cannot deactivate a device which is not active."; + << "Cannot deactivate a device which is not activae."; return; } if (!OnDeactivation()) { return; } DisableOIGTL(); m_DeviceState = State_Connected; this->UpdateServiceProperty( mitk::USDevice::GetPropertyKeys().US_PROPKEY_ISACTIVE, false); this->UpdateServiceProperty( mitk::USDevice::GetPropertyKeys().US_PROPKEY_LABEL, this->GetServicePropertyLabel()); } void mitk::USDevice::DisableOIGTL() { // TODO: This seems not to be enough cleanup to catch all cases. For example, if the device is disconnected // from the OIGTL GUI, this won't get cleaned up correctly. m_IGTLServer->CloseConnection(); m_IGTLMessageProvider->UnRegisterMicroservice(); m_ImageToIGTLMsgFilter->UnRegisterMicroservice(); } void mitk::USDevice::SetIsFreezed(bool freeze) { if (!this->GetIsActive()) { MITK_WARN("mitkUSDevice") << "Cannot freeze or unfreeze if device is not active."; return; } this->OnFreeze(freeze); if (freeze) { m_IsFreezed = true; } else { m_IsFreezed = false; // wake up the image acquisition thread m_FreezeBarrier->Signal(); } } bool mitk::USDevice::GetIsFreezed() { + /* if (!this->GetIsActive()) { MITK_WARN("mitkUSDevice")("mitkUSTelemedDevice") << "Cannot get freeze state if the hardware interface is not ready. " "Returning false..."; return false; - } + }*/ return m_IsFreezed; } void mitk::USDevice::PushFilter(AbstractOpenCVImageFilter::Pointer filter) { mitk::USImageSource::Pointer imageSource = this->GetUSImageSource(); if (imageSource.IsNull()) { MITK_ERROR << "ImageSource must not be null when pushing a filter."; mitkThrow() << "ImageSource must not be null when pushing a filter."; } imageSource->PushFilter(filter); } void mitk::USDevice::PushFilterIfNotPushedBefore( AbstractOpenCVImageFilter::Pointer filter) { mitk::USImageSource::Pointer imageSource = this->GetUSImageSource(); if (imageSource.IsNull()) { MITK_ERROR << "ImageSource must not be null when pushing a filter."; mitkThrow() << "ImageSource must not be null when pushing a filter."; } if (!imageSource->GetIsFilterInThePipeline(filter)) { imageSource->PushFilter(filter); } } bool mitk::USDevice::RemoveFilter(AbstractOpenCVImageFilter::Pointer filter) { mitk::USImageSource::Pointer imageSource = this->GetUSImageSource(); if (imageSource.IsNull()) { MITK_ERROR << "ImageSource must not be null when pushing a filter."; mitkThrow() << "ImageSource must not be null when removing a filter."; } return imageSource->RemoveFilter(filter); } void mitk::USDevice::UpdateServiceProperty(std::string key, std::string value) { m_ServiceProperties[key] = value; m_ServiceRegistration.SetProperties(m_ServiceProperties); // send event to notify listeners about the changed property m_PropertyChangedMessage(key, value); } void mitk::USDevice::UpdateServiceProperty(std::string key, double value) { std::stringstream stream; stream << value; this->UpdateServiceProperty(key, stream.str()); } void mitk::USDevice::UpdateServiceProperty(std::string key, bool value) { this->UpdateServiceProperty( key, value ? std::string("true") : std::string("false")); } /** mitk::Image* mitk::USDevice::GetOutput() { if (this->GetNumberOfOutputs() < 1) return nullptr; return static_cast(this->ProcessObject::GetPrimaryOutput()); } mitk::Image* mitk::USDevice::GetOutput(unsigned int idx) { if (this->GetNumberOfOutputs() < 1) return nullptr; return static_cast(this->ProcessObject::GetOutput(idx)); } void mitk::USDevice::GraftOutput(itk::DataObject *graft) { this->GraftNthOutput(0, graft); } void mitk::USDevice::GraftNthOutput(unsigned int idx, itk::DataObject *graft) { if ( idx >= this->GetNumberOfOutputs() ) { itkExceptionMacro(<<"Requested to graft output " << idx << " but this filter only has " << this->GetNumberOfOutputs() << " Outputs."); } if ( !graft ) { itkExceptionMacro(<<"Requested to graft output with a nullptr pointer object" ); } itk::DataObject* output = this->GetOutput(idx); if ( !output ) { itkExceptionMacro(<<"Requested to graft output that is a nullptr pointer" ); } // Call Graft on USImage to copy member data output->Graft( graft ); } */ void mitk::USDevice::GrabImage() { std::vector image = this->GetUSImageSource()->GetNextImage(); m_ImageMutex->Lock(); this->SetImageVector(image); m_ImageMutex->Unlock(); } //########### GETTER & SETTER ##################// bool mitk::USDevice::GetIsInitialized() { return m_DeviceState == State_Initialized; } bool mitk::USDevice::GetIsActive() { return m_DeviceState == State_Activated; } bool mitk::USDevice::GetIsConnected() { return m_DeviceState == State_Connected; } std::string mitk::USDevice::GetDeviceManufacturer() { return m_Manufacturer; } std::string mitk::USDevice::GetDeviceModel() { return m_Name; } std::string mitk::USDevice::GetDeviceComment() { return m_Comment; } +void mitk::USDevice::SetSpacing(double xSpacing, double ySpacing) +{ + m_Spacing[0] = xSpacing; + m_Spacing[1] = ySpacing; + m_Spacing[2] = 1; + + + if( m_ImageVector.size() > 0 ) + { + for( size_t index = 0; index < m_ImageVector.size(); ++index ) + { + auto& image = m_ImageVector[index]; + if( image.IsNotNull() && image->IsInitialized() ) + { + image->GetGeometry()->SetSpacing(m_Spacing); + } + } + this->Modified(); + } + MITK_INFO << "Spacing: " << m_Spacing; +} + void mitk::USDevice::GenerateData() { m_ImageMutex->Lock(); for (unsigned int i = 0; i < m_ImageVector.size() && i < this->GetNumberOfIndexedOutputs(); ++i) { auto& image = m_ImageVector[i]; if (image.IsNull() || !image->IsInitialized()) { // skip image } else { mitk::Image::Pointer output = this->GetOutput(i); if (!output->IsInitialized() || output->GetDimension(0) != image->GetDimension(0) || output->GetDimension(1) != image->GetDimension(1) || output->GetDimension(2) != image->GetDimension(2) || output->GetPixelType() != image->GetPixelType()) { output->Initialize(image->GetPixelType(), image->GetDimension(), image->GetDimensions()); } // copy contents of the given image into the member variable mitk::ImageReadAccessor inputReadAccessor(image); output->SetImportVolume(inputReadAccessor.GetData()); - output->SetGeometry(image->GetGeometry()); } - } + } m_ImageMutex->Unlock(); }; std::string mitk::USDevice::GetServicePropertyLabel() { std::string isActive; if (this->GetIsActive()) { isActive = " (Active)"; } else { isActive = " (Inactive)"; } // e.g.: Zonare MyLab5 (Active) return m_Manufacturer + " " + m_Name + isActive; } ITK_THREAD_RETURN_TYPE mitk::USDevice::Acquire(void* pInfoStruct) { /* extract this pointer from Thread Info structure */ struct itk::MultiThreader::ThreadInfoStruct* pInfo = (struct itk::MultiThreader::ThreadInfoStruct*)pInfoStruct; mitk::USDevice* device = (mitk::USDevice*)pInfo->UserData; while (device->GetIsActive()) { // lock this thread when ultrasound device is freezed if (device->m_IsFreezed) { itk::SimpleMutexLock* mutex = &(device->m_FreezeMutex); mutex->Lock(); if (device->m_FreezeBarrier.IsNotNull()) { device->m_FreezeBarrier->Wait(mutex); } } device->GrabImage(); } return ITK_THREAD_RETURN_VALUE; } ITK_THREAD_RETURN_TYPE mitk::USDevice::ConnectThread(void* pInfoStruct) { /* extract this pointer from Thread Info structure */ struct itk::MultiThreader::ThreadInfoStruct* pInfo = (struct itk::MultiThreader::ThreadInfoStruct*)pInfoStruct; mitk::USDevice* device = (mitk::USDevice*)pInfo->UserData; device->Connect(); return ITK_THREAD_RETURN_VALUE; } void mitk::USDevice::ProbeChanged(std::string probename) { this->UpdateServiceProperty(mitk::USDevice::GetPropertyKeys().US_PROPKEY_PROBES_SELECTED, probename); } void mitk::USDevice::DepthChanged(double depth) { this->UpdateServiceProperty(mitk::USDevice::GetPropertyKeys().US_PROPKEY_BMODE_DEPTH, depth); } diff --git a/Modules/US/USModel/mitkUSDevice.h b/Modules/US/USModel/mitkUSDevice.h index 2806feace0..b54f67c583 100644 --- a/Modules/US/USModel/mitkUSDevice.h +++ b/Modules/US/USModel/mitkUSDevice.h @@ -1,478 +1,490 @@ /*=================================================================== 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 MITKUSDevice_H_HEADER_INCLUDED_ #define MITKUSDevice_H_HEADER_INCLUDED_ // STL #include // MitkUS #include "mitkUSProbe.h" #include #include "mitkUSImageSource.h" // MitkIGTL #include "mitkIGTLMessageProvider.h" #include "mitkIGTLServer.h" #include "mitkIGTLDeviceSource.h" #include "mitkImageToIGTLMessageFilter.h" // MITK #include #include #include // ITK #include #include // Microservices #include #include #include // DEPRECATED #include "mitkUSImageMetadata.h" namespace itk { template class SmartPointer; } namespace mitk { class USAbstractControlInterface; class USControlInterfaceBMode; class USControlInterfaceProbes; class USControlInterfaceDoppler; /** * \brief A device holds information about it's model, make and the connected probes. It is the * common super class for all devices and acts as an image source for mitkUSImages. It is the base class * for all US Devices, and every new device should extend it. * * US Devices support output of calibrated images, i.e. images that include a specific geometry. * To achieve this, call SetCalibration, and make sure that the subclass also calls apply * transformation at some point (The USDevice does not automatically apply the transformation to the image) * * Note that USDevices will be removed from micro servive when their * destructor is called. Registering into micro service is done when * mitk::USDevice::Initialize() is called. * * \ingroup US */ class MITKUS_EXPORT USDevice : public mitk::ImageSource { public: enum DeviceStates { State_NoState, State_Initialized, State_Connected, State_Activated }; mitkClassMacro(USDevice, mitk::ImageSource); + itkSetMacro(SpawnAcquireThread, bool); + itkGetMacro(SpawnAcquireThread, bool); struct USImageCropArea { int cropLeft; int cropRight; int cropBottom; int cropTop; }; /** * \brief These constants are used in conjunction with Microservices. * The constants aren't defined as static member attributes to avoid the * "static initialization order fiasco", which would occur when objects of * this class are used in module activators (for restoring stored device, * for example). */ struct PropertyKeys { const std::string US_INTERFACE_NAME; // Common Interface name of all US Devices. Used to refer to this device via Microservices const std::string US_PROPKEY_MANUFACTURER; const std::string US_PROPKEY_NAME; const std::string US_PROPKEY_COMMENT; const std::string US_PROPKEY_LABEL; // Human readable text represntation of this device const std::string US_PROPKEY_ISCONNECTED; // Whether this device is connected or not. const std::string US_PROPKEY_ISACTIVE; // Whether this device is active or not. const std::string US_PROPKEY_CLASS; // Class Name of this Object const std::string US_PROPKEY_PROBES_SELECTED; const std::string US_PROPKEY_BMODE_FREQUENCY; const std::string US_PROPKEY_BMODE_POWER; const std::string US_PROPKEY_BMODE_DEPTH; const std::string US_PROPKEY_BMODE_GAIN; const std::string US_PROPKEY_BMODE_REJECTION; const std::string US_PROPKEY_BMODE_DYNAMIC_RANGE; PropertyKeys() : US_INTERFACE_NAME("org.mitk.services.UltrasoundDevice"), US_PROPKEY_MANUFACTURER(US_INTERFACE_NAME + ".manufacturer"), US_PROPKEY_NAME(US_INTERFACE_NAME + ".name"), US_PROPKEY_COMMENT(US_INTERFACE_NAME + ".comment"), US_PROPKEY_LABEL(US_INTERFACE_NAME + ".label"), US_PROPKEY_ISCONNECTED(US_INTERFACE_NAME + ".isConnected"), US_PROPKEY_ISACTIVE(US_INTERFACE_NAME + ".isActive"), US_PROPKEY_CLASS(US_INTERFACE_NAME + ".class"), US_PROPKEY_PROBES_SELECTED(US_INTERFACE_NAME + ".probes.selected"), US_PROPKEY_BMODE_FREQUENCY(US_INTERFACE_NAME + ".bmode.frequency"), US_PROPKEY_BMODE_POWER(US_INTERFACE_NAME + ".bmode.power"), US_PROPKEY_BMODE_DEPTH(US_INTERFACE_NAME + ".bmode.depth"), US_PROPKEY_BMODE_GAIN(US_INTERFACE_NAME + ".bmode.gain"), US_PROPKEY_BMODE_REJECTION(US_INTERFACE_NAME + ".bmode.rejection"), US_PROPKEY_BMODE_DYNAMIC_RANGE(US_INTERFACE_NAME + ".bmode.dynamicRange") {} }; /** * \brief Event for being notified about changes of the micro service properties. * This event can be used if no micro service context is available. */ mitkNewMessage2Macro(PropertyChanged, const std::string&, const std::string&) /** * \return keys for the microservice properties of ultrasound devices */ static mitk::USDevice::PropertyKeys GetPropertyKeys(); /** * \brief Default getter for the custom control interface. * Has to be implemented in a subclass if a custom control interface is * available. Default implementation returns null. * * \return null pointer */ virtual itk::SmartPointer GetControlInterfaceCustom(); /** * \brief Default getter for the b mode control interface. * Has to be implemented in a subclass if a b mode control interface is * available. Default implementation returns null. * * \return null pointer */ virtual itk::SmartPointer GetControlInterfaceBMode(); /** * \brief Default getter for the probes control interface. * Has to be implemented in a subclass if a probes control interface is * available. Default implementation returns null. * * \return null pointer */ virtual itk::SmartPointer GetControlInterfaceProbes(); /** * \brief Default getter for the doppler control interface. * Has to be implemented in a subclass if a doppler control interface is * available. Default implementation returns null. * * \return null pointer */ virtual itk::SmartPointer GetControlInterfaceDoppler(); /** * \brief Changes device state to mitk::USDevice::State_Initialized. * During initialization the virtual method * mitk::USDevice::OnInitialization will be called. If this method * returns false the initialization process will be canceled. Otherwise * the mitk::USDevice is registered in a micro service. */ bool Initialize(); /** * \brief Connects this device. A connected device is ready to deliver images (i.e. be Activated). A Connected Device can be active. A disconnected Device cannot be active. * Internally calls onConnect and then registers the device with the service. A device usually should * override the OnConnection() method, but never the Connect() method, since this will possibly exclude the device * from normal service management. The exact flow of events is: * 0. Check if the device is already connected. If yes, return true anyway, but don't do anything. * 1. Call OnConnection() Here, a device should establish it's connection with the hardware Afterwards, it should be ready to start transmitting images at any time. * 2. If OnConnection() returns true ("successful"), then the device is registered with the service. * 3. if not, it the method itself returns false or may throw an expection, depeneding on the device implementation. * */ bool Connect(); void ConnectAsynchron(); /** * \brief Works analogously to mitk::USDevice::Connect(). Don't override this Method, but onDisconnection instead. */ bool Disconnect(); /** * \brief Activates this device. * After the activation process, the device will start to produce images. * This Method will fail, if the device is not connected. */ bool Activate(); /** * \brief Deactivates this device. * After the deactivation process, the device will no longer produce * images, but still be connected. */ void Deactivate(); /** * \brief Can toggle if ultrasound image is currently updated or freezed. * * \param freeze true to stop updating the ultrasound image, false to start updating again */ virtual void SetIsFreezed(bool freeze); /** * \return true if device is currently freezed (no image update is done), false otherwise */ virtual bool GetIsFreezed(); void PushFilter(AbstractOpenCVImageFilter::Pointer filter); void PushFilterIfNotPushedBefore(AbstractOpenCVImageFilter::Pointer filter); bool RemoveFilter(AbstractOpenCVImageFilter::Pointer filter); /** * @brief To be called when the used probe changed. Will update the service properties * @param probename of the now used probe */ void ProbeChanged(std::string probename); /** * @brief To be called when the scanning depth of the probe changed. Will update the service properties * @param depth that is now used */ void DepthChanged(double depth); /** * \brief Given property is updated in the device micro service. * This method is mainly for being used by the control interface * superclasses. You do not need to call it by yoursefs in your * concrete control interface classes. */ void UpdateServiceProperty(std::string key, std::string value); void UpdateServiceProperty(std::string key, double value); void UpdateServiceProperty(std::string key, bool value); //########### GETTER & SETTER ##################// /** * \brief Returns the Class of the Device. This Method must be reimplemented by every Inheriting Class. */ virtual std::string GetDeviceClass() = 0; /** * \brief True, if the device object is created and initialized, false otherwise. */ bool GetIsInitialized(); /** * \brief True, if the device is currently generating image data, false otherwise. */ bool GetIsActive(); /** * \brief True, if the device is currently ready to start transmitting image data or is already * transmitting image data. A disconnected device cannot be activated. */ bool GetIsConnected(); /* @return Returns the area that will be cropped from the US image. Is disabled / [0,0,0,0] by default. */ mitk::USDevice::USImageCropArea GetCropArea(); + /* @return Returns the size of the m_ImageVector of the ultrasound device.*/ + unsigned int GetSizeOfImageVector(); + /** @return Returns the current image source of this device. */ virtual USImageSource::Pointer GetUSImageSource() = 0; /** \brief Deprecated -> use GetManufacturer() instead */ DEPRECATED(std::string GetDeviceManufacturer()); /** \brief Deprecated -> use GetName() instead */ DEPRECATED(std::string GetDeviceModel()); /** \brief Deprecated -> use GetCommend() instead */ DEPRECATED(std::string GetDeviceComment()); itkGetMacro(Manufacturer, std::string); itkGetMacro(Name, std::string); itkGetMacro(Comment, std::string); void SetManufacturer(std::string manufacturer); void SetName(std::string name); void SetComment(std::string comment); itkGetMacro(DeviceState, DeviceStates) itkGetMacro(ServiceProperties, us::ServiceProperties) void GrabImage(); + virtual void SetSpacing(double xSpacing, double ySpacing); + + protected: + + // Threading-Related + itk::ConditionVariable::Pointer m_FreezeBarrier; + itk::SimpleMutexLock m_FreezeMutex; + itk::MultiThreader::Pointer m_MultiThreader; ///< itk::MultiThreader used for thread handling + itk::FastMutexLock::Pointer m_ImageMutex; ///< mutex for images provided by the image source + int m_ThreadID; ///< ID of the started thread + virtual void SetImageVector(std::vector vec) { if (this->m_ImageVector != vec) { this->m_ImageVector = vec; this->Modified(); } } - itkSetMacro(SpawnAcquireThread, bool); - itkGetMacro(SpawnAcquireThread, bool); static ITK_THREAD_RETURN_TYPE Acquire(void* pInfoStruct); static ITK_THREAD_RETURN_TYPE ConnectThread(void* pInfoStruct); std::vector m_ImageVector; //mitk::Image::Pointer m_OutputImage; + // Variables to determine if spacing was calibrated and needs to be applied to the incoming images + mitk::Vector3D m_Spacing; + /** * \brief Registers an OpenIGTLink device as a microservice so that we can send the images of * this device via the network. */ void ProvideViaOIGTL(); /** * \brief Deregisters the microservices for OpenIGTLink. */ void DisableOIGTL(); mitk::IGTLServer::Pointer m_IGTLServer; mitk::IGTLMessageProvider::Pointer m_IGTLMessageProvider; mitk::ImageToIGTLMessageFilter::Pointer m_ImageToIGTLMsgFilter; bool m_IsFreezed; DeviceStates m_DeviceState; /* @brief defines the area that should be cropped from the US image */ USImageCropArea m_CropArea; /** * \brief This Method constructs the service properties which can later be used to * register the object with the Microservices * Return service properties */ us::ServiceProperties ConstructServiceProperties(); /** * \brief Remove this device from the micro service. */ void UnregisterOnService(); /** * \brief Is called during the initialization process. * Override this method in a subclass to handle the actual initialization. * If it returns false, the initialization process will be canceled. * * \return true if successful and false if unsuccessful * \throw mitk::Exception implementation may throw an exception to clarify what went wrong */ virtual bool OnInitialization() = 0; /** * \brief Is called during the connection process. * Override this method in a subclass to handle the actual connection. * If it returns false, the connection process will be canceled. * * \return true if successful and false if unsuccessful * \throw mitk::Exception implementation may throw an exception to clarify what went wrong */ virtual bool OnConnection() = 0; /** * \brief Is called during the disconnection process. * Override this method in a subclass to handle the actual disconnection. * If it returns false, the disconnection process will be canceled. * * \return true if successful and false if unsuccessful * \throw mitk::Exception implementation may throw an exception to clarify what went wrong */ virtual bool OnDisconnection() = 0; /** * \brief Is called during the activation process. * After this method is finished, the device should be generating images. * If it returns false, the activation process will be canceled. * * \return true if successful and false if unsuccessful * \throw mitk::Exception implementation may throw an exception to clarify what went wrong */ virtual bool OnActivation() = 0; /** * \brief Is called during the deactivation process. * After a call to this method the device should still be connected, * but not producing images anymore. * * \return true if successful and false if unsuccessful * \throw mitk::Exception implementation may throw an exception to clarify what went wrong */ virtual bool OnDeactivation() = 0; /** * \brief Called when mitk::USDevice::SetIsFreezed() is called. * Subclasses can overwrite this method to do additional actions. Default * implementation does noting. */ virtual void OnFreeze(bool) { } /** * \brief Enforces minimal Metadata to be set. */ USDevice(std::string manufacturer, std::string model); /** * \brief Constructs a device with the given Metadata. Make sure the Metadata contains meaningful content! * \deprecated Use USDevice(std::string manufacturer, std::string model) instead. */ USDevice(mitk::USImageMetadata::Pointer metadata); ~USDevice() override; /** * \brief Grabs the next frame from the Video input. * This method is called internally, whenever Update() is invoked by an Output. */ void GenerateData() override; std::string GetServicePropertyLabel(); unsigned int m_NumberOfOutputs; - /** - * \brief The device's ServiceRegistration object that allows to modify it's Microservice registraton details. - */ - us::ServiceRegistration m_ServiceRegistration; /** * \brief Properties of the device's Microservice. */ us::ServiceProperties m_ServiceProperties; + /** + * \brief The device's ServiceRegistration object that allows to modify it's Microservice registraton details. + */ + us::ServiceRegistration m_ServiceRegistration; + + private: std::string m_Manufacturer; std::string m_Name; std::string m_Comment; bool m_SpawnAcquireThread; - // Threading-Related - itk::ConditionVariable::Pointer m_FreezeBarrier; - itk::SimpleMutexLock m_FreezeMutex; - itk::MultiThreader::Pointer m_MultiThreader; ///< itk::MultiThreader used for thread handling - itk::FastMutexLock::Pointer m_ImageMutex; ///< mutex for images provided by the image source - int m_ThreadID; ///< ID of the started thread - bool m_UnregisteringStarted; }; } // namespace mitk // This is the microservice declaration. Do not meddle! MITK_DECLARE_SERVICE_INTERFACE(mitk::USDevice, "org.mitk.services.UltrasoundDevice") #endif // MITKUSDevice_H_HEADER_INCLUDED_ diff --git a/Modules/US/USModel/mitkUSDeviceReaderWriterConstants.h b/Modules/US/USModel/mitkUSDeviceReaderWriterConstants.h new file mode 100644 index 0000000000..5535469911 --- /dev/null +++ b/Modules/US/USModel/mitkUSDeviceReaderWriterConstants.h @@ -0,0 +1,53 @@ +/*=================================================================== + +The Medical Imaging Interaction Toolkit (MITK) + +Copyright (c) German Cancer Research Center, +Division of Medical and Biological Informatics. +All rights reserved. + +This software is distributed WITHOUT ANY WARRANTY; without +even the implied warranty of MERCHANTABILITY or FITNESS FOR +A PARTICULAR PURPOSE. + +See LICENSE.txt or http://www.mitk.org for details. + +===================================================================*/ + + +#ifndef mitkUSDeviceReaderWriterConstants_H_HEADER_INCLUDED_ +#define mitkUSDeviceReaderWriterConstants_H_HEADER_INCLUDED_ + +const static char* TAG_ULTRASOUNDDEVICE = "ULTRASOUNDDEVICE"; +const static char* TAG_GENERALSETTINGS = "GENERALSETTINGS"; +const static char* TAG_PROBES = "PROBES"; +const static char* TAG_PROBE = "PROBE"; +const static char* TAG_DEPTHS = "DEPTHS"; +const static char* TAG_DEPTH = "DEPTH"; +const static char* TAG_SPACING = "SPACING"; +const static char* TAG_CROPPING = "CROPPING"; + +const static char* ATTR_FILEVERS = "filevers"; +const static char* ATTR_TYPE = "type"; +const static char* ATTR_NAME = "name"; +const static char* ATTR_MANUFACTURER = "manufacturer"; +const static char* ATTR_MODEL = "model"; +const static char* ATTR_COMMENT = "comment"; +const static char* ATTR_IMAGESTREAMS = "imagestreams"; +const static char* ATTR_GREYSCALE = "greyscale"; +const static char* ATTR_RESOLUTIONOVERRIDE = "resolutionOverride"; +const static char* ATTR_RESOLUTIONWIDTH = "resolutionWidth"; +const static char* ATTR_RESOLUTIONHEIGHT = "resolutionHeight"; +const static char* ATTR_SOURCEID = "sourceID"; +const static char* ATTR_FILEPATH = "filepath"; +const static char* ATTR_OPENCVPORT = "opencvPort"; +const static char* ATTR_DEPTH = "depth"; +const static char* ATTR_X = "x"; +const static char* ATTR_Y = "y"; +const static char* ATTR_TOP = "top"; +const static char* ATTR_BOTTOM = "bottom"; +const static char* ATTR_LEFT = "left"; +const static char* ATTR_RIGHT = "right"; + + +#endif // mitkUSDeviceReaderWriterConstants_H_HEADER_INCLUDED_ diff --git a/Modules/US/USModel/mitkUSDeviceReaderXML.cpp b/Modules/US/USModel/mitkUSDeviceReaderXML.cpp new file mode 100644 index 0000000000..7e7aed1530 --- /dev/null +++ b/Modules/US/USModel/mitkUSDeviceReaderXML.cpp @@ -0,0 +1,205 @@ +/*=================================================================== + +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. + +===================================================================*/ + +// MITK +#include "mitkUSDeviceReaderWriterConstants.h" +#include "mitkUSDeviceReaderXML.h" +#include +#include + +#include + +// Third Party +#include +#include +#include + +mitk::USDeviceReaderXML::USDeviceReaderXML() : AbstractFileReader( + mitk::IGTMimeTypes::USDEVICEINFORMATIONXML_MIMETYPE(), + "MITK USDevice Reader (XML)"), m_Filename("") +{ + RegisterService(); +} + +mitk::USDeviceReaderXML::~USDeviceReaderXML() +{ +} + +mitk::USDeviceReaderXML::USVideoDeviceConfigData &mitk::USDeviceReaderXML::GetUSVideoDeviceConfigData() +{ + return m_DeviceConfig; +} + +mitk::USDeviceReaderXML::USDeviceReaderXML(const mitk::USDeviceReaderXML& other) : AbstractFileReader(other) +{ +} + +mitk::USDeviceReaderXML* mitk::USDeviceReaderXML::Clone() const +{ + return new USDeviceReaderXML(*this); +} + + + + +std::vector> mitk::USDeviceReaderXML::Read() +{ + MITK_WARN << "This method is not implemented. \ + Please use the method ReadUltrasoundDeviceConfiguration() instead."; + std::vector result; + return result; +} + +bool mitk::USDeviceReaderXML::ReadUltrasoundDeviceConfiguration() +{ + MITK_INFO << "Try to start reading xml device configuration..."; + if (m_Filename == "") + { + MITK_WARN << "Cannot read file - empty filename!"; + return false; + } + + TiXmlDocument document(m_Filename); + if (!document.LoadFile()) + { + MITK_ERROR << "Error when opening and reading file :" << m_Filename; + return false; + } + + TiXmlHandle documentHandle(&document); + TiXmlElement* ultrasoundDeviceTag = documentHandle.FirstChildElement(TAG_ULTRASOUNDDEVICE).ToElement(); + if (ultrasoundDeviceTag == nullptr) + { + MITK_ERROR << "Error parsing the file :" << m_Filename << std::endl << "Wrong xml format structure."; + return false; + } + + //Extract attribute information of the ULTRASOUNDDEVICE-Tag: + this->ExtractAttributeInformationOfUltrasoundDeviceTag(ultrasoundDeviceTag); + + TiXmlElement* generalSettingsTag = documentHandle.FirstChildElement(TAG_ULTRASOUNDDEVICE).FirstChildElement(TAG_GENERALSETTINGS).ToElement(); + if (generalSettingsTag == nullptr) + { + MITK_ERROR << "Error parsing the GENERALSETTINGS-Tag in the file :" << m_Filename; + return false; + } + + //Extract attribute information of the GENERALSETTINGS-Tag: + this->ExtractAttributeInformationOfGeneralSettingsTag(generalSettingsTag); + + TiXmlElement* probesTag = documentHandle.FirstChildElement(TAG_ULTRASOUNDDEVICE).FirstChildElement(TAG_PROBES).ToElement(); + if (probesTag == nullptr) + { + MITK_ERROR << "Error: PROBES-Tag was not found in the file :" << m_Filename << "Therefore, creating default probe."; + //Create default ultrasound probe: + mitk::USProbe::Pointer ultrasoundProbeDefault = mitk::USProbe::New(); + ultrasoundProbeDefault->SetName("default"); + ultrasoundProbeDefault->SetDepth(0); + m_DeviceConfig.probes.push_back(ultrasoundProbeDefault); + return true; + } + + //Extract all saved and configured probes of the USDevice: + for (TiXmlElement* probeTag = probesTag->FirstChildElement(TAG_PROBE); + probeTag != nullptr; probeTag = probeTag->NextSiblingElement()) + { + this->ExtractProbe(probeTag); + } + return true; +} + +void mitk::USDeviceReaderXML::SetFilename(std::string filename) +{ + m_Filename = filename; +} + +void mitk::USDeviceReaderXML::ExtractAttributeInformationOfUltrasoundDeviceTag(TiXmlElement *ultrasoundTag) +{ + ultrasoundTag->QueryDoubleAttribute(ATTR_FILEVERS, &m_DeviceConfig.fileversion); + ultrasoundTag->QueryStringAttribute(ATTR_TYPE, &m_DeviceConfig.deviceType); + ultrasoundTag->QueryStringAttribute(ATTR_NAME, &m_DeviceConfig.deviceName); + ultrasoundTag->QueryStringAttribute(ATTR_MANUFACTURER, &m_DeviceConfig.manufacturer); + ultrasoundTag->QueryStringAttribute(ATTR_MODEL, &m_DeviceConfig.model); + ultrasoundTag->QueryStringAttribute(ATTR_COMMENT, &m_DeviceConfig.comment); + ultrasoundTag->QueryIntAttribute(ATTR_IMAGESTREAMS, &m_DeviceConfig.numberOfImageStreams); +} + +void mitk::USDeviceReaderXML::ExtractAttributeInformationOfGeneralSettingsTag(TiXmlElement *generalSettingsTag) +{ + generalSettingsTag->QueryBoolAttribute(ATTR_GREYSCALE, &m_DeviceConfig.useGreyscale); + generalSettingsTag->QueryBoolAttribute(ATTR_RESOLUTIONOVERRIDE, &m_DeviceConfig.useResolutionOverride); + generalSettingsTag->QueryIntAttribute(ATTR_RESOLUTIONHEIGHT, &m_DeviceConfig.resolutionHeight); + generalSettingsTag->QueryIntAttribute(ATTR_RESOLUTIONWIDTH, &m_DeviceConfig.resolutionWidth); + generalSettingsTag->QueryIntAttribute(ATTR_SOURCEID, &m_DeviceConfig.sourceID); + generalSettingsTag->QueryStringAttribute(ATTR_FILEPATH, &m_DeviceConfig.filepathVideoSource); + generalSettingsTag->QueryIntAttribute(ATTR_OPENCVPORT, &m_DeviceConfig.opencvPort); +} + +void mitk::USDeviceReaderXML::ExtractProbe(TiXmlElement *probeTag) +{ + mitk::USProbe::Pointer ultrasoundProbe = mitk::USProbe::New(); + std::string probeName; + probeTag->QueryStringAttribute(ATTR_NAME, &probeName); + ultrasoundProbe->SetName(probeName); + + TiXmlElement* depthsTag = probeTag->FirstChildElement(TAG_DEPTHS); + if (depthsTag != nullptr) + { + for (TiXmlElement* depthTag = depthsTag->FirstChildElement(TAG_DEPTH); + depthTag != nullptr; depthTag = depthTag->NextSiblingElement()) + { + int depth = 0; + mitk::Vector3D spacing; + spacing[0] = 1; + spacing[1] = 1; + spacing[2] = 1; + + depthTag->QueryIntAttribute(ATTR_DEPTH, &depth); + + TiXmlElement* spacingTag = depthTag->FirstChildElement(TAG_SPACING); + if (spacingTag != nullptr) + { + spacingTag->QueryDoubleAttribute(ATTR_X, &spacing[0]); + spacingTag->QueryDoubleAttribute(ATTR_Y, &spacing[1]); + } + + ultrasoundProbe->SetDepthAndSpacing(depth, spacing); + } + } + else + { + MITK_ERROR << "Error: DEPTHS-Tag was not found in the file :" << m_Filename + << "Therefore, creating default depth [0] and spacing [1,1,1] for the probe."; + ultrasoundProbe->SetDepth(0); + } + + unsigned int croppingTop = 0; + unsigned int croppingBottom = 0; + unsigned int croppingLeft = 0; + unsigned int croppingRight = 0; + + TiXmlElement* croppingTag = probeTag->FirstChildElement(TAG_CROPPING); + if (croppingTag != nullptr) + { + croppingTag->QueryUnsignedAttribute(ATTR_TOP, &croppingTop); + croppingTag->QueryUnsignedAttribute(ATTR_BOTTOM, &croppingBottom); + croppingTag->QueryUnsignedAttribute(ATTR_LEFT, &croppingLeft); + croppingTag->QueryUnsignedAttribute(ATTR_RIGHT, &croppingRight); + } + + ultrasoundProbe->SetProbeCropping(croppingTop, croppingBottom, croppingLeft, croppingRight); + m_DeviceConfig.probes.push_back(ultrasoundProbe); +} diff --git a/Modules/US/USModel/mitkUSDeviceReaderXML.h b/Modules/US/USModel/mitkUSDeviceReaderXML.h new file mode 100644 index 0000000000..2b6c0a4fde --- /dev/null +++ b/Modules/US/USModel/mitkUSDeviceReaderXML.h @@ -0,0 +1,101 @@ +/*=================================================================== + +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 mitkUSDeviceReaderXML_H_HEADER_INCLUDED_ +#define mitkUSDeviceReaderXML_H_HEADER_INCLUDED_ + +#include + +#include +#include + +class TiXmlElement; +class TiXmlNode; + +namespace mitk { + + class MITKUS_EXPORT USDeviceReaderXML : public AbstractFileReader + { + public: + USDeviceReaderXML(); + ~USDeviceReaderXML() override; + + using AbstractFileReader::Read; + std::vector> Read() override; + bool ReadUltrasoundDeviceConfiguration(); + + void SetFilename(std::string filename); + + typedef struct USVideoDeviceConfigData_ + { + double fileversion; + std::string deviceType; + std::string deviceName; + std::string manufacturer; + std::string model; + std::string comment; + int numberOfImageStreams; + + bool useGreyscale; + bool useResolutionOverride; + int resolutionWidth; + int resolutionHeight; + int sourceID; + std::string filepathVideoSource; + int opencvPort; + + std::vector probes; + + USVideoDeviceConfigData_() + : fileversion(0), deviceType("Unknown"), deviceName("Unknown"), + manufacturer("Unknown"), comment(""), numberOfImageStreams(1), + useGreyscale(true), useResolutionOverride(true), + resolutionWidth(640), resolutionHeight(480), sourceID(0), + filepathVideoSource(""), opencvPort(0) + { }; + + }USVideoDeviceConfigData; + + USVideoDeviceConfigData &GetUSVideoDeviceConfigData(); + + protected: + USDeviceReaderXML(const USDeviceReaderXML& other); + mitk::USDeviceReaderXML* Clone() const override; + + /** + * \brief Extracts all stored attribute information of the ULTRASOUNDDEVICE-Tag. + */ + void ExtractAttributeInformationOfUltrasoundDeviceTag(TiXmlElement *element); + + /** + * \brief Extracts all stored attribute information of the GENERALSETTINGS-Tag. + */ + void ExtractAttributeInformationOfGeneralSettingsTag(TiXmlElement *element); + + /** + * \brief Extracts all stored information of a single ultrasound probe. + */ + void ExtractProbe(TiXmlElement *element); + + private: + std::string m_Filename; + USVideoDeviceConfigData m_DeviceConfig; + }; + +} // namespace mitk + +#endif // mitkUSDeviceReaderXML_H_HEADER_INCLUDED_ diff --git a/Modules/US/USModel/mitkUSDeviceWriterXML.cpp b/Modules/US/USModel/mitkUSDeviceWriterXML.cpp new file mode 100644 index 0000000000..3a05ddb3d8 --- /dev/null +++ b/Modules/US/USModel/mitkUSDeviceWriterXML.cpp @@ -0,0 +1,157 @@ +/*=================================================================== + +The Medical Imaging Interaction Toolkit (MITK) + +Copyright (c) German Cancer Research Center, +Division of Medical and Biological Informatics. +All rights reserved. + +This software is distributed WITHOUT ANY WARRANTY; without +even the implied warranty of MERCHANTABILITY or FITNESS FOR +A PARTICULAR PURPOSE. + +See LICENSE.txt or http://www.mitk.org for details. + +===================================================================*/ + +// MITK +#include "mitkUSDeviceReaderWriterConstants.h" +#include "mitkUSDeviceWriterXML.h" +#include +#include +#include + +// Third Party +#include +#include +#include +#include + +mitk::USDeviceWriterXML::USDeviceWriterXML() : AbstractFileWriter(USDevice::GetStaticNameOfClass(), + mitk::IGTMimeTypes::USDEVICEINFORMATIONXML_MIMETYPE(), + "MITK USDevice Writer (XML)"), m_Filename("") +{ + RegisterService(); +} + +mitk::USDeviceWriterXML::USDeviceWriterXML(const mitk::USDeviceWriterXML& other) : AbstractFileWriter(other) +{ +} + +mitk::USDeviceWriterXML::~USDeviceWriterXML() +{ +} + +mitk::USDeviceWriterXML* mitk::USDeviceWriterXML::Clone() const +{ + return new USDeviceWriterXML(*this); +} + +void mitk::USDeviceWriterXML::Write() +{ + if (m_Filename == "") + { + MITK_WARN << "Cannot write to file - empty filename!"; + return; + } +} + +void mitk::USDeviceWriterXML::SetFilename(std::string filename) +{ + m_Filename = filename; +} + +bool mitk::USDeviceWriterXML::WriteUltrasoundVideoDeviceConfiguration(mitk::USDeviceReaderXML::USVideoDeviceConfigData & config) +{ + TiXmlDocument document; + TiXmlDeclaration* xmlDeclaration = new TiXmlDeclaration("1.0", "", ""); + document.LinkEndChild(xmlDeclaration); + + + //Create the xml information of the ULTRASOUNDDEVICE-Tag: + TiXmlElement *ultrasoundDeviceTag = new TiXmlElement(TAG_ULTRASOUNDDEVICE); + this->CreateXmlInformationOfUltrasoundDeviceTag(document, ultrasoundDeviceTag, config); + + + //Create the xml information of the GENERALSETTINGS-Tag: + TiXmlElement *generalSettingsTag = new TiXmlElement(TAG_GENERALSETTINGS); + this->CreateXmlInformationOfGeneralSettingsTag(ultrasoundDeviceTag, generalSettingsTag, config); + + //Create the xml information of the PROBES-Tag: + this->CreateXmlInformationOfProbesTag(ultrasoundDeviceTag, config); + + return document.SaveFile(m_Filename); +} + +void mitk::USDeviceWriterXML::CreateXmlInformationOfUltrasoundDeviceTag( + TiXmlDocument &document, TiXmlElement * ultrasoundDeviceTag, + mitk::USDeviceReaderXML::USVideoDeviceConfigData &config) +{ + ultrasoundDeviceTag->SetAttribute(ATTR_FILEVERS, config.fileversion); + ultrasoundDeviceTag->SetAttribute(ATTR_TYPE, config.deviceType); + ultrasoundDeviceTag->SetAttribute(ATTR_NAME, config.deviceName); + ultrasoundDeviceTag->SetAttribute(ATTR_MANUFACTURER, config.manufacturer); + ultrasoundDeviceTag->SetAttribute(ATTR_MODEL, config.model); + ultrasoundDeviceTag->SetAttribute(ATTR_COMMENT, config.comment); + ultrasoundDeviceTag->SetAttribute(ATTR_IMAGESTREAMS, config.numberOfImageStreams); + + document.LinkEndChild(ultrasoundDeviceTag); +} + +void mitk::USDeviceWriterXML::CreateXmlInformationOfGeneralSettingsTag(TiXmlElement *parentTag, TiXmlElement *generalSettingsTag, mitk::USDeviceReaderXML::USVideoDeviceConfigData & config) +{ + std::string value = config.useGreyscale ? "true" : "false"; + generalSettingsTag->SetAttribute(ATTR_GREYSCALE, value); + value = config.useResolutionOverride ? "true" : "false"; + generalSettingsTag->SetAttribute(ATTR_RESOLUTIONOVERRIDE, value); + generalSettingsTag->SetAttribute(ATTR_RESOLUTIONWIDTH, config.resolutionWidth); + generalSettingsTag->SetAttribute(ATTR_RESOLUTIONHEIGHT, config.resolutionHeight); + + generalSettingsTag->SetAttribute(ATTR_SOURCEID, config.sourceID); + generalSettingsTag->SetAttribute(ATTR_FILEPATH, config.filepathVideoSource); + generalSettingsTag->SetAttribute(ATTR_OPENCVPORT, config.opencvPort); + + parentTag->LinkEndChild(generalSettingsTag); +} + +void mitk::USDeviceWriterXML::CreateXmlInformationOfProbesTag(TiXmlElement * parentTag, mitk::USDeviceReaderXML::USVideoDeviceConfigData & config) +{ + if (config.probes.size() != 0) + { + TiXmlElement *probesTag = new TiXmlElement(TAG_PROBES); + parentTag->LinkEndChild(probesTag); + + for (size_t index = 0; index < config.probes.size(); ++index) + { + TiXmlElement *probeTag = new TiXmlElement(TAG_PROBE); + probesTag->LinkEndChild(probeTag); + + mitk::USProbe::Pointer probe = config.probes.at(index); + probeTag->SetAttribute(ATTR_NAME, probe->GetName()); + std::map depthsAndSpacing = probe->GetDepthsAndSpacing(); + if (depthsAndSpacing.size() != 0) + { + TiXmlElement *depthsTag = new TiXmlElement(TAG_DEPTHS); + probeTag->LinkEndChild(depthsTag); + for (std::map::iterator it = depthsAndSpacing.begin(); it != depthsAndSpacing.end(); it++) + { + TiXmlElement *depthTag = new TiXmlElement(TAG_DEPTH); + depthTag->SetAttribute(ATTR_DEPTH, it->first); + depthsTag->LinkEndChild(depthTag); + + TiXmlElement *spacingTag = new TiXmlElement(TAG_SPACING); + spacingTag->SetDoubleAttribute(ATTR_X, it->second[0], 6); + spacingTag->SetDoubleAttribute(ATTR_Y, it->second[1], 6); + depthTag->LinkEndChild(spacingTag); + } + + TiXmlElement *croppingTag = new TiXmlElement(TAG_CROPPING); + probeTag->LinkEndChild(croppingTag); + croppingTag->SetAttribute(ATTR_TOP, probe->GetProbeCropping().top); + croppingTag->SetAttribute(ATTR_BOTTOM, probe->GetProbeCropping().bottom); + croppingTag->SetAttribute(ATTR_LEFT, probe->GetProbeCropping().left); + croppingTag->SetAttribute(ATTR_RIGHT, probe->GetProbeCropping().right); + } + } + } +} diff --git a/Modules/US/USModel/mitkUSDeviceWriterXML.h b/Modules/US/USModel/mitkUSDeviceWriterXML.h new file mode 100644 index 0000000000..027733c578 --- /dev/null +++ b/Modules/US/USModel/mitkUSDeviceWriterXML.h @@ -0,0 +1,92 @@ +/*=================================================================== + +The Medical Imaging Interaction Toolkit (MITK) + +Copyright (c) German Cancer Research Center, +Division of Medical and Biological Informatics. +All rights reserved. + +This software is distributed WITHOUT ANY WARRANTY; without +even the implied warranty of MERCHANTABILITY or FITNESS FOR +A PARTICULAR PURPOSE. + +See LICENSE.txt or http://www.mitk.org for details. + +===================================================================*/ + +#ifndef mitkUSDeviceWriterXML_H_Header_INCLUDED_ +#define mitkUSDeviceWriterXML_H_Header_INCLUDED_ + +#include + +#include +#include + +class TiXmlDocument; +class TiXmlElement; + +namespace mitk { + class MITKUS_EXPORT USDeviceWriterXML : public AbstractFileWriter + { + public: + + USDeviceWriterXML(); + ~USDeviceWriterXML() override; + + using AbstractFileWriter::Write; + void Write() override; + + /** + * \brief Sets the filename of the ultrasound device configuration file which should be created. + */ + void SetFilename(std::string filename); + + /** + * \brief Writes the configuration settings of an ultrasound device to a xml-file. + * \param config The struct containing all information of the ultrasound device. + */ + bool WriteUltrasoundVideoDeviceConfiguration(mitk::USDeviceReaderXML::USVideoDeviceConfigData &config); + + protected: + USDeviceWriterXML(const USDeviceWriterXML& other); + mitk::USDeviceWriterXML* Clone() const override; + + /** + * \brief Creates the xml ULTRASOUNDDEVICE-Tag entry of the ultrasound video device configuration file. + * \param document A reference to the xml document. + * \param ultrasoundDeviceTag The ULTRASOUNDDEVICETAG which should be created. + * \param config The struct containing all information of the ultrasound device. + */ + void CreateXmlInformationOfUltrasoundDeviceTag( TiXmlDocument &document, + TiXmlElement *ultrasoundDeviceTag, + mitk::USDeviceReaderXML::USVideoDeviceConfigData &config); + + /** + * \brief Creates the xml GENERALSETTINGS-Tag entry of the ultrasound video device configuration file. + * \param parentTag The xml parent tag of the GENERALSETTINGS-Tag. This is the ULTRASOUNDDEVICE-Tag. + * \param generalSettingsTag The GENERALSETTINGS-Tag which should be created. + * \param config The struct containing all information of the ultrasound device. + */ + void CreateXmlInformationOfGeneralSettingsTag( TiXmlElement *parentTag, + TiXmlElement *generalSettingsTag, + mitk::USDeviceReaderXML::USVideoDeviceConfigData &config); + + /** + * \brief Creates the xml PROBES-Tag entry of the ultrasound video device configuration file. All information + * of all configured probes is extracted and then stored in the xml file. + * \param parentTag The xml parent tag of the GENERALSETTINGS-Tag. This is the ULTRASOUNDDEVICE-Tag. + * \param config The struct containing all information of the ultrasound device. + */ + void CreateXmlInformationOfProbesTag( TiXmlElement *parentTag, + mitk::USDeviceReaderXML::USVideoDeviceConfigData &config); + private: + /** + * \brief The filename of the ultrasound device configuration file which should be created. + */ + std::string m_Filename; + + + }; +} + +#endif // mitkUSDeviceWriterXML_H_Header_INCLUDED_ diff --git a/Modules/US/USModel/mitkUSProbe.cpp b/Modules/US/USModel/mitkUSProbe.cpp index d1d0f7abe5..fd8fef5ac7 100644 --- a/Modules/US/USModel/mitkUSProbe.cpp +++ b/Modules/US/USModel/mitkUSProbe.cpp @@ -1,88 +1,111 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkUSProbe.h" #include -mitk::USProbe::USProbe() : itk::Object() +mitk::USProbe::USProbe() : itk::Object(), m_CurrentDepth(0) { } mitk::USProbe::USProbe(std::string identifier) - : m_Name(identifier) + : m_Name(identifier), m_CurrentDepth(0) { } mitk::USProbe::~USProbe() { } +void mitk::USProbe::SetProbeCropping(unsigned int top, unsigned int bottom, unsigned int left, unsigned int right) +{ + m_Cropping.top = top; + m_Cropping.bottom = bottom; + m_Cropping.left = left; + m_Cropping.right = right; +} + +mitk::USProbe::USProbeCropping mitk::USProbe::GetProbeCropping() +{ + return m_Cropping; +} + bool mitk::USProbe::IsEqualToProbe(mitk::USProbe::Pointer probe) { if (m_Name.compare(probe->GetName()) == 0) return true; else return false; } void mitk::USProbe::SetDepthAndSpacing(int depth, mitk::Vector3D spacing) { m_DepthsAndSpacings.insert(std::pair(depth, spacing)); } std::map mitk::USProbe::GetDepthsAndSpacing() { return m_DepthsAndSpacings; } void mitk::USProbe::SetDepth(int depth) { mitk::Vector3D defaultSpacing; defaultSpacing[0] = 1; defaultSpacing[1] = 1; - defaultSpacing[2] = 0; + defaultSpacing[2] = 1; m_DepthsAndSpacings.insert(std::pair(depth, defaultSpacing)); } void mitk::USProbe::RemoveDepth(int depthToRemove) { m_DepthsAndSpacings.erase(depthToRemove); } void mitk::USProbe::SetSpacingForGivenDepth(int givenDepth, Vector3D spacing) { m_DepthsAndSpacings[givenDepth][0] = spacing[0]; m_DepthsAndSpacings[givenDepth][1] = spacing[1]; m_DepthsAndSpacings[givenDepth][2] = spacing[2]; } mitk::Vector3D mitk::USProbe::GetSpacingForGivenDepth(int givenDepth) { mitk::Vector3D spacing; std::map::iterator it = m_DepthsAndSpacings.find(givenDepth); if (it != m_DepthsAndSpacings.end()) //check if given depth really exists { spacing[0] = it->second[0]; spacing[1] = it->second[1]; spacing[2] = it->second[2]; } else - { //spacing does not exist, so set default spacing (1,1,0) + { //spacing does not exist, so set default spacing (1,1,1) spacing[0] = 1; spacing[1] = 1; - spacing[2] = 0; + spacing[2] = 1; } return spacing; } + +bool mitk::USProbe::IsDepthAndSpacingEmpty() +{ + if( m_DepthsAndSpacings.size() == 0 ) + { + return true; + } + + return false; +} diff --git a/Modules/US/USModel/mitkUSProbe.h b/Modules/US/USModel/mitkUSProbe.h index 8544e93d84..2ca2f931ed 100644 --- a/Modules/US/USModel/mitkUSProbe.h +++ b/Modules/US/USModel/mitkUSProbe.h @@ -1,96 +1,130 @@ /*=================================================================== 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 MITKUSProbe_H_HEADER_INCLUDED_ #define MITKUSProbe_H_HEADER_INCLUDED_ #include #include #include #include #include namespace mitk { /**Documentation * \brief Right now, the US Probe is only a fancy name for a string. Later, it could handle probe specific parameters * like the current frequency etc. It is able to compare itself to other probes for device managment though. * * \ingroup US */ //Be sure to check the isEqualTo() method if you expand this class to see if it needs work! class MITKUS_EXPORT USProbe : public itk::Object { public: mitkClassMacroItkParent(USProbe, itk::Object); itkFactorylessNewMacro(Self) itkCloneMacro(Self) mitkNewMacro1Param(Self, std::string); + /** + * \brief Struct to define a probe specific ultrasound image cropping. + */ + typedef struct USProbeCropping_ + { + unsigned int top; + unsigned int bottom; + unsigned int left; + unsigned int right; + + USProbeCropping_() + : top(0), bottom(0), left(0), right(0) { }; + + USProbeCropping_(unsigned int top, unsigned int bottom, unsigned int left, unsigned int right) + : top(top), bottom(bottom), left(left), right(right) { }; + }USProbeCropping; + + /** + * \brief Sets the probe cropping. + */ + void SetProbeCropping(unsigned int top, unsigned int bottom, unsigned int left, unsigned int right); + USProbeCropping GetProbeCropping(); + /** * \brief Compares this probe to another probe and returns true if they are equal in terms of name AND NAME ONLY * be sure to sufficiently extend this method along with further capabilities probes. */ bool IsEqualToProbe(mitk::USProbe::Pointer probe); /** * \brief Sets a scanning depth of the probe and the associated spacing */ void SetDepthAndSpacing(int depth, Vector3D spacing); /** * \brief Gets all scanning depths and the associates spacings of the probe as an std::map with depth as key (represented by an int) and *spacing as value (represented by a Vector3D) */ std::map GetDepthsAndSpacing(); /** - * \brief Sets a scanning depth of the probe with the default spacing (1,1,0). Exact spacing needs to be calibrated. + * \brief Sets a scanning depth of the probe with the default spacing (1,1,1). Exact spacing needs to be calibrated. */ void SetDepth(int depth); /** * \brief Removes the given depth of the probe, if it exists */ void RemoveDepth(int depthToRemove); /** * \ brief Sets the spacing associated to the given depth of the probe. Spacing needs to be calibrated. */ void SetSpacingForGivenDepth(int givenDepth, Vector3D spacing); /** * \brief Returns the spacing that is associated to the given depth of the probe. - *If spacing was not calibrated or if depth does not exist for this probe the default spacing (1,1,0) is returned. + *If spacing was not calibrated or if depth does not exist for this probe the default spacing (1,1,1) is returned. */ Vector3D GetSpacingForGivenDepth(int givenDepth); + /** + * \brief Checks, whether the std::map m_DepthAndSpacings contains at least one depth element or not. + * \return True, if the the std::map m_DepthAndSpacings does not contain at least one depth element, else false. + */ + bool IsDepthAndSpacingEmpty(); + //## getter and setter ## itkGetMacro(Name, std::string); itkSetMacro(Name, std::string); + itkGetMacro(CurrentDepth, double); + itkSetMacro(CurrentDepth, double); protected: USProbe(); USProbe(std::string identifier); ~USProbe() override; std::string m_Name; + double m_CurrentDepth; // Map containing the depths and the associated spacings as an std::vector with depth as key and spacing as value std::map m_DepthsAndSpacings; + + USProbeCropping m_Cropping; }; } // namespace mitk #endif diff --git a/Modules/US/USModel/mitkUSVideoDevice.cpp b/Modules/US/USModel/mitkUSVideoDevice.cpp index 5c42355452..6d216e8296 100644 --- a/Modules/US/USModel/mitkUSVideoDevice.cpp +++ b/Modules/US/USModel/mitkUSVideoDevice.cpp @@ -1,195 +1,258 @@ /*=================================================================== 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 "mitkUSVideoDevice.h" #include "mitkUSVideoDeviceCustomControls.h" mitk::USVideoDevice::USVideoDevice(int videoDeviceNumber, std::string manufacturer, std::string model) : mitk::USDevice(manufacturer, model) { Init(); m_SourceIsFile = false; m_DeviceID = videoDeviceNumber; m_FilePath = ""; } mitk::USVideoDevice::USVideoDevice(std::string videoFilePath, std::string manufacturer, std::string model) : mitk::USDevice(manufacturer, model) { Init(); m_SourceIsFile = true; m_FilePath = videoFilePath; } mitk::USVideoDevice::USVideoDevice(int videoDeviceNumber, mitk::USImageMetadata::Pointer metadata) : mitk::USDevice(metadata) { Init(); m_SourceIsFile = false; m_DeviceID = videoDeviceNumber; m_FilePath = ""; } mitk::USVideoDevice::USVideoDevice(std::string videoFilePath, mitk::USImageMetadata::Pointer metadata) : mitk::USDevice(metadata) { Init(); m_SourceIsFile = true; m_FilePath = videoFilePath; } mitk::USVideoDevice::~USVideoDevice() { //m_Source->UnRegister(); m_Source = nullptr; } void mitk::USVideoDevice::Init() { m_Source = mitk::USImageVideoSource::New(); m_ControlInterfaceCustom = mitk::USVideoDeviceCustomControls::New(this); //this->SetNumberOfInputs(1); this->SetNumberOfIndexedOutputs(1); // mitk::USImage::Pointer output = mitk::USImage::New(); // output->Initialize(); this->SetNthOutput(0, this->MakeOutput(0)); } std::string mitk::USVideoDevice::GetDeviceClass() { return mitk::USVideoDevice::GetDeviceClassStatic(); } std::string mitk::USVideoDevice::GetDeviceClassStatic() { return "org.mitk.modules.us.USVideoDevice"; } mitk::USAbstractControlInterface::Pointer mitk::USVideoDevice::GetControlInterfaceCustom() { return m_ControlInterfaceCustom.GetPointer(); } bool mitk::USVideoDevice::OnInitialization() { // nothing to do at initialization of video device return true; } bool mitk::USVideoDevice::OnConnection() { if (m_SourceIsFile){ m_Source->SetVideoFileInput(m_FilePath); } else { m_Source->SetCameraInput(m_DeviceID); } //SetSourceCropArea(); return true; } bool mitk::USVideoDevice::OnDisconnection() { if (m_DeviceState == State_Activated) this->Deactivate(); m_Source->ReleaseInput(); return true; } bool mitk::USVideoDevice::OnActivation() { // make sure that video device is ready before aquiring images if (!m_Source->GetIsReady()) { MITK_WARN("mitkUSDevice")("mitkUSVideoDevice") << "Could not activate us video device. Check if video grabber is configured correctly."; return false; } MITK_INFO << "Activated UsVideoDevice!"; return true; } bool mitk::USVideoDevice::OnDeactivation() { // happens automatically when m_Active is set to false return true; } +void mitk::USVideoDevice::GenerateData() +{ + Superclass::GenerateData(); + + if( m_ImageVector.size() == 0 || this->GetNumberOfIndexedOutputs() == 0 ) + { + return; + } + + m_ImageMutex->Lock(); + auto& image = m_ImageVector[0]; + if( image.IsNotNull() && image->IsInitialized() && m_CurrentProbe.IsNotNull() ) + { + //MITK_INFO << "Spacing CurrentProbe: " << m_CurrentProbe->GetSpacingForGivenDepth(m_CurrentProbe->GetCurrentDepth()); + image->GetGeometry()->SetSpacing(m_CurrentProbe->GetSpacingForGivenDepth(m_CurrentProbe->GetCurrentDepth())); + this->GetOutput(0)->SetGeometry(image->GetGeometry()); + } + m_ImageMutex->Unlock(); +} + void mitk::USVideoDevice::UnregisterOnService() { if (m_DeviceState == State_Activated) { this->Deactivate(); } if (m_DeviceState == State_Connected) { this->Disconnect(); } mitk::USDevice::UnregisterOnService(); } mitk::USImageSource::Pointer mitk::USVideoDevice::GetUSImageSource() { return m_Source.GetPointer(); } std::vector mitk::USVideoDevice::GetAllProbes() { if (m_Probes.empty()) { MITK_INFO << "No probes exist for this USVideDevice. Empty vector is returned"; } return m_Probes; } +void mitk::USVideoDevice::DeleteAllProbes() +{ + m_Probes.clear(); +} + mitk::USProbe::Pointer mitk::USVideoDevice::GetCurrentProbe() { if (m_CurrentProbe.IsNotNull()) { return m_CurrentProbe; } else { return nullptr; } } mitk::USProbe::Pointer mitk::USVideoDevice::GetProbeByName(std::string name) { for (std::vector::iterator it = m_Probes.begin(); it != m_Probes.end(); it++) { if (name.compare((*it)->GetName()) == 0) return (*it); } MITK_INFO << "No probe with given name " << name << " was found."; return nullptr; //no matching probe was found so 0 is returned } void mitk::USVideoDevice::RemoveProbeByName(std::string name) { for (std::vector::iterator it = m_Probes.begin(); it != m_Probes.end(); it++) { if (name.compare((*it)->GetName()) == 0) { m_Probes.erase(it); return; } } MITK_INFO << "No Probe with given name " << name << " was found"; } void mitk::USVideoDevice::AddNewProbe(mitk::USProbe::Pointer probe) { m_Probes.push_back(probe); } bool mitk::USVideoDevice::GetIsSourceFile() { return m_SourceIsFile; } + +void mitk::USVideoDevice::SetDefaultProbeAsCurrentProbe() +{ + if( m_Probes.size() == 0 ) + { + std::string name = "default"; + mitk::USProbe::Pointer defaultProbe = mitk::USProbe::New( name ); + m_Probes.push_back( defaultProbe ); + } + + m_CurrentProbe = m_Probes.at(0); + MITK_INFO << "SetDefaultProbeAsCurrentProbe()"; + this->ProbeChanged( m_CurrentProbe->GetName() ); +} + +void mitk::USVideoDevice::SetCurrentProbe(std::string probename) +{ + m_CurrentProbe = this->GetProbeByName( probename ); + MITK_INFO << "SetCurrentProbe() " << probename; +} + +void mitk::USVideoDevice::SetSpacing(double xSpacing, double ySpacing) +{ + mitk::Vector3D spacing; + spacing[0] = xSpacing; + spacing[1] = ySpacing; + spacing[2] = 1; + MITK_INFO << "Spacing: " << spacing; + + if( m_CurrentProbe.IsNotNull() ) + { + m_CurrentProbe->SetSpacingForGivenDepth(m_CurrentProbe->GetCurrentDepth(), spacing); + } + else + { + MITK_WARN << "Cannot set spacing. Current ultrasound probe not set."; + } +} diff --git a/Modules/US/USModel/mitkUSVideoDevice.h b/Modules/US/USModel/mitkUSVideoDevice.h index 00790af550..df205dd6c0 100644 --- a/Modules/US/USModel/mitkUSVideoDevice.h +++ b/Modules/US/USModel/mitkUSVideoDevice.h @@ -1,225 +1,252 @@ /*=================================================================== 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 MITKUSVideoDevice_H_HEADER_INCLUDED_ #define MITKUSVideoDevice_H_HEADER_INCLUDED_ #include #include #include "mitkUSDevice.h" #include "mitkUSImageVideoSource.h" #include "mitkUSProbe.h" #include namespace itk { template class SmartPointer; } namespace mitk { class USVideoDeviceCustomControls; class USAbstractControlInterface; /** * \brief A mitk::USVideoDevice is the common class for video only devices. * They capture video input either from a file or from a device and * transform the output into an mitk::USImage with attached metadata. * This simple implementation does only capture and display 2d images without * registration for example. * * \ingroup US */ class MITKUS_EXPORT USVideoDevice : public mitk::USDevice { public: mitkClassMacro(USVideoDevice, mitk::USDevice); // To open a device (DeviceID, Manufacturer, Model) mitkNewMacro3Param(Self, int, std::string, std::string); // To open A VideoFile (Path, Manufacturer, Model) mitkNewMacro3Param(Self, std::string, std::string, std::string); // To open a device (DeviceID, Metadata) mitkNewMacro2Param(Self, int, mitk::USImageMetadata::Pointer); // To open A VideoFile (Path, Metadata) mitkNewMacro2Param(Self, std::string, mitk::USImageMetadata::Pointer); /** * \return the qualified name of this class (as returned by GetDeviceClassStatic()) */ std::string GetDeviceClass() override; /** * This methode is necessary instead of a static member attribute to avoid * "static initialization order fiasco" when an instance of this class is * used in a module activator. * * \return the qualified name of this class */ static std::string GetDeviceClassStatic(); /** * Getter for the custom control interface which was created during the * construction process of mitk::USVideoDevice. * * \return custom control interface of the video device */ itk::SmartPointer GetControlInterfaceCustom() override; /** * \brief Remove this device from the micro service. * This method is public for mitk::USVideoDevice, because this devices * can be completly removed. This is not possible for API devices, which * should be available while their sub module is loaded. */ void UnregisterOnService(); /** * \return mitk::USImageSource connected to this device */ USImageSource::Pointer GetUSImageSource() override; /** * \brief Return all probes for this USVideoDevice or an empty vector it no probes were set * Returns a std::vector of all probes that exist for this USVideoDevice if there were probes set while creating or modifying this USVideoDevice. * Otherwise it returns an empty vector. Therefore always check if vector is filled, before using it! */ std::vector GetAllProbes(); + /** + * \brief Cleans the std::vector containing all configured probes. + */ + void DeleteAllProbes(); + /** * \brief Return current active probe for this USVideoDevice * Returns a pointer to the probe that is currently in use. If there were probes set while creating or modifying this USVideoDevice. * Returns null otherwise */ mitk::USProbe::Pointer GetCurrentProbe(); /** \brief adds a new probe to the device */ void AddNewProbe(mitk::USProbe::Pointer probe); /** * \brief get the probe by its name * Returns a pointer to the probe identified by the given name. If no probe of given name exists for this Device 0 is returned. */ mitk::USProbe::Pointer GetProbeByName(std::string name); /** * \brief Removes the Probe with the given name */ void RemoveProbeByName(std::string name); /** \brief True, if this Device plays back a file, false if it recieves data from a device */ bool GetIsSourceFile(); + /** + * \brief Sets the first existing probe or the default probe of the video device + * as the current probe of it. + */ + void SetDefaultProbeAsCurrentProbe(); + + /** + * \brief Sets the probe with the given name as current probe if the named probe exists. + */ + void SetCurrentProbe( std::string probename ); + + /** + * \brief Sets the given spacing of the current depth of the current probe. + */ + void SetSpacing( double xSpacing, double ySpacing ) override; + itkGetMacro(ImageVector, std::vector); itkGetMacro(DeviceID, int); itkGetMacro(FilePath, std::string); protected: /** * \brief Creates a new device that will deliver USImages taken from a video device. * under windows, try -1 for device number, which will grab the first available one * (Open CV functionality) */ USVideoDevice(int videoDeviceNumber, std::string manufacturer, std::string model); /** * \brief Creates a new device that will deliver USImages taken from a video file. */ USVideoDevice(std::string videoFilePath, std::string manufacturer, std::string model); /** * \brief Creates a new device that will deliver USImages taken from a video device. * under windows, try -1 for device number, which will grab the first available one * (Open CV functionality) */ USVideoDevice(int videoDeviceNumber, mitk::USImageMetadata::Pointer metadata); /** * \brief Creates a new device that will deliver USImages taken from a video file. */ USVideoDevice(std::string videoFilePath, mitk::USImageMetadata::Pointer metadata); ~USVideoDevice() override; /** * \brief Initializes common properties for all constructors. */ void Init(); /** * \brief Is called during the initialization process. * Returns true if successful and false if unsuccessful. Additionally, you may throw an exception to clarify what went wrong. */ bool OnInitialization() override; /** * \brief Is called during the connection process. * Returns true if successful and false if unsuccessful. Additionally, you may throw an exception to clarify what went wrong. */ bool OnConnection() override; /** * \brief Is called during the disconnection process. * Returns true if successful and false if unsuccessful. Additionally, you may throw an exception to clarify what went wrong. */ bool OnDisconnection() override; /** * \brief Is called during the activation process. After this method is finsihed, the device should be generating images */ bool OnActivation() override; /** * \brief Is called during the deactivation process. After a call to this method the device should still be connected, but not producing images anymore. */ bool OnDeactivation() override; + /** + * \brief Grabs the next frame from the Video input. + * This method is called internally, whenever Update() is invoked by an Output. + */ + virtual void GenerateData() override; + /** * \brief The image source that we use to aquire data */ mitk::USImageVideoSource::Pointer m_Source; /** * \brief True, if this source plays back a file, false if it recieves data from a device */ bool m_SourceIsFile; /** * \brief The device id to connect to. Undefined, if m_SourceIsFile == true; */ int m_DeviceID; /** * \brief The Filepath id to connect to. Undefined, if m_SourceIsFile == false; */ std::string m_FilePath; /** * \brief custom control interface for us video device */ itk::SmartPointer m_ControlInterfaceCustom; /** * \brief probes for this USVideoDevice */ std::vector < mitk::USProbe::Pointer > m_Probes; /** \brief probe that is currently in use */ mitk::USProbe::Pointer m_CurrentProbe; }; } // namespace mitk #endif // MITKUSVideoDevice_H_HEADER_INCLUDED_ diff --git a/Modules/US/USModel/mitkUSVideoDeviceCustomControls.cpp b/Modules/US/USModel/mitkUSVideoDeviceCustomControls.cpp index 58a1f664b4..bf6ab39efc 100644 --- a/Modules/US/USModel/mitkUSVideoDeviceCustomControls.cpp +++ b/Modules/US/USModel/mitkUSVideoDeviceCustomControls.cpp @@ -1,96 +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. ===================================================================*/ #include "mitkUSVideoDeviceCustomControls.h" mitk::USVideoDeviceCustomControls::USVideoDeviceCustomControls(itk::SmartPointer device) : mitk::USAbstractControlInterface(device.GetPointer()), m_IsActive(false) { m_ImageSource = dynamic_cast(m_Device->GetUSImageSource().GetPointer()); } mitk::USVideoDeviceCustomControls::~USVideoDeviceCustomControls() { } void mitk::USVideoDeviceCustomControls::SetIsActive(bool isActive) { m_IsActive = isActive; } bool mitk::USVideoDeviceCustomControls::GetIsActive() { return m_IsActive; } void mitk::USVideoDeviceCustomControls::SetCropArea(mitk::USImageVideoSource::USImageCropping newArea) { MITK_INFO << "Set Crop Area L:" << newArea.left << " R:" << newArea.right << " T:" << newArea.top << " B:" << newArea.bottom; if (m_ImageSource.IsNotNull()) { // if area is empty, remove region if ((newArea.bottom == 0) && (newArea.top == 0) && (newArea.left == 0) && (newArea.right == 0)) { m_ImageSource->RemoveRegionOfInterest(); } else { m_ImageSource->SetCropping(newArea); } } else { MITK_WARN << "Cannot set crop are, source is not initialized!"; } } void mitk::USVideoDeviceCustomControls::SetNewDepth(double depth) { + mitk::USVideoDevice::Pointer device = dynamic_cast(m_Device.GetPointer()); + if (device.IsNotNull()) + { + if( device->GetCurrentProbe().IsNotNull() ) + { + device->GetCurrentProbe()->SetCurrentDepth(depth); + MITK_INFO << "SetCurrentDepth of currentProbe: " << depth; + } + } m_Device->DepthChanged(depth); } void mitk::USVideoDeviceCustomControls::SetNewProbeIdentifier(std::string probename) { + mitk::USVideoDevice::Pointer device = dynamic_cast(m_Device.GetPointer()); + if( device.IsNotNull() ) + { + device->SetCurrentProbe(probename); + } m_Device->ProbeChanged(probename); } mitk::USImageVideoSource::USImageCropping mitk::USVideoDeviceCustomControls::GetCropArea() { // just return the crop area set at the image source return m_ImageSource->GetCropping(); } std::vector mitk::USVideoDeviceCustomControls::GetProbes() { mitk::USVideoDevice::Pointer device = dynamic_cast(m_Device.GetPointer()); return device->GetAllProbes(); } std::vector mitk::USVideoDeviceCustomControls::GetDepthsForProbe(std::string name) { mitk::USVideoDevice::Pointer device = dynamic_cast(m_Device.GetPointer()); mitk::USProbe::Pointer probe = device->GetProbeByName(name); std::map depthsAndSpacings = probe->GetDepthsAndSpacing(); std::vector depths; for (std::map::iterator it = depthsAndSpacings.begin(); it != depthsAndSpacings.end(); it++) { depths.push_back((it->first)); } return depths; } + +void mitk::USVideoDeviceCustomControls::SetDefaultProbeAsCurrentProbe() +{ + mitk::USVideoDevice::Pointer device = dynamic_cast(m_Device.GetPointer()); + device->SetDefaultProbeAsCurrentProbe(); +} diff --git a/Modules/US/USModel/mitkUSVideoDeviceCustomControls.h b/Modules/US/USModel/mitkUSVideoDeviceCustomControls.h index 11e94f0d7f..ce90da24b1 100644 --- a/Modules/US/USModel/mitkUSVideoDeviceCustomControls.h +++ b/Modules/US/USModel/mitkUSVideoDeviceCustomControls.h @@ -1,95 +1,101 @@ /*=================================================================== 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 MITKUSVideoDeviceCustomControls_H_HEADER_INCLUDED_ #define MITKUSVideoDeviceCustomControls_H_HEADER_INCLUDED_ #include "mitkUSAbstractControlInterface.h" #include "mitkUSImageVideoSource.h" #include "mitkUSVideoDevice.h" #include namespace mitk { /** * \brief Custom controls for mitk::USVideoDevice. * Controls image cropping of the corresponding mitk::USImageVideoSource. */ class MITKUS_EXPORT USVideoDeviceCustomControls : public USAbstractControlInterface { public: mitkClassMacro(USVideoDeviceCustomControls, USAbstractControlInterface); mitkNewMacro1Param(Self, itk::SmartPointer); /** * Activate or deactivate the custom controls. This is just for handling * widget visibility in a GUI for example. Cropping will not be deactivated * if this method is called with false. Use * mitk::USVideoDeviceCustomControls::SetCropArea() with an empty are * instead. */ void SetIsActive(bool isActive) override; /** * \return if this custom controls are currently activated */ bool GetIsActive() override; /** * \brief Sets the area that will be cropped from the US image. * Set [0,0,0,0] to disable it, which is also default. */ void SetCropArea(USImageVideoSource::USImageCropping newArea); /** * \return area currently set for image cropping */ mitk::USImageVideoSource::USImageCropping GetCropArea(); /** - * \brief Sets new depth value + * \brief Sets a new depth value to the current probe. */ void SetNewDepth(double depth); /** * \ brief Sets new probe identifier */ void SetNewProbeIdentifier(std::string probename); /** *\brief Get all the probes for the current device */ std::vector GetProbes(); /** * \brief Get the scanning dephts of the given probe */ std::vector GetDepthsForProbe(std::string name); + /** + * \brief Sets the first existing probe or the default probe of a USVideoDevice + * as the current probe of the USVideoDevice. + */ + void SetDefaultProbeAsCurrentProbe(); + protected: /** * Class needs an mitk::USImageVideoSource object for beeing constructed. * This object will be manipulated by the custom controls methods. */ USVideoDeviceCustomControls(itk::SmartPointer device); ~USVideoDeviceCustomControls() override; bool m_IsActive; USImageVideoSource::Pointer m_ImageSource; }; } // namespace mitk #endif // MITKUSVideoDeviceCustomControls_H_HEADER_INCLUDED_ \ No newline at end of file diff --git a/Modules/US/files.cmake b/Modules/US/files.cmake index 93bf7bcfb2..42c099ed21 100644 --- a/Modules/US/files.cmake +++ b/Modules/US/files.cmake @@ -1,34 +1,36 @@ SET(CPP_FILES ## Module Activator mitkUSActivator.cpp ## Model Classes USModel/mitkUSImage.cpp USModel/mitkUSImageMetadata.cpp USModel/mitkUSDevice.cpp USModel/mitkUSIGTLDevice.cpp USModel/mitkUSVideoDevice.cpp USModel/mitkUSVideoDeviceCustomControls.cpp USModel/mitkUSProbe.cpp USModel/mitkUSDevicePersistence.cpp +USModel/mitkUSDeviceReaderXML.cpp +USModel/mitkUSDeviceWriterXML.cpp ## Filters and Sources USFilters/mitkUSImageLoggingFilter.cpp USFilters/mitkUSImageSource.cpp USFilters/mitkUSImageVideoSource.cpp USFilters/mitkIGTLMessageToUSImageFilter.cpp ## Control Interfaces USControlInterfaces/mitkUSAbstractControlInterface.cpp USControlInterfaces/mitkUSControlInterfaceBMode.cpp USControlInterfaces/mitkUSControlInterfaceProbes.cpp USControlInterfaces/mitkUSControlInterfaceDoppler.cpp USControlInterfaces/mitkUSDiPhASDeviceCustomControls.cpp ) set(RESOURCE_FILES Interactions/USPointMarkInteractions.xml Interactions/USZoneInteractions.xml Interactions/USZoneInteractionsHold.xml ) diff --git a/Modules/USUI/Qmitk/QmitkUSControlsCustomVideoDeviceWidget.cpp b/Modules/USUI/Qmitk/QmitkUSControlsCustomVideoDeviceWidget.cpp index e997ecfd79..6785955e4a 100644 --- a/Modules/USUI/Qmitk/QmitkUSControlsCustomVideoDeviceWidget.cpp +++ b/Modules/USUI/Qmitk/QmitkUSControlsCustomVideoDeviceWidget.cpp @@ -1,165 +1,172 @@ /*=================================================================== 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 "QmitkUSControlsCustomVideoDeviceWidget.h" #include "ui_QmitkUSControlsCustomVideoDeviceWidget.h" #include #include QmitkUSControlsCustomVideoDeviceWidget::QmitkUSControlsCustomVideoDeviceWidget() : ui(new Ui::QmitkUSControlsCustomVideoDeviceWidget) { } QmitkUSControlsCustomVideoDeviceWidget::QmitkUSControlsCustomVideoDeviceWidget(QWidget *parent) : QmitkUSAbstractCustomWidget(parent), ui(new Ui::QmitkUSControlsCustomVideoDeviceWidget) { m_Cropping.left = 0; m_Cropping.top = 0; m_Cropping.right = 0; m_Cropping.bottom = 0; } QmitkUSControlsCustomVideoDeviceWidget::~QmitkUSControlsCustomVideoDeviceWidget() { delete ui; } std::string QmitkUSControlsCustomVideoDeviceWidget::GetDeviceClass() const { return mitk::USVideoDevice::GetDeviceClassStatic(); } QmitkUSAbstractCustomWidget* QmitkUSControlsCustomVideoDeviceWidget::Clone(QWidget* parent) const { QmitkUSAbstractCustomWidget* clonedWidget = new QmitkUSControlsCustomVideoDeviceWidget(parent); clonedWidget->SetDevice(this->GetDevice()); return clonedWidget; } void QmitkUSControlsCustomVideoDeviceWidget::OnDeviceSet() { m_ControlInterface = dynamic_cast (this->GetDevice()->GetControlInterfaceCustom().GetPointer()); if (m_ControlInterface.IsNotNull()) { mitk::USImageVideoSource::USImageCropping cropping = m_ControlInterface->GetCropArea(); ui->crop_left->setValue(cropping.left); ui->crop_right->setValue(cropping.right); ui->crop_bot->setValue(cropping.bottom); ui->crop_top->setValue(cropping.top); //get all probes and put their names into a combobox std::vector probes = m_ControlInterface->GetProbes(); for (std::vector::iterator it = probes.begin(); it != probes.end(); it++) { std::string probeName = (*it)->GetName(); ui->m_ProbeIdentifier->addItem(QString::fromUtf8(probeName.data(), probeName.size())); } + + m_ControlInterface->SetDefaultProbeAsCurrentProbe(); + + SetDepthsForProbe( ui->m_ProbeIdentifier->currentText().toStdString() ); + m_ControlInterface->SetNewDepth( ui->m_UsDepth->currentText().toDouble() ); + connect(ui->m_UsDepth, SIGNAL(currentTextChanged(const QString &)), this, SLOT(OnDepthChanged())); connect(ui->m_ProbeIdentifier, SIGNAL(currentTextChanged(const QString &)), this, SLOT(OnProbeChanged())); } else { MITK_WARN("QmitkUSAbstractCustomWidget")("QmitkUSControlsCustomVideoDeviceWidget") << "Did not get a custom video device control interface."; } ui->crop_left->setEnabled(m_ControlInterface.IsNotNull()); ui->crop_right->setEnabled(m_ControlInterface.IsNotNull()); ui->crop_bot->setEnabled(m_ControlInterface.IsNotNull()); ui->crop_top->setEnabled(m_ControlInterface.IsNotNull()); } void QmitkUSControlsCustomVideoDeviceWidget::Initialize() { ui->setupUi(this); connect(ui->crop_left, SIGNAL(valueChanged(int)), this, SLOT(OnCropAreaChanged())); connect(ui->crop_right, SIGNAL(valueChanged(int)), this, SLOT(OnCropAreaChanged())); connect(ui->crop_top, SIGNAL(valueChanged(int)), this, SLOT(OnCropAreaChanged())); connect(ui->crop_bot, SIGNAL(valueChanged(int)), this, SLOT(OnCropAreaChanged())); } void QmitkUSControlsCustomVideoDeviceWidget::OnCropAreaChanged() { if (m_ControlInterface.IsNull()) { return; } mitk::USImageVideoSource::USImageCropping cropping; cropping.left = ui->crop_left->value(); cropping.top = ui->crop_top->value(); cropping.right = ui->crop_right->value(); cropping.bottom = ui->crop_bot->value(); try { m_ControlInterface->SetCropArea(cropping); m_Cropping = cropping; } catch (mitk::Exception e) { m_ControlInterface->SetCropArea(m_Cropping); // reset to last valid crop //reset values BlockSignalAndSetValue(ui->crop_left, m_Cropping.left); BlockSignalAndSetValue(ui->crop_right, m_Cropping.right); BlockSignalAndSetValue(ui->crop_top, m_Cropping.top); BlockSignalAndSetValue(ui->crop_bot, m_Cropping.bottom); // inform user QMessageBox msgBox; msgBox.setInformativeText("The crop area you specified is invalid.\nPlease make sure that no more pixels are cropped than are available."); msgBox.setStandardButtons(QMessageBox::Ok); msgBox.exec(); MITK_WARN << "User tried to crop beyond limits of the image"; } } void QmitkUSControlsCustomVideoDeviceWidget::OnDepthChanged() { double depth = ui->m_UsDepth->currentText().toDouble(); + MITK_INFO << "OnDepthChanged() " << depth; m_ControlInterface->SetNewDepth(depth); } void QmitkUSControlsCustomVideoDeviceWidget::OnProbeChanged() { std::string probename = ui->m_ProbeIdentifier->currentText().toStdString(); m_ControlInterface->SetNewProbeIdentifier(probename); SetDepthsForProbe(probename); } void QmitkUSControlsCustomVideoDeviceWidget::BlockSignalAndSetValue(QSpinBox* target, int value) { bool oldState = target->blockSignals(true); target->setValue(value); target->blockSignals(oldState); } void QmitkUSControlsCustomVideoDeviceWidget::SetDepthsForProbe(std::string probename) { ui->m_UsDepth->clear(); std::vector depths = m_ControlInterface->GetDepthsForProbe(probename); for (std::vector::iterator it = depths.begin(); it != depths.end(); it++) { ui->m_UsDepth->addItem(QString::number(*it)); } } diff --git a/Modules/USUI/Qmitk/QmitkUSNewVideoDeviceWidget.cpp b/Modules/USUI/Qmitk/QmitkUSNewVideoDeviceWidget.cpp index 4912d53cad..8b72ad79a4 100644 --- a/Modules/USUI/Qmitk/QmitkUSNewVideoDeviceWidget.cpp +++ b/Modules/USUI/Qmitk/QmitkUSNewVideoDeviceWidget.cpp @@ -1,405 +1,811 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ //#define _USE_MATH_DEFINES #include // QT headers #include +#include // mitk headers // itk headers +#include +#include + const std::string QmitkUSNewVideoDeviceWidget::VIEW_ID = "org.mitk.views.QmitkUSNewVideoDeviceWidget"; QmitkUSNewVideoDeviceWidget::QmitkUSNewVideoDeviceWidget(QWidget* parent, Qt::WindowFlags f) : QWidget(parent, f) { m_Controls = nullptr; CreateQtPartControl(this); } QmitkUSNewVideoDeviceWidget::~QmitkUSNewVideoDeviceWidget() {} //////////////////// INITIALIZATION ///////////////////// void QmitkUSNewVideoDeviceWidget::CreateQtPartControl(QWidget* parent) { if (!m_Controls) { // create GUI widgets m_Controls = new Ui::QmitkUSNewVideoDeviceWidgetControls; m_Controls->setupUi(parent); this->CreateConnections(); } } void QmitkUSNewVideoDeviceWidget::CreateConnections() { if (m_Controls) { // connect(m_Controls->m_BtnDone, SIGNAL(clicked()), this, // SLOT(OnClickedDone())); connect(m_Controls->m_BtnCancel, SIGNAL(clicked()), this, SLOT(OnClickedCancel())); connect(m_Controls->m_RadioDeviceSource, SIGNAL(clicked()), this, SLOT(OnDeviceTypeSelection())); connect(m_Controls->m_RadioFileSource, SIGNAL(clicked()), this, SLOT(OnDeviceTypeSelection())); connect(m_Controls->m_RadioOIGTLClientSource, SIGNAL(clicked()), this, SLOT(OnDeviceTypeSelection())); connect(m_Controls->m_RadioOIGTLServerSource, SIGNAL(clicked()), this, SLOT(OnDeviceTypeSelection())); connect(m_Controls->m_OpenFileButton, SIGNAL(clicked()), this, SLOT(OnOpenFileButtonClicked())); + connect(m_Controls->m_BtnSave, SIGNAL(clicked()), this, + SLOT(OnSaveButtonClicked())); + connect(m_Controls->m_BtnLoadConfiguration, SIGNAL(clicked()), this, + SLOT(OnLoadConfigurationButtonClicked())); + //Connect buttons and functions for editing of probes + connect(m_Controls->m_AddNewProbePushButton, SIGNAL(clicked()), this, SLOT(OnAddNewProbeClicked())); connect(m_Controls->m_BtnRemoveProbe, SIGNAL(clicked()), this, SLOT(OnClickedRemoveProbe())); connect(m_Controls->m_BtnRemoveDepth, SIGNAL(clicked()), this, SLOT(OnClickedRemoveDepth())); connect(m_Controls->m_BtnAddDepths, SIGNAL(clicked()), this, SLOT(OnClickedAddDepths())); connect(m_Controls->m_Probes, SIGNAL(currentTextChanged(const QString &)), this, SLOT(OnProbeChanged(const QString &))); + connect(m_Controls->m_Depths, SIGNAL(currentTextChanged(const QString &)), this, SLOT(OnDepthChanged(const QString &))); + + connect(m_Controls->m_XSpacingSpinBox, SIGNAL(valueChanged(double)), this, SLOT(OnXSpacingSpinBoxChanged(double))); + connect(m_Controls->m_YSpacingSpinBox, SIGNAL(valueChanged(double)), this, SLOT(OnYSpacingSpinBoxChanged(double))); + connect(m_Controls->m_CroppingTopSpinBox, SIGNAL(valueChanged(int)), this, SLOT(OnCroppingTopSpinBoxChanged(int))); + connect(m_Controls->m_CroppingRightSpinBox, SIGNAL(valueChanged(int)), this, SLOT(OnCroppingRightSpinBoxChanged(int))); + connect(m_Controls->m_CroppingBottomSpinBox, SIGNAL(valueChanged(int)), this, SLOT(OnCroppingBottomSpinBoxChanged(int))); + connect(m_Controls->m_CroppingLeftSpinBox, SIGNAL(valueChanged(int)), this, SLOT(OnCroppingLeftSpinBoxChanged(int))); } } ///////////// Methods & Slots Handling Direct Interaction ///////////////// void QmitkUSNewVideoDeviceWidget::OnClickedDone() { m_Active = false; // Create Device mitk::USVideoDevice::Pointer newDevice; if (m_Controls->m_RadioDeviceSource->isChecked()) { newDevice = mitk::USVideoDevice::New( m_Controls->m_DeviceSelector->value(), m_Controls->m_Manufacturer->text().toStdString(), m_Controls->m_Model->text().toStdString()); newDevice->SetComment(m_Controls->m_Comment->text().toStdString()); } else if (m_Controls->m_RadioFileSource->isChecked()) { newDevice = mitk::USVideoDevice::New( m_Controls->m_FilePathSelector->text().toStdString(), m_Controls->m_Manufacturer->text().toStdString(), m_Controls->m_Model->text().toStdString()); newDevice->SetComment(m_Controls->m_Comment->text().toStdString()); } else if (m_Controls->m_RadioOIGTLClientSource->isChecked()) { std::string host = m_Controls->m_OIGTLClientHost->text().toStdString(); int port = m_Controls->m_OIGTLClientPort->value(); // Create a new USIGTLDevice. The last parameter tells the device that it should be a client. mitk::USIGTLDevice::Pointer device = mitk::USIGTLDevice::New(m_Controls->m_Manufacturer->text().toStdString(), m_Controls->m_Model->text().toStdString(), host, port, false); device->Initialize(); emit Finished(); // The rest of this method does stuff that is specific to USVideoDevices, // which we don't need. So we return directly. return; } else { std::string host = m_Controls->m_OIGTLServerHost->text().toStdString(); int port = m_Controls->m_OIGTLServerPort->value(); // Create a new USIGTLDevice. The last parameter tells the device that it should be a server. mitk::USIGTLDevice::Pointer device = mitk::USIGTLDevice::New(m_Controls->m_Manufacturer->text().toStdString(), m_Controls->m_Model->text().toStdString(), host, port, true); device->Initialize(); emit Finished(); // The rest of this method does stuff that is specific to USVideoDevices, // which we don't need. So we return directly. return; } // get USImageVideoSource from new device mitk::USImageVideoSource::Pointer imageSource = dynamic_cast( newDevice->GetUSImageSource().GetPointer()); if (!imageSource) { MITK_ERROR << "There is no USImageVideoSource at the current device."; mitkThrow() << "There is no USImageVideoSource at the current device."; } // Set Video Options imageSource->SetColorOutput(!m_Controls->m_CheckGreyscale->isChecked()); // If Resolution override is activated, apply it if (m_Controls->m_CheckResolutionOverride->isChecked()) { int width = m_Controls->m_ResolutionWidth->value(); int height = m_Controls->m_ResolutionHeight->value(); imageSource->OverrideResolution(width, height); imageSource->SetResolutionOverride(true); } - if (!m_Controls->m_ProbesInformation->text().isEmpty()) //there are informations about the probes of the device, so create the probes + if (m_Controls->m_Probes->count() != 0 ) //there are informations about the probes of the device, so create the probes { - AddProbesToDevice(newDevice); + this->AddProbesToDevice(newDevice); } else //no information about the probes of the device, so set default value { mitk::USProbe::Pointer probe = mitk::USProbe::New("default"); probe->SetDepth(0); + newDevice->DeleteAllProbes(); newDevice->AddNewProbe(probe); } newDevice->Initialize(); CleanUpAfterCreatingNewDevice(); emit Finished(); } void QmitkUSNewVideoDeviceWidget::OnClickedFinishedEditing() { m_Active = false; m_TargetDevice->SetManufacturer(m_Controls->m_Manufacturer->text().toStdString()); m_TargetDevice->SetName(m_Controls->m_Model->text().toStdString()); m_TargetDevice->SetComment(m_Controls->m_Comment->text().toStdString()); - if (!m_Controls->m_ProbesInformation->text().isEmpty()){ //there is information about probes to add, so add them - AddProbesToDevice(m_TargetDevice); + if (m_Controls->m_Probes->count() != 0) //there are informations about the probes of the device, so create the probes + { + this->AddProbesToDevice(m_TargetDevice); + } + else //no information about the probes of the device, so set default value + { + mitk::USProbe::Pointer probe = mitk::USProbe::New("default"); + probe->SetDepth(0); + m_TargetDevice->DeleteAllProbes(); + m_TargetDevice->AddNewProbe(probe); } + mitk::USImageVideoSource::Pointer imageSource = dynamic_cast( m_TargetDevice->GetUSImageSource().GetPointer()); if (!imageSource) { MITK_ERROR << "There is no USImageVideoSource at the current device."; mitkThrow() << "There is no USImageVideoSource at the current device."; } // Set Video Options imageSource->SetColorOutput(!m_Controls->m_CheckGreyscale->isChecked()); // If Resolution override is activated, apply it if (m_Controls->m_CheckResolutionOverride->isChecked()) { int width = m_Controls->m_ResolutionWidth->value(); int height = m_Controls->m_ResolutionHeight->value(); imageSource->OverrideResolution(width, height); imageSource->SetResolutionOverride(true); } CleanUpAfterEditingOfDevice(); MITK_INFO << "Finished Editing"; emit Finished(); } void QmitkUSNewVideoDeviceWidget::OnClickedCancel() { m_TargetDevice = nullptr; m_Active = false; CleanUpAfterCreatingNewDevice(); CleanUpAfterEditingOfDevice(); emit Finished(); } void QmitkUSNewVideoDeviceWidget::OnDeviceTypeSelection() { m_Controls->m_FilePathSelector->setEnabled( m_Controls->m_RadioFileSource->isChecked()); m_Controls->m_DeviceSelector->setEnabled( m_Controls->m_RadioDeviceSource->isChecked()); m_Controls->m_OIGTLClientHost->setEnabled( m_Controls->m_RadioOIGTLClientSource->isChecked()); m_Controls->m_OIGTLClientPort->setEnabled( m_Controls->m_RadioOIGTLClientSource->isChecked()); m_Controls->m_OIGTLServerHost->setEnabled( m_Controls->m_RadioOIGTLServerSource->isChecked()); m_Controls->m_OIGTLServerPort->setEnabled( m_Controls->m_RadioOIGTLServerSource->isChecked()); } void QmitkUSNewVideoDeviceWidget::OnOpenFileButtonClicked() { QString fileName = QFileDialog::getOpenFileName(nullptr, "Open Video File"); if (fileName.isNull()) { return; } // user pressed cancel m_Controls->m_FilePathSelector->setText(fileName); m_Controls->m_RadioFileSource->setChecked(true); this->OnDeviceTypeSelection(); } ///////////////// Methods & Slots Handling Logic ////////////////////////// void QmitkUSNewVideoDeviceWidget::EditDevice(mitk::USDevice::Pointer device) { // If no VideoDevice is given, throw an exception - if (device.IsNull()) - { - mitkThrow() << "No device selected"; - } - else if (device->GetDeviceClass().compare("org.mitk.modules.us.USVideoDevice") != + if (device->GetDeviceClass().compare("org.mitk.modules.us.USVideoDevice") != 0) { // TODO Alert if bad path mitkThrow() << "NewVideoDeviceWidget recieved an incompatible device type " "to edit. Type was: " << device->GetDeviceClass(); } m_TargetDevice = static_cast(device.GetPointer()); m_Active = true; - + m_ConfigProbes.clear(); + m_ConfigProbes = m_TargetDevice->GetAllProbes(); ChangeUIEditingUSVideoDevice(); } void QmitkUSNewVideoDeviceWidget::CreateNewDevice() { - //When new device is created there are no probes to edit, therefore disable the Groupbox - m_Controls->m_GroupBoxEditProbes->setEnabled(false); - //Toggle functionality of Btn_Done connect(m_Controls->m_BtnDone, SIGNAL(clicked()), this, SLOT(OnClickedDone())); m_Controls->m_BtnDone->setText("Add Video Device"); //Fill Metadata with default information m_Controls->m_Manufacturer->setText("Unknown Manufacturer"); m_Controls->m_Model->setText("Unknown Model"); m_Controls->m_Comment->setText("None"); m_TargetDevice = nullptr; + m_ConfigProbes.clear(); m_Active = true; } /////////////////////// HOUSEHOLDING CODE /////////////////////////////// QListWidgetItem* QmitkUSNewVideoDeviceWidget::ConstructItemFromDevice( mitk::USDevice::Pointer device) { QListWidgetItem* result = new QListWidgetItem; std::string text = device->GetManufacturer() + "|" + device->GetName(); result->setText(text.c_str()); return result; } void QmitkUSNewVideoDeviceWidget::ChangeUIEditingUSVideoDevice() { - //activate the groupbox contaning the options to edit the probes of the device and fill it with information - m_Controls->m_GroupBoxEditProbes->setEnabled(true); - std::vector probes = m_TargetDevice->GetAllProbes(); - for (std::vector::iterator it = probes.begin(); it != probes.end(); it++) + for (std::vector::iterator it = m_ConfigProbes.begin(); it != m_ConfigProbes.end(); it++) { std::string probeName = (*it)->GetName(); m_Controls->m_Probes->addItem(QString::fromUtf8(probeName.data(), probeName.size())); } OnProbeChanged(m_Controls->m_Probes->currentText()); //Toggle functionality of Btn_Done - m_Controls->m_BtnDone->setText("Save Changes"); + m_Controls->m_BtnDone->setText("Apply Changes"); connect(m_Controls->m_BtnDone, SIGNAL(clicked()), this, SLOT(OnClickedFinishedEditing())); //Fill Metadata with Information provided by the Device selected to edit m_Controls->m_Manufacturer->setText(m_TargetDevice->GetManufacturer().c_str()); m_Controls->m_Model->setText(m_TargetDevice->GetName().c_str()); m_Controls->m_Comment->setText(m_TargetDevice->GetComment().c_str()); } void QmitkUSNewVideoDeviceWidget::OnClickedAddDepths() { if (!m_Controls->m_Probes->currentText().isEmpty()) { - std::string probename = m_Controls->m_Probes->currentText().toStdString(); - mitk::USProbe::Pointer currentProbe = m_TargetDevice->GetProbeByName(probename); QString depths = m_Controls->m_AddDepths->text(); + if( depths.isEmpty() ) + return; + + std::string probename = m_Controls->m_Probes->currentText().toStdString(); + mitk::USProbe::Pointer currentProbe = this->CheckIfProbeExistsAlready(probename); + QStringList singleDepths = depths.split(','); for (int i = 0; i < singleDepths.size(); i++) { currentProbe->SetDepth(singleDepths.at(i).toInt()); } m_Controls->m_AddDepths->clear(); + m_Controls->m_Depths->setEnabled(true); + m_Controls->m_BtnRemoveDepth->setEnabled(true); OnProbeChanged(m_Controls->m_Probes->currentText()); } } void QmitkUSNewVideoDeviceWidget::OnClickedRemoveDepth() { if (!m_Controls->m_Probes->currentText().isEmpty() && !m_Controls->m_Depths->currentText().isEmpty()) { std::string probename = m_Controls->m_Probes->currentText().toStdString(); int indexOfDepthToRemove = m_Controls->m_Depths->currentIndex(); - mitk::USProbe::Pointer currentProbe = m_TargetDevice->GetProbeByName(probename); + mitk::USProbe::Pointer currentProbe = this->CheckIfProbeExistsAlready(probename); currentProbe->RemoveDepth(m_Controls->m_Depths->currentText().toInt()); m_Controls->m_Depths->removeItem(indexOfDepthToRemove); + + if (m_Controls->m_Depths->count() == 0) + { + m_Controls->m_Depths->setEnabled(false); + m_Controls->m_BtnRemoveDepth->setEnabled(false); + + this->EnableDisableSpacingAndCropping(false); + } } } void QmitkUSNewVideoDeviceWidget::OnClickedRemoveProbe() { if (!m_Controls->m_Probes->currentText().isEmpty()) { std::string probename = m_Controls->m_Probes->currentText().toStdString(); int indexOfProbeToRemove = m_Controls->m_Probes->currentIndex(); - m_TargetDevice->RemoveProbeByName(probename); + m_ConfigProbes.erase(m_ConfigProbes.begin() + indexOfProbeToRemove); m_Controls->m_Probes->removeItem(indexOfProbeToRemove); + if( m_Controls->m_Probes->count() == 0 ) + { + m_Controls->m_Probes->setEnabled(false); + m_Controls->m_BtnRemoveProbe->setEnabled(false); + m_Controls->m_BtnAddDepths->setEnabled(false); + m_Controls->m_AddDepths->setEnabled(false); + m_Controls->m_Depths->setEnabled(false); + m_Controls->m_BtnRemoveDepth->setEnabled(false); + + this->EnableDisableSpacingAndCropping(false); + } } } void QmitkUSNewVideoDeviceWidget::OnProbeChanged(const QString & probename) { if (!probename.isEmpty()) { std::string name = probename.toStdString(); - mitk::USProbe::Pointer probe = m_TargetDevice->GetProbeByName(name); - std::map depths = probe->GetDepthsAndSpacing(); + mitk::USProbe::Pointer probe = this->CheckIfProbeExistsAlready(name); + if( probe.IsNotNull() ) + { + std::map depths = probe->GetDepthsAndSpacing(); + m_Controls->m_Depths->clear(); + for (std::map::iterator it = depths.begin(); it != depths.end(); it++) + { + m_Controls->m_Depths->addItem(QString::number(it->first)); + } + + this->OnDepthChanged(m_Controls->m_Depths->currentText().toInt(), probe); + } + } + else + { m_Controls->m_Depths->clear(); - for (std::map::iterator it = depths.begin(); it != depths.end(); it++) + m_Controls->m_Depths->setEnabled(false); + m_Controls->m_BtnRemoveDepth->setEnabled(false); + } +} + +void QmitkUSNewVideoDeviceWidget::OnDepthChanged(int depth, mitk::USProbe* probe) +{ + if (m_Controls->m_Depths->count() == 0) + { + m_Controls->m_Depths->setEnabled(false); + m_Controls->m_BtnRemoveDepth->setEnabled(false); + + this->EnableDisableSpacingAndCropping(false); + return; + } + + if (probe != nullptr) + { + mitk::Vector3D spacing = probe->GetSpacingForGivenDepth(depth); + m_Controls->m_XSpacingSpinBox->setValue(spacing[0]); + m_Controls->m_YSpacingSpinBox->setValue(spacing[1]); + + mitk::USProbe::USProbeCropping cropping = probe->GetProbeCropping(); + m_Controls->m_CroppingTopSpinBox->setValue(cropping.top); + m_Controls->m_CroppingRightSpinBox->setValue(cropping.right); + m_Controls->m_CroppingBottomSpinBox->setValue(cropping.bottom); + m_Controls->m_CroppingLeftSpinBox->setValue(cropping.left); + + this->EnableDisableSpacingAndCropping(true); + + m_Controls->m_Depths->setEnabled(true); + m_Controls->m_BtnRemoveDepth->setEnabled(true); + m_Controls->m_AddDepths->setEnabled(true); + m_Controls->m_BtnAddDepths->setEnabled(true); + m_Controls->m_Probes->setEnabled(true); + m_Controls->m_BtnRemoveProbe->setEnabled(true); + } +} + +void QmitkUSNewVideoDeviceWidget::OnDepthChanged(const QString &depth) +{ + MITK_INFO << "OnDepthChanged(int, mitk::USProbe)"; + if( depth.isEmpty() ) + { + this->EnableDisableSpacingAndCropping(false); + return; + } + QString probeName = m_Controls->m_Probes->currentText(); + + this->OnDepthChanged(depth.toInt(), this->CheckIfProbeExistsAlready(probeName.toStdString()) ); +} + +void QmitkUSNewVideoDeviceWidget::OnSaveButtonClicked() +{ + QString fileName = QFileDialog::getSaveFileName(nullptr, "Save Configuration ...", "", "XML files (*.xml)"); + if( fileName.isNull() ) + { + return; + } // user pressed cancel + + mitk::USDeviceWriterXML deviceWriter; + deviceWriter.SetFilename(fileName.toStdString()); + + mitk::USDeviceReaderXML::USVideoDeviceConfigData config; + this->CollectUltrasoundVideoDeviceConfigInformation(config); + + if (!deviceWriter.WriteUltrasoundVideoDeviceConfiguration(config)) + { + QMessageBox msgBox; + msgBox.setText("Error when writing the configuration to the selected file. Could not write device information."); + msgBox.exec(); + return; + } + +} + +void QmitkUSNewVideoDeviceWidget::OnLoadConfigurationButtonClicked() +{ + QString fileName = QFileDialog::getOpenFileName(this, "Open ultrasound device configuration ..."); + if (fileName.isNull()) + { + return; + } // user pressed cancel + + mitk::USDeviceReaderXML deviceReader; + deviceReader.SetFilename(fileName.toStdString()); + if (!deviceReader.ReadUltrasoundDeviceConfiguration()) + { + QMessageBox msgBox; + msgBox.setText("Error when parsing the selected file. Could not load stored device information."); + msgBox.exec(); + return; + } + mitk::USDeviceReaderXML::USVideoDeviceConfigData config = deviceReader.GetUSVideoDeviceConfigData(); + + if( config.fileversion == 1.0 ) + { + if (config.deviceType.compare("video") == 0) + { + //Fill info in metadata groupbox: + m_Controls->m_DeviceName->setText(QString::fromStdString(config.deviceName)); + m_Controls->m_Manufacturer->setText(QString::fromStdString(config.manufacturer)); + m_Controls->m_Model->setText(QString::fromStdString(config.model)); + m_Controls->m_Comment->setText(QString::fromStdString(config.comment)); + + //Fill info about video source: + m_Controls->m_DeviceSelector->setValue(config.sourceID); + m_Controls->m_FilePathSelector->setText(QString::fromStdString(config.filepathVideoSource)); + + //Fill video options: + m_Controls->m_CheckGreyscale->setChecked(config.useGreyscale); + + //Fill override options: + m_Controls->m_CheckResolutionOverride->setChecked(config.useResolutionOverride); + m_Controls->m_ResolutionWidth->setValue(config.resolutionWidth); + m_Controls->m_ResolutionHeight->setValue(config.resolutionHeight); + + //Fill information about probes: + m_ConfigProbes.clear(); + m_ConfigProbes = config.probes; + + m_Controls->m_Probes->clear(); + m_Controls->m_ProbeNameLineEdit->clear(); + m_Controls->m_AddDepths->clear(); + m_Controls->m_Depths->clear(); + + for( size_t index = 0; index < m_ConfigProbes.size(); ++index) + { + m_Controls->m_Probes->addItem(QString::fromStdString(config.probes.at(index)->GetName())); + } + this->OnProbeChanged(m_Controls->m_Probes->currentText()); + + } + else { - m_Controls->m_Depths->addItem(QString::number(it->first)); + MITK_WARN << "Unknown device type detected. The device type must be of type |video|"; } } + else + { + MITK_WARN << "Unknown fileversion. Only fileversion 1.0 is known to the system."; + } +} + +void QmitkUSNewVideoDeviceWidget::OnAddNewProbeClicked() +{ + QString probeName = m_Controls->m_ProbeNameLineEdit->text(); + probeName = probeName.trimmed(); + if (probeName.isEmpty()) + { + m_Controls->m_ProbeNameLineEdit->clear(); + return; + } + + if( this->CheckIfProbeExistsAlready(probeName.toStdString() ) != nullptr ) + { + QMessageBox msgBox; + msgBox.setText("Probe name already exists. Please choose another name for the probe."); + msgBox.exec(); + m_Controls->m_ProbeNameLineEdit->clear(); + } + else + { + mitk::USProbe::Pointer newProbe = mitk::USProbe::New(probeName.toStdString()); + m_ConfigProbes.push_back(newProbe); + m_Controls->m_Probes->addItem(QString::fromStdString(probeName.toStdString())); + + m_Controls->m_Probes->setEnabled(true); + m_Controls->m_BtnRemoveProbe->setEnabled(true); + m_Controls->m_BtnAddDepths->setEnabled(true); + m_Controls->m_AddDepths->setEnabled(true); + m_Controls->m_ProbeNameLineEdit->clear(); + } +} + +void QmitkUSNewVideoDeviceWidget::OnXSpacingSpinBoxChanged(double value) +{ + MITK_INFO << "Changing x-spacing to: " << value; + QString probeName = m_Controls->m_Probes->currentText(); + int depth = m_Controls->m_Depths->currentText().toInt(); + + mitk::USProbe::Pointer probe = this->CheckIfProbeExistsAlready(probeName.toStdString()); + if (probe.IsNull()) + { + QMessageBox msgBox; + msgBox.setText("An error occurred when changing the spacing. \ + The specified probe does not exist. \ + Please restart the configuration process."); + msgBox.exec(); + return; + } + + mitk::Vector3D spacing = probe->GetSpacingForGivenDepth(depth); + spacing[0] = value; + probe->SetSpacingForGivenDepth(depth, spacing); +} + +void QmitkUSNewVideoDeviceWidget::OnYSpacingSpinBoxChanged(double value) +{ + MITK_INFO << "Changing y-spacing to: " << value; + QString probeName = m_Controls->m_Probes->currentText(); + int depth = m_Controls->m_Depths->currentText().toInt(); + + mitk::USProbe::Pointer probe = this->CheckIfProbeExistsAlready(probeName.toStdString()); + if (probe.IsNull()) + { + QMessageBox msgBox; + msgBox.setText("An error occurred when changing the spacing. \ + The specified probe does not exist. \ + Please restart the configuration process."); + msgBox.exec(); + return; + } + + mitk::Vector3D spacing = probe->GetSpacingForGivenDepth(depth); + spacing[1] = value; + probe->SetSpacingForGivenDepth(depth, spacing); +} + +void QmitkUSNewVideoDeviceWidget::OnCroppingTopSpinBoxChanged(int value) +{ + MITK_INFO << "Changing cropping top to: " << value; + QString probeName = m_Controls->m_Probes->currentText(); + + mitk::USProbe::Pointer probe = this->CheckIfProbeExistsAlready(probeName.toStdString()); + if (probe.IsNull()) + { + QMessageBox msgBox; + msgBox.setText("An error occurred when changing the probe cropping. \ + The specified probe does not exist. \ + Please restart the configuration process."); + msgBox.exec(); + return; + } + + mitk::USProbe::USProbeCropping cropping = probe->GetProbeCropping(); + probe->SetProbeCropping(value, cropping.bottom, cropping.left, cropping.right); +} + +void QmitkUSNewVideoDeviceWidget::OnCroppingRightSpinBoxChanged(int value) +{ + MITK_INFO << "Changing cropping right to: " << value; + QString probeName = m_Controls->m_Probes->currentText(); + + mitk::USProbe::Pointer probe = this->CheckIfProbeExistsAlready(probeName.toStdString()); + if (probe.IsNull()) + { + QMessageBox msgBox; + msgBox.setText("An error occurred when changing the probe cropping. \ + The specified probe does not exist. \ + Please restart the configuration process."); + msgBox.exec(); + return; + } + + mitk::USProbe::USProbeCropping cropping = probe->GetProbeCropping(); + probe->SetProbeCropping(cropping.top, cropping.bottom, cropping.left, value); +} + +void QmitkUSNewVideoDeviceWidget::OnCroppingBottomSpinBoxChanged(int value) +{ + MITK_INFO << "Changing cropping bottom to: " << value; + QString probeName = m_Controls->m_Probes->currentText(); + + mitk::USProbe::Pointer probe = this->CheckIfProbeExistsAlready(probeName.toStdString()); + if (probe.IsNull()) + { + QMessageBox msgBox; + msgBox.setText("An error occurred when changing the probe cropping. \ + The specified probe does not exist. \ + Please restart the configuration process."); + msgBox.exec(); + return; + } + + mitk::USProbe::USProbeCropping cropping = probe->GetProbeCropping(); + probe->SetProbeCropping(cropping.top, value, cropping.left, cropping.right); +} + +void QmitkUSNewVideoDeviceWidget::OnCroppingLeftSpinBoxChanged(int value) +{ + MITK_INFO << "Changing cropping left to: " << value; + QString probeName = m_Controls->m_Probes->currentText(); + + mitk::USProbe::Pointer probe = this->CheckIfProbeExistsAlready(probeName.toStdString()); + if (probe.IsNull()) + { + QMessageBox msgBox; + msgBox.setText("An error occurred when changing the probe cropping. \ + The specified probe does not exist. \ + Please restart the configuration process."); + msgBox.exec(); + return; + } + + mitk::USProbe::USProbeCropping cropping = probe->GetProbeCropping(); + probe->SetProbeCropping(cropping.top, cropping.bottom, value, cropping.right); } void QmitkUSNewVideoDeviceWidget::CleanUpAfterCreatingNewDevice() { disconnect(m_Controls->m_BtnDone, SIGNAL(clicked()), this, SLOT(OnClickedDone())); - m_Controls->m_ProbesInformation->clear(); + m_Controls->m_Probes->clear(); + m_Controls->m_Depths->clear(); + m_Controls->m_AddDepths->clear(); + m_Controls->m_ProbeNameLineEdit->clear(); + m_ConfigProbes.clear(); } void QmitkUSNewVideoDeviceWidget::CleanUpAfterEditingOfDevice() { disconnect(m_Controls->m_BtnDone, SIGNAL(clicked()), this, SLOT(OnClickedFinishedEditing())); m_Controls->m_Probes->clear(); m_Controls->m_Depths->clear(); m_Controls->m_AddDepths->clear(); - m_Controls->m_ProbesInformation->clear(); + m_ConfigProbes.clear(); } void QmitkUSNewVideoDeviceWidget::AddProbesToDevice(mitk::USVideoDevice::Pointer device) { - QString probesInformation = m_Controls->m_ProbesInformation->text(); - QStringList probes = probesInformation.split(';'); //split the different probes - for (int i = 0; i < probes.size(); i++) + device->DeleteAllProbes(); + for( std::vector::iterator it = m_ConfigProbes.begin(); + it != m_ConfigProbes.end(); it++) { - QStringList depths = probes.at(i).split(','); //now for every probe split the probe name and the different depths - mitk::USProbe::Pointer probe = mitk::USProbe::New(); - probe->SetName(depths.at(0).toStdString()); //first element is the probe name - for (int i = 1; i < depths.size(); i++) //all the other elements are the depths for the specific probe so add them to the probe + if ((*it)->IsDepthAndSpacingEmpty()) { - probe->SetDepth(depths.at(i).toInt()); + (*it)->SetDepth(0); } - device->AddNewProbe(probe); + device->AddNewProbe((*it)); + } +} + +mitk::USProbe::Pointer QmitkUSNewVideoDeviceWidget::CheckIfProbeExistsAlready(std::string &probeName) +{ + for( std::vector::iterator it = m_ConfigProbes.begin(); + it != m_ConfigProbes.end(); it++ ) + { + if( probeName.compare((*it)->GetName()) == 0) + return (*it); } + return nullptr; //no matching probe was found so nullptr is returned +} + +void QmitkUSNewVideoDeviceWidget::CollectUltrasoundVideoDeviceConfigInformation(mitk::USDeviceReaderXML::USVideoDeviceConfigData &config) +{ + config.fileversion = 1.0; + config.deviceType = "video"; + + //Fill info in metadata groupbox: + config.deviceName = m_Controls->m_DeviceName->text().toStdString(); + config.manufacturer = m_Controls->m_Manufacturer->text().toStdString(); + config.model = m_Controls->m_Model->text().toStdString(); + config.comment = m_Controls->m_Comment->text().toStdString(); + + //Fill info about video source: + config.sourceID = m_Controls->m_DeviceSelector->value(); + config.filepathVideoSource = m_Controls->m_FilePathSelector->text().toStdString(); + + //Fill video options: + config.useGreyscale = m_Controls->m_CheckGreyscale->isChecked(); + + //Fill override options: + config.useResolutionOverride = m_Controls->m_CheckResolutionOverride->isChecked(); + config.resolutionWidth = m_Controls->m_ResolutionWidth->value(); + config.resolutionHeight = m_Controls->m_ResolutionHeight->value(); + + //Fill information about probes: + config.probes = m_ConfigProbes; +} + +void QmitkUSNewVideoDeviceWidget::EnableDisableSpacingAndCropping(bool enable) +{ + m_Controls->m_XSpacingSpinBox->setEnabled(enable); + m_Controls->m_YSpacingSpinBox->setEnabled(enable); + m_Controls->m_XSpacingLabel->setEnabled(enable); + m_Controls->m_YSpacingLabel->setEnabled(enable); + + m_Controls->m_CroppingTopSpinBox->setEnabled(enable); + m_Controls->m_CroppingRightSpinBox->setEnabled(enable); + m_Controls->m_CroppingBottomSpinBox->setEnabled(enable); + m_Controls->m_CroppingLeftSpinBox->setEnabled(enable); + m_Controls->m_CroppingTopLabel->setEnabled(enable); + m_Controls->m_CroppingBottomLabel->setEnabled(enable); + m_Controls->m_CroppingLeftLabel->setEnabled(enable); + m_Controls->m_CroppingRightLabel->setEnabled(enable); } diff --git a/Modules/USUI/Qmitk/QmitkUSNewVideoDeviceWidget.h b/Modules/USUI/Qmitk/QmitkUSNewVideoDeviceWidget.h index be811eb4ed..531d3a342b 100644 --- a/Modules/USUI/Qmitk/QmitkUSNewVideoDeviceWidget.h +++ b/Modules/USUI/Qmitk/QmitkUSNewVideoDeviceWidget.h @@ -1,130 +1,169 @@ /*=================================================================== 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 _QmitkUSNewVideoDeviceWidget_H_INCLUDED #define _QmitkUSNewVideoDeviceWidget_H_INCLUDED #include "MitkUSUIExports.h" #include "ui_QmitkUSNewVideoDeviceWidgetControls.h" #include "mitkUSVideoDevice.h" #include "mitkUSIGTLDevice.h" +#include "mitkUSDeviceReaderXML.h" //QT headers #include #include //mitk header /** * @brief This Widget enables the USer to create and connect Video Devices. * * @ingroup USUI */ class MITKUSUI_EXPORT QmitkUSNewVideoDeviceWidget :public QWidget { //this is needed for all Qt objects that should have a MOC object (everything that derives from QObject) Q_OBJECT public: static const std::string VIEW_ID; QmitkUSNewVideoDeviceWidget(QWidget* p = nullptr, Qt::WindowFlags f1 = nullptr); ~QmitkUSNewVideoDeviceWidget() override; /* @brief This method is part of the widget an needs not to be called seperately. */ virtual void CreateQtPartControl(QWidget *parent); /* @brief This method is part of the widget an needs not to be called seperately. (Creation of the connections of main and control widget.)*/ virtual void CreateConnections(); signals: void Finished(); public slots: /* \brief Activates the widget and displays the given device's Data to edit. */ void EditDevice(mitk::USDevice::Pointer device); /* \brief Activates the widget with fields empty. */ void CreateNewDevice(); protected slots: /* \brief Called, when the the user clicks the "Done" button (Labeled either "Add Device" or "Edit Device", depending on the situation. */ void OnClickedDone(); void OnClickedFinishedEditing(); /* \brief Called, when the button "Cancel" was clicked */ void OnClickedCancel(); /* \brief Called, when the Use selects one of the Radiobuttons */ void OnDeviceTypeSelection(); void OnOpenFileButtonClicked(); void OnClickedRemoveProbe(); void OnClickedRemoveDepth(); void OnClickedAddDepths(); void OnProbeChanged(const QString & probename); + void OnDepthChanged(int depth, mitk::USProbe* probe); + + void OnDepthChanged(const QString &depth); + + void OnSaveButtonClicked(); + + void OnLoadConfigurationButtonClicked(); + + void OnAddNewProbeClicked(); + + void OnXSpacingSpinBoxChanged(double value); + + void OnYSpacingSpinBoxChanged(double value); + + void OnCroppingTopSpinBoxChanged(int value); + + void OnCroppingRightSpinBoxChanged(int value); + + void OnCroppingBottomSpinBoxChanged(int value); + + void OnCroppingLeftSpinBoxChanged(int value); + protected: Ui::QmitkUSNewVideoDeviceWidgetControls* m_Controls; ///< member holding the UI elements of this widget /* \brief Constructs a ListItem from the given device for display in the list of active devices */ QListWidgetItem* ConstructItemFromDevice(mitk::USDevice::Pointer device); void ChangeUIEditingUSVideoDevice(); void CleanUpAfterEditingOfDevice(); void CleanUpAfterCreatingNewDevice(); void AddProbesToDevice(mitk::USVideoDevice::Pointer device); + mitk::USProbe::Pointer CheckIfProbeExistsAlready(std::string &probe); + + void CollectUltrasoundVideoDeviceConfigInformation(mitk::USDeviceReaderXML::USVideoDeviceConfigData &config); + + /** + * \brief Enables or disables the GUI elements of the spacing and cropping options. + * \param enable If true: the GUI elements are enabled. If false: elements are disabled. + */ + void EnableDisableSpacingAndCropping(bool enable); + /* \brief Displays whether this widget is active or not. It gets activated by either sending a Signal to * the "CreateNewDevice" Slot or to the "EditDevice" Slot. If the user finishes editing the device, a * "EditingComplete" Signal is sent, and the widget is set to inactive again. Clicking Cancel also * deactivates it. */ bool m_Active; /** * \brief This is the device to edit. It is either the device transmitted in the "EditDevice" signal, or a new one * if the "CreateNewDevice slot was called. */ mitk::USVideoDevice::Pointer m_TargetDevice; + + /** + * \brief The config probes are used to have a possibility to configure ultrasound probes without having an existing + * created USVideoDevice yet. + */ + std::vector m_ConfigProbes; }; #endif // _QmitkUSNewVideoDeviceWidget_H_INCLUDED diff --git a/Modules/USUI/Qmitk/QmitkUSNewVideoDeviceWidgetControls.ui b/Modules/USUI/Qmitk/QmitkUSNewVideoDeviceWidgetControls.ui index 33f618298a..44e004599b 100644 --- a/Modules/USUI/Qmitk/QmitkUSNewVideoDeviceWidgetControls.ui +++ b/Modules/USUI/Qmitk/QmitkUSNewVideoDeviceWidgetControls.ui @@ -1,543 +1,710 @@ QmitkUSNewVideoDeviceWidgetControls 0 0 - 333 + 332 972 0 0 QmitkUSNewVideoDeviceWidget - + QLayout::SetDefaultConstraint - - QFormLayout::AllNonFixedFieldsGrow - 0 - 0 + 10 0 0 - + + + + + + Add Video Device + + + + :/USUI/accept.png:/USUI/accept.png + + + + + + + Cancel + + + + :/USUI/restart.png:/USUI/restart.png + + + + + + + + + 0 + + + + + Load Device Configuration + + + + + + + Save->File + + + + :/USUI/accept.png:/USUI/accept.png + + + + + + + + + Metadata: + + + + + + Unknown Manufacturer + + + + + + + Model + + + + + + + + 50 + false + true + + + + Device Information: + + + + + + + Comment + + + + + + + Manufacturer + + + + + + + Unknown Model + + + + + + + None + + + + + + + Name + + + + + + + USVideoDevice + + + + + + + Video Source: From Device: true 0 10 0 From File: false ... OIGTL Server false localhost false 1 65535 18944 OIGTL Client false localhost false 1 65535 18944 - - - - Video Options: - - - - - - Greyscale Image (Significantly faster) - - - true - - - - - - - - - - - - Add Video Device - - - - :/USUI/accept.png:/USUI/accept.png - - - - - - - Cancel - - - - :/USUI/restart.png:/USUI/restart.png - - - - - - - - - - 0 - 0 - + + + + true - 50 - 130 + 0 + 180 - Add Probes - - - Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop + Edit Probes - - - - - Enter the names of the probes of the VideoDevice and, separated by commas, its scanning depths. If you want to add several probes separate the probes and their informations by semicolons E.g. ACV,100,120,150;BDW,90,100;CSV,120,150,180 + + + + + false - - Qt::AutoText + + 6 - - true + + 0.000001000000000 - - Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop + + 1.000000000000000 - - true + + 0.010000000000000 - - -2 + + 1.000000000000000 - - + + + + false + + + Qt::WheelFocus + + + Enter a depth to add to the chosen probe + + - - - - - - - Override: - - - - + + + + true + - Width: + Probes - - - - - 0 - 0 - + + + + false - - 10 + + Qt::WheelFocus - - 2048 + + Add Depth - - 10 + + + + + + false - - 480 + + Cropping Right - - + + + + false + - Height: + Cropping Top - - - - - 0 - 0 - - - - 10 + + + + false - - 2048 + + Cropping Bottom - - 10 + + + + + + - - 640 + + Enter the name of the probe - - + + - Enable Resolution Override + Add New Probe - - - - If you encounter problems with devices (e.g. Images of uniform color and error messages in the log window), then try to set the resolution externally using the device's driver panel and then enter the same resolution here. + + + + false - - true + + Spacing Y-direction - - - - - - - false - - - - 0 - 180 - - - - Edit Probes - - - - + + + + false + - Probes + Spacing X-direction - - - - - 0 - 0 - + + + + false + + + 6 + + + 0.000001000000000 + + + 1.000000000000000 + + + 0.010000000000000 + + + 1.000000000000000 - + + + false + 0 0 Qt::WheelFocus Remove this Probe - - - - Scanning Depths for chosen Probe + + + + false + + + + 0 + 0 + - + + + false + 0 0 - + + + false + 0 0 Qt::WheelFocus Remove this Depth - + + + false + - Enter Scanning Depths you want to add to chosen Probe. -Seperate them by commas. E.g. 120,150,180 + Scanning depths for chosen probe - - - - Qt::WheelFocus + + + + false + + + 400 - - - - Qt::WheelFocus + + + + false + + + 400 + + + + + + + false - Add Depths + Cropping Left + + + + + + + false + + + 400 + + + + + + + false + + + 400 - - + + - Metadata: + Video Options: - - - + + + - Comment + Greyscale Image (Significantly faster) + + + true - - - - - 50 - false - true - - + + + + + + + Override: + + + + - Device Information: + Width: - - - - Manufacturer + + + + + 0 + 0 + + + + 10 + + + 2048 + + + 10 + + + 480 - - + + - Unknown Manufacturer + Height: - - - - Model + + + + + 0 + 0 + + + + 10 + + + 2048 + + + 10 + + + 640 - - + + - Unknown Model + Enable Resolution Override - - + + - None + If you encounter problems with devices (e.g. Images of uniform color and error messages in the log window), then try to set the resolution externally using the device's driver panel and then enter the same resolution here. + + + true m_Manufacturer m_Model m_Comment m_RadioDeviceSource m_DeviceSelector m_RadioFileSource m_CheckResolutionOverride diff --git a/Plugins/org.mitk.gui.qt.igt.app.echotrack/src/internal/QmitkUltrasoundCalibration.cpp b/Plugins/org.mitk.gui.qt.igt.app.echotrack/src/internal/QmitkUltrasoundCalibration.cpp index fc4c1fa540..6da1109e5c 100644 --- a/Plugins/org.mitk.gui.qt.igt.app.echotrack/src/internal/QmitkUltrasoundCalibration.cpp +++ b/Plugins/org.mitk.gui.qt.igt.app.echotrack/src/internal/QmitkUltrasoundCalibration.cpp @@ -1,1140 +1,1141 @@ /*=================================================================== 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 "QmitkUltrasoundCalibration.h" #include // Qt #include #include #include #include // MITK #include //#include #include #include #include #include #include #include "mitkIRenderingManager.h" // us #include //VTK #include #include #include #include #include #include #include "internal/org_mbi_gui_qt_usnavigation_Activator.h" //sleep headers #include #include const std::string QmitkUltrasoundCalibration::VIEW_ID = "org.mitk.views.ultrasoundcalibration"; QmitkUltrasoundCalibration::QmitkUltrasoundCalibration() : m_USDeviceChanged(this, &QmitkUltrasoundCalibration::OnUSDepthChanged) { ctkPluginContext* pluginContext = mitk::PluginActivator::GetContext(); if (pluginContext) { // to be notified about service event of an USDevice pluginContext->connectServiceListener(this, "OnDeviceServiceEvent", QString::fromStdString("(" + us::ServiceConstants::OBJECTCLASS() + "=" + us_service_interface_iid() + ")")); } } QmitkUltrasoundCalibration::~QmitkUltrasoundCalibration() { m_Controls.m_CombinedModalityManagerWidget->blockSignals(true); mitk::USCombinedModality::Pointer combinedModality; combinedModality = m_Controls.m_CombinedModalityManagerWidget->GetSelectedCombinedModality(); if (combinedModality.IsNotNull()) { combinedModality->GetUltrasoundDevice()->RemovePropertyChangedListener(m_USDeviceChanged); } m_Timer->stop(); // Sleep(500); //This might be problematic... seems like sometimes some ressources are still in use at calling time. this->OnStopCalibrationProcess(); this->OnStopPlusCalibration(); /*mitk::DataNode::Pointer node = this->GetDataStorage()->GetNamedNode("Tool Calibration Points"); if (node.IsNotNull())this->GetDataStorage()->Remove(node); node = this->GetDataStorage()->GetNamedNode("Image Calibration Points"); if (node.IsNotNull())this->GetDataStorage()->Remove(node); node = this->GetDataStorage()->GetNamedNode("US Image Stream"); if (node.IsNotNull())this->GetDataStorage()->Remove(node);*/ mitk::DataNode::Pointer node = this->GetDataStorage()->GetNamedNode("Needle Path"); if (node.IsNotNull())this->GetDataStorage()->Remove(node); this->GetDataStorage()->Remove(m_VerificationReferencePointsDataNode); delete m_Timer; } void QmitkUltrasoundCalibration::SetFocus() { m_Controls.m_ToolBox->setFocus(); } void QmitkUltrasoundCalibration::CreateQtPartControl(QWidget *parent) { // create GUI widgets from the Qt Designer's .ui file m_Controls.setupUi(parent); m_Controls.m_CombinedModalityManagerWidget->SetCalibrationLoadedNecessary(false); m_Timer = new QTimer(this); m_StreamingTimer = new QTimer(this); m_Controls.m_SpacingBtnFreeze->setEnabled(true); m_Controls.m_SpacingAddPoint->setEnabled(false); m_Controls.m_CalculateSpacing->setEnabled(false); m_SpacingPointsCount = 0; m_SpacingPoints = mitk::PointSet::New(); m_SpacingNode = mitk::DataNode::New(); m_SpacingNode->SetName("Spacing Points"); m_SpacingNode->SetData(this->m_SpacingPoints); this->GetDataStorage()->Add(m_SpacingNode); // Pointset for Calibration Points m_CalibPointsTool = mitk::PointSet::New(); // Pointset for Worldpoints m_CalibPointsImage = mitk::PointSet::New(); m_CalibPointsCount = 0; // Evaluation Pointsets (Non-Visualized) m_EvalPointsImage = mitk::PointSet::New(); m_EvalPointsTool = mitk::PointSet::New(); m_EvalPointsProjected = mitk::PointSet::New(); // Neelde Projection Filter m_NeedleProjectionFilter = mitk::NeedleProjectionFilter::New(); // Tracking Status Widgets m_Controls.m_CalibTrackingStatus->ShowStatusLabels(); m_Controls.m_EvalTrackingStatus->ShowStatusLabels(); m_OverrideSpacing = false; // General & Device Selection connect(m_Timer, SIGNAL(timeout()), this, SLOT(Update())); //connect(m_Controls.m_ToolBox, SIGNAL(currentChanged(int)), this, SLOT(OnTabSwitch(int))); // Calibration connect(m_Controls.m_CalibBtnFreeze, SIGNAL(clicked()), this, SLOT(SwitchFreeze())); // Freeze connect(m_Controls.m_CalibBtnAddPoint, SIGNAL(clicked()), this, SLOT(OnAddCalibPoint())); // Tracking & Image Points (Calibration) connect(m_Controls.m_CalibBtnCalibrate, SIGNAL(clicked()), this, SLOT(OnCalibration())); // Perform Calibration // Evaluation connect(m_Controls.m_EvalBtnStep1, SIGNAL(clicked()), this, SLOT(OnAddEvalProjectedPoint())); // Needle Projection connect(m_Controls.m_EvalBtnStep2, SIGNAL(clicked()), this, SLOT(SwitchFreeze())); // Freeze connect(m_Controls.m_EvalBtnStep3, SIGNAL(clicked()), this, SLOT(OnAddEvalTargetPoint())); // Tracking & Image Points (Evaluation) connect(m_Controls.m_EvalBtnSave, SIGNAL(clicked()), this, SLOT(OnSaveEvaluation())); // Save Evaluation Results connect(m_Controls.m_CalibBtnSaveCalibration, SIGNAL(clicked()), this, SLOT(OnSaveCalibration())); // Save Evaluation Results connect(m_Controls.m_BtnReset, SIGNAL(clicked()), this, SLOT(OnReset())); // Reset Pointsets // PLUS Calibration connect(m_Controls.m_GetCalibrationFromPLUS, SIGNAL(clicked()), this, SLOT(OnGetPlusCalibration())); connect(m_Controls.m_StartStreaming, SIGNAL(clicked()), this, SLOT(OnStartStreaming())); connect(m_StreamingTimer, SIGNAL(timeout()), this, SLOT(OnStreamingTimerTimeout())); connect(m_Controls.m_StopPlusCalibration, SIGNAL(clicked()), this, SLOT(OnStopPlusCalibration())); connect(m_Controls.m_SavePlusCalibration, SIGNAL(clicked()), this, SLOT(OnSaveCalibration())); connect(this, SIGNAL(NewConnectionSignal()), this, SLOT(OnNewConnection())); //Determine Spacing for Calibration of USVideoDevice connect(m_Controls.m_SpacingBtnFreeze, SIGNAL(clicked()), this, SLOT(OnFreezeClicked())); connect(m_Controls.m_SpacingAddPoint, SIGNAL(clicked()), this, SLOT(OnAddSpacingPoint())); connect(m_Controls.m_CalculateSpacing, SIGNAL(clicked()), this, SLOT(OnCalculateSpacing())); //connect( m_Controls.m_CombinedModalityManagerWidget, SIGNAL(SignalCombinedModalitySelected(mitk::USCombinedModality::Pointer)), // this, SLOT(OnSelectDevice(mitk::USCombinedModality::Pointer)) ); connect(m_Controls.m_CombinedModalityManagerWidget, SIGNAL(SignalReadyForNextStep()), this, SLOT(OnDeviceSelected())); connect(m_Controls.m_CombinedModalityManagerWidget, SIGNAL(SignalNoLongerReadyForNextStep()), this, SLOT(OnDeviceDeselected())); connect(m_Controls.m_StartCalibrationButton, SIGNAL(clicked()), this, SLOT(OnStartCalibrationProcess())); connect(m_Controls.m_StartPlusCalibrationButton, SIGNAL(clicked()), this, SLOT(OnStartPlusCalibration())); connect(m_Controls.m_CalibBtnRestartCalibration, SIGNAL(clicked()), this, SLOT(OnReset())); connect(m_Controls.m_CalibBtnStopCalibration, SIGNAL(clicked()), this, SLOT(OnStopCalibrationProcess())); connect(m_Controls.m_AddReferencePoints, SIGNAL(clicked()), this, SLOT(OnAddCurrentTipPositionToReferencePoints())); connect(m_Controls.m_AddCurrentPointerTipForVerification, SIGNAL(clicked()), this, SLOT(OnAddCurrentTipPositionForVerification())); connect(m_Controls.m_StartVerification, SIGNAL(clicked()), this, SLOT(OnStartVerification())); //initialize data storage combo box m_Controls.m_ReferencePointsComboBox->SetDataStorage(this->GetDataStorage()); m_Controls.m_ReferencePointsComboBox->SetAutoSelectNewItems(true); m_Controls.m_ReferencePointsComboBox->SetPredicate(mitk::NodePredicateDataType::New("PointSet")); //initialize point list widget if (m_VerificationReferencePoints.IsNull()) { m_VerificationReferencePoints = mitk::PointSet::New(); } if (m_VerificationReferencePointsDataNode.IsNull()) { m_VerificationReferencePointsDataNode = mitk::DataNode::New(); m_VerificationReferencePointsDataNode->SetName("US Verification Reference Points"); m_VerificationReferencePointsDataNode->SetData(m_VerificationReferencePoints); this->GetDataStorage()->Add(m_VerificationReferencePointsDataNode); } m_Controls.m_ReferencePointsPointListWidget->SetPointSetNode(m_VerificationReferencePointsDataNode); m_Controls.m_ToolBox->setCurrentIndex(0); } void QmitkUltrasoundCalibration::OnSelectionChanged(berry::IWorkbenchPart::Pointer /*source*/, const QList& /*nodes*/) { } void QmitkUltrasoundCalibration::OnTabSwitch(int index) { switch (index) { case 0: if (m_Controls.m_ToolBox->isItemEnabled(1) || m_Controls.m_ToolBox->isItemEnabled(2)) { this->OnStopCalibrationProcess(); } break; default: ; } } //void QmitkUltrasoundCalibration::OnSelectDevice(mitk::USCombinedModality::Pointer combinedModality) void QmitkUltrasoundCalibration::OnDeviceSelected() { mitk::USCombinedModality::Pointer combinedModality; combinedModality = m_Controls.m_CombinedModalityManagerWidget->GetSelectedCombinedModality(); if (combinedModality.IsNotNull()) { //m_Tracker = m_CombinedModality->GetNavigationDataSource(); // Construct Pipeline //this->m_NeedleProjectionFilter->SetInput(0, m_Tracker->GetOutput(0)); combinedModality->GetUltrasoundDevice()->AddPropertyChangedListener(m_USDeviceChanged); m_Controls.m_StartCalibrationButton->setEnabled(true); m_Controls.m_StartPlusCalibrationButton->setEnabled(true); m_Controls.m_ToolBox->setItemEnabled(1, true); m_Controls.m_ToolBox->setItemEnabled(2, true); } } void QmitkUltrasoundCalibration::OnDeviceDeselected() { mitk::USCombinedModality::Pointer combinedModality; combinedModality = m_Controls.m_CombinedModalityManagerWidget->GetSelectedCombinedModality(); if (combinedModality.IsNotNull()) { combinedModality->GetUltrasoundDevice()->RemovePropertyChangedListener(m_USDeviceChanged); } m_Controls.m_StartCalibrationButton->setEnabled(false); m_Controls.m_StartPlusCalibrationButton->setEnabled(false); m_Controls.m_ToolBox->setCurrentIndex(0); m_Controls.m_ToolBox->setItemEnabled(1, false); m_Controls.m_ToolBox->setItemEnabled(2, false); } void QmitkUltrasoundCalibration::OnAddCurrentTipPositionToReferencePoints() { if (m_Controls.m_VerificationPointerChoser->GetSelectedNavigationDataSource().IsNull() || (m_Controls.m_VerificationPointerChoser->GetSelectedToolID() == -1)) { MITK_WARN << "No tool selected, aborting"; return; } mitk::NavigationData::Pointer currentPointerData = m_Controls.m_VerificationPointerChoser->GetSelectedNavigationDataSource()->GetOutput(m_Controls.m_VerificationPointerChoser->GetSelectedToolID()); mitk::Point3D currentTipPosition = currentPointerData->GetPosition(); m_VerificationReferencePoints->InsertPoint(m_VerificationReferencePoints->GetSize(), currentTipPosition); } void QmitkUltrasoundCalibration::OnStartVerification() { m_currentPoint = 0; mitk::PointSet::Pointer selectedPointSet = dynamic_cast(m_Controls.m_ReferencePointsComboBox->GetSelectedNode()->GetData()); m_Controls.m_CurrentPointLabel->setText("Point " + QString::number(m_currentPoint) + " of " + QString::number(selectedPointSet->GetSize())); m_allErrors = std::vector(); m_allReferencePoints = std::vector(); for (int i = 0; i < selectedPointSet->GetSize(); i++) { m_allReferencePoints.push_back(selectedPointSet->GetPoint(i)); } } void QmitkUltrasoundCalibration::OnAddCurrentTipPositionForVerification() { if (m_currentPoint == -1) { MITK_WARN << "Cannot add point"; return; } if (m_Controls.m_VerificationPointerChoser->GetSelectedNavigationDataSource().IsNull() || (m_Controls.m_VerificationPointerChoser->GetSelectedToolID() == -1)) { MITK_WARN << "No tool selected, aborting"; return; } mitk::NavigationData::Pointer currentPointerData = m_Controls.m_VerificationPointerChoser->GetSelectedNavigationDataSource()->GetOutput(m_Controls.m_VerificationPointerChoser->GetSelectedToolID()); mitk::Point3D currentTipPosition = currentPointerData->GetPosition(); double currentError = m_allReferencePoints.at(m_currentPoint).EuclideanDistanceTo(currentTipPosition); MITK_INFO << "Current Error: " << currentError << " mm"; m_allErrors.push_back(currentError); if (++m_currentPoint < static_cast(m_allReferencePoints.size())) { m_Controls.m_CurrentPointLabel->setText("Point " + QString::number(m_currentPoint) + " of " + QString::number(m_allReferencePoints.size())); } else { m_currentPoint = -1; double meanError = 0; for (std::size_t i = 0; i < m_allErrors.size(); ++i) { meanError += m_allErrors[i]; } meanError /= m_allErrors.size(); QString result = "Finished verification! \n Verification of " + QString::number(m_allErrors.size()) + " points, mean error: " + QString::number(meanError) + " mm"; m_Controls.m_ResultsTextEdit->setText(result); MITK_INFO << result.toStdString(); } } void QmitkUltrasoundCalibration::OnStartCalibrationProcess() { // US Image Stream m_Node = mitk::DataNode::New(); m_Node->SetName("US Calibration Viewing Stream"); //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_Node->SetData(dummyImage); this->GetDataStorage()->Add(m_Node); // data node for calibration point set m_CalibNode = mitk::DataNode::New(); m_CalibNode->SetName("Tool Calibration Points"); m_CalibNode->SetData(this->m_CalibPointsImage); this->GetDataStorage()->Add(m_CalibNode); // data node for world point set m_WorldNode = mitk::DataNode::New(); m_WorldNode->SetName("Image Calibration Points"); m_WorldNode->SetData(this->m_CalibPointsTool); this->GetDataStorage()->Add(m_WorldNode); m_CombinedModality = m_Controls.m_CombinedModalityManagerWidget->GetSelectedCombinedModality(); if (m_CombinedModality.IsNull()) { return; } m_Tracker = m_CombinedModality->GetNavigationDataSource(); //QString curDepth = service.getProperty(QString::fromStdString(mitk::USDevice::US_PROPKEY_BMODE_DEPTH)).toString(); // Construct Pipeline this->m_NeedleProjectionFilter->SetInput(0, m_Tracker->GetOutput(0)); QApplication::setOverrideCursor(Qt::WaitCursor); // make sure that the combined modality is in connected state before using it if (m_CombinedModality->GetDeviceState() < mitk::USDevice::State_Connected) { m_CombinedModality->Connect(); } if (m_CombinedModality->GetDeviceState() < mitk::USDevice::State_Activated) { m_CombinedModality->Activate(); } QApplication::restoreOverrideCursor(); this->SwitchFreeze(); // Todo: Maybe display this elsewhere this->ShowNeedlePath(); // Switch active tab to Calibration page m_Controls.m_ToolBox->setItemEnabled(1, true); m_Controls.m_ToolBox->setCurrentIndex(1); } void QmitkUltrasoundCalibration::OnStartPlusCalibration() { if (m_CombinedModality.IsNull()){ m_CombinedModality = m_Controls.m_CombinedModalityManagerWidget->GetSelectedCombinedModality(); if (m_CombinedModality.IsNull()) { return; } //something went wrong, there is no combined modality } //setup server to send UltrasoundImages to PLUS mitk::IGTLServer::Pointer m_USServer = mitk::IGTLServer::New(true); m_USServer->SetName("EchoTrack Image Source"); m_USServer->SetHostname("127.0.0.1"); m_USServer->SetPortNumber(18944); m_USMessageProvider = mitk::IGTLMessageProvider::New(); m_USMessageProvider->SetIGTLDevice(m_USServer); m_USMessageProvider->SetFPS(5); m_USImageToIGTLMessageFilter = mitk::ImageToIGTLMessageFilter::New(); m_USImageToIGTLMessageFilter->ConnectTo(m_CombinedModality->GetUltrasoundDevice()); m_USImageToIGTLMessageFilter->SetName("USImage Filter"); //setup server to send TrackingData to PLUS m_TrackingServer = mitk::IGTLServer::New(true); m_TrackingServer->SetName("EchoTrack Tracking Source"); m_TrackingServer->SetHostname("127.0.0.1"); m_TrackingServer->SetPortNumber(18945); m_TrackingMessageProvider = mitk::IGTLMessageProvider::New(); m_TrackingMessageProvider->SetIGTLDevice(m_TrackingServer); m_TrackingMessageProvider->SetFPS(5); m_TrackingToIGTLMessageFilter = mitk::NavigationDataToIGTLMessageFilter::New(); m_TrackingToIGTLMessageFilter->ConnectTo(m_CombinedModality->GetTrackingDevice()); m_TrackingToIGTLMessageFilter->SetName("Tracker Filter"); typedef itk::SimpleMemberCommand< QmitkUltrasoundCalibration > CurCommandType; CurCommandType::Pointer newConnectionCommand = CurCommandType::New(); newConnectionCommand->SetCallbackFunction( this, &QmitkUltrasoundCalibration::OnPlusConnected); this->m_NewConnectionObserverTag = this->m_TrackingServer->AddObserver( mitk::NewClientConnectionEvent(), newConnectionCommand); //Open connections of both servers if (m_USServer->OpenConnection()) { MITK_INFO << "US Server opened its connection successfully"; m_USServer->StartCommunication(); } else { MITK_INFO << "US Server could not open its connection"; } if (m_TrackingServer->OpenConnection()) { MITK_INFO << "Tracking Server opened its connection successfully"; m_TrackingServer->StartCommunication(); } else { MITK_INFO << "Tracking Server could not open its connection"; } if (m_USMessageProvider->IsCommunicating() && m_TrackingMessageProvider->IsCommunicating()) { m_Controls.m_StartPlusCalibrationButton->setEnabled(false); m_Controls.m_GetCalibrationFromPLUS->setEnabled(true); m_Controls.m_StartStreaming->setEnabled(false); m_Controls.m_SavePlusCalibration->setEnabled(false); m_Controls.m_SetupStatus->setStyleSheet("QLabel { color : green; }"); m_Controls.m_SetupStatus->setText("Setup successfull you can now connect PLUS"); } else { m_Controls.m_SetupStatus->setStyleSheet("QLabel { color : red; }"); m_Controls.m_SetupStatus->setText("Something went wrong. Please try again"); } } void QmitkUltrasoundCalibration::OnStopPlusCalibration() { //closing all server and clients when PlusCalibration is finished if (m_USMessageProvider.IsNotNull()) { if (m_USMessageProvider->IsStreaming()) { m_USMessageProvider->StopStreamingOfSource(m_USImageToIGTLMessageFilter); } } if (m_TrackingMessageProvider.IsNotNull()) { if (m_TrackingMessageProvider->IsStreaming()) { m_TrackingMessageProvider->StopStreamingOfSource(m_TrackingToIGTLMessageFilter); } } if (m_USServer.IsNotNull()) { m_USServer->CloseConnection(); } if (m_TrackingServer.IsNotNull()) { m_TrackingServer->CloseConnection(); } if (m_TransformClient.IsNotNull()) { m_TransformClient->CloseConnection(); } m_Controls.m_GotCalibrationLabel->setText(""); m_Controls.m_ConnectionStatus->setText(""); m_Controls.m_SetupStatus->setText(""); m_Controls.m_StartPlusCalibrationButton->setEnabled(true); m_StreamingTimer->stop(); delete m_StreamingTimer; } void QmitkUltrasoundCalibration::OnPlusConnected() { emit NewConnectionSignal(); } void QmitkUltrasoundCalibration::OnNewConnection() { m_Controls.m_StartStreaming->setEnabled(true); m_Controls.m_ConnectionStatus->setStyleSheet("QLabel { color : green; }"); m_Controls.m_ConnectionStatus->setText("Connection successfull you can now start streaming"); } void QmitkUltrasoundCalibration::OnStreamingTimerTimeout() { m_USMessageProvider->Update(); m_TrackingMessageProvider->Update(); } void QmitkUltrasoundCalibration::OnStartStreaming() { m_USMessageProvider->StartStreamingOfSource(m_USImageToIGTLMessageFilter, 5); m_TrackingMessageProvider->StartStreamingOfSource(m_TrackingToIGTLMessageFilter, 5); m_Controls.m_StartStreaming->setEnabled(false); m_Controls.m_ConnectionStatus->setText(""); m_StreamingTimer->start((1.0 / 5.0 * 1000.0)); } void QmitkUltrasoundCalibration::OnGetPlusCalibration() { m_TransformClient = mitk::IGTLClient::New(true); m_TransformClient->SetHostname("127.0.0.1"); m_TransformClient->SetPortNumber(18946); m_TransformDeviceSource = mitk::IGTLDeviceSource::New(); m_TransformDeviceSource->SetIGTLDevice(m_TransformClient); m_TransformDeviceSource->Connect(); if (m_TransformDeviceSource->IsConnected()) { MITK_INFO << "successfully connected"; m_TransformDeviceSource->StartCommunication(); if (m_TransformDeviceSource->IsCommunicating()) { MITK_INFO << "communication started"; mitk::IGTLMessage::Pointer receivedMessage; bool condition = false; igtl::Matrix4x4 transformPLUS; while (!(receivedMessage.IsNotNull() && receivedMessage->IsDataValid())) { std::this_thread::sleep_for(std::chrono::milliseconds(50)); m_TransformDeviceSource->Update(); receivedMessage = m_TransformDeviceSource->GetOutput(); igtl::TransformMessage::Pointer msg = dynamic_cast(m_TransformDeviceSource->GetOutput()->GetMessage().GetPointer()); if (msg == nullptr || msg.IsNull()) { MITK_INFO << "Received message could not be casted to TransformMessage. Skipping.."; continue; } else { if (std::strcmp(msg->GetDeviceName(), "ImageToTracker") != 0) { MITK_INFO << "Was not Image to Tracker Transform. Skipping..."; continue; } else { msg->GetMatrix(transformPLUS); condition = true; break; } } } if (condition) { this->ProcessPlusCalibration(transformPLUS); } else { m_Controls.m_GotCalibrationLabel->setStyleSheet("QLabel { color : red; }"); m_Controls.m_GotCalibrationLabel->setText("Something went wrong. Please try again"); } } else { MITK_INFO << " no connection"; m_Controls.m_GotCalibrationLabel->setStyleSheet("QLabel { color : red; }"); m_Controls.m_GotCalibrationLabel->setText("Something went wrong. Please try again"); } } else { m_Controls.m_GotCalibrationLabel->setStyleSheet("QLabel { color : red; }"); m_Controls.m_GotCalibrationLabel->setText("Something went wrong. Please try again"); } } void QmitkUltrasoundCalibration::ProcessPlusCalibration(igtl::Matrix4x4& imageToTracker) { mitk::AffineTransform3D::Pointer imageToTrackerTransform = mitk::AffineTransform3D::New(); itk::Matrix rotationFloat = itk::Matrix(); itk::Vector translationFloat = itk::Vector(); rotationFloat[0][0] = imageToTracker[0][0]; rotationFloat[0][1] = imageToTracker[0][1]; rotationFloat[0][2] = imageToTracker[0][2]; rotationFloat[1][0] = imageToTracker[1][0]; rotationFloat[1][1] = imageToTracker[1][1]; rotationFloat[1][2] = imageToTracker[1][2]; rotationFloat[2][0] = imageToTracker[2][0]; rotationFloat[2][1] = imageToTracker[2][1]; rotationFloat[2][2] = imageToTracker[2][2]; translationFloat[0] = imageToTracker[0][3]; translationFloat[1] = imageToTracker[1][3]; translationFloat[2] = imageToTracker[2][3]; imageToTrackerTransform->SetTranslation(translationFloat); imageToTrackerTransform->SetMatrix(rotationFloat); m_CombinedModality->SetCalibration(imageToTrackerTransform); m_Controls.m_ToolBox->setItemEnabled(2, true); m_Controls.m_SavePlusCalibration->setEnabled(true); m_Controls.m_GotCalibrationLabel->setStyleSheet("QLabel { color : green; }"); m_Controls.m_GotCalibrationLabel->setText("Recieved Calibration from PLUS you can now save it"); } void QmitkUltrasoundCalibration::OnStopCalibrationProcess() { this->ClearTemporaryMembers(); m_Timer->stop(); this->GetDataStorage()->Remove(m_Node); m_Node = 0; this->GetDataStorage()->Remove(m_CalibNode); m_CalibNode = 0; this->GetDataStorage()->Remove(m_WorldNode); m_WorldNode = 0; m_Controls.m_ToolBox->setCurrentIndex(0); } void QmitkUltrasoundCalibration::OnDeviceServiceEvent(const ctkServiceEvent event) { if (m_CombinedModality.IsNull() || event.getType() != ctkServiceEvent::MODIFIED) { return; } ctkServiceReference service = event.getServiceReference(); QString curDepth = service.getProperty(QString::fromStdString(mitk::USDevice::GetPropertyKeys().US_PROPKEY_BMODE_DEPTH)).toString(); if (m_CurrentDepth != curDepth) { m_CurrentDepth = curDepth; this->OnReset(); } } void QmitkUltrasoundCalibration::OnAddCalibPoint() { mitk::Point3D world = this->GetRenderWindowPart()->GetSelectedPosition(); this->m_CalibPointsImage->InsertPoint(m_CalibPointsCount, world); this->m_CalibPointsTool->InsertPoint(m_CalibPointsCount, this->m_FreezePoint); QString text = text.number(m_CalibPointsCount + 1); text = "Point " + text; this->m_Controls.m_CalibPointList->addItem(text); m_CalibPointsCount++; SwitchFreeze(); } void QmitkUltrasoundCalibration::OnCalibration() { // Compute transformation vtkSmartPointer transform = vtkSmartPointer::New(); transform->SetSourceLandmarks(this->ConvertPointSetToVtkPolyData(m_CalibPointsImage)->GetPoints()); transform->SetTargetLandmarks(this->ConvertPointSetToVtkPolyData(m_CalibPointsTool)->GetPoints()); if (m_Controls.m_ScaleTransform->isChecked()) { transform->SetModeToSimilarity(); } //use affine transform else { transform->SetModeToRigidBody(); } //use similarity transform: scaling is not touched transform->Modified(); transform->Update(); // Convert from vtk to itk data types itk::Matrix rotationFloat = itk::Matrix(); itk::Vector translationFloat = itk::Vector(); vtkSmartPointer m = transform->GetMatrix(); rotationFloat[0][0] = m->GetElement(0, 0); rotationFloat[0][1] = m->GetElement(0, 1); rotationFloat[0][2] = m->GetElement(0, 2); rotationFloat[1][0] = m->GetElement(1, 0); rotationFloat[1][1] = m->GetElement(1, 1); rotationFloat[1][2] = m->GetElement(1, 2); rotationFloat[2][0] = m->GetElement(2, 0); rotationFloat[2][1] = m->GetElement(2, 1); rotationFloat[2][2] = m->GetElement(2, 2); translationFloat[0] = m->GetElement(0, 3); translationFloat[1] = m->GetElement(1, 3); translationFloat[2] = m->GetElement(2, 3); mitk::DataNode::Pointer CalibPointsImage = mitk::DataNode::New(); CalibPointsImage->SetName("Calibration Points Image"); CalibPointsImage->SetData(m_CalibPointsImage); this->GetDataStorage()->Add(CalibPointsImage); mitk::DataNode::Pointer CalibPointsTracking = mitk::DataNode::New(); CalibPointsTracking->SetName("Calibration Points Tracking"); CalibPointsTracking->SetData(m_CalibPointsTool); this->GetDataStorage()->Add(CalibPointsTracking); mitk::PointSet::Pointer ImagePointsTransformed = m_CalibPointsImage->Clone(); this->ApplyTransformToPointSet(ImagePointsTransformed, transform); mitk::DataNode::Pointer CalibPointsImageTransformed = mitk::DataNode::New(); CalibPointsImageTransformed->SetName("Calibration Points Image (Transformed)"); CalibPointsImageTransformed->SetData(ImagePointsTransformed); this->GetDataStorage()->Add(CalibPointsImageTransformed); // Set output variable mitk::AffineTransform3D::Pointer oldUSImageTransform = m_Image->GetGeometry()->GetIndexToWorldTransform(); //including spacing! MITK_INFO << "Old US Image transform: " << oldUSImageTransform; mitk::AffineTransform3D::Pointer calibTransform = mitk::AffineTransform3D::New(); calibTransform->SetTranslation(translationFloat); calibTransform->SetMatrix(rotationFloat); MITK_INFO << "Calibration transform: " << calibTransform; m_Transformation = mitk::AffineTransform3D::New(); if (!m_Controls.m_ScaleTransform->isChecked()) { m_Transformation->Compose(oldUSImageTransform); } m_Transformation->Compose(calibTransform); MITK_INFO << "New combined transform: " << m_Transformation; mitk::SlicedGeometry3D::Pointer sliced3d = dynamic_cast (m_Node->GetData()->GetGeometry()); mitk::PlaneGeometry::Pointer plane = const_cast (sliced3d->GetPlaneGeometry(0)); plane->SetIndexToWorldTransform(m_Transformation); // Save to US-Device m_CombinedModality->SetCalibration(m_Transformation); m_Controls.m_ToolBox->setItemEnabled(2, true); // Save to NeedleProjectionFilter m_NeedleProjectionFilter->SetTargetPlane(m_Transformation); // Update Calibration FRE m_CalibrationStatistics = mitk::PointSetDifferenceStatisticsCalculator::New(); mitk::PointSet::Pointer p1 = this->m_CalibPointsTool->Clone(); // We use clones to calculate statistics to avoid concurrency Problems // Create point set with transformed image calibration points for // calculating the difference of image calibration and tool // calibration points in one geometry space mitk::PointSet::Pointer p2 = mitk::PointSet::New(); int n = 0; for (mitk::PointSet::PointsConstIterator it = m_CalibPointsImage->Begin(); it != m_CalibPointsImage->End(); ++it, ++n) { p2->InsertPoint(n, m_Transformation->TransformPoint(it->Value())); } m_CalibrationStatistics->SetPointSets(p1, p2); //QString text = text.number(m_CalibrationStatistics->GetRMS()); QString text = QString::number(ComputeFRE(m_CalibPointsImage, m_CalibPointsTool, transform)); MITK_INFO << "Calibration FRE: " << text.toStdString().c_str(); m_Controls.m_EvalLblCalibrationFRE->setText(text); m_Node->SetStringProperty("Calibration FRE", text.toStdString().c_str()); // Enable Button to save Calibration m_Controls.m_CalibBtnSaveCalibration->setEnabled(true); } void QmitkUltrasoundCalibration::OnAddEvalTargetPoint() { mitk::Point3D world = this->GetRenderWindowPart()->GetSelectedPosition(); this->m_EvalPointsImage->InsertPoint(m_EvalPointsImage->GetSize(), world); this->m_EvalPointsTool->InsertPoint(m_EvalPointsTool->GetSize(), this->m_FreezePoint); QString text = text.number(this->m_EvalPointsTool->GetSize()); this->m_Controls.m_EvalLblNumTargetPoints->setText(text); // Update FREs // Update Evaluation FRE, but only if it contains more than one point (will crash otherwise) if ((m_EvalPointsProjected->GetSize() > 1) && (m_EvalPointsTool->GetSize() > 1)) { m_EvaluationStatistics = mitk::PointSetDifferenceStatisticsCalculator::New(); m_ProjectionStatistics = mitk::PointSetDifferenceStatisticsCalculator::New(); mitk::PointSet::Pointer p1 = this->m_EvalPointsTool->Clone(); // We use clones to calculate statistics to avoid concurrency Problems mitk::PointSet::Pointer p2 = this->m_EvalPointsImage->Clone(); mitk::PointSet::Pointer p3 = this->m_EvalPointsProjected->Clone(); m_EvaluationStatistics->SetPointSets(p1, p2); m_ProjectionStatistics->SetPointSets(p1, p3); QString evalText = evalText.number(m_EvaluationStatistics->GetRMS()); QString projText = projText.number(m_ProjectionStatistics->GetRMS()); m_Controls.m_EvalLblEvaluationFRE->setText(evalText); m_Controls.m_EvalLblProjectionFRE->setText(projText); } SwitchFreeze(); } void QmitkUltrasoundCalibration::OnAddEvalProjectedPoint() { MITK_WARN << "Projection Evaluation may currently be inaccurate."; // TODO: Verify correct Evaluation. Is the Point that is added really current? mitk::Point3D projection = this->m_NeedleProjectionFilter->GetProjection()->GetPoint(1); m_EvalPointsProjected->InsertPoint(m_EvalPointsProjected->GetSize(), projection); QString text = text.number(this->m_EvalPointsProjected->GetSize()); this->m_Controls.m_EvalLblNumProjectionPoints->setText(text); } void QmitkUltrasoundCalibration::OnSaveEvaluation() { //Filename without suffix QString filename = m_Controls.m_EvalFilePath->text() + "//" + m_Controls.m_EvalFilePrefix->text(); MITK_WARN << "CANNOT SAVE, ABORTING!"; /* not working any more TODO! mitk::PointSetWriter::Pointer psWriter = mitk::PointSetWriter::New(); psWriter->SetInput(0, m_CalibPointsImage); psWriter->SetInput(1, m_CalibPointsTool); psWriter->SetInput(2, m_EvalPointsImage); psWriter->SetInput(3, m_EvalPointsTool); psWriter->SetInput(4, m_EvalPointsProjected); psWriter->SetFileName(filename.toStdString() + ".xml"); psWriter->Write(); */ // TODO: New writer for transformations must be implemented. /* mitk::TransformationFileWriter::Pointer tWriter = mitk::TransformationFileWriter::New(); tWriter->SetInput(0, m_CalibPointsImage); tWriter->SetInput(1, m_CalibPointsTool); tWriter->SetInput(2, m_EvalPointsImage); tWriter->SetInput(3, m_EvalPointsTool); tWriter->SetInput(4, m_EvalPointsProjected); tWriter->SetOutputFilename(filename.toStdString() + ".txt"); tWriter->DoWrite(this->m_Transformation); */ } void QmitkUltrasoundCalibration::OnSaveCalibration() { m_Controls.m_GotCalibrationLabel->setText(""); QString filename = QFileDialog::getSaveFileName(QApplication::activeWindow(), "Save Calibration", "", "Calibration files *.cal"); QFile file(filename); if (!file.open(QIODevice::WriteOnly | QIODevice::Text | QIODevice::Truncate)) { MITK_WARN << "Cannot open file '" << filename.toStdString() << "' for writing."; return; } std::string calibrationSerialization = m_CombinedModality->SerializeCalibration(); QTextStream outStream(&file); outStream << QString::fromStdString(calibrationSerialization); //save additional information if (m_Controls.m_saveAdditionalCalibrationLog->isChecked()) { mitk::SceneIO::Pointer mySceneIO = mitk::SceneIO::New(); QString filenameScene = filename + "_mitkScene.mitk"; mitk::NodePredicateNot::Pointer isNotHelperObject = mitk::NodePredicateNot::New(mitk::NodePredicateProperty::New("helper object", mitk::BoolProperty::New(true))); mitk::DataStorage::SetOfObjects::ConstPointer nodesToBeSaved = this->GetDataStorage()->GetSubset(isNotHelperObject); mySceneIO->SaveScene(nodesToBeSaved, this->GetDataStorage(), filenameScene.toStdString().c_str()); } } void QmitkUltrasoundCalibration::OnReset() { this->ClearTemporaryMembers(); if (m_Transformation.IsNull()) { m_Transformation = mitk::AffineTransform3D::New(); } m_Transformation->SetIdentity(); if (m_Node.IsNotNull() && (m_Node->GetData() != nullptr) && (m_Node->GetData()->GetGeometry() != nullptr)) { mitk::SlicedGeometry3D::Pointer sliced3d = dynamic_cast (m_Node->GetData()->GetGeometry()); mitk::PlaneGeometry::Pointer plane = const_cast (sliced3d->GetPlaneGeometry(0)); plane->SetIndexToWorldTransform(m_Transformation); } QString text1 = text1.number(this->m_EvalPointsTool->GetSize()); this->m_Controls.m_EvalLblNumTargetPoints->setText(text1); QString text2 = text2.number(this->m_EvalPointsProjected->GetSize()); this->m_Controls.m_EvalLblNumProjectionPoints->setText(text2); } void QmitkUltrasoundCalibration::Update() { //QList nodes = this->GetDataManagerSelection(); // if (nodes.empty()) return; // Update Tracking Data std::vector* datas = new std::vector(); datas->push_back(m_Tracker->GetOutput()); m_Controls.m_CalibTrackingStatus->SetNavigationDatas(datas); m_Controls.m_CalibTrackingStatus->Refresh(); m_Controls.m_EvalTrackingStatus->SetNavigationDatas(datas); m_Controls.m_EvalTrackingStatus->Refresh(); // Update US Image m_CombinedModality->Modified(); m_CombinedModality->Update(); mitk::Image::Pointer m_Image = m_CombinedModality->GetOutput(); if (m_Image.IsNotNull() && m_Image->IsInitialized()) { if (m_OverrideSpacing) { m_Image->GetGeometry()->SetSpacing(m_Spacing); } if (m_Image.IsNotNull() && m_Image->IsInitialized()) { m_Node->SetData(m_Image); } } // Update Needle Projection m_NeedleProjectionFilter->Update(); //only update 2d window because it is faster //this->RequestRenderWindowUpdate(mitk::RenderingManager::REQUEST_UPDATE_2DWINDOWS); } void QmitkUltrasoundCalibration::SwitchFreeze() { m_Controls.m_CalibBtnAddPoint->setEnabled(false); // generally deactivate // We use the activity state of the timer to determine whether we are currently viewing images if (!m_Timer->isActive()) // Activate Imaging { // if (m_Node) m_Node->ReleaseData(); if (m_CombinedModality.IsNull()){ m_Timer->stop(); return; } m_CombinedModality->Update(); m_Image = m_CombinedModality->GetOutput(); if (m_Image.IsNotNull() && m_Image->IsInitialized()) { m_Node->SetData(m_Image); } std::vector datas; datas.push_back(m_Tracker->GetOutput()); m_Controls.m_CalibTrackingStatus->SetNavigationDatas(&datas); m_Controls.m_CalibTrackingStatus->ShowStatusLabels(); m_Controls.m_CalibTrackingStatus->Refresh(); m_Controls.m_EvalTrackingStatus->SetNavigationDatas(&datas); m_Controls.m_EvalTrackingStatus->ShowStatusLabels(); m_Controls.m_EvalTrackingStatus->Refresh(); int interval = 40; m_Timer->setInterval(interval); m_Timer->start(); m_CombinedModality->SetIsFreezed(false); } else if (this->m_Tracker->GetOutput(0)->IsDataValid()) { //deactivate Imaging m_Timer->stop(); // Remember last tracking coordinates m_FreezePoint = this->m_Tracker->GetOutput(0)->GetPosition(); m_Controls.m_CalibBtnAddPoint->setEnabled(true); // activate only, if valid point is set m_CombinedModality->SetIsFreezed(true); } } void QmitkUltrasoundCalibration::ShowNeedlePath() { // Init Filter this->m_NeedleProjectionFilter->SelectInput(0); // Create Node for Pointset mitk::DataNode::Pointer node = this->GetDataStorage()->GetNamedNode("Needle Path"); if (node.IsNull()) { node = mitk::DataNode::New(); node->SetName("Needle Path"); node->SetData(m_NeedleProjectionFilter->GetProjection()); node->SetBoolProperty("show contour", true); this->GetDataStorage()->Add(node); } } void QmitkUltrasoundCalibration::ClearTemporaryMembers() { m_CalibPointsTool->Clear(); m_CalibPointsImage->Clear(); m_CalibPointsCount = 0; m_EvalPointsImage->Clear(); m_EvalPointsTool->Clear(); m_EvalPointsProjected->Clear(); this->m_Controls.m_CalibPointList->clear(); m_SpacingPoints->Clear(); m_Controls.m_SpacingPointsList->clear(); m_SpacingPointsCount = 0; } vtkSmartPointer QmitkUltrasoundCalibration::ConvertPointSetToVtkPolyData(mitk::PointSet::Pointer PointSet) { vtkSmartPointer returnValue = vtkSmartPointer::New(); vtkSmartPointer points = vtkSmartPointer::New(); for (int i = 0; i < PointSet->GetSize(); i++) { double point[3] = { PointSet->GetPoint(i)[0], PointSet->GetPoint(i)[1], PointSet->GetPoint(i)[2] }; points->InsertNextPoint(point); } vtkSmartPointer temp = vtkSmartPointer::New(); temp->SetPoints(points); vtkSmartPointer vertexFilter = vtkSmartPointer::New(); vertexFilter->SetInputData(temp); vertexFilter->Update(); returnValue->ShallowCopy(vertexFilter->GetOutput()); return returnValue; } double QmitkUltrasoundCalibration::ComputeFRE(mitk::PointSet::Pointer imageFiducials, mitk::PointSet::Pointer realWorldFiducials, vtkSmartPointer transform) { if (imageFiducials->GetSize() != realWorldFiducials->GetSize()) return -1; double FRE = 0; for (int i = 0; i < imageFiducials->GetSize(); ++i) { itk::Point current_image_fiducial_point = imageFiducials->GetPoint(i); if (transform != nullptr) { current_image_fiducial_point = transform->TransformPoint(imageFiducials->GetPoint(i)[0], imageFiducials->GetPoint(i)[1], imageFiducials->GetPoint(i)[2]); } double cur_error_squared = current_image_fiducial_point.SquaredEuclideanDistanceTo(realWorldFiducials->GetPoint(i)); FRE += cur_error_squared; } FRE = sqrt(FRE / (double)imageFiducials->GetSize()); return FRE; } void QmitkUltrasoundCalibration::ApplyTransformToPointSet(mitk::PointSet::Pointer pointSet, vtkSmartPointer transform) { for (int i = 0; i < pointSet->GetSize(); ++i) { itk::Point current_point_transformed = itk::Point(); current_point_transformed = transform->TransformPoint(pointSet->GetPoint(i)[0], pointSet->GetPoint(i)[1], pointSet->GetPoint(i)[2]); pointSet->SetPoint(i, current_point_transformed); } } void QmitkUltrasoundCalibration::OnFreezeClicked() { if (m_CombinedModality->GetIsFreezed()) { //device was already frozen so we need to delete all Spacing points because they need to be collected all at once // no need to check if all four points are already collected, because if thats the case you can no longer click the Freeze Button m_SpacingPoints->Clear(); m_Controls.m_SpacingPointsList->clear(); m_SpacingPointsCount = 0; m_Controls.m_SpacingAddPoint->setEnabled(false); m_CombinedModality->SetIsFreezed(false); } else { m_CombinedModality->SetIsFreezed(true); m_Controls.m_SpacingAddPoint->setEnabled(true); } //SwitchFreeze(); } void QmitkUltrasoundCalibration::OnAddSpacingPoint() { mitk::Point3D point = this->GetRenderWindowPart()->GetSelectedPosition(); this->m_SpacingPoints->InsertPoint(m_SpacingPointsCount, point); QString text = text.number(m_SpacingPointsCount + 1); text = "Point " + text; this->m_Controls.m_SpacingPointsList->addItem(text); m_SpacingPointsCount++; if (m_SpacingPointsCount == 4) //now we have all 4 points needed { m_Controls.m_SpacingAddPoint->setEnabled(false); m_Controls.m_CalculateSpacing->setEnabled(true); m_Controls.m_SpacingBtnFreeze->setEnabled(false); } } void QmitkUltrasoundCalibration::OnCalculateSpacing() { mitk::Point3D horizontalOne = m_SpacingPoints->GetPoint(0); mitk::Point3D horizontalTwo = m_SpacingPoints->GetPoint(1); mitk::Point3D verticalOne = m_SpacingPoints->GetPoint(2); mitk::Point3D verticalTwo = m_SpacingPoints->GetPoint(3); //Get the distances between the points in the image double xDistance = horizontalOne.EuclideanDistanceTo(horizontalTwo); double yDistance = verticalOne.EuclideanDistanceTo(verticalTwo); //Calculate the spacing of the image and fill a vector with it double xSpacing = 30 / xDistance; double ySpacing = 20 / yDistance; m_Spacing[0] = xSpacing; m_Spacing[1] = ySpacing; m_Spacing[2] = 1; MITK_INFO << m_Spacing; //Make sure the new spacing is applied to the USVideoDeviceImages m_OverrideSpacing = true; //Now that the spacing is set clear all stuff and return to Calibration m_SpacingPoints->Clear(); m_Controls.m_SpacingPointsList->clear(); m_SpacingPointsCount = 0; m_CombinedModality->SetIsFreezed(false); } void QmitkUltrasoundCalibration::OnUSDepthChanged(const std::string& key, const std::string&) { //whenever depth of USImage is changed the spacing should no longer be overwritten if (key == mitk::USDevice::GetPropertyKeys().US_PROPKEY_BMODE_DEPTH) { + m_OverrideSpacing = false; } }