Fraxinus  17.12-rc3
An IGT application
cxIGTLinkConversionImage.cpp
Go to the documentation of this file.
1 /*=========================================================================
2 This file is part of CustusX, an Image Guided Therapy Application.
3 
4 Copyright (c) 2008-2014, SINTEF Department of Medical Technology
5 All rights reserved.
6 
7 Redistribution and use in source and binary forms, with or without
8 modification, are permitted provided that the following conditions are met:
9 
10 1. Redistributions of source code must retain the above copyright notice,
11  this list of conditions and the following disclaimer.
12 
13 2. Redistributions in binary form must reproduce the above copyright notice,
14  this list of conditions and the following disclaimer in the documentation
15  and/or other materials provided with the distribution.
16 
17 3. Neither the name of the copyright holder nor the names of its contributors
18  may be used to endorse or promote products derived from this software
19  without specific prior written permission.
20 
21 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
22 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
24 DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
25 FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
27 SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
28 CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
29 OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 =========================================================================*/
32 
33 /*==========================================================================
34 
35  Portions (c) Copyright 2008-2009 Brigham and Women's Hospital (BWH) All Rights Reserved.
36 
37  See Doc/copyright/copyright.txt
38  or http://www.slicer.org/copyright/copyright.txt for details.
39 
40  Program: 3D Slicer
41  Module: $HeadURL: http://svn.slicer.org/Slicer3/trunk/Modules/OpenIGTLinkIF/vtkIGTLToMRMLImage.h $
42  Date: $Date: 2010-11-23 00:58:13 -0500 (Tue, 23 Nov 2010) $
43  Version: $Revision: 15552 $
44 
45 ==========================================================================*/
47 #include "vtkImageData.h"
48 
49 #include <igtl_util.h>
50 #include "cxLogger.h"
53 
54 
55 namespace cx
56 {
57 
58 igtl::ImageMessage::Pointer IGTLinkConversionImage::encode(ImagePtr image, PATIENT_COORDINATE_SYSTEM externalSpace)
59 {
60  igtl::ImageMessage::Pointer retval = igtl::ImageMessage::New();
61 
62  retval->SetDeviceName(cstring_cast(image->getName()));
63  IGTLinkConversionBase().encode_timestamp(image->getAcquisitionTime(), retval);
64  this->encode_vtkImageData(image->getBaseVtkImageData(), retval);
65  this->encode_rMd(image, retval, externalSpace);
66 
67  return retval;
68 }
69 
70 ImagePtr IGTLinkConversionImage::decode(igtl::ImageMessage *msg)
71 {
72  vtkImageDataPtr vtkImage = this->decode_vtkImageData(msg);
73  QDateTime timestamp = IGTLinkConversionBase().decode_timestamp(msg);
74  QString deviceName = msg->GetDeviceName();
75 
76  ImagePtr retval(new Image(deviceName, vtkImage));
77  retval->setAcquisitionTime(timestamp);
78  this->decode_rMd(msg, retval);
79 
80  return retval;
81 }
82 
83 
84 namespace { // unnamed namespace
85 
86 //---------------------------------------------------------------------------
87 // Stream copy + byte swap
88 //---------------------------------------------------------------------------
89 int swapCopy16(igtlUint16 * dst, igtlUint16 * src, int n)
90 {
91  igtlUint16 * esrc = src + n;
92  while (src < esrc)
93  {
94  *dst = BYTE_SWAP_INT16(*src);
95  dst ++;
96  src ++;
97  }
98  return 1;
99 }
100 
101 int swapCopy32(igtlUint32 * dst, igtlUint32 * src, int n)
102 {
103  igtlUint32 * esrc = src + n;
104  while (src < esrc)
105  {
106  *dst = BYTE_SWAP_INT32(*src);
107  dst ++;
108  src ++;
109  }
110  return 1;
111 }
112 
113 int swapCopy64(igtlUint64 * dst, igtlUint64 * src, int n)
114 {
115  igtlUint64 * esrc = src + n;
116  while (src < esrc)
117  {
118  *dst = BYTE_SWAP_INT64(*src);
119  dst ++;
120  src ++;
121  }
122  return 1;
123 }
124 } // unnamed namespace
125 
126 vtkImageDataPtr IGTLinkConversionImage::decode_vtkImageData(igtl::ImageMessage *imgMsg)
127 {
128  // NOTE: This method is mostly a copy-paste from Slicer.
129  // MRML and coordinate stuff are removed.
130  // Avoid refactoring the internals, as it is important to keep the code similar to the origin.
131 
132  // Retrieve the image data
133  int size[3]; // image dimension
134  float spacing[3]; // spacing (mm/pixel)
135  int svsize[3]; // sub-volume size
136  int svoffset[3]; // sub-volume offset
137  int scalarType; // VTK scalar type
138  int numComponents; // number of scalar components
139  int endian;
140 
141  scalarType = IGTLToVTKScalarType( imgMsg->GetScalarType() );
142  endian = imgMsg->GetEndian();
143  imgMsg->GetDimensions(size);
144  imgMsg->GetSpacing(spacing);
145  numComponents = imgMsg->GetNumComponents();
146  imgMsg->GetSubVolume(svsize, svoffset);
147  // imgMsg->GetMatrix(matrix);
148 
149  // check if the IGTL data fits to the current MRML node
150  int sizeInNode[3]={0,0,0};
151  int scalarTypeInNode=VTK_VOID;
152  int numComponentsInNode=0;
153  // Get vtk image from MRML node
154  vtkSmartPointer<vtkImageData> imageData = vtkSmartPointer<vtkImageData>::New();
155  imageData->SetDimensions(size[0], size[1], size[2]);
156  imageData->SetExtent(0, size[0]-1, 0, size[1]-1, 0, size[2]-1);
157  imageData->SetOrigin(0.0, 0.0, 0.0);
158 // imageData->SetSpacing(1.0, 1.0, 1.0); // Slicer inserts spacing into its IKTtoRAS matrix, we dont.
159  imageData->SetSpacing(spacing[0], spacing[1], spacing[2]);
160  imageData->AllocateScalars(scalarType, numComponents);
161 
162  // Check scalar size
163  int scalarSize = imgMsg->GetScalarSize();
164 
165  int fByteSwap = 0;
166  // Check if bytes-swap is required
167  if (scalarSize > 1 &&
168  ((igtl_is_little_endian() && endian == igtl::ImageMessage::ENDIAN_BIG) ||
169  (!igtl_is_little_endian() && endian == igtl::ImageMessage::ENDIAN_LITTLE)))
170  {
171  // Needs byte swap
172  fByteSwap = 1;
173  }
174 
175  if (imgMsg->GetImageSize() == imgMsg->GetSubVolumeImageSize())
176  {
177  // In case that volume size == sub-volume size,
178  // image is read directly to the memory area of vtkImageData
179  // for better performance.
180  if (fByteSwap)
181  {
182  switch (scalarSize)
183  {
184  case 2:
185  swapCopy16((igtlUint16 *)imageData->GetScalarPointer(),
186  (igtlUint16 *)imgMsg->GetScalarPointer(),
187  imgMsg->GetSubVolumeImageSize() / 2);
188  break;
189  case 4:
190  swapCopy32((igtlUint32 *)imageData->GetScalarPointer(),
191  (igtlUint32 *)imgMsg->GetScalarPointer(),
192  imgMsg->GetSubVolumeImageSize() / 4);
193  break;
194  case 8:
195  swapCopy64((igtlUint64 *)imageData->GetScalarPointer(),
196  (igtlUint64 *)imgMsg->GetScalarPointer(),
197  imgMsg->GetSubVolumeImageSize() / 8);
198  break;
199  default:
200  break;
201  }
202  }
203  else
204  {
205  memcpy(imageData->GetScalarPointer(),
206  imgMsg->GetScalarPointer(), imgMsg->GetSubVolumeImageSize());
207  }
208  }
209  else
210  {
211  // In case of volume size != sub-volume size,
212  // image is loaded into ImageReadBuffer, then copied to
213  // the memory area of vtkImageData.
214  char* imgPtr = (char*) imageData->GetScalarPointer();
215  char* bufPtr = (char*) imgMsg->GetScalarPointer();
216  int sizei = size[0];
217  int sizej = size[1];
218  //int sizek = size[2];
219  int subsizei = svsize[0];
220 
221  int bg_i = svoffset[0];
222  //int ed_i = bg_i + svsize[0];
223  int bg_j = svoffset[1];
224  int ed_j = bg_j + svsize[1];
225  int bg_k = svoffset[2];
226  int ed_k = bg_k + svsize[2];
227 
228  if (fByteSwap)
229  {
230  switch (scalarSize)
231  {
232  case 2:
233  for (int k = bg_k; k < ed_k; k ++)
234  {
235  for (int j = bg_j; j < ed_j; j ++)
236  {
237  swapCopy16((igtlUint16 *)&imgPtr[(sizei*sizej*k + sizei*j + bg_i)*scalarSize],
238  (igtlUint16 *)bufPtr,
239  subsizei);
240  bufPtr += subsizei*scalarSize;
241  }
242  }
243  break;
244  case 4:
245  for (int k = bg_k; k < ed_k; k ++)
246  {
247  for (int j = bg_j; j < ed_j; j ++)
248  {
249  swapCopy32((igtlUint32 *)&imgPtr[(sizei*sizej*k + sizei*j + bg_i)*scalarSize],
250  (igtlUint32 *)bufPtr,
251  subsizei);
252  bufPtr += subsizei*scalarSize;
253  }
254  }
255  break;
256  case 8:
257  for (int k = bg_k; k < ed_k; k ++)
258  {
259  for (int j = bg_j; j < ed_j; j ++)
260  {
261  swapCopy64((igtlUint64 *)&imgPtr[(sizei*sizej*k + sizei*j + bg_i)*scalarSize],
262  (igtlUint64 *)bufPtr,
263  subsizei);
264  bufPtr += subsizei*scalarSize;
265  }
266  }
267  break;
268  default:
269  break;
270  }
271  }
272  else
273  {
274  for (int k = bg_k; k < ed_k; k ++)
275  {
276  for (int j = bg_j; j < ed_j; j ++)
277  {
278  memcpy(&imgPtr[(sizei*sizej*k + sizei*j + bg_i)*scalarSize],
279  bufPtr, subsizei*scalarSize);
280  bufPtr += subsizei*scalarSize;
281  }
282  }
283  }
284 
285  }
286 
287  imageData->Modified();
288  return imageData;
289 }
290 
291 void IGTLinkConversionImage::encode_vtkImageData(vtkImageDataPtr in, igtl::ImageMessage *outmsg)
292 {
293  // NOTE: This method is mostly a copy-paste from Slicer.
294  // MRML and coordinate stuff are removed.
295  // Avoid refactoring the internals, as it is important to keep the code similar to the origin.
296 
297  vtkImageDataPtr imageData = in;
298  int isize[3]; // image dimension
299  int scalarType; // scalar type
300  double *spacing; // spacing (mm/pixel)
301  int svoffset[] = {0, 0, 0}; // sub-volume offset
302  int endian;
303 
304  scalarType = imageData->GetScalarType();
305  imageData->GetDimensions(isize);
306  spacing = imageData->GetSpacing();
307  int numComponents = imageData->GetNumberOfScalarComponents();
308 
309  // Check endianness of the machine
310  endian = igtl::ImageMessage::ENDIAN_BIG;
311  if (igtl_is_little_endian())
312  {
313  endian = igtl::ImageMessage::ENDIAN_LITTLE;
314  }
315 
316  outmsg->SetDimensions(isize);
317  outmsg->SetSpacing((float)spacing[0], (float)spacing[1], (float)spacing[2]);
318  outmsg->SetScalarType(scalarType);
319  outmsg->SetEndian(endian);
320  outmsg->SetSubVolume(isize, svoffset);
321  outmsg->SetNumComponents(numComponents);
322  outmsg->AllocateScalars();
323 
324  memcpy(outmsg->GetScalarPointer(),
325  imageData->GetScalarPointer(),
326  outmsg->GetImageSize());
327 }
328 
329 
330 void IGTLinkConversionImage::decode_rMd(igtl::ImageMessage *msg, ImagePtr out)
331 {
332  Transform3D sMigtl = this->getMatrix(msg);
333 
334  Vector3D c = out->boundingBox().center();
336 
337  // s is the inbound reference system, i.e. LPS or RAS.
338  PATIENT_COORDINATE_SYSTEM s = this->getPatientCoordinateSystem(msg->GetCoordinateSystem());
340 
341  Transform3D rMd = rMs * sMigtl * igtlMd;
342 
343  out->get_rMd_History()->setRegistration(rMd);
344 }
345 
346 int IGTLinkConversionImage::getIgtlCoordinateSystem(PATIENT_COORDINATE_SYSTEM space) const
347 {
348  if (space==pcsRAS)
349  return igtl::ImageMessage::COORDINATE_RAS;
350  if (space==pcsLPS)
351  return igtl::ImageMessage::COORDINATE_LPS;
352 
353  return igtl::ImageMessage::COORDINATE_RAS; // default
354 }
355 
356 PATIENT_COORDINATE_SYSTEM IGTLinkConversionImage::getPatientCoordinateSystem(int igtlSpace) const
357 {
358  if (igtlSpace==igtl::ImageMessage::COORDINATE_RAS)
359  return pcsRAS;
360  if (igtlSpace==igtl::ImageMessage::COORDINATE_LPS)
361  return pcsLPS;
362 
363  return pcsRAS; // default
364 }
365 
366 void IGTLinkConversionImage::encode_rMd(ImagePtr image, igtl::ImageMessage *outmsg, PATIENT_COORDINATE_SYSTEM externalSpace)
367 {
368  Transform3D rMd = image->get_rMd();
369 
370  // NOTE: there seems to be a bug in Slicer3D: LPS not supported in OpenIGTLinkIF,
371  // thus using LPS will fail against Slicer3D.
372  // This doesn't matter much, as POLYDATA anyway must use RAS.
373 
374  // s is the outbound reference system, i.e. LPS or RAS.
376  outmsg->SetCoordinateSystem(this->getIgtlCoordinateSystem(externalSpace));
377 
378 // if (coordinateSystem=="RAS")
379 // {
380 // // NOTE: there seems to be a bug in Slicer: LPS not supported in OpenIGTLinkIF.
381 // // This doesn't matter much, as POLYDATA anyway must use RAS.
382 // sMr = createTransformLPS2RAS();
383 // outmsg->SetCoordinateSystem(igtl::ImageMessage::COORDINATE_RAS);
384 // }
385 // else // i.e. LPS
386 // {
387 // sMr = Transform3D::Identity();
388 // outmsg->SetCoordinateSystem(igtl::ImageMessage::COORDINATE_LPS);
389 // }
390 
391  // shift origin to image center
392  Vector3D c = image->boundingBox().center();
394  Transform3D sMigtl = sMr * rMd * dMigtl;
395 
396 // CX_LOG_CHANNEL_DEBUG("ca") << "rMd encode\n" << rMd;
397 // CX_LOG_CHANNEL_DEBUG("ca") << "sMr encode\n" << sMr;
398 // CX_LOG_CHANNEL_DEBUG("ca") << "sMigtl encode\n" << sMigtl;
399 
400  this->setMatrix(outmsg, sMigtl);
401 }
402 
403 Transform3D IGTLinkConversionImage::getMatrix(igtl::ImageMessage *msg)
404 {
405  igtl::Matrix4x4 matrix;
406  msg->GetMatrix(matrix);
407  return Transform3D::fromFloatArray(matrix);
408 }
409 
410 void IGTLinkConversionImage::setMatrix(igtl::ImageMessage *msg, Transform3D matrix)
411 {
412  igtl::Matrix4x4 m;
413  for (int r = 0; r < 4; ++r)
414  for (int c = 0; c < 4; ++c)
415  m[r][c] = matrix(r,c);
416 
417  msg->SetMatrix(m);
418 }
419 
420 int IGTLinkConversionImage::IGTLToVTKScalarType(int igtlType)
421 {
422  switch (igtlType)
423  {
424  case igtl::ImageMessage::TYPE_INT8: return VTK_CHAR;
425  case igtl::ImageMessage::TYPE_UINT8: return VTK_UNSIGNED_CHAR;
426  case igtl::ImageMessage::TYPE_INT16: return VTK_SHORT;
427  case igtl::ImageMessage::TYPE_UINT16: return VTK_UNSIGNED_SHORT;
428  case igtl::ImageMessage::TYPE_INT32: return VTK_UNSIGNED_LONG;
429  case igtl::ImageMessage::TYPE_UINT32: return VTK_UNSIGNED_LONG;
430  case igtl::ImageMessage::TYPE_FLOAT32: return VTK_FLOAT;
431  case igtl::ImageMessage::TYPE_FLOAT64: return VTK_DOUBLE;
432  default:
433  CX_LOG_CHANNEL_ERROR("igtl") << "Invalid IGTL scalar Type: "<< igtlType;
434  return VTK_VOID;
435  }
436 }
437 
438 
439 } //namespace cx
440 
441 
pcsRAS
Right-Anterior-Superior, used by Slicer3D, ITK-Snap, nifti, MINC.
Transform3D Transform3D
Transform3D is a representation of an affine 3D transform.
cxResource_EXPORT Transform3D createTransformFromReferenceToExternal(PATIENT_COORDINATE_SYSTEM external)
boost::shared_ptr< class Image > ImagePtr
Definition: cxDicomWidget.h:48
cstring_cast_Placeholder cstring_cast(const T &val)
A volumetric data set.
Definition: cxImage.h:66
Transform3D createTransformTranslate(const Vector3D &translation)
pcsLPS
Left-Posterior-Superior, used internally by CustusX, also DICOM, ITK.
Eigen::Vector3d Vector3D
Vector3D is a representation of a point or vector in 3D.
Definition: cxVector3D.h:63
vtkSmartPointer< class vtkImageData > vtkImageDataPtr
#define CX_LOG_CHANNEL_ERROR(channel)
Definition: cxLogger.h:132
Namespace for all CustusX production code.