diff --git a/Modules/US/USModel/mitkUSDevice.cpp b/Modules/US/USModel/mitkUSDevice.cpp index 0576d57b64..e78a1ad22a 100644 --- a/Modules/US/USModel/mitkUSDevice.cpp +++ b/Modules/US/USModel/mitkUSDevice.cpp @@ -1,708 +1,700 @@ /*=================================================================== 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_OverrideSpacing(false), 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_OverrideSpacing(false), 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 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; - m_OverrideSpacing = true; if( m_ImageVector.size() > 0 ) { for( int 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::SetOverrideSpacing(bool overriding) -{ - m_OverrideSpacing = overriding; -} - 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()); //Important: set the spacing before setting the geometry to the output. if (m_OverrideSpacing) { image->GetGeometry()->SetSpacing(m_Spacing); //___MITK_INFO << "------------setSpacing ---- " << m_Spacing; } 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 f717107e44..6925db6d6e 100644 --- a/Modules/US/USModel/mitkUSDevice.h +++ b/Modules/US/USModel/mitkUSDevice.h @@ -1,491 +1,489 @@ /*=================================================================== 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(); - void SetSpacing(double xSpacing, double ySpacing); - void SetOverrideSpacing( bool overriding ); + 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(); } } 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; - bool m_OverrideSpacing; /** * \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; private: std::string m_Manufacturer; std::string m_Name; std::string m_Comment; bool m_SpawnAcquireThread; /** * \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; 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/mitkUSProbe.cpp b/Modules/US/USModel/mitkUSProbe.cpp index d1d0f7abe5..7f6e08cfc4 100644 --- a/Modules/US/USModel/mitkUSProbe.cpp +++ b/Modules/US/USModel/mitkUSProbe.cpp @@ -1,88 +1,88 @@ /*=================================================================== 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() { } 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; } diff --git a/Modules/US/USModel/mitkUSProbe.h b/Modules/US/USModel/mitkUSProbe.h index 8544e93d84..9462beb29e 100644 --- a/Modules/US/USModel/mitkUSProbe.h +++ b/Modules/US/USModel/mitkUSProbe.h @@ -1,96 +1,99 @@ /*=================================================================== 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 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. */ 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. */ Vector3D GetSpacingForGivenDepth(int givenDepth); //## 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; }; } // namespace mitk #endif diff --git a/Modules/US/USModel/mitkUSVideoDevice.cpp b/Modules/US/USModel/mitkUSVideoDevice.cpp index 579a41d449..2db712f3ca 100644 --- a/Modules/US/USModel/mitkUSVideoDevice.cpp +++ b/Modules/US/USModel/mitkUSVideoDevice.cpp @@ -1,214 +1,253 @@ /*=================================================================== 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_OverrideSpacing ) + if( image.IsNotNull() && image->IsInitialized() && m_CurrentProbe.IsNotNull() ) { - image->GetGeometry()->SetSpacing(m_Spacing); + //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; } 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 786fe44a8a..774f3d4b9d 100644 --- a/Modules/US/USModel/mitkUSVideoDevice.h +++ b/Modules/US/USModel/mitkUSVideoDevice.h @@ -1,231 +1,247 @@ /*=================================================================== 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 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/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/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..5b105c098e 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,1143 @@ /*=================================================================== 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) { +<<<<<<< HEAD m_OverrideSpacing = false; +======= +>>>>>>> 67a979a... Realized the logic between the mapping of different calculable spacing vectors and various ultrasound probes and their corresponding depths. This belongs mainly to the USVideoDevice class. Additionally, the calculated spacing is set automatically to the actual ultrasound images if the used ultrasound device type belongs to the USVideoDevice. } }