MITK-IGT
IGT Extension of MITK
Loading...
Searching...
No Matches
mitkUSDevice.cpp
Go to the documentation of this file.
1/*============================================================================
2
3The Medical Imaging Interaction Toolkit (MITK)
4
5Copyright (c) German Cancer Research Center (DKFZ)
6All rights reserved.
7
8Use of this source code is governed by a 3-clause BSD license that can be
9found in the LICENSE file.
10
11============================================================================*/
12
13#include "mitkUSDevice.h"
14#include "mitkImageReadAccessor.h"
15
16// US Control Interfaces
20
21// Microservices
22#include <usGetModuleContext.h>
23#include <usModule.h>
24#include <usServiceProperties.h>
25#include <usModuleContext.h>
26
28{
29 static mitk::USDevice::PropertyKeys propertyKeys;
30 return propertyKeys;
31}
32
34{
35 MITK_INFO << "Return Crop Area L:" << m_CropArea.cropLeft
36 << " R:" << m_CropArea.cropRight << " T:" << m_CropArea.cropTop
37 << " B:" << m_CropArea.cropBottom;
38 return m_CropArea;
39}
40
42{
43 return m_ImageVector.size();
44}
45
46mitk::USDevice::USDevice(std::string manufacturer, std::string model)
47 : mitk::ImageSource(),
48 m_ImageVector(),
49 m_Spacing(),
50 m_IGTLServer(nullptr),
51 m_IGTLMessageProvider(nullptr),
52 m_ImageToIGTLMsgFilter(nullptr),
53 m_IsFreezed(false),
54 m_DeviceState(State_NoState),
55 m_NumberOfOutputs(1),
56 m_ServiceProperties(),
57 m_ServiceRegistration(),
58 m_Manufacturer(manufacturer),
59 m_Name(model),
60 m_Comment(),
61 m_SpawnAcquireThread(true),
62 m_UnregisteringStarted(false)
63{
64 USImageCropArea empty;
65 empty.cropBottom = 0;
66 empty.cropTop = 0;
67 empty.cropLeft = 0;
68 empty.cropRight = 0;
69 this->m_CropArea = empty;
70
71 // set number of outputs
72 this->SetNumberOfIndexedOutputs(m_NumberOfOutputs);
73
74 // create a new output
75 mitk::Image::Pointer newOutput = mitk::Image::New();
76 this->SetNthOutput(0, newOutput);
77}
78
79mitk::USDevice::USDevice(mitk::USImageMetadata::Pointer metadata)
80 : mitk::ImageSource(),
81 m_ImageVector(),
82 m_Spacing(),
83 m_IGTLServer(nullptr),
84 m_IGTLMessageProvider(nullptr),
85 m_ImageToIGTLMsgFilter(nullptr),
86 m_IsFreezed(false),
87 m_DeviceState(State_NoState),
88 m_NumberOfOutputs(1),
89 m_ServiceProperties(),
90 m_ServiceRegistration(),
91 m_SpawnAcquireThread(true),
92 m_UnregisteringStarted(false)
93{
94 m_Manufacturer = metadata->GetDeviceManufacturer();
95 m_Name = metadata->GetDeviceModel();
96 m_Comment = metadata->GetDeviceComment();
97
98 USImageCropArea empty;
99 empty.cropBottom = 0;
100 empty.cropTop = 0;
101 empty.cropLeft = 0;
102 empty.cropRight = 0;
103 this->m_CropArea = empty;
104
105 // set number of outputs
106 this->SetNumberOfIndexedOutputs(m_NumberOfOutputs);
107
108 // create a new output
109 mitk::Image::Pointer newOutput = mitk::Image::New();
110 this->SetNthOutput(0, newOutput);
111}
112
114{
115 if (m_Thread.joinable())
116 m_Thread.detach();
117
118 // make sure that the us device is not registered at the micro service
119 // anymore after it is destructed
120 this->UnregisterOnService();
121}
122
123mitk::USAbstractControlInterface::Pointer
125{
126 MITK_INFO << "Custom control interface does not exist for this object.";
127 return nullptr;
128}
129
130mitk::USControlInterfaceBMode::Pointer
132{
133 MITK_INFO << "Control interface BMode does not exist for this object.";
134 return nullptr;
135}
136
137mitk::USControlInterfaceProbes::Pointer
139{
140 MITK_INFO << "Control interface Probes does not exist for this object.";
141 return nullptr;
142}
143
144mitk::USControlInterfaceDoppler::Pointer
146{
147 MITK_INFO << "Control interface Doppler does not exist for this object.";
148 return nullptr;
149}
150
151void mitk::USDevice::SetManufacturer(std::string manufacturer)
152{
153 m_Manufacturer = manufacturer;
154 if (m_DeviceState >= State_Initialized)
155 {
156 this->UpdateServiceProperty(
157 mitk::USDevice::GetPropertyKeys().US_PROPKEY_MANUFACTURER,
158 manufacturer);
159 }
160}
161
162void mitk::USDevice::SetName(std::string name)
163{
164 m_Name = name;
165 if (m_DeviceState >= State_Initialized)
166 {
167 this->UpdateServiceProperty(
168 mitk::USDevice::GetPropertyKeys().US_PROPKEY_NAME, name);
169 }
170}
171
172void mitk::USDevice::SetComment(std::string comment)
173{
174 m_Comment = comment;
175 if (m_DeviceState >= State_Initialized)
176 {
177 this->UpdateServiceProperty(
178 mitk::USDevice::GetPropertyKeys().US_PROPKEY_COMMENT, comment);
179 }
180}
181
183{
185
186 us::ServiceProperties props;
187
188 props[propertyKeys.US_PROPKEY_ISCONNECTED] =
189 this->GetIsConnected() ? "true" : "false";
190 props[propertyKeys.US_PROPKEY_ISACTIVE] =
191 this->GetIsActive() ? "true" : "false";
192
193 props[propertyKeys.US_PROPKEY_LABEL] = this->GetServicePropertyLabel();
194
195 // get identifier of selected probe if there is one selected
196 mitk::USControlInterfaceProbes::Pointer probesControls =
197 this->GetControlInterfaceProbes();
198 if (probesControls.IsNotNull() && probesControls->GetIsActive())
199 {
200 mitk::USProbe::Pointer probe = probesControls->GetSelectedProbe();
201 if (probe.IsNotNull())
202 {
203 props[propertyKeys.US_PROPKEY_PROBES_SELECTED] = probe->GetName();
204 }
205 }
206
207 props[propertyKeys.US_PROPKEY_CLASS] = GetDeviceClass();
208 props[propertyKeys.US_PROPKEY_MANUFACTURER] = m_Manufacturer;
209 props[propertyKeys.US_PROPKEY_NAME] = m_Name;
210 props[propertyKeys.US_PROPKEY_COMMENT] = m_Comment;
211
212 m_ServiceProperties = props;
213
214 return props;
215}
216
218{
219 // unregister on micro service
220 if (m_ServiceRegistration && !m_UnregisteringStarted)
221 {
222 // make sure that unregister is not started a second
223 // time due to a callback during unregister for example
224 m_UnregisteringStarted = true;
225 m_ServiceRegistration.Unregister();
226 m_ServiceRegistration = 0;
227 }
228}
229
231{
232 if (!this->OnInitialization())
233 {
234 return false;
235 }
236
237 m_DeviceState = State_Initialized;
238
239 // Get Context and Module
240 us::ModuleContext* context = us::GetModuleContext();
241 us::ServiceProperties props = this->ConstructServiceProperties();
242
243 m_ServiceRegistration = context->RegisterService(this, props);
244
245 return true;
246}
247
249{
250 MITK_DEBUG << "mitk::USDevice::Connect() called";
251
252 if (this->GetIsConnected())
253 {
254 MITK_INFO("mitkUSDevice") << "Tried to connect an ultrasound device that "
255 "was already connected. Ignoring call...";
256 return true;
257 }
258
259 if (!this->GetIsInitialized())
260 {
261 MITK_ERROR("mitkUSDevice")
262 << "Cannot connect device if it is not in initialized state.";
263 return false;
264 }
265
266 // Prepare connection, fail if this fails.
267 if (!this->OnConnection())
268 {
269 return false;
270 }
271
272 // Update state
273 m_DeviceState = State_Connected;
274
275 this->UpdateServiceProperty(
276 mitk::USDevice::GetPropertyKeys().US_PROPKEY_ISCONNECTED, true);
277 return true;
278}
279
281{
282 m_Thread = std::thread(&USDevice::ConnectThread, this);
283}
284
286{
287 if (!GetIsConnected())
288 {
289 MITK_WARN << "Tried to disconnect an ultrasound device that was not "
290 "connected. Ignoring call...";
291 return false;
292 }
293 // Prepare connection, fail if this fails.
294 if (!this->OnDisconnection())
295 return false;
296
297 // Update state
298 m_DeviceState = State_Initialized;
299
300 this->UpdateServiceProperty(
301 mitk::USDevice::GetPropertyKeys().US_PROPKEY_ISCONNECTED, false);
302
303 return true;
304}
305
307{
308 if (!this->GetIsConnected())
309 {
310 MITK_INFO("mitkUSDevice")
311 << "Cannot activate device if it is not in connected state.";
312 return true;
313 }
314
315 if (OnActivation())
316 {
317 m_DeviceState = State_Activated;
318
319 // spawn thread for aquire images if us device is active
320 if (m_SpawnAcquireThread)
321 {
322 m_Thread = std::thread(&USDevice::Acquire, this);
323 }
324
325 this->UpdateServiceProperty(
326 mitk::USDevice::GetPropertyKeys().US_PROPKEY_ISACTIVE, true);
327 this->UpdateServiceProperty(
328 mitk::USDevice::GetPropertyKeys().US_PROPKEY_LABEL,
329 this->GetServicePropertyLabel());
330 // initialize the b mode control properties of the micro service
331 mitk::USControlInterfaceBMode::Pointer bmodeControls =
332 this->GetControlInterfaceBMode();
333 if (bmodeControls.IsNotNull())
334 {
335 bmodeControls->Initialize();
336 }
337 }
338
339 this->ProvideViaOIGTL();
340
341 return m_DeviceState == State_Activated;
342}
343
345{
346 // create a new OpenIGTLink Server
347 if (m_IGTLServer.IsNull())
348 m_IGTLServer = mitk::IGTLServer::New(true);
349
350 m_IGTLServer->SetName(this->GetName());
351
352 // create a new OpenIGTLink Device source
353 if (m_IGTLMessageProvider.IsNull())
354 m_IGTLMessageProvider = mitk::IGTLMessageProvider::New();
355
356 // set the OpenIGTLink server as the source for the device source
357 m_IGTLMessageProvider->SetIGTLDevice(m_IGTLServer);
358
359 // register the provider so that it can be configured with the IGTL manager
360 // plugin. This could be hardcoded but now I already have the fancy plugin.
361 m_IGTLMessageProvider->RegisterAsMicroservice();
362
363 m_ImageToIGTLMsgFilter = mitk::ImageToIGTLMessageFilter::New();
364 m_ImageToIGTLMsgFilter->ConnectTo(this);
365
366 // set the name of this filter to identify it easier
367 m_ImageToIGTLMsgFilter->SetName(this->GetName());
368
369 // register this filter as micro service. The message provider looks for
370 // provided IGTLMessageSources, once it found this microservice and someone
371 // requested this data type then the provider will connect with this filter
372 // automatically.
373 m_ImageToIGTLMsgFilter->RegisterAsMicroservice();
374}
375
377{
378 if (!this->GetIsActive())
379 {
380 MITK_WARN("mitkUSDevice")
381 << "Cannot deactivate a device which is not activae.";
382 return;
383 }
384
385 if (!OnDeactivation())
386 {
387 return;
388 }
389
390 DisableOIGTL();
391 m_DeviceState = State_Connected;
392
393 this->UpdateServiceProperty(
394 mitk::USDevice::GetPropertyKeys().US_PROPKEY_ISACTIVE, false);
395 this->UpdateServiceProperty(
396 mitk::USDevice::GetPropertyKeys().US_PROPKEY_LABEL,
397 this->GetServicePropertyLabel());
398}
399
401{
402 // TODO: This seems not to be enough cleanup to catch all cases. For example, if the device is disconnected
403 // from the OIGTL GUI, this won't get cleaned up correctly.
404 m_IGTLServer->CloseConnection();
405 m_IGTLMessageProvider->UnRegisterMicroservice();
406 m_ImageToIGTLMsgFilter->UnRegisterMicroservice();
407}
408
410{
411 if (!this->GetIsActive())
412 {
413 MITK_WARN("mitkUSDevice")
414 << "Cannot freeze or unfreeze if device is not active.";
415 return;
416 }
417
418 this->OnFreeze(freeze);
419
420 if (freeze)
421 {
422 std::lock_guard<std::mutex> lock(m_FreezeMutex);
423 m_IsFreezed = true;
424 }
425 else
426 {
427 {
428 std::lock_guard<std::mutex> lock(m_FreezeMutex);
429 m_IsFreezed = false;
430 }
431
432 // wake up the image acquisition thread
433 m_FreezeBarrier.notify_one();
434 }
435}
436
438{
439 /*
440 if (!this->GetIsActive())
441 {
442 MITK_WARN("mitkUSDevice")("mitkUSTelemedDevice")
443 << "Cannot get freeze state if the hardware interface is not ready. "
444 "Returning false...";
445 return false;
446 }*/
447
448 return m_IsFreezed;
449}
450
451void mitk::USDevice::PushFilter(AbstractOpenCVImageFilter::Pointer filter)
452{
453 mitk::USImageSource::Pointer imageSource = this->GetUSImageSource();
454 if (imageSource.IsNull())
455 {
456 MITK_ERROR << "ImageSource must not be null when pushing a filter.";
457 mitkThrow() << "ImageSource must not be null when pushing a filter.";
458 }
459
460 imageSource->PushFilter(filter);
461}
462
464 AbstractOpenCVImageFilter::Pointer filter)
465{
466 mitk::USImageSource::Pointer imageSource = this->GetUSImageSource();
467 if (imageSource.IsNull())
468 {
469 MITK_ERROR << "ImageSource must not be null when pushing a filter.";
470 mitkThrow() << "ImageSource must not be null when pushing a filter.";
471 }
472
473 if (!imageSource->GetIsFilterInThePipeline(filter))
474 {
475 imageSource->PushFilter(filter);
476 }
477}
478
479bool mitk::USDevice::RemoveFilter(AbstractOpenCVImageFilter::Pointer filter)
480{
481 mitk::USImageSource::Pointer imageSource = this->GetUSImageSource();
482 if (imageSource.IsNull())
483 {
484 MITK_ERROR << "ImageSource must not be null when pushing a filter.";
485 mitkThrow() << "ImageSource must not be null when removing a filter.";
486 }
487
488 return imageSource->RemoveFilter(filter);
489}
490
491void mitk::USDevice::UpdateServiceProperty(std::string key, std::string value)
492{
493 m_ServiceProperties[key] = value;
494 m_ServiceRegistration.SetProperties(m_ServiceProperties);
495
496 // send event to notify listeners about the changed property
497 m_PropertyChangedMessage(key, value);
498}
499
500void mitk::USDevice::UpdateServiceProperty(std::string key, double value)
501{
502 std::stringstream stream;
503 stream << value;
504 this->UpdateServiceProperty(key, stream.str());
505}
506
507void mitk::USDevice::UpdateServiceProperty(std::string key, bool value)
508{
509 this->UpdateServiceProperty(
510 key, value ? std::string("true") : std::string("false"));
511}
512
559{
560 std::vector<mitk::Image::Pointer> image = this->GetUSImageSource()->GetNextImage();
561 m_ImageMutex.lock();
562 this->SetImageVector(image);
563 m_ImageMutex.unlock();
564}
565
566//########### GETTER & SETTER ##################//
567
569{
570 return m_DeviceState == State_Initialized;
571}
572
573bool mitk::USDevice::GetIsActive() { return m_DeviceState == State_Activated; }
574
576{
577 return m_DeviceState == State_Connected;
578}
579
580std::string mitk::USDevice::GetDeviceManufacturer() { return m_Manufacturer; }
581
582std::string mitk::USDevice::GetDeviceModel() { return m_Name; }
583
584std::string mitk::USDevice::GetDeviceComment() { return m_Comment; }
585
586void mitk::USDevice::SetSpacing(double xSpacing, double ySpacing)
587{
588 m_Spacing[0] = xSpacing;
589 m_Spacing[1] = ySpacing;
590 m_Spacing[2] = 1;
591
592
593 if( m_ImageVector.size() > 0 )
594 {
595 for( size_t index = 0; index < m_ImageVector.size(); ++index )
596 {
597 auto& image = m_ImageVector[index];
598 if( image.IsNotNull() && image->IsInitialized() )
599 {
600 image->GetGeometry()->SetSpacing(m_Spacing);
601 }
602 }
603 this->Modified();
604 }
605 MITK_INFO << "Spacing: " << m_Spacing;
606}
607
609{
610 m_ImageMutex.lock();
611
612 for (unsigned int i = 0; i < m_ImageVector.size() && i < this->GetNumberOfIndexedOutputs(); ++i)
613 {
614 auto& image = m_ImageVector[i];
615 if (image.IsNull() || !image->IsInitialized())
616 {
617 // skip image
618 }
619 else
620 {
621 mitk::Image::Pointer output = this->GetOutput(i);
622
623 if (!output->IsInitialized() ||
624 output->GetDimension(0) != image->GetDimension(0) ||
625 output->GetDimension(1) != image->GetDimension(1) ||
626 output->GetDimension(2) != image->GetDimension(2) ||
627 output->GetPixelType() != image->GetPixelType())
628 {
629 output->Initialize(image->GetPixelType(), image->GetDimension(),
630 image->GetDimensions());
631 }
632
633 // copy contents of the given image into the member variable
634 mitk::ImageReadAccessor inputReadAccessor(image);
635 output->SetImportVolume(inputReadAccessor.GetData());
636 output->SetGeometry(image->GetGeometry());
637 }
638 }
639 m_ImageMutex.unlock();
640};
641
643{
644 std::string isActive;
645 if (this->GetIsActive())
646 {
647 isActive = " (Active)";
648 }
649 else
650 {
651 isActive = " (Inactive)";
652 }
653 // e.g.: Zonare MyLab5 (Active)
654 return m_Manufacturer + " " + m_Name + isActive;
655}
656
658{
659 while (this->GetIsActive())
660 {
661 // lock this thread when ultrasound device is freezed
662 std::unique_lock<std::mutex> lock(m_FreezeMutex);
663 m_FreezeBarrier.wait(lock, [this] { return !m_IsFreezed; });
664
665 this->GrabImage();
666 }
667}
668
670{
671 this->Connect();
672}
673
674
675void mitk::USDevice::ProbeChanged(std::string probename)
676{
677 this->UpdateServiceProperty(mitk::USDevice::GetPropertyKeys().US_PROPKEY_PROBES_SELECTED, probename);
678}
679
681{
682 this->UpdateServiceProperty(mitk::USDevice::GetPropertyKeys().US_PROPKEY_BMODE_DEPTH, depth);
683}
bool Disconnect()
Works analogously to mitk::USDevice::Connect(). Don't override this Method, but onDisconnection inste...
void GenerateData() override
Grabs the next frame from the Video input. This method is called internally, whenever Update() is inv...
virtual itk::SmartPointer< USControlInterfaceProbes > GetControlInterfaceProbes()
Default getter for the probes control interface. Has to be implemented in a subclass if a probes cont...
mitk::USDevice::USImageCropArea GetCropArea()
bool RemoveFilter(AbstractOpenCVImageFilter::Pointer filter)
~USDevice() override
void DisableOIGTL()
Deregisters the microservices for OpenIGTLink.
us::ServiceProperties ConstructServiceProperties()
This Method constructs the service properties which can later be used to register the object with the...
unsigned int GetSizeOfImageVector()
void ProvideViaOIGTL()
Registers an OpenIGTLink device as a microservice so that we can send the images of this device via t...
bool GetIsConnected()
True, if the device is currently ready to start transmitting image data or is already transmitting im...
USImageCropArea m_CropArea
virtual itk::SmartPointer< USAbstractControlInterface > GetControlInterfaceCustom()
Default getter for the custom control interface. Has to be implemented in a subclass if a custom cont...
void DepthChanged(double depth)
To be called when the scanning depth of the probe changed. Will update the service properties.
bool GetIsActive()
True, if the device is currently generating image data, false otherwise.
void PushFilter(AbstractOpenCVImageFilter::Pointer filter)
virtual void SetIsFreezed(bool freeze)
Can toggle if ultrasound image is currently updated or freezed.
void UnregisterOnService()
Remove this device from the micro service.
void SetManufacturer(std::string manufacturer)
bool Initialize()
Changes device state to mitk::USDevice::State_Initialized. During initialization the virtual method m...
void SetComment(std::string comment)
virtual void SetSpacing(double xSpacing, double ySpacing)
void UpdateServiceProperty(std::string key, std::string value)
Given property is updated in the device micro service. This method is mainly for being used by the co...
void SetName(std::string name)
bool Activate()
Activates this device. After the activation process, the device will start to produce images....
static mitk::USDevice::PropertyKeys GetPropertyKeys()
void PushFilterIfNotPushedBefore(AbstractOpenCVImageFilter::Pointer filter)
void Deactivate()
Deactivates this device. After the deactivation process, the device will no longer produce images,...
virtual itk::SmartPointer< USControlInterfaceDoppler > GetControlInterfaceDoppler()
Default getter for the doppler control interface. Has to be implemented in a subclass if a doppler co...
USDevice(std::string manufacturer, std::string model)
Enforces minimal Metadata to be set.
void ProbeChanged(std::string probename)
To be called when the used probe changed. Will update the service properties.
unsigned int m_NumberOfOutputs
virtual itk::SmartPointer< USControlInterfaceBMode > GetControlInterfaceBMode()
Default getter for the b mode control interface. Has to be implemented in a subclass if a b mode cont...
virtual bool GetIsFreezed()
std::string GetServicePropertyLabel()
bool Connect()
Connects this device. A connected device is ready to deliver images (i.e. be Activated)....
bool GetIsInitialized()
True, if the device object is created and initialized, false otherwise.
IGT Exceptions.
These constants are used in conjunction with Microservices. The constants aren't defined as static me...
const std::string US_PROPKEY_CLASS
const std::string US_PROPKEY_ISACTIVE
const std::string US_PROPKEY_COMMENT
const std::string US_PROPKEY_ISCONNECTED
const std::string US_PROPKEY_MANUFACTURER
const std::string US_PROPKEY_NAME
const std::string US_PROPKEY_PROBES_SELECTED
const std::string US_PROPKEY_LABEL