diff --git a/Modules/Classification/CLActiveLearning/include/mitkPredictionUncertaintyFilter.h b/Modules/Classification/CLActiveLearning/include/mitkPredictionUncertaintyFilter.h index 4e153aaaee..376da26e78 100644 --- a/Modules/Classification/CLActiveLearning/include/mitkPredictionUncertaintyFilter.h +++ b/Modules/Classification/CLActiveLearning/include/mitkPredictionUncertaintyFilter.h @@ -1,90 +1,90 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef mitkPredictionUncertaintyFilter_h #define mitkPredictionUncertaintyFilter_h #include #include // ITK #include #include #include #include #include namespace mitk { /** \class PredictionUncertaintyFilter * \brief Base class for filters that calculate an uncertainty measure on a vector image * * The input image should be a vector image. For each vector, the entries represent class * probabilities. From these an uncertainty is calculated, so that one vector of probabilities * will result in a scalar uncertainty. * Derivative classes will need to implement CalculateUncertainty. */ template class MITKCLACTIVELEARNING_EXPORT PredictionUncertaintyFilter : public itk::ImageToImageFilter { public: typedef itk::ImageToImageFilter SuperClassName; mitkClassMacroItkParent(PredictionUncertaintyFilter, SuperClassName) - itkNewMacro(Self) +// itkNewMacro(Self) typedef typename InputImageType::PixelType InputImagePixelType; typedef typename OutputImageType::PixelType OutputImagePixelType; typedef typename OutputImageType::RegionType OutputImageRegionType; protected: PredictionUncertaintyFilter(){} ~PredictionUncertaintyFilter(){} virtual OutputImagePixelType CalculateUncertainty(const InputImagePixelType& inputVector) = 0; void GenerateData() override // virtual void ThreadedGenerateData(const OutputImageRegionType& outputRegionForThread, itk::ThreadIdType threadId) override { auto input = this->GetInput(); // const auto output = this->GetOutput(); // itk::ImageRegionConstIterator inputIt(input, outputRegionForThread); // itk::ImageRegionIterator outputIt(output, outputRegionForThread); itk::ImageRegionConstIterator inputIt(input, input->GetLargestPossibleRegion()); itk::ImageRegionIterator outputIt(output, output->GetLargestPossibleRegion()); while (!outputIt.IsAtEnd()) { outputIt.Set(CalculateUncertainty(inputIt.Get())); ++inputIt; ++outputIt; } } private: // ITK examples do this... PredictionUncertaintyFilter(const Self&); void operator=(const Self&); }; } #endif diff --git a/Plugins/org.mitk.gui.qt.activelearning/CMakeLists.txt b/Plugins/org.mitk.gui.qt.activelearning/CMakeLists.txt index 4bb6cb134c..02910e6413 100644 --- a/Plugins/org.mitk.gui.qt.activelearning/CMakeLists.txt +++ b/Plugins/org.mitk.gui.qt.activelearning/CMakeLists.txt @@ -1,7 +1,7 @@ project(org_mitk_gui_qt_activelearning) mitk_create_plugin( EXPORT_DIRECTIVE ACTIVELEARNING_EXPORT EXPORTED_INCLUDE_SUFFIXES src - MODULE_DEPENDS MitkQtWidgetsExt MitkSegmentationUI MitkCLVigraRandomForest MitkCLActiveLearning + MODULE_DEPENDS MitkQtWidgetsExt MitkSegmentationUI MitkCLActiveLearning ) diff --git a/Plugins/org.mitk.gui.qt.activelearning/src/internal/QmitkActiveLearning.cpp b/Plugins/org.mitk.gui.qt.activelearning/src/internal/QmitkActiveLearning.cpp index 865fbd2002..83bb1f0680 100644 --- a/Plugins/org.mitk.gui.qt.activelearning/src/internal/QmitkActiveLearning.cpp +++ b/Plugins/org.mitk.gui.qt.activelearning/src/internal/QmitkActiveLearning.cpp @@ -1,479 +1,484 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ // Blueberry #include #include // Qt #include #include // Qmitk #include "QmitkActiveLearning.h" #include // MITK #include #include #include #include #include #include // Returns true if list has at least one entry and all entries are valid mitk::Images, otherwise false static bool SelectionAllImages(const QList& nodes) { if (nodes.empty()) { return false; } for (const auto& node : nodes) { if(!(node.IsNotNull() && dynamic_cast(node->GetData()) != nullptr)) return false; } return true; } // QColor to mitk::Color static mitk::Color QColorToMitkColor(const QColor& qcolor) { mitk::Color color; color.SetRed((float)qcolor.red() / 255); color.SetGreen((float)qcolor.green() / 255); color.SetBlue((float)qcolor.blue() / 255); return color; } +// For debugging static void PrintAllLabels(mitk::LabelSetImage* image) { for (auto it=image->GetActiveLabelSet()->IteratorBegin(); it!=image->GetActiveLabelSet()->IteratorConstEnd(); ++it) { MITK_INFO << "Key: " << it->first << " - Name: " << it->second->GetName() << " - Value: " << it->second->GetValue() << " - Color: " << it->second->GetColor(); } } // Make values of labels a consistent range static void FillLabelValues(mitk::LabelSetImage* image) { int value(0); for (auto it=image->GetActiveLabelSet()->IteratorBegin(); it!=image->GetActiveLabelSet()->IteratorConstEnd(); ++it) { it->second->SetValue(value); value++; } image->GetActiveLabelSet()->SetActiveLabel(0); } /* ================================================================== * PUBLIC SLOTS * =============================================================== */ void ActiveLearning::Initialize() { + // HANDLE WHAT HAPPENS IF INITIALIZED BEFORE, MAYBE RESET EVERYTHING? + // MAYBE ALSO DISPLAY WARNING + auto nodes = this->GetDataManagerSelection(); if (!SelectionAllImages(nodes)) return; // Set names again QString nameList = QString::fromStdString(nodes[0]->GetName()); if (nodes.length() >= 2) { for (int i=1; i"); nameList += QString::fromStdString(nodes[i]->GetName()); } } m_Controls.m_InitializeLabel->setText(nameList); // We're now active m_Active = true; // Initialize segmentation image with first selected image m_Segmentation = mitk::LabelSetImage::New(); try { auto referenceImage = dynamic_cast(nodes[0]->GetData()); m_Segmentation->Initialize(referenceImage); } catch (mitk::Exception& e) { MITK_ERROR << "Exception caught: " << e.GetDescription(); QMessageBox::information(m_Parent, "Error", "Could not initialize segmentation image"); return; } - mitk::DataNode::Pointer segmentationNode = mitk::DataNode::New(); + auto segmentationNode = mitk::DataNode::New(); segmentationNode->SetData(m_Segmentation); segmentationNode->SetName("Labels"); segmentationNode->SetColor(0., 0., 0.); + segmentationNode->SetBoolProperty("helper object", true); m_Segmentation->GetExteriorLabel()->SetProperty("name.parent", mitk::StringProperty::New(nodes[0]->GetName().c_str())); m_Segmentation->GetExteriorLabel()->SetProperty("name.image", mitk::StringProperty::New("Labels")); if (nodes.contains(segmentationNode)) { MITK_DEBUG << "Segmentation node shouldn't be selected"; return; } if (this->GetDataStorage()->Exists(segmentationNode)) { this->GetDataStorage()->Remove(segmentationNode); } this->GetDataStorage()->Add(segmentationNode, nodes[0]); // CALCULATE FEATURES HERE // Automatically add first label OnAddLabelPushButtonClicked(); // Interactor auto activeLearningLib = us::ModuleRegistry::GetModule("mitkCLActiveLearning"); m_Interactor = mitk::ActiveLearningInteractor::New(); m_Interactor->LoadStateMachine("Paint.xml", activeLearningLib); m_Interactor->SetEventConfig("PaintConfig.xml", activeLearningLib); m_Interactor->SetDataNode(segmentationNode); m_Controls.m_LabelControlsFrame->setVisible(true); } /* ================================================================== * PUBLIC * =============================================================== */ ActiveLearning::ActiveLearning() : m_Parent(nullptr), m_Segmentation(nullptr), m_Active(false), m_PaintingPixelValue(0), m_Painted(false) { } ActiveLearning::~ActiveLearning() { } void ActiveLearning::CreateQtPartControl( QWidget *parent ) { m_Controls.setupUi(parent); m_Parent = parent; // Label model m_LabelListModel = new QStandardItemModel(0, 3, this); m_Controls.m_LabelTableView->setModel(m_LabelListModel); m_Controls.m_LabelTableView->horizontalHeader()->setDefaultSectionSize(20); m_Controls.m_LabelTableView->verticalHeader()->setDefaultSectionSize(20); NotEditableDelegate* itemDelegate = new NotEditableDelegate(parent); m_Controls.m_LabelTableView->setItemDelegateForColumn(1, itemDelegate); connect(m_Controls.m_LabelTableView, SIGNAL(doubleClicked(QModelIndex)), this, SLOT(OnColorIconDoubleClicked(QModelIndex))); connect(m_Controls.m_LabelTableView->selectionModel(), SIGNAL(selectionChanged(QItemSelection, QItemSelection)), this, SLOT(OnLabelListSelectionChanged(QItemSelection, QItemSelection))); connect(m_LabelListModel, SIGNAL(dataChanged(QModelIndex, QModelIndex)), this, SLOT(OnLabelNameChanged(QModelIndex, QModelIndex))); // Buttons connect(m_Controls.m_InitializePushButton, SIGNAL(clicked()), this, SLOT(Initialize())); connect(m_Controls.m_AddLabelPushButton, SIGNAL(clicked()), this, SLOT(OnAddLabelPushButtonClicked())); connect(m_Controls.m_RemoveLabelPushButton, SIGNAL(clicked()), this, SLOT(OnRemoveLabelPushButtonClicked())); connect(m_Controls.m_PaintToolButton, SIGNAL(clicked()), this, SLOT(OnPaintToolButtonClicked())); connect(m_Controls.m_EraseToolButton, SIGNAL(clicked()), this, SLOT(OnEraseToolButtonClicked())); // Set start configuration m_Controls.m_LabelControlsFrame->setVisible(false); SetInitializeReady(false); } void ActiveLearning::ResetLabels() { // Remove all labels but the first for (auto it=m_Segmentation->GetActiveLabelSet()->IteratorBegin(); it!=m_Segmentation->GetActiveLabelSet()->IteratorConstEnd(); it++) { if (it->first != 0) { m_Segmentation->GetActiveLabelSet()->RemoveLabel(it->first); } } // Fill with labels from list for (int i=0; irowCount(); i++) { QString name = m_LabelListModel->item(i, 2)->text(); m_LabelListModel->item(i, 1)->setText(QString::number(i + 1)); QColor color = m_LabelListModel->item(i)->background().color(); m_Segmentation->GetActiveLabelSet()->AddLabel(name.toStdString(), QColorToMitkColor(color)); } } const std::string ActiveLearning::VIEW_ID = "org.mitk.views.activelearning"; /* ================================================================== * PROTECTED SLOTS * =============================================================== */ void ActiveLearning::OnAddLabelPushButtonClicked() { QString labelName = QString("Label ") + QString::number(m_Segmentation->GetActiveLabelSet()->ReverseIteratorBegin()->first + 1); QColor labelColor = Qt::GlobalColor(m_LabelListModel->rowCount() % 12 + 7); // We only want Qt default colors 7 to 18 // Create icon QStandardItem* colorSquare = new QStandardItem; colorSquare->setBackground(labelColor); colorSquare->setEditable(false); QPixmap colorPixmap(20, 20); colorPixmap.fill(labelColor); colorSquare->setIcon(QIcon(colorPixmap)); // Key is the highest existing key + 1 int value = (int)m_Segmentation->GetActiveLabelSet()->ReverseIteratorBegin()->first + 1; QStandardItem* valueItem = new QStandardItem(); valueItem->setText(QString::number(value)); // Create label item QStandardItem* label = new QStandardItem(labelName); // Add to image m_Segmentation->GetActiveLabelSet()->AddLabel(labelName.toStdString(), QColorToMitkColor(labelColor)); PrintAllLabels(m_Segmentation); // Make list and insert QList list; list.append(colorSquare); list.append(valueItem); list.append(label); m_LabelListModel->appendRow(list); // If this is the first label, we activate the paint button if (m_LabelListModel->rowCount() == 1) { OnPaintToolButtonClicked(); m_Controls.m_PaintToolButton->setChecked(true); } } void ActiveLearning::OnRemoveLabelPushButtonClicked() { QItemSelectionModel* selection = m_Controls.m_LabelTableView->selectionModel(); if (selection->hasSelection()) { unsigned int removeIndex = selection->selectedRows().first().row(); QString removeMessage = QString("Remove label '") + m_LabelListModel->item(removeIndex, 2)->text() + QString("'?"); QMessageBox::StandardButton removeReply; removeReply = QMessageBox::question(m_Parent, "Remove Label", removeMessage, QMessageBox::Yes | QMessageBox::No); if (removeReply == QMessageBox::Yes) { // if there are no annotations, reset labels if (m_Painted) { std::vector labels; labels.push_back(m_LabelListModel->item(removeIndex, 1)->text().toInt()); m_Segmentation->RemoveLabels(labels); m_LabelListModel->removeRow(removeIndex); } else { m_LabelListModel->removeRow(removeIndex); ResetLabels(); } PrintAllLabels(m_Segmentation); } } } void ActiveLearning::OnPaintToolButtonClicked() { } void ActiveLearning::OnEraseToolButtonClicked() { } void ActiveLearning::OnActivateGuidancePushButtonToggled(bool toggled) { } void ActiveLearning::OnSaveSegmentationPushButtonClicked() { } void ActiveLearning::OnSavePredictionsPushButtonClicked() { } void ActiveLearning::OnExportSegmentationPushButtonClicked() { } void ActiveLearning::OnExportPredictionsPushButtonClicked() { } void ActiveLearning::OnColorIconDoubleClicked(const QModelIndex& index) { // Check if click is really from color icon if (index.column() != 0) { return; } else { // Color change dialog QColor setColor = QColorDialog::getColor(m_LabelListModel->itemFromIndex(index)->background().color(), m_Parent, "Select Label Color"); if (setColor.isValid()) { m_LabelListModel->itemFromIndex(index)->setBackground(setColor); QPixmap colorPixmap(20, 20); colorPixmap.fill(setColor); m_LabelListModel->itemFromIndex(index)->setIcon(QIcon(colorPixmap)); // Set color on label m_Segmentation->GetActiveLabelSet()->GetLabel(m_LabelListModel->item(index.row(), 1)->text().toInt())->SetColor(QColorToMitkColor(setColor)); PrintAllLabels(m_Segmentation); } } } void ActiveLearning::OnLabelListSelectionChanged(const QItemSelection& selected, const QItemSelection& /*deselected*/) { if (selected.empty()) return; // This assumes that only one item can be selected (single selection table view) try { m_Segmentation->GetActiveLabelSet()->SetActiveLabel(m_LabelListModel->item(selected.indexes()[0].row(), 1)->text().toInt()); } catch (...) { m_Segmentation->GetActiveLabelSet()->SetActiveLabel(0); } } void ActiveLearning::OnLabelNameChanged(const QModelIndex& topLeft, const QModelIndex& /*bottomRight*/) { auto item = m_LabelListModel->itemFromIndex(topLeft); if (item->column() != 2) return; m_Segmentation->GetActiveLabelSet()->GetLabel(m_LabelListModel->item(item->row(), 1)->text().toInt())->SetName(item->text().toStdString()); } /* ================================================================== * PROTECTED * =============================================================== */ void ActiveLearning::OnSelectionChanged(berry::IWorkbenchPart::Pointer /*source*/, const QList& nodes) { if (!SelectionAllImages(nodes)) { SetInitializeReady(false); return; } if (nodes.length() >= 2) { // First selection is the reference (could be any other) mitk::Image::Pointer referenceImage = dynamic_cast(nodes[0]->GetData()); mitk::BaseGeometry* referenceGeometry = referenceImage->GetTimeGeometry()->GetGeometryForTimeStep(0); // This needs to be current timestep for (int i=1; i(nodes[i]->GetData()); mitk::BaseGeometry* currentGeometry = currentImage->GetTimeGeometry()->GetGeometryForTimeStep(0); // This needs to be current timestep if (!mitk::Equal(*currentGeometry, *referenceGeometry, mitk::eps, true)) { SetInitializeReady(false); return; } } } // All nodes have the same geometry, allow init SetInitializeReady(true); } void ActiveLearning::SetFocus() { } /* ================================================================== * PRIVATE * =============================================================== */ void ActiveLearning::SetInitializeReady(bool ready) { if (ready) { // get selection, check again just to be sure auto nodes = this->GetDataManagerSelection(); if (!SelectionAllImages(nodes)) return; m_Controls.m_InitializePushButton->setEnabled(true); if (!m_Active) { QString nameList = QString::fromStdString(nodes[0]->GetName()); if (nodes.length() >= 2) { for (int i=1; i"); nameList += QString::fromStdString(nodes[i]->GetName()); } } m_Controls.m_InitializeLabel->setText(nameList); } } else { m_Controls.m_InitializePushButton->setDisabled(true); if (!m_Active) { m_Controls.m_InitializeLabel->setText("Selected images must have matching geometries"); } } }