CustusX  22.04-rc3
An IGT application
cxImportDataTypeWidget.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) SINTEF Department of Medical Technology.
5 All rights reserved.
6 
7 CustusX is released under a BSD 3-Clause license.
8 
9 See Lisence.txt (https://github.com/SINTEFMedtek/CustusX/blob/master/License.txt) for details.
10 =========================================================================*/
11 
12 #include "cxImportDataTypeWidget.h"
13 #include <QVBoxLayout>
14 #include <QHBoxLayout>
15 #include <QLabel>
16 #include <QTableWidget>
17 #include <QHeaderView>
18 #include <QApplication>
19 #include <QDesktopWidget>
20 #include <QCheckBox>
21 #include <QGroupBox>
22 #include <QFileInfo>
23 #include "cxOptionsWidget.h"
25 #include "cxFileManagerService.h"
26 #include "cxLogger.h"
27 #include "cxImage.h"
28 #include "cxPointMetric.h"
30 #include "cxVolumeHelpers.h"
31 #include "cxImageTF3D.h"
32 #include "cxImageLUT2D.h"
33 #include "cxViewService.h"
34 #include "cxImportWidget.h"
35 #include "cxCustomMetaImage.h"
36 
37 namespace cx
38 {
39 
40 ImportDataTypeWidget::ImportDataTypeWidget(ImportWidget *parent, VisServicesPtr services, std::vector<DataPtr> data, std::vector<DataPtr> &parentCandidates, QString filename) :
41  BaseWidget(parent, "ImportDataTypeWidget", "Import"),
42  mImportWidget(parent),
43  mServices(services),
44  mData(data),
45  mFilename(filename),
46  mParentCandidates(parentCandidates),
47  mSelectedIndexInTable(0),
48  mImageTypeCombo(NULL),
49  mModalityCombo(NULL)
50 
51 {
52  mAnatomicalCoordinateSystems = new QComboBox();
53  mAnatomicalCoordinateSystems->addItem("LPS"); //CX
54  mAnatomicalCoordinateSystems->addItem("RAS"); //NIfTI
55 
56  if(isInputFileInNiftiFormat())
57  mAnatomicalCoordinateSystems->setCurrentText("RAS");
58 
59  mShouldImportParentTransform = new QComboBox();
60  mShouldImportParentTransform->addItem("No");
61  mShouldImportParentTransform->addItem("Yes");
62 
63  mParentCandidatesCB = new QComboBox();
64 
65  mShouldConvertDataToUnsigned = new QCheckBox();
66  mShouldConvertDataToUnsigned->setCheckState(Qt::Unchecked);
67 
68  mTableWidget = new QTableWidget();
69  mTableWidget->setRowCount(0);
70  mTableWidget->setColumnCount(4);
71  mTableHeader<<"#"<<"Type"<<"Name"<<"Space";
72  mTableWidget->setHorizontalHeaderLabels(mTableHeader);
73  mTableWidget->horizontalHeader()->setStretchLastSection(true);
74  mTableWidget->horizontalHeader()->setSectionResizeMode(QHeaderView::ResizeToContents);
75  mTableWidget->verticalHeader()->setVisible(false);
76  mTableWidget->setEditTriggers(QAbstractItemView::NoEditTriggers);
77  mTableWidget->setSelectionBehavior(QAbstractItemView::SelectRows);
78  mTableWidget->setSelectionMode(QAbstractItemView::SingleSelection);
79  mTableWidget->setShowGrid(false);
80  mTableWidget->setStyleSheet("QTableView {selection-background-color: #ACCEF7;}");
81  mTableWidget->setGeometry(QApplication::desktop()->screenGeometry());
82 
83  QString type, name;
84  for(unsigned i=0; i<mData.size(); ++i)
85  {
86  if(!mData[i])
87  {
88  CX_LOG_WARNING() << "ImportDataTypeWidget::ImportDataTypeWidget: No data";
89  continue;
90  }
91  type = mData[i]->getType();
92  name = mData[i]->getName();
93  QString space = mData[i]->getSpace();
94  //create point metric groups
95  if(type == PointMetric::getTypeName())
96  {
97  space = boost::dynamic_pointer_cast<PointMetric>(mData[i])->getSpace().toString();
98  (mPointMetricGroups[space]).push_back(mData[i]);
99  }
100  //add image or mesh directly to the table
101  else
102  {
103  int newRowIndex = mTableWidget->rowCount();
104  mTableWidget->setRowCount(newRowIndex+1);
105  mTableWidget->setItem(newRowIndex, 0, new QTableWidgetItem("1"));
106  mTableWidget->setItem(newRowIndex, 1, new QTableWidgetItem(name));
107  mTableWidget->setItem(newRowIndex, 2, new QTableWidgetItem(type));
108  mTableWidget->setItem(newRowIndex, 3, new QTableWidgetItem(space));
109  }
110  this->createDataSpecificGui(mData[i]);
111  }
112  this->addPointMetricGroupsToTable();
113 
114  //gui
115  QVBoxLayout *topLayout = new QVBoxLayout(this);
116  this->setLayout(topLayout);
117 
118  QFileInfo fileInfo(filename);
119  QString title = fileInfo.fileName();
120 
121  QGroupBox *groupBox = new QGroupBox(title);
122 
123  QGridLayout *gridLayout = new QGridLayout();
124  gridLayout->addWidget(new QLabel("For all data in the file: "), 0, 0, 1, 2);
125  gridLayout->addWidget(new QLabel("Specify anatomical coordinate system"), 1, 0);
126  gridLayout->addWidget(mAnatomicalCoordinateSystems, 1, 1);
127  gridLayout->addWidget(new QLabel("Import parents transform?"), 2, 0);
128  gridLayout->addWidget(mShouldImportParentTransform, 2, 1);
129  gridLayout->addWidget(new QLabel("Set parent"), 3, 0);
130  gridLayout->addWidget(mParentCandidatesCB, 3, 1);
131  gridLayout->addWidget(new QLabel("Convert data to unsigned?"), 4, 0);
132  gridLayout->addWidget(mShouldConvertDataToUnsigned, 4,1);
133  gridLayout->addWidget(mTableWidget, 5, 0, 1, 2);
134  if(mModalityCombo)
135  gridLayout->addWidget(mModalityCombo);
136  if(mImageTypeCombo)
137  gridLayout->addWidget(mImageTypeCombo);
138 
139  groupBox->setLayout(gridLayout);
140  topLayout->addWidget(groupBox);
141 
144 }
145 
147 {
149  disconnect(mImportWidget, &ImportWidget::parentCandidatesUpdated, this, &ImportDataTypeWidget::update);
150 }
151 
152 
153 void ImportDataTypeWidget::createDataSpecificGui(DataPtr data)
154 {
155  ImagePtr image = boost::dynamic_pointer_cast<Image>(data);
156 
157  if(image)
158  {
159  mModalityAdapter = StringPropertyDataModality::New(mServices->patient());
160  mModalityCombo = new LabeledComboBoxWidget(this, mModalityAdapter);
161  mModalityAdapter->setData(image);
162 
163  mImageTypeAdapter = StringPropertyImageType::New(mServices->patient());
164  mImageTypeCombo = new LabeledComboBoxWidget(this, mImageTypeAdapter);
165  mImageTypeAdapter->setData(image);
166 
167  if(isInputFileInNiftiFormat()) // NIfTI files are usually MR. Set this as the default
168  {
169  mModalityAdapter->setValue(enum2string(imMR));
170  updateImageType();
171  }
172  }
173 }
174 
175 void ImportDataTypeWidget::updateImageType()
176 {
177  // Test code: Trying to use convertToImageSubType on file name to find correct subtype.
178  IMAGE_SUBTYPE imageSubType = convertToImageSubType(mFilename);
179  mImageTypeAdapter->setValue(enum2string(imageSubType));
180 }
181 
182 std::map<QString, QString> ImportDataTypeWidget::getParentCandidateList()
183 {
184  std::map<QString, QString> parentCandidates;
185  for(unsigned i=0; i<mParentCandidates.size(); ++i)
186  {
187  parentCandidates[mParentCandidates[i]->getName()] = mParentCandidates[i]->getUid();
188  }
189 
190  return parentCandidates;
191 }
192 
193 void ImportDataTypeWidget::updateSpaceComboBox(QComboBox *box, QString pointMetricGroupId)
194 {
195  box->clear();
196  std::map<QString, QString> parentCandidates = this->getParentCandidateList();
197  std::map<QString, QString>::iterator it;
198  for(it= parentCandidates.begin(); it != parentCandidates.end(); ++it)
199  {
200  QVariant id(it->second);
201  box->addItem(it->first, id);
202  }
203  std::vector<DataPtr> pointMetricGroup = mPointMetricGroups[pointMetricGroupId];
204  QString currentSpace = pointMetricGroup[0]->getSpace();
205  box->setCurrentText(currentSpace);
206 }
207 
208 void ImportDataTypeWidget::updateParentCandidatesComboBox()
209 {
210  //remember selection
211  QString selectedParentId = (mParentCandidatesCB->itemData(mParentCandidatesCB->currentIndex()).toString());
212 
213  mParentCandidatesCB->clear();
214  std::map<QString, QString> parentCandidates = this->getParentCandidateList();
215  std::map<QString, QString>::iterator it;
216  QVariant emptyId("");
217  mParentCandidatesCB->addItem("", emptyId);
218  int selectedIndex = 0;
219  for(it= parentCandidates.begin(); it != parentCandidates.end(); ++it)
220  {
221  QString idString = it->second;
222  QVariant id(idString);
223  mParentCandidatesCB->addItem(it->first, id);
224  if(selectedParentId.compare(idString, Qt::CaseInsensitive) == 0)
225  selectedIndex = mParentCandidatesCB->count()-1;
226  }
227 
228  if(selectedIndex != 0)
229  mParentCandidatesCB->setCurrentIndex(selectedIndex);
230 
231  //TODO parent guess:
232 
233  QString parentGuess = this->getInitialGuessForParentFrame();
234 // CX_LOG_DEBUG() << "ParentGuess: " << parentGuess;
235  mParentCandidatesCB->setCurrentText(parentGuess);
236 
237 
238 }
239 
240 void ImportDataTypeWidget::importAllData()
241 {
242  for(unsigned i=0; i<mData.size(); ++i)
243  {
244  if(mData[i])
245  {
246  QString parentId = (mParentCandidatesCB->itemData(mParentCandidatesCB->currentIndex()).toString());
247  mData[i]->get_rMd_History()->setParentSpace(parentId);
248 
249  mServices->patient()->insertData(mData[i]);
250  mServices->view()->autoShowData(mData[i]);
251  }
252  }
253 }
254 
255 void ImportDataTypeWidget::applyParentTransformImport()
256 {
257  CX_LOG_DEBUG() << "applyParentTransformImport()";
258 
259  QString parentId = (mParentCandidatesCB->itemData(mParentCandidatesCB->currentIndex()).toString());
260  DataPtr parent = mServices->patient()->getData(parentId);
261 
262  if(!parent)
263  {
264  CX_LOG_ERROR() << "Could not find parent data with uid: " << parentId;
265  return;
266  }
267 
268  std::vector<DataPtr>::iterator it = mData.begin();
269  for(;it!=mData.end(); ++it)
270  {
271  DataPtr data = (*it);
272  data->get_rMd_History()->setRegistration(parent->get_rMd());
273  report("Assigned rMd from data [" + parent->getName() + "] to data [" + data->getName() + "]");
274  }
275 
276  /*
277  if (!mTransformFromParentFrameCheckBox->isChecked())
278  return;
279  if(!mData)
280  return;
281  DataPtr parent = mPatientModelService->getData(mData->getParentSpace());
282  if (!parent)
283  return;
284  mData->get_rMd_History()->setRegistration(parent->get_rMd());
285  report("Assigned rMd from data [" + parent->getName() + "] to data [" + mData->getName() + "]");
286  */
287 }
288 
289 void ImportDataTypeWidget::applyConversionLPS()
290 {
291  CX_LOG_DEBUG() << "applyConversionLPS()";
292 
293  std::vector<DataPtr>::iterator it = mData.begin();
294  for(;it!=mData.end(); ++it)
295  {
296  DataPtr data = (*it);
297  Transform3D sMd = data->get_rMd();
299  Transform3D rMd = sMr.inv() * sMd;
300  data->get_rMd_History()->setRegistration(rMd);
301  report("Nifti import: Converted data " + data->getName() + " from LPS to RAS coordinates.");
302  }
303 
304  /*
305  if (!mNiftiFormatCheckBox->isChecked())
306  return;
307  if(!mData)
308  return;
309  Transform3D sMd = mData->get_rMd();
310  Transform3D sMr = createTransformFromReferenceToExternal(pcsRAS);
311  // rMd = createTransformRotateZ(M_PI) * rMd;
312  Transform3D rMd = sMr.inv() * sMd;
313  mData->get_rMd_History()->setRegistration(rMd);
314  report("Nifti import: Converted data " + mData->getName() + " from LPS to RAS coordinates.");
315  */
316 
317 }
318 
319 void ImportDataTypeWidget::applyConversionToUnsigned()
320 {
321  CX_LOG_DEBUG() << "applyConversionToUnsigned()";
322 
323  std::vector<DataPtr>::iterator it = mData.begin();
324  for(;it!=mData.end(); ++it)
325  {
326  DataPtr data = (*it);
327 
328  ImagePtr image = boost::dynamic_pointer_cast<Image>(data);
329  if (!image)
330  return;
331 
332  ImagePtr converted = convertImageToUnsigned(mServices->patient(), image);
333 
334  image->setVtkImageData(converted->getBaseVtkImageData());
335 
336  ImageTF3DPtr TF3D = converted->getTransferFunctions3D()->createCopy();
337  ImageLUT2DPtr LUT2D = converted->getLookupTable2D()->createCopy();
338  image->setLookupTable2D(LUT2D);
339  image->setTransferFunctions3D(TF3D);
340  //mServices->patient()->insertData(image);
341 
342  DataPtr convertedData = boost::dynamic_pointer_cast<Data>(image);
343  (*it) = convertedData;
344  }
345 
346  /*
347  if (!mConvertToUnsignedCheckBox->isChecked())
348  return;
349 
350  ImagePtr image = boost::dynamic_pointer_cast<Image>(mData);
351  if (!image)
352  return;
353 
354  ImagePtr converted = convertImageToUnsigned(mPatientModelService, image);
355 
356  image->setVtkImageData(converted->getBaseVtkImageData());
357 
358  ImageTF3DPtr TF3D = converted->getTransferFunctions3D()->createCopy();
359  ImageLUT2DPtr LUT2D = converted->getLookupTable2D()->createCopy();
360  image->setLookupTable2D(LUT2D);
361  image->setTransferFunctions3D(TF3D);
362  mPatientModelService->insertData(image);
363  */
364 }
365 
367 {
368  this->updateParentCandidatesComboBox();
369 
370  std::map<QString, QComboBox *>::iterator it;
371  for(it=mSpaceCBs.begin(); it != mSpaceCBs.end(); ++it)
372  {
373  QString id = it->first;
374  QComboBox *box = it->second;
375  this->updateSpaceComboBox(box,id);
376  }
377 }
378 
380 {
381  if(mShouldConvertDataToUnsigned->isChecked())
382  this->applyConversionToUnsigned();
383 
384  this->importAllData();
385 
386  if(mShouldImportParentTransform->currentText() == "Yes")
387  this->applyParentTransformImport();
388  if(mAnatomicalCoordinateSystems->currentText() != "LPS")
389  this->applyConversionLPS();
390 }
391 
392 void ImportDataTypeWidget::showEvent(QShowEvent *event)
393 {
394  BaseWidget::showEvent(event);
395  this->update();
396 }
397 
398 void ImportDataTypeWidget::pointMetricGroupSpaceChanged(int index)
399 {
400  QComboBox *box = qobject_cast<QComboBox*>(QObject::sender());
401  QString newSpace = box->currentData().toString();
402 
403  QString pointMetricsGroupId;
404  std::map<QString, QComboBox *>::iterator it;
405  for(it = mSpaceCBs.begin(); it != mSpaceCBs.end(); ++it)
406  {
407  if(it->second == box)
408  pointMetricsGroupId = it->first;
409  }
410  std::vector<DataPtr> pointMetricGroup = mPointMetricGroups[pointMetricsGroupId];
411  for(unsigned i=0; i<pointMetricGroup.size(); ++i)
412  {
413  CoordinateSystem cs(csDATA, newSpace);
414  boost::dynamic_pointer_cast<PointMetric>(pointMetricGroup[i])->setSpace(cs);
415  }
416 }
417 
418 QString ImportDataTypeWidget::getInitialGuessForParentFrame()
419 {
420  int candidateScore = 0;
421  QString bestCandidate;
422 
423  for(unsigned i=0; i < mData.size(); ++i)
424  {
425  DataPtr data = mData[i];
426  std::map<QString, QString> parentCandidates = this->getParentCandidateList();
427 
428  std::map<QString, QString>::iterator iter;
429  for(iter= parentCandidates.begin(); iter != parentCandidates.end(); ++iter)
430  {
431  int similarity = similatiryMeasure(data->getUid(), iter->second);
432  if(similarity > candidateScore && !isSegmentation(iter->second))
433  {
434  candidateScore = similarity;
435  bestCandidate = iter->first;
436  }
437  }
438  }
439 
440  return bestCandidate;
441 }
442 
443 int ImportDataTypeWidget::similatiryMeasure(QString current, QString candidate)
444 {
445  QStringList currentList = splitStringIntoSeparateParts(current);
446  QStringList candidateList = splitStringIntoSeparateParts(candidate);
447  return countEqualListElements(currentList, candidateList);
448 }
449 
450 QStringList ImportDataTypeWidget::splitStringIntoSeparateParts(QString current)
451 {
452  current = removeParenthesis(current);
453 
454  QStringList list = current.split(".", QString::SkipEmptyParts);
455  QStringList list2;
456  for (int i = 0; i < list.size(); ++i)
457  {
458  list2 << list[i].split("_", QString::SkipEmptyParts);
459  }
460  QStringList currentParts;
461  for (int i = 0; i < list2.size(); ++i)
462  {
463  currentParts << list2[i].split("-", QString::SkipEmptyParts);
464  }
465  return currentParts;
466 }
467 
468 QString ImportDataTypeWidget::removeParenthesis(QString current)
469 {
470  int startParenthesis;
471  do
472  {
473  startParenthesis = current.indexOf("{");
474  int endParenthesis = current.indexOf("}");
475  current.replace(startParenthesis, endParenthesis-startParenthesis+1, "");
476  } while (startParenthesis != -1);
477  return current;
478 }
479 
480 int ImportDataTypeWidget::countEqualListElements(QStringList first, QStringList second)
481 {
482  int retval = 0;
483  int numComparedElements = 0;
484  for (int i = 0; i < first.size(); ++i)
485  {
486  if(excludeElement(first[i]))
487  continue;
488  ++numComparedElements;
489  for (int j = 0; j < second.size(); ++j)
490  {
491  if(first[i].compare(second[j]) == 0)
492  {
493  ++retval;
494  break;//Don't count repeating elements
495  }
496  }
497  }
498 
499  if (retval == numComparedElements)
500  return 0;//Don't match equal list
501  return retval;
502 }
503 
504 bool ImportDataTypeWidget::excludeElement(QString element)
505 {
506  if (isSegmentation(element))
507  return true;
508  return false;
509 }
510 
511 void ImportDataTypeWidget::addPointMetricGroupsToTable()
512 {
513  QString type, name;
514  int groupnr = 0;
515  std::map<QString, std::vector<DataPtr> >::iterator it = mPointMetricGroups.begin();
516  for(; it != mPointMetricGroups.end(); ++it)
517  {
518  groupnr +=1;
519 
520  QString space = it->first;
521  std::vector<DataPtr> datas = it->second;
522  DataPtr data = datas[0];
523  if(datas.empty() || !data)
524  {
525  continue;
526  }
527 
528  QComboBox *spaceCB = new QComboBox();
529  mSpaceCBs[space] = spaceCB;
530  connect(spaceCB, SIGNAL(currentIndexChanged(int)), this, SLOT(pointMetricGroupSpaceChanged(int)));
531  this->updateSpaceComboBox(spaceCB, space);
532 
533  type = data->getType();
534  name = "Point metric group "+QString::number(groupnr);
535 
536  int newRowIndex = mTableWidget->rowCount();
537  mTableWidget->setRowCount(newRowIndex+1);
538  mTableWidget->setItem(newRowIndex, 0, new QTableWidgetItem(QString::number(datas.size())));
539  mTableWidget->setItem(newRowIndex, 1, new QTableWidgetItem(name));
540  mTableWidget->setItem(newRowIndex, 2, new QTableWidgetItem(type));
541  mTableWidget->setCellWidget(newRowIndex, 3, spaceCB);
542  }
543 }
544 
545 bool ImportDataTypeWidget::isInputFileInNiftiFormat()
546 {
547  if(mFilename.endsWith(".nii", Qt::CaseInsensitive))
548  return true;
549  return false;
550 }
551 
552 bool ImportDataTypeWidget::isSegmentation(QString filename)
553 {
554  if(filename.contains("label", Qt::CaseInsensitive))
555  return true;
556  if(filename.contains("seg", Qt::CaseInsensitive))
557  return true;
558  return false;
559 }
560 
561 }
pcsRAS
Right-Anterior-Superior, used by Slicer3D, ITK-Snap, nifti, MINC.
boost::shared_ptr< class VisServices > VisServicesPtr
Definition: cxMainWindow.h:40
std::string toString(T const &value)
converts any type to a string
Definition: catch.hpp:755
imMR
Transform3D Transform3D
Transform3D is a representation of an affine 3D transform.
cxResource_EXPORT Transform3D createTransformFromReferenceToExternal(PATIENT_COORDINATE_SYSTEM external)
static StringPropertyDataModalityPtr New(PatientModelServicePtr patientModelService)
boost::shared_ptr< class Image > ImagePtr
Definition: cxDicomWidget.h:27
Composite widget for string selection.
csDATA
a datas space (d)
Definition: cxDefinitions.h:90
virtual void showEvent(QShowEvent *event)
ImagePtr convertImageToUnsigned(PatientModelServicePtr dataManager, ImagePtr image, vtkImageDataPtr suggestedConvertedVolume, bool verbose)
boost::shared_ptr< class Data > DataPtr
static StringPropertyImageTypePtr New(PatientModelServicePtr patientModelService)
boost::shared_ptr< class ImageLUT2D > ImageLUT2DPtr
ImportDataTypeWidget(ImportWidget *parent, VisServicesPtr services, std::vector< DataPtr > data, std::vector< DataPtr > &parentCandidates, QString filename)
#define CX_LOG_ERROR
Definition: cxLogger.h:99
A volumetric data set.
Definition: cxImage.h:45
IMAGE_SUBTYPE convertToImageSubType(QString imageTypeSubString)
Identification of a Coordinate system.
Data class that represents a single point.
Definition: cxPointMetric.h:42
void parentCandidatesUpdated()
Interface for QWidget which handles widgets uniformly for the system.
Definition: cxBaseWidget.h:88
#define CX_LOG_DEBUG
Definition: cxLogger.h:95
Superclass for all data objects.
Definition: cxData.h:89
void report(QString msg)
Definition: cxLogger.cpp:69
bool compare(T1 const &lhs, T2 const &rhs)
Definition: catch.hpp:1060
#define CX_LOG_WARNING
Definition: cxLogger.h:98
static QString getTypeName()
Definition: cxPointMetric.h:58
QString enum2string(const ENUM &val)
boost::shared_ptr< class ImageTF3D > ImageTF3DPtr
Namespace for all CustusX production code.