Medical Imaging Interaction Toolkit  2018.4.99-a3d2e8fb
Medical Imaging Interaction Toolkit
QmitkImageStatisticsTreeModel.cpp
Go to the documentation of this file.
1 /*============================================================================
2 
3 The Medical Imaging Interaction Toolkit (MITK)
4 
5 Copyright (c) German Cancer Research Center (DKFZ)
6 All rights reserved.
7 
8 Use of this source code is governed by a 3-clause BSD license that can be
9 found in the LICENSE file.
10 
11 ============================================================================*/
12 
14 
16 #include "itkMutexLockHolder.h"
21 
22 #include "QmitkStyleManager.h"
23 
25 {
26  m_RootItem = new QmitkImageStatisticsTreeItem();
27 }
28 
30 {
31  // set data storage to nullptr so that the event listener gets removed
32  this->SetDataStorage(nullptr);
33  delete m_RootItem;
34 };
35 
37 {
38  emit beginResetModel();
39  UpdateByDataStorage();
40  emit endResetModel();
41  emit modelChanged();
42 }
43 
45 {
46  emit beginResetModel();
47  UpdateByDataStorage();
48  emit endResetModel();
49  emit modelChanged();
50 }
51 
52 int QmitkImageStatisticsTreeModel::columnCount(const QModelIndex& /*parent*/) const
53 {
54  int columns = m_StatisticNames.size() + 1;
55  return columns;
56 }
57 
58 int QmitkImageStatisticsTreeModel::rowCount(const QModelIndex &parent) const
59 {
60  QmitkImageStatisticsTreeItem *parentItem;
61  if (parent.column() > 0)
62  return 0;
63 
64  if (!parent.isValid())
65  parentItem = m_RootItem;
66  else
67  parentItem = static_cast<QmitkImageStatisticsTreeItem *>(parent.internalPointer());
68 
69  return parentItem->childCount();
70 }
71 
72 QVariant QmitkImageStatisticsTreeModel::data(const QModelIndex &index, int role) const
73 {
74  if (!index.isValid())
75  return QVariant();
76 
77  QmitkImageStatisticsTreeItem* item = static_cast<QmitkImageStatisticsTreeItem*>(index.internalPointer());
78 
79  if (role == Qt::DisplayRole)
80  {
81  return item->data(index.column());
82  }
83  else if (role == Qt::DecorationRole && index.column() == 0 && item->isWIP() && item->childCount()==0)
84  {
85  return QVariant(QmitkStyleManager::ThemeIcon(QStringLiteral(":/Qmitk/hourglass-half-solid.svg")));
86  }
87  return QVariant();
88 }
89 
90 QModelIndex QmitkImageStatisticsTreeModel::index(int row, int column, const QModelIndex &parent) const
91 {
92  if (!hasIndex(row, column, parent))
93  return QModelIndex();
94 
95  QmitkImageStatisticsTreeItem *parentItem;
96 
97  if (!parent.isValid())
98  parentItem = m_RootItem;
99  else
100  parentItem = static_cast<QmitkImageStatisticsTreeItem *>(parent.internalPointer());
101 
102  QmitkImageStatisticsTreeItem *childItem = parentItem->child(row);
103  if (childItem)
104  return createIndex(row, column, childItem);
105  else
106  return QModelIndex();
107 }
108 
109 QModelIndex QmitkImageStatisticsTreeModel::parent(const QModelIndex &child) const
110 {
111  if (!child.isValid())
112  return QModelIndex();
113 
114  QmitkImageStatisticsTreeItem *childItem = static_cast<QmitkImageStatisticsTreeItem *>(child.internalPointer());
115  QmitkImageStatisticsTreeItem *parentItem = childItem->parentItem();
116 
117  if (parentItem == m_RootItem)
118  return QModelIndex();
119 
120  return createIndex(parentItem->row(), 0, parentItem);
121 }
122 
123 Qt::ItemFlags QmitkImageStatisticsTreeModel::flags(const QModelIndex &index) const
124 {
125  if (!index.isValid())
126  return nullptr;
127 
128  return QAbstractItemModel::flags(index);
129 }
130 
131 QVariant QmitkImageStatisticsTreeModel::headerData(int section, Qt::Orientation orientation, int role) const
132 {
133  if ((Qt::DisplayRole == role) && (Qt::Horizontal == orientation))
134  {
135  if (section == 0)
136  {
137  return m_HeaderFirstColumn;
138  }
139  else
140  {
141  return QVariant(m_StatisticNames.at(section - 1).c_str());
142  }
143  }
144  return QVariant();
145 }
146 
147 void QmitkImageStatisticsTreeModel::SetImageNodes(const std::vector<mitk::DataNode::ConstPointer> &nodes)
148 {
149  std::vector<std::pair<mitk::DataNode::ConstPointer, unsigned int>> tempNodes;
150  for (const auto &node : nodes)
151  {
152  auto data = node->GetData();
153  if (data)
154  {
155  auto timeSteps = data->GetTimeSteps();
156  for (unsigned int i = 0; i < timeSteps; i++)
157  {
158  tempNodes.push_back(std::make_pair(node, i));
159  }
160  }
161  }
162 
163  emit beginResetModel();
164  m_TimeStepResolvedImageNodes = std::move(tempNodes);
165  m_ImageNodes = nodes;
166  UpdateByDataStorage();
167  emit endResetModel();
168  emit modelChanged();
169 }
170 
171 void QmitkImageStatisticsTreeModel::SetMaskNodes(const std::vector<mitk::DataNode::ConstPointer> &nodes)
172 {
173  std::vector<std::pair<mitk::DataNode::ConstPointer, unsigned int>> tempNodes;
174  for (const auto &node : nodes)
175  {
176  auto data = node->GetData();
177  if (data)
178  {
179  auto timeSteps = data->GetTimeSteps();
180  // special case: apply one mask to each timestep of an 4D image
181  if (timeSteps == 1 && m_TimeStepResolvedImageNodes.size() > 1)
182  {
183  timeSteps = m_TimeStepResolvedImageNodes.size();
184  }
185  for (unsigned int i = 0; i < timeSteps; i++)
186  {
187  tempNodes.push_back(std::make_pair(node, i));
188  }
189  }
190  }
191 
192  emit beginResetModel();
193  m_TimeStepResolvedMaskNodes = std::move(tempNodes);
194  m_MaskNodes = nodes;
195  UpdateByDataStorage();
196  emit endResetModel();
197  emit modelChanged();
198 }
199 
201 {
202  emit beginResetModel();
203  m_Statistics.clear();
204  m_ImageNodes.clear();
205  m_TimeStepResolvedImageNodes.clear();
206  m_MaskNodes.clear();
207  m_StatisticNames.clear();
208  emit endResetModel();
209  emit modelChanged();
210 }
211 
213 {
214  if (m_IgnoreZeroValueVoxel != _arg)
215  {
216  emit beginResetModel();
217  m_IgnoreZeroValueVoxel = _arg;
218  UpdateByDataStorage();
219  emit endResetModel();
220  emit modelChanged();
221  }
222 }
223 
225 {
226  return this->m_IgnoreZeroValueVoxel;
227 }
228 
230 {
231  if (m_HistogramNBins != nbins)
232  {
233  emit beginResetModel();
234  m_HistogramNBins = nbins;
235  UpdateByDataStorage();
236  emit endResetModel();
237  emit modelChanged();
238  }
239 }
240 
242 {
243  return this->m_HistogramNBins;
244 }
245 
246 void QmitkImageStatisticsTreeModel::UpdateByDataStorage()
247 {
248  StatisticsContainerVector newStatistics;
249 
250  auto datamanager = m_DataStorage.Lock();
251 
252  if (datamanager.IsNotNull())
253  {
254  for (const auto &image : m_ImageNodes)
255  {
256  if (m_MaskNodes.empty())
257  {
258  auto stats = mitk::ImageStatisticsContainerManager::GetImageStatistics(datamanager, image->GetData(), nullptr, m_IgnoreZeroValueVoxel, m_HistogramNBins, true, false);
259 
260  if (stats.IsNotNull())
261  {
262  newStatistics.emplace_back(stats);
263  }
264  }
265  else
266  {
267  for (const auto &mask : m_MaskNodes)
268  {
269  auto stats =
270  mitk::ImageStatisticsContainerManager::GetImageStatistics(datamanager, image->GetData(), mask->GetData(), m_IgnoreZeroValueVoxel, m_HistogramNBins, true, false);
271  if (stats.IsNotNull())
272  {
273  newStatistics.emplace_back(stats);
274  }
275  }
276  }
277  }
278  if (!newStatistics.empty())
279  {
280  emit dataAvailable();
281  }
282  }
283 
284  {
286  m_Statistics = newStatistics;
287  }
288 
289  m_StatisticNames = mitk::GetAllStatisticNames(m_Statistics);
290  BuildHierarchicalModel();
291 }
292 
293 void QmitkImageStatisticsTreeModel::BuildHierarchicalModel()
294 {
295  // reset old model
296  delete m_RootItem;
297  m_RootItem = new QmitkImageStatisticsTreeItem();
298 
299  bool hasMask = false;
300  bool hasMultipleTimesteps = false;
301 
302  std::map<mitk::DataNode::ConstPointer, QmitkImageStatisticsTreeItem *> dataNodeToTreeItem;
303 
304  for (auto statistic : m_Statistics)
305  {
306  bool isWIP = statistic->GetProperty(mitk::STATS_GENERATION_STATUS_PROPERTY_NAME.c_str()).IsNotNull();
307  // get the connected image data node/mask data node
308  auto imageRule = mitk::StatisticsToImageRelationRule::New();
309  auto imageOfStatisticsPredicate = imageRule->GetDestinationsDetector(statistic);
310  auto imageFinding = std::find_if(m_ImageNodes.begin(), m_ImageNodes.end(), [&imageOfStatisticsPredicate](const mitk::DataNode::ConstPointer& testNode) { return imageOfStatisticsPredicate->CheckNode(testNode); });
311 
312  auto maskRule = mitk::StatisticsToMaskRelationRule::New();
313  auto maskOfStatisticsPredicate = maskRule->GetDestinationsDetector(statistic);
314  auto maskFinding = std::find_if(m_MaskNodes.begin(), m_MaskNodes.end(), [&maskOfStatisticsPredicate](const mitk::DataNode::ConstPointer& testNode) { return maskOfStatisticsPredicate->CheckNode(testNode); });
315 
316  if (imageFinding == m_ImageNodes.end())
317  {
318  mitkThrow() << "no image found connected to statistic" << statistic << " Aborting.";
319  }
320 
321  auto& image = *imageFinding;
322 
323  // image: 1. hierarchy level
324  QmitkImageStatisticsTreeItem *imageItem = nullptr;
325  auto search = dataNodeToTreeItem.find(image);
326  // the tree item was created previously
327  if (search != dataNodeToTreeItem.end())
328  {
329  imageItem = search->second;
330  }
331  // create the tree item
332  else
333  {
334  QString imageLabel = QString::fromStdString(image->GetName());
335  if (statistic->GetTimeSteps() == 1 && maskFinding == m_MaskNodes.end())
336  {
337  auto statisticsObject = statistic->GetStatisticsForTimeStep(0);
338  imageItem = new QmitkImageStatisticsTreeItem(statisticsObject, m_StatisticNames, imageLabel, isWIP, m_RootItem);
339  }
340  else
341  {
342  imageItem = new QmitkImageStatisticsTreeItem(m_StatisticNames, imageLabel, isWIP, m_RootItem);
343  }
344  m_RootItem->appendChild(imageItem);
345  dataNodeToTreeItem.emplace(image, imageItem);
346  }
347 
348  // mask: 2. hierarchy level (optional, only if mask exists)
349  QmitkImageStatisticsTreeItem *lastParent = nullptr;
350  if (maskFinding != m_MaskNodes.end())
351  {
352  auto& mask = *maskFinding;
353  QString maskLabel = QString::fromStdString(mask->GetName());
355  // add statistical values directly in this hierarchy level
356  if (statistic->GetTimeSteps() == 1)
357  {
358  auto statisticsObject = statistic->GetStatisticsForTimeStep(0);
359  maskItem = new QmitkImageStatisticsTreeItem(statisticsObject, m_StatisticNames, maskLabel, isWIP, imageItem);
360  }
361  else
362  {
363  maskItem = new QmitkImageStatisticsTreeItem(m_StatisticNames, maskLabel, isWIP, imageItem);
364  }
365 
366  imageItem->appendChild(maskItem);
367  lastParent = maskItem;
368  hasMask = true;
369  }
370  else
371  {
372  lastParent = imageItem;
373  }
374  // 3. hierarchy level (optional, only if >1 timestep)
375  if (statistic->GetTimeSteps() > 1)
376  {
377  for (unsigned int i = 0; i < statistic->GetTimeSteps(); i++)
378  {
379  QString timeStepLabel = "[" + QString::number(i) + "] " +
380  QString::number(statistic->GetTimeGeometry()->TimeStepToTimePoint(i)) + " ms";
381  if (statistic->TimeStepExists(i))
382  {
383  auto statisticsItem = new QmitkImageStatisticsTreeItem(
384  statistic->GetStatisticsForTimeStep(i), m_StatisticNames, timeStepLabel, isWIP, lastParent);
385  lastParent->appendChild(statisticsItem);
386  }
387  }
388  hasMultipleTimesteps = true;
389  }
390  }
391  QString headerString = "Images";
392  if (hasMask)
393  {
394  headerString += "/Masks";
395  }
396  if (hasMultipleTimesteps)
397  {
398  headerString += "/Timesteps";
399  }
400  m_HeaderFirstColumn = headerString;
401 }
402 
404 {
405  emit beginResetModel();
406  UpdateByDataStorage();
407  emit endResetModel();
408  emit modelChanged();
409 }
410 
412 {
413  emit beginResetModel();
414  UpdateByDataStorage();
415  emit endResetModel();
416  emit modelChanged();
417 }
418 
420 {
421  emit beginResetModel();
422  UpdateByDataStorage();
423  emit endResetModel();
424  emit modelChanged();
425 }
itk::SmartPointer< T > Lock() const
int columnCount(const QModelIndex &parent=QModelIndex()) const override
void SetDataStorage(mitk::DataStorage *dataStorage)
QmitkImageStatisticsTreeModel(QObject *parent=nullptr)
QmitkImageStatisticsTreeItem * parentItem()
static const std::string STATS_GENERATION_STATUS_PROPERTY_NAME
QmitkImageStatisticsTreeItem * child(int row)
void NodeRemoved(const mitk::DataNode *node) override
void SetMaskNodes(const std::vector< mitk::DataNode::ConstPointer > &nodes)
int rowCount(const QModelIndex &parent=QModelIndex()) const override
QModelIndex parent(const QModelIndex &child) const override
Qt::ItemFlags flags(const QModelIndex &index) const override
void appendChild(QmitkImageStatisticsTreeItem *child)
void SetImageNodes(const std::vector< mitk::DataNode::ConstPointer > &nodes)
#define mitkThrow()
ImageStatisticsContainer::ImageStatisticsObject::StatisticNameVector GetAllStatisticNames(const ImageStatisticsContainer *container)
mitk::Image::Pointer image
void NodeChanged(const mitk::DataNode *node) override
QVariant headerData(int section, Qt::Orientation orientation, int role) const override
static mitk::ImageStatisticsContainer::Pointer GetImageStatistics(const mitk::DataStorage *dataStorage, const mitk::BaseData *image, const mitk::BaseData *mask=nullptr, bool ignoreZeroVoxel=false, unsigned int histogramNBins=100, bool onlyIfUpToDate=true, bool noWIP=true)
Returns the StatisticsContainer for the given image and mask from the storage-.
mitk::Image::Pointer mask
QVariant data(const QModelIndex &index, int role) const override
QModelIndex index(int row, int column, const QModelIndex &parent=QModelIndex()) const override
static QIcon ThemeIcon(const QByteArray &originalSVG)
void NodeAdded(const mitk::DataNode *node) override
Class for nodes of the DataTree.
Definition: mitkDataNode.h:57
mitk::WeakPointer< mitk::DataStorage > m_DataStorage