fw4spl
SImagesBlend.cpp
1 /* ***** BEGIN LICENSE BLOCK *****
2  * FW4SPL - Copyright (C) IRCAD, 2009-2018.
3  * Distributed under the terms of the GNU Lesser General Public License (LGPL) as
4  * published by the Free Software Foundation.
5  * ****** END LICENSE BLOCK ****** */
6 
7 #include "visuVTKAdaptor/SImagesBlend.hpp"
8 
9 #include "visuVTKAdaptor/SImage.hpp"
10 
11 #include <fwCom/Slot.hpp>
12 #include <fwCom/Slot.hxx>
13 #include <fwCom/Slots.hpp>
14 #include <fwCom/Slots.hxx>
15 
16 #include <fwData/Boolean.hpp>
17 #include <fwData/Color.hpp>
18 #include <fwData/Image.hpp>
19 #include <fwData/Integer.hpp>
20 #include <fwData/String.hpp>
21 #include <fwData/TransferFunction.hpp>
22 
23 #include <fwDataTools/fieldHelper/Image.hpp>
24 #include <fwDataTools/fieldHelper/MedicalImageHelpers.hpp>
25 
26 #include <fwGui/dialog/MessageDialog.hpp>
27 
28 #include <fwMath/Compare.hpp>
29 
30 #include <fwServices/macros.hpp>
31 #include <fwServices/op/Add.hpp>
32 
33 #include <fwVtkIO/vtk.hpp>
34 
35 #include <boost/foreach.hpp>
36 
37 #include <vtkImageBlend.h>
38 #include <vtkImageCheckerboard.h>
39 #include <vtkImageData.h>
40 #include <vtkImageMapToColors.h>
41 #include <vtkLookupTable.h>
42 
43 #include <regex>
44 
46 
47 namespace visuVTKAdaptor
48 {
49 
50 const ::fwCom::Slots::SlotKeyType s_CHANGE_MODE_SLOT = "changeMode";
51 const ::fwCom::Slots::SlotKeyType s_CHANGE_CHECKERBOARD_DIVISION_SLOT = "changeCheckerboardDivision";
52 const ::fwCom::Slots::SlotKeyType s_SET_TOP_IMAGE_TRANSPARENCY_SLOT = "setTopImageTransparency";
53 
54 static const ::fwServices::IService::KeyType s_IMAGE_GROUP = "image";
55 static const ::fwServices::IService::KeyType s_TF_GROUP = "tf";
56 
57 //------------------------------------------------------------------------------
58 
59 SImagesBlend::SImagesBlend() noexcept :
60  m_imageAlgorithm(nullptr),
61  m_checkerboardDivision(10),
62  m_zDivision(m_checkerboardDivision)
63 {
64  newSlot(s_CHANGE_MODE_SLOT, &SImagesBlend::changeMode, this);
65  newSlot(s_CHANGE_CHECKERBOARD_DIVISION_SLOT, &SImagesBlend::changeCheckerboardDivision, this);
66  newSlot(s_SET_TOP_IMAGE_TRANSPARENCY_SLOT, &SImagesBlend::setTopImageTransparency, this);
67 }
68 
69 //------------------------------------------------------------------------------
70 
71 SImagesBlend::~SImagesBlend() noexcept
72 {
73  m_imageAlgorithm = nullptr;
74 }
75 
76 //------------------------------------------------------------------------------
77 
79 {
80  this->initialize();
81 
82  SLM_ASSERT("Image register is empty", !m_imageRegisterId.empty());
83 
84  // Try to downcast as an vtkImageBlend first
85  m_imageAlgorithm = vtkImageBlend::SafeDownCast(this->getVtkObject(m_imageRegisterId));
86 
87  if(nullptr == m_imageAlgorithm)
88  {
89  // If we have no vtkImageBlend, try to downcast as an vtkImageCheckerboard
90  m_imageAlgorithm = vtkImageCheckerboard::SafeDownCast(this->getVtkObject(m_imageRegisterId));
91  }
92 
93  // If we have a null m_imageAlgorithm, then we have a problem Houston
94  SLM_ASSERT("Stored image algorithm doesn't contain a vtkImageBlend or vtkImageCheckerboard",
95  nullptr != m_imageAlgorithm);
96 
97  this->addImageAdaptors();
98 
99  // Set the divisions once all image info has been gathered.
100  this->changeCheckerboardDivision(m_checkerboardDivision);
101 }
102 
103 //------------------------------------------------------------------------------
104 
106 {
107  this->removeImageAdaptors();
108  m_imageAlgorithm = nullptr;
109  m_imagesInfo.clear();
110 }
111 
112 //------------------------------------------------------------------------------
113 
115 {
116  this->removeImageAdaptors();
117  this->addImageAdaptors();
118 }
119 
120 //------------------------------------------------------------------------------
121 
123 {
124  this->configureParams();
125 
126  const ConfigType srvConfig = this->getConfigTree();
127 
128  BOOST_FOREACH(const ::fwServices::IService::ConfigType::value_type& cfg, srvConfig.equal_range("inout"))
129  {
130  const ConfigType inoutConfig = cfg.second;
131  const std::string group = inoutConfig.get<std::string>("<xmlattr>.group");
132  if (group == s_IMAGE_GROUP)
133  {
134  BOOST_FOREACH(const ::fwServices::IService::ConfigType::value_type& v, inoutConfig.equal_range("key"))
135  {
136  const ::fwServices::IService::ConfigType& specAssoc = v.second;
137  const ::fwServices::IService::ConfigType& attr = specAssoc.get_child("<xmlattr>");
138  const std::string tfalpha = attr.get("tfalpha", "no");
139  const double opacity = attr.get("opacity", 1.0);
140 
141  ImageInfo info;
142  info.m_imageOpacity = opacity;
143  info.m_useTFAlfa = (tfalpha == "yes");
144  m_imagesInfo.push_back(info);
145  }
146  }
147  }
148 
149  const ConfigType config = srvConfig.get_child("config.<xmlattr>");
150 
151  const std::string vtkimageregister = config.get("vtkimageregister", "");
152  SLM_ASSERT("'vtkimageregister' must be defined", !vtkimageregister.empty());
153  this->setVtkImageRegisterId(vtkimageregister);
154 
155  // Get the default division count for checkerboard algorithm
156  m_checkerboardDivision = config.get<int>("checkerboardDivision", 10);
157 }
158 
159 //------------------------------------------------------------------------------
160 
161 void SImagesBlend::swapping(const KeyType& key)
162 {
163  const std::string regexStr = "([[:alnum:]]+)#([[:digit:]]+)";
164  const std::regex re(regexStr);
165  std::smatch match;
166 
167  if( std::regex_match(key, match, re) )
168  {
169  const std::string group = match[1];
170  const unsigned long index = std::stoul(match[2]);
171 
172  if (group == s_TF_GROUP && this->getRegisteredServices().size() > index)
173  {
174  ::fwServices::IService::wptr wsrv = this->getRegisteredServices()[index];
175 
176  if (!wsrv.expired())
177  {
178  ::fwData::TransferFunction::sptr tf = this->getInOut< ::fwData::TransferFunction >(s_TF_GROUP, index);
179  ::fwServices::IService::sptr service = wsrv.lock();
180  if (tf)
181  {
182  service->registerInOut(tf, SImage::s_TF_INOUT, false, true);
183  service->swapKey(SImage::s_TF_INOUT, nullptr);
184  }
185  else if(::fwServices::OSR::isRegistered(SImage::s_TF_INOUT, AccessType::INOUT, service))
186  {
187  ::fwServices::OSR::unregisterService(SImage::s_TF_INOUT, AccessType::INOUT, service);
188  service->swapKey(SImage::s_TF_INOUT, nullptr);
189  }
190  }
191  }
192  }
193 }
194 
195 //------------------------------------------------------------------------------
196 
198 {
202 
203  bool haveSameInfo = true;
204 
205  const size_t nbImages = this->getKeyGroupSize(s_IMAGE_GROUP);
206 
207  for(size_t i = 0; i < nbImages; ++i)
208  {
209  ::fwData::Image::csptr img = this->getInOut< ::fwData::Image >(s_IMAGE_GROUP, i);
210 
212  {
213  if (size.empty() && spacing.empty() && origin.empty())
214  {
215  size = img->getSize();
216  spacing = img->getSpacing();
217  origin = img->getOrigin();
218 
219  if(size.size() < 3 || size[2] <= 1)
220  {
221  m_zDivision = 1;
222  }
223  }
224  else
225  {
226  if ( size != img->getSize() ||
227  !::fwMath::isContainerEqual< const ::fwData::Image::SpacingType >(spacing,
228  img->getSpacing()) ||
229  !::fwMath::isContainerEqual< const ::fwData::Image::OriginType >(origin,
230  img->getOrigin()) )
231  {
232  OSLM_ERROR("imgA size : " << size[0] << " / " << size[1] << " / "<< size[2] );
233  OSLM_ERROR("imgA spacing : " << spacing[0] << " / " << spacing[1] << " / "<< spacing[2] );
234  OSLM_ERROR("imgA origin : " << origin[0] << " / " << origin[1] << " / "<< origin[2] );
235 
236  OSLM_ERROR(
237  "imgB size : " << img->getSize()[0] << " / " << img->getSize()[1] << " / "<<
238  img->getSize()[2] );
239  OSLM_ERROR(
240  "imgB spacing : " << img->getSpacing()[0] << " / " << img->getSpacing()[1] << " / "<<
241  img->getSpacing()[2] );
242  OSLM_ERROR(
243  "imgB origin : " << img->getOrigin()[0] << " / " << img->getOrigin()[1] << " / "<<
244  img->getOrigin()[2] );
245 
246  haveSameInfo = false;
247  std::string errorMsg = "Warning : images in blend have not the same";
248  errorMsg += (size != img->getSize()) ? " size" : "";
249  errorMsg += (spacing != img->getSpacing()) ? " spacing" : "";
250  errorMsg += (origin != img->getOrigin()) ? " origin" : "";
251  errorMsg += ".\n Background image size, spacing and origin are use.";
253  errorMsg,
254  ::fwGui::dialog::IMessageDialog::WARNING);
255  break;
256  }
257  }
258  }
259  }
260 
261  return haveSameInfo;
262 }
263 
264 //------------------------------------------------------------------------------
265 
266 void SImagesBlend::addImageAdaptors()
267 {
268  this->checkImageInformations();
269 
270  int addedImageCount = 0;
271  size_t lastValidIndex = 0;
272 
273  const size_t nbImages = this->getKeyGroupSize(s_IMAGE_GROUP);
274  const size_t nbTFs = this->getKeyGroupSize(s_TF_GROUP);
275  SLM_ASSERT("'" + s_TF_GROUP + "' group must have the same number of elements that '" + s_IMAGE_GROUP +"'",
276  nbTFs == 0 || nbImages == nbTFs);
277 
278  for(size_t i = 0; i < nbImages; ++i)
279  {
280  ::fwData::Image::sptr img = this->getInOut< ::fwData::Image >(s_IMAGE_GROUP, i);
281  ::fwData::TransferFunction::sptr tf;
282 
283  if (img)
284  {
285  if (nbTFs > 0)
286  {
287  tf = this->getInOut< ::fwData::TransferFunction >(s_TF_GROUP, i);
288  }
289 
290  const ImageInfo& info = m_imagesInfo[i];
291 
293  if (imageIsValid)
294  {
295  this->addImage(img, tf, info);
296 
297  ++addedImageCount;
298  lastValidIndex = i;
299  }
300  }
301  }
302 
303  // If Checkerboard is used and only one image is valid, we must duplicate the image adaptor to display the image
304  if(addedImageCount == 1 && nullptr != vtkImageCheckerboard::SafeDownCast(this->getVtkObject(m_imageRegisterId)))
305  {
306  ::fwData::Image::sptr img = this->getInOut< ::fwData::Image >(s_IMAGE_GROUP, lastValidIndex);
307  ::fwData::TransferFunction::sptr tf;
308  if (nbTFs > 0)
309  {
310  tf = this->getInOut< ::fwData::TransferFunction >(s_TF_GROUP, lastValidIndex);
311  }
312  const ImageInfo& info = m_imagesInfo[lastValidIndex];
313  this->addImage(img, tf, info);
314  }
315 }
316 
317 //------------------------------------------------------------------------------
318 
319 void SImagesBlend::removeImageAdaptors()
320 {
321  this->unregisterServices();
322 }
323 
324 //------------------------------------------------------------------------------
325 
327 {
328  KeyConnectionsMap connections;
329  connections.push(s_IMAGE_GROUP, ::fwData::Image::s_MODIFIED_SIG, s_UPDATE_SLOT);
330  connections.push(s_IMAGE_GROUP, ::fwData::Image::s_BUFFER_MODIFIED_SIG, s_UPDATE_SLOT);
331 
332  return connections;
333 }
334 
335 //------------------------------------------------------------------------------
336 
337 void SImagesBlend::changeMode(std::string _value, std::string _key)
338 {
339  if( _key == "ImageSource" )
340  {
341  // Select the right algorithm
342  m_imageRegisterId = _value;
343 
344  // Try to downcast as an vtkImageBlend first
345  m_imageAlgorithm = vtkImageBlend::SafeDownCast(this->getVtkObject(m_imageRegisterId));
346 
347  if(nullptr == m_imageAlgorithm)
348  {
349  // If we have no vtkImageBlend, try to downcast as an vtkImageCheckerboard
350  vtkImageCheckerboard* imageCheckerboard =
351  vtkImageCheckerboard::SafeDownCast(this->getVtkObject(m_imageRegisterId));
352 
353  if(nullptr != imageCheckerboard)
354  {
355  // Set the number of subdivision
356  imageCheckerboard->SetNumberOfDivisions(m_checkerboardDivision, m_checkerboardDivision,
357  m_zDivision);
358 
359  // Assign as an vtkThreadedImageAlgorithm
360  m_imageAlgorithm = imageCheckerboard;
361  }
362  }
363 
364  // Update
365  this->updating();
366  }
367 }
368 
369 //------------------------------------------------------------------------------
370 
371 void SImagesBlend::addImage(::fwData::Image::sptr img, ::fwData::TransferFunction::sptr tf, const ImageInfo& info)
372 {
373  // create the srv configuration for objects auto-connection
374  auto imageAdaptor = this->registerService< ::visuVTKAdaptor::SImage>("::visuVTKAdaptor::SImage");
375  // register image
376  imageAdaptor->registerInOut(img, SImage::s_IMAGE_INOUT, true);
377  if (tf)
378  {
379  imageAdaptor->registerInOut(tf, SImage::s_TF_INOUT, false, true);
380  }
381 
382  imageAdaptor->setRenderService(this->getRenderService());
383  imageAdaptor->setRendererId( this->getRendererId() );
384  imageAdaptor->setPickerId( this->getPickerId() );
385  imageAdaptor->setTransformId( this->getTransformId() );
386 
387  imageAdaptor->setVtkImageRegister(m_imageAlgorithm);
388  imageAdaptor->setImageOpacity(info.m_imageOpacity);
389  imageAdaptor->setAllowAlphaInTF(info.m_useTFAlfa);
390 
391  imageAdaptor->start();
392 }
393 
394 //------------------------------------------------------------------------------
395 
396 void SImagesBlend::setTopImageTransparency(int transparency)
397 {
398  SLM_ASSERT("The image group is empty. Have you started the service?", m_imagesInfo.size() > 0);
399  SLM_ERROR_IF("Transparency must be in [0, 255].", transparency < 0 || transparency > 255);
400  auto topImageInfo = m_imagesInfo.rbegin();
401 
402  topImageInfo->m_imageOpacity = 1. - (double(transparency) / 255.);
403  this->updating();
404 }
405 
406 //------------------------------------------------------------------------------
407 
408 void SImagesBlend::changeCheckerboardDivision(const int division)
409 {
410  m_checkerboardDivision = division;
411 
412  // If we have no vtkImageBlend, try to downcast as an vtkImageCheckerboard
413  vtkImageCheckerboard* imageCheckerboard =
414  vtkImageCheckerboard::SafeDownCast(this->getVtkObject(m_imageRegisterId));
415 
416  if(nullptr != imageCheckerboard)
417  {
418  // Set the number of subdivision
419  imageCheckerboard->SetNumberOfDivisions(m_checkerboardDivision, m_checkerboardDivision,
420  m_zDivision);
421 
422  // Assign as an vtkThreadedImageAlgorithm
423  m_imageAlgorithm = imageCheckerboard;
424  }
425 
426  this->updating();
427 }
428 
429 } //namespace visuVTKAdaptor
This class is a helper to define the connections of a service and its data.
Definition: IService.hpp:454
FWRENDERVTK_API vtkObject * getVtkObject(const SRender::VtkObjectIdType &objectId) const
Returns the vtk object defined by &#39;objectId&#39; in the vtk scene.
The namespace visuVTKAdaptor contains the list of adaptors available for the generic scene...
static FWGUI_API IMessageDialog::Buttons showMessageDialog(const std::string &title, const std::string &message,::fwGui::dialog::IMessageDialog::Icons icon=INFO)
FWRENDERVTK_API void configureParams()
Parse the xml configuration for renderer, picker and transform.
VISUVTKADAPTOR_API void updating() override
Perform some computations according to object (this service is attached to) attribute values and its ...
const ServiceVector & getRegisteredServices() const
Get all subservices linked to this service.
VISUVTKADAPTOR_API void starting() override
Initialize the service activity.
Manage blend for image given in configuration.
virtual void swapping()
Swap the service from associated object to another object.
Definition: IService.hpp:613
FWRENDERVTK_API SRender::sptr getRenderService() const
Returd the associated render service.
FWRENDERVTK_API SRender::PickerIdType getPickerId() const
Gets the identifier of the picker used by this adaptor.
VISUVTKADAPTOR_API void stopping() override
Uninitialize the service activity. The stop() method is always invoked before destroying a service...
#define OSLM_ERROR(message)
Definition: spyLog.hpp:274
#define SLM_ERROR_IF(message, cond)
Definition: spyLog.hpp:276
FWSERVICES_API void unregisterServices(const std::string &_implType="")
Unregister all services linked to this service, optionally matches only a given type of services...
#define SLM_ASSERT(message, cond)
work like &#39;assert&#39; from &#39;cassert&#39;, with in addition a message logged by spylog (with FATAL loglevel) ...
Definition: spyLog.hpp:308
bool checkImageInformations()
Return true if images to blend have the same spacing, origin and size. Else show a message dialog and...
FWRENDERVTK_API SRender::VtkObjectIdType getTransformId() const
Returns the identifier of the transform used by this adaptor.
virtual VISUVTKADAPTOR_API KeyConnectionsMap getAutoConnections() const override
Returns proposals to connect service slots to associated object signals, this method is used for obj/...
size_t getKeyGroupSize(const KeyType &keybase) const
Return the number of key in a group of keys.
Definition: IService.hxx:119
static FWDATA_APIconst::fwCom::Signals::SignalKeyType s_BUFFER_MODIFIED_SIG
Type of signal when image&#39;s buffer is added.
static FWDATA_APIconst::fwCom::Signals::SignalKeyType s_MODIFIED_SIG
Key in m_signals map of signal m_sigModified.
std::vector< double > SpacingType
Image spacing type.
FWRENDERVTK_API SRender::RendererIdType getRendererId() const
Returns the renderer identifier.
static FWDATATOOLS_API bool checkImageValidity(::fwData::Image::csptr _pImg)
Check if the image is valid.
FWRENDERVTK_API void initialize()
Initialize the adaptor with the associated render service. (must be call in starting).
This class defines a composite object.
VISUVTKADAPTOR_API void configuring() override
Configure the service before starting. Apply the configuration to service.
FWSERVICES_API bool isRegistered(const ::fwServices::IService::KeyType &objKey,::fwServices::IService::AccessType access,::fwServices::IService::sptr service)
Return true if a key is registered for a given service.
static FWSERVICES_APIconst::fwCom::Slots::SlotKeyType s_UPDATE_SLOT
Slot to call start method.
Definition: IService.hpp:177
::fwData::Array::SizeType SizeType
Image size type.
virtual FWSERVICES_API void info(std::ostream &_sstream)
Write information in a stream.
Definition: IService.cpp:74
std::vector< double > OriginType
Image origin type.
FWSERVICES_API ConfigType getConfigTree() const
Return the configuration, in an boost property tree.
Definition: IService.cpp:247