Statismo  0.10.1
 All Classes Namespaces Functions Typedefs
itkStandardImageRepresenter.hxx
1 /*
2 * This file is part of the statismo library.
3 *
4 * Author: Marcel Luethi (marcel.luethi@unibas.ch)
5 *
6 * Copyright (c) 2011 University of Basel
7 * All rights reserved.
8 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
11 * are met:
12 *
13 * Redistributions of source code must retain the above copyright notice,
14 * this list of conditions and the following disclaimer.
15 *
16 * Redistributions in binary form must reproduce the above copyright
17 * notice, this list of conditions and the following disclaimer in the
18 * documentation and/or other materials provided with the distribution.
19 *
20 * Neither the name of the project's author nor the names of its
21 * contributors may be used to endorse or promote products derived from
22 * this software without specific prior written permission.
23 *
24 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
25 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
26 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
27 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
28 * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
29 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
30 * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
31 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
32 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
33 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
34 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
35 *
36 */
37 
38 #ifndef __itkStandardImageRepresenter_hxx
39 #define __itkStandardImageRepresenter_hxx
40 
41 
42 #include <iostream>
43 
44 #include <itkImageDuplicator.h>
45 #include <itkImageIterator.h>
46 #include <itkImageRegionConstIterator.h>
47 #include <itkImageRegionIterator.h>
48 #include <itkImageFileReader.h>
49 #include <itkImageFileWriter.h>
50 #include <itkIndex.h>
51 #include <itkPoint.h>
52 #include <itkVector.h>
53 
54 #include "HDF5Utils.h"
55 #include "StatismoUtils.h"
56 
57 #include "itkStandardImageRepresenter.h"
58 
59 
60 namespace itk {
61 
62 template <class TPixel, unsigned ImageDimension>
63 StandardImageRepresenter<TPixel, ImageDimension>::StandardImageRepresenter()
64  : m_reference(0) {
65 }
66 template <class TPixel, unsigned ImageDimension>
67 StandardImageRepresenter<TPixel, ImageDimension>::~StandardImageRepresenter() {
68 }
69 
70 template <class TPixel, unsigned ImageDimension>
71 StandardImageRepresenter<TPixel, ImageDimension>*
72 StandardImageRepresenter<TPixel, ImageDimension>::Clone() const {
73 
74  StandardImageRepresenter* clone = new StandardImageRepresenter();
75  clone->Register();
76 
77  DatasetPointerType clonedReference = this->CloneDataset(m_reference);
78  clone->SetReference(clonedReference);
79  return clone;
80 }
81 
82 
83 
84 template <class TPixel, unsigned ImageDimension>
85 void
86 StandardImageRepresenter<TPixel, ImageDimension>::Load(const H5::Group& fg) {
87 
88  std::string repName = statismo::HDF5Utils::readStringAttribute(fg, "name");
89  if (repName == "vtkStructuredPointsRepresenter" || repName == "itkImageRepresenter" || repName == "itkVectorImageRepresenter") {
90  this->SetReference(LoadRefLegacy(fg));
91  } else {
92  this->SetReference(LoadRef(fg));
93  }
94 
95 }
96 
97 
98 template <class TPixel, unsigned ImageDimension>
99 typename StandardImageRepresenter<TPixel, ImageDimension>::ImageType::Pointer
100 StandardImageRepresenter<TPixel, ImageDimension>::LoadRef(const H5::Group& fg) const {
101 
102 
103  int readImageDimension = statismo::HDF5Utils::readInt(fg, "imageDimension");
104  if (readImageDimension != ImageDimension) {
105  throw statismo::StatisticalModelException("the image dimension specified in the statismo file does not match the one specified as template parameter");
106  }
107 
108 
109  statismo::VectorType originVec;
110  statismo::HDF5Utils::readVector(fg, "origin", originVec);
111  typename ImageType::PointType origin;
112  for (unsigned i = 0; i < ImageDimension; i++) {
113  origin[i] = originVec[i];
114  }
115 
116  statismo::VectorType spacingVec;
117  statismo::HDF5Utils::readVector(fg, "spacing", spacingVec);
118  typename ImageType::SpacingType spacing;
119  for (unsigned i = 0; i < ImageDimension; i++) {
120  spacing[i] = spacingVec[i];
121  }
122 
123  typename statismo::GenericEigenType<int>::VectorType sizeVec;
124  statismo::HDF5Utils::readVectorOfType<int>(fg, "size", sizeVec);
125  typename ImageType::SizeType size;
126  for (unsigned i = 0; i < ImageDimension; i++) {
127  size[i] = sizeVec[i];
128  }
129 
130  statismo::MatrixType directionMat;
131  statismo::HDF5Utils::readMatrix(fg, "direction", directionMat);
132  typename ImageType::DirectionType direction;
133  for (unsigned i = 0; i < directionMat.rows(); i++) {
134  for (unsigned j = 0; j < directionMat.rows(); j++) {
135  direction[i][j] = directionMat(i,j);
136  }
137  }
138 
139  H5::Group pdGroup = fg.openGroup("./pointData");
140  unsigned readPixelDimension = static_cast<unsigned>(statismo::HDF5Utils::readInt(pdGroup, "pixelDimension"));
141  if (readPixelDimension != GetDimensions()) {
142  throw statismo::StatisticalModelException("the pixel dimension specified in the statismo file does not match the one specified as template parameter");
143  }
144 
145  typename statismo::GenericEigenType<double>::MatrixType pixelMatDouble;
146  statismo::HDF5Utils::readMatrixOfType<double>(pdGroup, "pixelValues", pixelMatDouble);
147  statismo::MatrixType pixelMat = pixelMatDouble.cast<statismo::ScalarType>();
148  typename ImageType::Pointer newImage = ImageType::New();
149  typename ImageType::IndexType start;
150  start.Fill(0);
151 
152 
153  H5::DataSet ds = pdGroup.openDataSet("pixelValues");
154  unsigned int type = static_cast<unsigned>(statismo::HDF5Utils::readIntAttribute(ds, "datatype"));
155  if (type != PixelConversionTrait<TPixel>::GetDataType()) {
156  std::cout << "Warning: The datatype specified for the scalars does not match the TPixel template argument used in this representer." << std::endl;
157  }
158  pdGroup.close();
159  typename ImageType::RegionType region(start, size);
160  newImage->SetRegions(region);
161  newImage->Allocate();
162  newImage->SetOrigin(origin);
163  newImage->SetSpacing(spacing);
164  newImage->SetDirection(direction);
165 
166 
167  itk::ImageRegionIterator<DatasetType> it(newImage, newImage->GetLargestPossibleRegion());
168  it.GoToBegin();
169  for (unsigned i = 0; !it.IsAtEnd(); ++it, i++) {
170  TPixel v = PixelConversionTrait<TPixel>::FromVector(pixelMat.col(i));
171  it.Set(v);
172  }
173 
174  return newImage;
175 }
176 
177 template <class TPixel, unsigned ImageDimension>
178 typename StandardImageRepresenter<TPixel, ImageDimension>::ImageType::Pointer
179 StandardImageRepresenter<TPixel, ImageDimension>::LoadRefLegacy(const H5::Group& fg) const {
180 
181  std::string tmpfilename;
182  tmpfilename = statismo::Utils::CreateTmpName(".vtk");
183  statismo::HDF5Utils::getFileFromHDF5(fg, "./reference", tmpfilename.c_str());
184 
185  typename itk::ImageFileReader<ImageType>::Pointer reader = itk::ImageFileReader<ImageType>::New();
186  reader->SetFileName(tmpfilename);
187  try {
188  reader->Update();
189  } catch (itk::ImageFileReaderException& e) {
190  throw statismo::StatisticalModelException((std::string("Could not read file ") + tmpfilename).c_str());
191  }
192  typename DatasetType::Pointer img = reader->GetOutput();
193  img->Register();
194  std::remove(tmpfilename.c_str());
195  return img;
196 
197 }
198 
199 
200 template <class TPixel, unsigned ImageDimension>
201 void
203  m_reference = reference;
204 
205  typename DomainType::DomainPointsListType domainPoints;
206  itk::ImageRegionConstIterator<DatasetType> it(reference, reference->GetLargestPossibleRegion());
207  it.GoToBegin();
208  for (;
209  it.IsAtEnd() == false
210  ;) {
211  PointType pt;
212  reference->TransformIndexToPhysicalPoint(it.GetIndex(), pt);
213  domainPoints.push_back(pt);
214  ++it;
215  }
216  m_domain = DomainType(domainPoints);
217 }
218 
219 template <class TPixel, unsigned ImageDimension>
220 statismo::VectorType
222  statismo::VectorType v(PointType::GetPointDimension());
223  for (unsigned i = 0; i < PointType::GetPointDimension(); i++) {
224  v(i) = pt[i];
225  }
226  return v;
227 
228 }
229 
230 
231 
232 template <class TPixel, unsigned ImageDimension>
233 typename StandardImageRepresenter<TPixel, ImageDimension>::DatasetPointerType
234 StandardImageRepresenter<TPixel, ImageDimension>::DatasetToSample(DatasetConstPointerType image) const {
235  // we don't do any alignment for images, but simply return a copy of the image
236 
237  typename itk::ImageDuplicator<ImageType>::Pointer duplicator = itk::ImageDuplicator<ImageType>::New();
238  duplicator->SetInputImage(image);
239  duplicator->Update();
240  return duplicator->GetOutput();
241 
242 }
243 
244 template <class TPixel, unsigned ImageDimension>
245 statismo::VectorType
246 StandardImageRepresenter<TPixel, ImageDimension>::SampleToSampleVector(DatasetConstPointerType image) const {
247  statismo::VectorType sample(this->GetNumberOfPoints() * GetDimensions());
248  itk::ImageRegionConstIterator<DatasetType> it(image, image->GetLargestPossibleRegion());
249 
250  it.GoToBegin();
251  for (unsigned i = 0;
252  it.IsAtEnd() == false;
253  ++i) {
254 
255  statismo::VectorType sampleAtPt = PixelConversionTrait<TPixel>::ToVector(it.Value());
256  for (unsigned j = 0; j < GetDimensions(); j++) {
257  unsigned idx = this->MapPointIdToInternalIdx(i, j);
258  sample[idx] = sampleAtPt[j];
259  }
260  ++it;
261  }
262  return sample;
263 }
264 
265 
266 template <class TPixel, unsigned ImageDimension>
267 typename StandardImageRepresenter<TPixel, ImageDimension>::DatasetPointerType
268 StandardImageRepresenter<TPixel, ImageDimension>::SampleVectorToSample(const statismo::VectorType& sample) const {
269 
270  typedef itk::ImageDuplicator< DatasetType > DuplicatorType;
271  typename DuplicatorType::Pointer duplicator = DuplicatorType::New();
272  duplicator->SetInputImage(this->m_reference);
273  duplicator->Update();
274  DatasetPointerType clonedImage = duplicator->GetOutput();
275 
276  itk::ImageRegionIterator<DatasetType> it(clonedImage, clonedImage->GetLargestPossibleRegion());
277  it.GoToBegin();
278  for (unsigned i = 0; !it.IsAtEnd(); ++it, i++) {
279 
280  statismo::VectorType valAtPoint(GetDimensions());
281  for (unsigned d = 0; d < GetDimensions(); d++) {
282  unsigned idx = this->MapPointIdToInternalIdx(i, d);
283  valAtPoint[d] = sample[idx];
284  }
285  ValueType v = PixelConversionTrait<TPixel>::FromVector(valAtPoint);
286  it.Set(v);
287  }
288  return clonedImage;
289 
290 }
291 
292 template <class TPixel, unsigned ImageDimension>
293 typename StandardImageRepresenter<TPixel, ImageDimension>::ValueType
294 StandardImageRepresenter<TPixel, ImageDimension>::PointSampleFromSample(DatasetConstPointerType sample, unsigned ptid) const {
295  if (ptid >= GetDomain().GetNumberOfPoints()) {
296  throw statismo::StatisticalModelException("invalid ptid provided to PointSampleFromSample");
297  }
298 
299  // we get the point with the id from the domain, as itk does not allow us get a point via its index.
300  PointType pt = GetDomain().GetDomainPoints()[ptid];
301  typename ImageType::IndexType idx;
302  sample->TransformPhysicalPointToIndex(pt, idx);
303  ValueType value = sample->GetPixel(idx);
304  return value;
305 
306 }
307 
308 template <class TPixel, unsigned ImageDimension>
309 typename StandardImageRepresenter<TPixel, ImageDimension>::ValueType
310 StandardImageRepresenter<TPixel, ImageDimension>::PointSampleVectorToPointSample(const statismo::VectorType& pointSample) const {
311  return PixelConversionTrait<TPixel>::FromVector(pointSample);
312 }
313 
314 template <class TPixel, unsigned ImageDimension>
315 statismo::VectorType
316 StandardImageRepresenter<TPixel, ImageDimension>::PointSampleToPointSampleVector(const ValueType& v) const {
317  return PixelConversionTrait<TPixel>::ToVector(v);
318 }
319 
320 
321 template <class TPixel, unsigned ImageDimension>
322 void
323 StandardImageRepresenter<TPixel, ImageDimension>::Save(const H5::Group& fg) const {
324 
325  typename ImageType::PointType origin = m_reference->GetOrigin();
326  statismo::VectorType originVec(ImageDimension);
327  for (unsigned i = 0; i < ImageDimension; i++) {
328  originVec(i) = origin[i];
329  }
330  statismo::HDF5Utils::writeVector(fg, "origin", originVec);
331 
332  typename ImageType::SpacingType spacing = m_reference->GetSpacing();
333  statismo::VectorType spacingVec(ImageDimension);
334  for (unsigned i = 0; i < ImageDimension; i++) {
335  spacingVec(i) = spacing[i];
336  }
337  statismo::HDF5Utils::writeVector(fg, "spacing", spacingVec);
338 
339 
340  statismo::GenericEigenType<int>::VectorType sizeVec(ImageDimension);
341  for (unsigned i = 0; i < ImageDimension; i++) {
342  sizeVec(i) = m_reference->GetLargestPossibleRegion().GetSize()[i];
343  }
344  statismo::HDF5Utils::writeVectorOfType<int>(fg, "size", sizeVec);
345 
346  typename ImageType::DirectionType direction = m_reference->GetDirection();
347  statismo::MatrixType directionMat(ImageDimension, ImageDimension);
348  for (unsigned i = 0; i < ImageDimension; i++) {
349  for (unsigned j = 0; j < ImageDimension; j++) {
350  directionMat(i,j) = direction[i][j];
351  }
352  }
353  statismo::HDF5Utils::writeMatrix(fg, "direction", directionMat);
354 
355  statismo::HDF5Utils::writeInt(fg, "imageDimension", ImageDimension);
356 
357  H5::Group pdGroup = fg.createGroup("pointData");
358  statismo::HDF5Utils::writeInt(pdGroup, "pixelDimension", GetDimensions());
359 
360 
361  typedef statismo::GenericEigenType<double>::MatrixType DoubleMatrixType;
362  statismo::MatrixType pixelMat(GetDimensions(), GetNumberOfPoints());
363 
364  itk::ImageRegionIterator<DatasetType> it(m_reference, m_reference->GetLargestPossibleRegion());
365  it.GoToBegin();
366  for (unsigned i = 0;
367  it.IsAtEnd() == false;
368  ++i) {
369  pixelMat.col(i) = PixelConversionTrait<TPixel>::ToVector(it.Get());
370  ++it;
371  }
372  DoubleMatrixType pixelMatDouble = pixelMat.cast<double>();
373  H5::DataSet ds = statismo::HDF5Utils::writeMatrixOfType<double>(pdGroup, "pixelValues", pixelMatDouble);
374  statismo::HDF5Utils::writeIntAttribute(ds, "datatype", PixelConversionTrait<TPixel>::GetDataType());
375  pdGroup.close();
376 }
377 
378 
379 template <class TPixel, unsigned ImageDimension>
380 unsigned
381 StandardImageRepresenter<TPixel, ImageDimension>::GetNumberOfPoints() const {
382  return m_reference->GetLargestPossibleRegion().GetNumberOfPixels();
383 }
384 
385 
386 template <class TPixel, unsigned ImageDimension>
387 unsigned
388 StandardImageRepresenter<TPixel, ImageDimension>::GetPointIdForPoint(const PointType& pt) const {
389  // itks organization is slice row col
390  typename DatasetType::IndexType idx;
391  bool ptInImage = this->m_reference->TransformPhysicalPointToIndex(pt, idx);
392 
393  typename DatasetType::SizeType size = this->m_reference->GetLargestPossibleRegion().GetSize();
394 
395  // It does not make sense to allow points outside the image, because only the inside is modeled.
396  // However, some discretization artifacts of image and surface operations may produce points that
397  // are just on the boundary of the image, but mathematically outside. We accept these points and
398  // return the iD of the closest image point.
399  // Any points further out will trigger an exception.
400  if(!ptInImage) {
401  for (unsigned int i=0; i<ImageType::ImageDimension; ++i) {
402  // As soon as one coordinate is further away than one pixel, we throw an exception.
403  if(idx[i] < -1 || idx[i] > size[i]) {
404  throw statismo::StatisticalModelException("GetPointIdForPoint computed invalid ptId. Make sure that the point is within the reference you chose ");
405  }
406  // If it is on the boundary, we set it to the nearest boundary coordinate.
407  if(idx[i] == -1) idx[i] = 0;
408  if(idx[i] == size[i]) idx[i] = size[i] - 1;
409  }
410  }
411 
412 
413  // in itk, idx 0 is by convention the fastest moving index
414  unsigned int index=0;
415  for (unsigned int i=0; i<ImageType::ImageDimension; ++i) {
416  unsigned int multiplier=1;
417  for (int d=i-1; d>=0; --d) {
418  multiplier*=size[d];
419  }
420  index+=multiplier*idx[i];
421  }
422 
423  return index;
424 }
425 
426 template <class TPixel, unsigned ImageDimension>
427 typename StandardImageRepresenter<TPixel, ImageDimension>::DatasetPointerType
428 StandardImageRepresenter<TPixel, ImageDimension>::CloneDataset(DatasetConstPointerType d) const {
429  typedef itk::ImageDuplicator< DatasetType > DuplicatorType;
430  typename DuplicatorType::Pointer duplicator = DuplicatorType::New();
431  duplicator->SetInputImage(d);
432  duplicator->Update();
433  DatasetPointerType clone = duplicator->GetOutput();
434  clone->DisconnectPipeline();
435  return clone;
436 }
437 
438 } // namespace itk
439 
440 #endif
A representer for scalar and vector valued images.
Definition: itkStandardImageRepresenter.h:63
static void writeIntAttribute(const H5::H5Object &fg, const char *name, int value)
Definition: HDF5Utils.hxx:406
static void readMatrix(const H5::CommonFG &fg, const char *name, MatrixType &matrix)
Definition: HDF5Utils.hxx:154
static H5::DataSet writeMatrix(const H5::CommonFG &fg, const char *name, const MatrixType &matrix)
Definition: HDF5Utils.hxx:248
statismo::VectorType PointToVector(const PointType &pt) const
Definition: itkStandardImageRepresenter.hxx:221
static std::string readStringAttribute(const H5::H5Object &group, const char *name)
Definition: HDF5Utils.hxx:397
void SetReference(ImageType *ds)
Definition: itkStandardImageRepresenter.hxx:202
static H5::DataSet writeInt(const H5::CommonFG &fg, const char *name, int value)
Definition: HDF5Utils.hxx:426
Generic Exception class for the statismo Library.
Definition: Exceptions.h:68
static void readVector(const H5::CommonFG &fg, const char *name, unsigned nElements, VectorType &vector)
Definition: HDF5Utils.hxx:296
static H5::DataSet writeVector(const H5::CommonFG &fg, const char *name, const VectorType &vector)
Definition: HDF5Utils.hxx:363
float ScalarType
the type that is used for all vector and matrices throughout the library.
Definition: CommonTypes.h:60
static void getFileFromHDF5(const H5::CommonFG &fg, const char *name, const char *filename)
Definition: HDF5Utils.hxx:462
Definition: Domain.h:51
static int readInt(const H5::CommonFG &fg, const char *name)
Definition: HDF5Utils.hxx:434
static int readIntAttribute(const H5::H5Object &group, const char *name)
Definition: HDF5Utils.hxx:416