From f694939d3110cbc92a8a48b97249b622bc58a7c1 Mon Sep 17 00:00:00 2001 From: Emmanuel Promayon Date: Wed, 24 Jul 2019 18:13:33 +0200 Subject: [PATCH 01/12] FIXED max value for dimensions depends on current selection The maximum value of resampling now corresponds to the currently selected image Also, 2D image is now working properly --- .../image/imageresampling/ResampleAction.cpp | 51 +++++++++++++++---- .../image/imageresampling/ResampleAction.h | 16 +++--- 2 files changed, 52 insertions(+), 15 deletions(-) diff --git a/sdk/actions/image/imageresampling/ResampleAction.cpp b/sdk/actions/image/imageresampling/ResampleAction.cpp index 3b986b2a..326f89b3 100644 --- a/sdk/actions/image/imageresampling/ResampleAction.cpp +++ b/sdk/actions/image/imageresampling/ResampleAction.cpp @@ -27,6 +27,8 @@ #include #include +#include + // Vtk includes #include #include "vtkImageShiftScale.h" @@ -51,22 +53,25 @@ ResampleAction::ResampleAction(ActionExtension* extension) : Action(extension) { // You can add tags here with the method addTag("tagName"); // Setting the actions parameters - Property* newImageDimensionPropertyX = new Property(tr("New image X dimension"), 256, tr("The new image width (in voxels)."), ""); + Property* newImageDimensionPropertyX = new Property(tr("New Image X Dimension"), 256, tr("The new image width (in voxels)."), ""); newImageDimensionPropertyX->setAttribute("minimum", 1); + newImageDimensionPropertyX->setAttribute("maximum", std::numeric_limits::max()); newImageDimensionPropertyX->setAttribute("singleStep", 1); addParameter(newImageDimensionPropertyX); - Property* newImageDimensionPropertyY = new Property(tr("New image Y dimension"), 256, tr("The new image height (in voxels)."), ""); + Property* newImageDimensionPropertyY = new Property(tr("New Image Y Dimension"), 256, tr("The new image height (in voxels)."), ""); newImageDimensionPropertyY->setAttribute("minimum", 1); + newImageDimensionPropertyY->setAttribute("maximum", std::numeric_limits::max()); newImageDimensionPropertyY->setAttribute("singleStep", 1); addParameter(newImageDimensionPropertyY); - Property* newImageDimensionPropertyZ = new Property(tr("New image Z dimension"), 256, tr("The new image depth (in voxels)."), ""); + Property* newImageDimensionPropertyZ = new Property(tr("New Image Z Dimension"), 256, tr("The new image depth (in voxels)."), ""); newImageDimensionPropertyZ->setAttribute("minimum", 1); + newImageDimensionPropertyZ->setAttribute("maximum", std::numeric_limits::max()); newImageDimensionPropertyZ->setAttribute("singleStep", 1); addParameter(newImageDimensionPropertyZ); - Property* scalarTypeProperty = new Property(tr("New image scalar type"), SAME_AS_INPUT, tr("The new image voxels scalar type"), ""); + Property* scalarTypeProperty = new Property(tr("New Image Scalar Type"), SAME_AS_INPUT, tr("The new image voxels scalar type"), ""); scalarTypeProperty->setEnumTypeName("ScalarType", this); addParameter(scalarTypeProperty); } @@ -76,6 +81,32 @@ ResampleAction::~ResampleAction() { } +// --------------- getWidget ------------------- +QWidget* ResampleAction::getWidget() { + + // set min/max for the dimensions using the last selected target dimensions + int * dims = getTargets().last()->getImageData()->GetDimensions(); + + camitk::Property *dimProperty = getProperty("New Image X Dimension"); + dimProperty->setAttribute("minimum", 1); + dimProperty->setAttribute("maximum", dims[0]); + setProperty("New Image X Dimension", dims[0]); + + dimProperty = getProperty("New Image Y Dimension"); + dimProperty->setAttribute("minimum", 1); + dimProperty->setAttribute("maximum", dims[1]); + setProperty("New Image Y Dimension", dims[1]); + + dimProperty = getProperty("New Image Z Dimension"); + dimProperty->setAttribute("minimum", 1); + dimProperty->setAttribute("maximum", dims[2]); + setProperty("New Image Z Dimension", dims[2]); + dimProperty->setReadOnly(dims[2] == 1); + + // return the default widget + return Action::getWidget(); +} + // --------------- apply ------------------- Action::ApplyStatus ResampleAction::apply() { @@ -87,6 +118,7 @@ Action::ApplyStatus ResampleAction::apply() { return SUCCESS; } +// --------------- process ------------------- void ResampleAction::process(ImageComponent* comp) { QApplication::setOverrideCursor(QCursor(Qt::WaitCursor)); Application::showStatusBarMessage(tr("Compute resample...")); @@ -95,9 +127,9 @@ void ResampleAction::process(ImageComponent* comp) { progressCallback->SetCallback(&Application::vtkProgressFunction); // Get the parameters - double xSize = property("New image X dimension").toDouble(); - double ySize = property("New image Y dimension").toDouble(); - double zSize = property("New image Z dimension").toDouble(); + double xSize = property("New Image X Dimension").toDouble(); + double ySize = property("New Image Y Dimension").toDouble(); + double zSize = property("New Image Z Dimension").toDouble(); // Getting the input image vtkSmartPointer inputImage = comp->getImageData(); @@ -210,7 +242,7 @@ void ResampleAction::process(ImageComponent* comp) { resampleFilter->Update(); // Create the output component - ImageComponent* outputComp = new ImageComponent(resampleFilter->GetOutput(), comp->getName() + nameScalarType + "_" + property("Size X (nb of Voxels)").toInt() + "*" + property("Size Y (nb of Voxels)").toInt() + "*" + property("Size Z (nb of Voxels)").toInt()); + ImageComponent* outputComp = new ImageComponent(resampleFilter->GetOutput(), comp->getName() + nameScalarType + "_" + property("New Image X Dimension").toString() + "x" + property("New Image Y Dimension").toString() + "x" + property("New Image Z Dimension").toString()); // consider frame policy on new image created Action::applyTargetPosition(comp, outputComp); @@ -223,7 +255,8 @@ void ResampleAction::process(ImageComponent* comp) { } +// --------------- getScalarType ------------------- ResampleAction::ScalarType ResampleAction::getScalarType() { - return (ScalarType) property("New image scalar type").toInt(); + return (ScalarType) property("New Image Scalar Type").toInt(); } diff --git a/sdk/actions/image/imageresampling/ResampleAction.h b/sdk/actions/image/imageresampling/ResampleAction.h index 753c0a8b..8a1d6506 100644 --- a/sdk/actions/image/imageresampling/ResampleAction.h +++ b/sdk/actions/image/imageresampling/ResampleAction.h @@ -63,20 +63,24 @@ public: /// Default Destructor virtual ~ResampleAction(); - /// Retrieve the scalar type - ScalarType getScalarType(); - + /// Required to update the resize values to half of the currently selected image size + virtual QWidget* getWidget(); + public slots: /// method called when the action is applied virtual ApplyStatus apply(); - private: - ///Scalar type of the output - ScalarType Scalar_Type; + /// Scalar type of the output + ScalarType scalarType; + /// helper method to simplify the target component processing virtual void process(camitk::ImageComponent*); + + /// Retrieve the scalar type + ScalarType getScalarType(); + }; Q_DECLARE_METATYPE(ResampleAction::ScalarType) -- GitLab From af6a44efbaa6dce988cf390b190c9b5bc69b1fac Mon Sep 17 00:00:00 2001 From: Emmanuel Promayon Date: Wed, 24 Jul 2019 18:16:12 +0200 Subject: [PATCH 02/12] NEW tutorial to demonstrates how to call actions from another one It implement some fixed hardcoded pipeline of actions. To demonstrates this, an image resize action followed by an Otsu filter are applied (while the intermediary result is deleted/never visible) --- .../actionsinaction/ActionsInActionAction.cpp | 145 ++++++++++++++++++ .../actionsinaction/ActionsInActionAction.h | 63 ++++++++ .../ActionsInActionExtension.cpp | 35 +++++ .../ActionsInActionExtension.h | 59 +++++++ .../actions/actionsinaction/CMakeLists.txt | 6 + 5 files changed, 308 insertions(+) create mode 100644 tutorials/actions/actionsinaction/ActionsInActionAction.cpp create mode 100644 tutorials/actions/actionsinaction/ActionsInActionAction.h create mode 100644 tutorials/actions/actionsinaction/ActionsInActionExtension.cpp create mode 100644 tutorials/actions/actionsinaction/ActionsInActionExtension.h create mode 100644 tutorials/actions/actionsinaction/CMakeLists.txt diff --git a/tutorials/actions/actionsinaction/ActionsInActionAction.cpp b/tutorials/actions/actionsinaction/ActionsInActionAction.cpp new file mode 100644 index 00000000..587b81b0 --- /dev/null +++ b/tutorials/actions/actionsinaction/ActionsInActionAction.cpp @@ -0,0 +1,145 @@ +/***************************************************************************** + * $CAMITK_LICENCE_BEGIN$ + * + * CamiTK - Computer Assisted Medical Intervention ToolKit + * (c) 2001-2018 Univ. Grenoble Alpes, CNRS, TIMC-IMAG UMR 5525 (GMCAO) + * + * Visit http://camitk.imag.fr for more information + * + * This file is part of CamiTK. + * + * CamiTK is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * CamiTK is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with CamiTK. If not, see . + * + * $CAMITK_LICENCE_END$ + ****************************************************************************/ +#include "ActionsInActionAction.h" + +// CamiTK +#include +#include +#include + +// Qt +#include + +using namespace camitk; + + +// --------------- Constructor ------------------- +ActionsInActionAction::ActionsInActionAction(ActionExtension* extension) : Action(extension) { + // Setting name, description and input component + setName(tr("Apply two actions from an action")); + setDescription("Demonstrate how to call one or more actions from another action."); + setComponent("ImageComponent"); + + // Setting classification family and tags + setFamily(tr("Tutorial")); + addTag(tr("Pipeline")); + + // Setting the action's parameters + addParameter(new Property(tr("Resample Factor"), QVariant(0.5), tr("The resample factor to use when applying the ResampleAction"), tr("factor"))); + +} + +// --------------- destructor ------------------- +ActionsInActionAction::~ActionsInActionAction() { + // Do not do anything yet. + // Delete stuff if you create stuff + // (except if you use smart pointers of course !!) +} + +// --------------- apply ------------------- +Action::ApplyStatus ActionsInActionAction::apply() { + + Action::ApplyStatus status; + PipelineActionStatus pipelineStatus = PipelineActionStatus::OK; + + ImageComponent* input = dynamic_cast(getTargets().last()); + + Action* resampleAction = Application::getAction("Resample"); + + if (resampleAction != nullptr) { + + // set the input + resampleAction->setInputComponent(input); + + // set the parameter + int* dims = input->getImageData()->GetDimensions(); + double factor = property("Resample Factor").toDouble(); + resampleAction->setProperty("New Image X Dimension", int(dims[0]*factor)); + resampleAction->setProperty("New Image Y Dimension", int(dims[1]*factor)); + resampleAction->setProperty("New Image Z Dimension", std::max(1,int(dims[2]*factor))); + + // apply the action + status = resampleAction->applyInPipeline(); + + // If the image has been saved, then it is not modified any more... + if (status == Action::SUCCESS) { + input->setModified(false); + + auto it = find_if(resampleAction->getOutputComponents().crbegin(), resampleAction->getOutputComponents().crend(), [] (const Component* c) { return c->isTopLevel(); } ); + // Equivalent of + // ComponentList::reverse_iterator it = resampleAction->getOutputComponents().rbegin(); + // while (it != resampleAction->getOutputComponents().rend() && !(*it)->isTopLevel()) { + // it++; + // } + + Component* resampled = (*it); + + Action* otsuThreshold = Application::getAction("Otsu Threshold Filter"); + if (otsuThreshold != nullptr) { + otsuThreshold->setInputComponent(resampled); + status = otsuThreshold->applyInPipeline(); + + if (status == Action::SUCCESS) { + // if all goes well, don't show the intermediate result + delete resampled; + } + else { + pipelineStatus = PipelineActionStatus::OTSU_APPLY_ERROR; + } + } + else { + pipelineStatus = PipelineActionStatus::OTSU_NOT_FOUND; + } + } + else { + pipelineStatus = PipelineActionStatus::RESAMPLE_APPLY_ERROR; + } + + } + else { + pipelineStatus = PipelineActionStatus::RESAMPLE_NOT_FOUND; + } + + switch (pipelineStatus) { + case OTSU_APPLY_ERROR: + CAMITK_ERROR("\"Otsu Threshold Filter\" apply error:" + Action::getStatusAsString(status)); + break; + case OTSU_NOT_FOUND: + CAMITK_ERROR("\"Otsu Threshold Filter\" action not found"); + break; + case RESAMPLE_APPLY_ERROR: + CAMITK_ERROR("\"Resample Factor\" apply error:" + Action::getStatusAsString(status)); + break; + case RESAMPLE_NOT_FOUND: + CAMITK_ERROR("\"Resample Factor\" action not found"); + break; + } + + Application::refresh(); + + return ((pipelineStatus == OK) ? SUCCESS : ERROR); +} + + diff --git a/tutorials/actions/actionsinaction/ActionsInActionAction.h b/tutorials/actions/actionsinaction/ActionsInActionAction.h new file mode 100644 index 00000000..1afafb86 --- /dev/null +++ b/tutorials/actions/actionsinaction/ActionsInActionAction.h @@ -0,0 +1,63 @@ +/***************************************************************************** + * $CAMITK_LICENCE_BEGIN$ + * + * CamiTK - Computer Assisted Medical Intervention ToolKit + * (c) 2001-2018 Univ. Grenoble Alpes, CNRS, TIMC-IMAG UMR 5525 (GMCAO) + * + * Visit http://camitk.imag.fr for more information + * + * This file is part of CamiTK. + * + * CamiTK is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * CamiTK is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with CamiTK. If not, see . + * + * $CAMITK_LICENCE_END$ + ****************************************************************************/ +#ifndef ACTIONSINACTIONACTION_H +#define ACTIONSINACTIONACTION_H + +#include + +#include + +class ActionsInActionAction : public camitk::Action { +Q_OBJECT +public: + + /// Default Constructor + ActionsInActionAction(camitk::ActionExtension*); + + /// Default Destructor + virtual ~ActionsInActionAction(); + +public slots: + /** this method is automatically called when the action is triggered. + * Call getTargets() method to get the list of components to use. + * \note getTargets() is automatically filtered so that it only contains compatible components, + * i.e., instances of ComputeNeighborValueAction (or a subclass). + */ + virtual ApplyStatus apply(); + +private: + // many things can happen during the execution of this action + enum PipelineActionStatus{ + RESAMPLE_NOT_FOUND, + OTSU_NOT_FOUND, + RESAMPLE_APPLY_ERROR, + OTSU_APPLY_ERROR, + OK, + }; + +}; + +#endif // ACTIONSINACTIONACTION_H + diff --git a/tutorials/actions/actionsinaction/ActionsInActionExtension.cpp b/tutorials/actions/actionsinaction/ActionsInActionExtension.cpp new file mode 100644 index 00000000..cfbe5218 --- /dev/null +++ b/tutorials/actions/actionsinaction/ActionsInActionExtension.cpp @@ -0,0 +1,35 @@ +/***************************************************************************** + * $CAMITK_LICENCE_BEGIN$ + * + * CamiTK - Computer Assisted Medical Intervention ToolKit + * (c) 2001-2018 Univ. Grenoble Alpes, CNRS, TIMC-IMAG UMR 5525 (GMCAO) + * + * Visit http://camitk.imag.fr for more information + * + * This file is part of CamiTK. + * + * CamiTK is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * CamiTK is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with CamiTK. If not, see . + * + * $CAMITK_LICENCE_END$ + ****************************************************************************/ +#include "ActionsInActionExtension.h" + +// include generated actions headers +#include "ActionsInActionAction.h" + +// --------------- getActions ------------------- +void ActionsInActionExtension::init() { + // Creating and registering the instance of ActionsInActionAction + registerNewAction(ActionsInActionAction); +} + diff --git a/tutorials/actions/actionsinaction/ActionsInActionExtension.h b/tutorials/actions/actionsinaction/ActionsInActionExtension.h new file mode 100644 index 00000000..9ae9c79e --- /dev/null +++ b/tutorials/actions/actionsinaction/ActionsInActionExtension.h @@ -0,0 +1,59 @@ +/***************************************************************************** + * $CAMITK_LICENCE_BEGIN$ + * + * CamiTK - Computer Assisted Medical Intervention ToolKit + * (c) 2001-2018 Univ. Grenoble Alpes, CNRS, TIMC-IMAG UMR 5525 (GMCAO) + * + * Visit http://camitk.imag.fr for more information + * + * This file is part of CamiTK. + * + * CamiTK is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * CamiTK is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with CamiTK. If not, see . + * + * $CAMITK_LICENCE_END$ + ****************************************************************************/ +#ifndef ACTIONS_IN_ACTION_EXTENSION_H +#define ACTIONS_IN_ACTION_EXTENSION_H + +#include + +class ActionsInActionExtension : public camitk::ActionExtension { + Q_OBJECT + Q_INTERFACES(camitk::ActionExtension); + Q_PLUGIN_METADATA(IID "fr.imag.camitk.tutorials.action.actionsinaction") + +public: + /// Constructor + ActionsInActionExtension() : ActionExtension() {}; + + /// Destructor + virtual ~ActionsInActionExtension() = default; + + /// Method returning the action extension name + virtual QString getName() { + return "ActionsInActionExtension"; + }; + + /// Method returning the action extension descrption + virtual QString getDescription() { + return "Demo of applying actions from another action"; + }; + + /// initialize all the actions + virtual void init(); + +}; + +#endif // ACTIONS_IN_ACTION_EXTENSION_H + + diff --git a/tutorials/actions/actionsinaction/CMakeLists.txt b/tutorials/actions/actionsinaction/CMakeLists.txt new file mode 100644 index 00000000..315353bd --- /dev/null +++ b/tutorials/actions/actionsinaction/CMakeLists.txt @@ -0,0 +1,6 @@ +# Call CamiTK CMake Macro to define the action +camitk_extension(ACTION_EXTENSION + ENABLE_AUTO_TEST + TEST_FILES brain.mha sinus.mhd +) + -- GitLab From 15684515fcb7adbea35ac8aa1ed79a38fc4ca783 Mon Sep 17 00:00:00 2001 From: Emmanuel Promayon Date: Thu, 25 Jul 2019 08:53:12 +0200 Subject: [PATCH 03/12] FIXED for Qt<5.6 and modularize/clean/simplify algorithm --- .../actionsinaction/ActionsInActionAction.cpp | 162 ++++++++++++------ .../actionsinaction/ActionsInActionAction.h | 37 +++- 2 files changed, 143 insertions(+), 56 deletions(-) diff --git a/tutorials/actions/actionsinaction/ActionsInActionAction.cpp b/tutorials/actions/actionsinaction/ActionsInActionAction.cpp index 587b81b0..1e87372e 100644 --- a/tutorials/actions/actionsinaction/ActionsInActionAction.cpp +++ b/tutorials/actions/actionsinaction/ActionsInActionAction.cpp @@ -61,80 +61,45 @@ ActionsInActionAction::~ActionsInActionAction() { // --------------- apply ------------------- Action::ApplyStatus ActionsInActionAction::apply() { - Action::ApplyStatus status; PipelineActionStatus pipelineStatus = PipelineActionStatus::OK; - ImageComponent* input = dynamic_cast(getTargets().last()); + // apply the resample action (first action in the pipeline) on the last selected component + pipelineStatus = applyResample(getTargets().last()); - Action* resampleAction = Application::getAction("Resample"); + // If the image has been saved, then it is not modified any more... + if (pipelineStatus == OK) { - if (resampleAction != nullptr) { + // You can most of the time safely ignore the safeguard bits below and simply use: + // Component* resampled = resampleAction->getOutputComponent() + // But in some rare case, the second action of this pipeline is not going to use the correct + // input. For these cases, you need to use: + Component* resampled = getLastTopLevelOutputComponents("Resample"); - // set the input - resampleAction->setInputComponent(input); + // apply the Otsu action (second action in the pipeline) + pipelineStatus = applyOtsu(resampled); - // set the parameter - int* dims = input->getImageData()->GetDimensions(); - double factor = property("Resample Factor").toDouble(); - resampleAction->setProperty("New Image X Dimension", int(dims[0]*factor)); - resampleAction->setProperty("New Image Y Dimension", int(dims[1]*factor)); - resampleAction->setProperty("New Image Z Dimension", std::max(1,int(dims[2]*factor))); - - // apply the action - status = resampleAction->applyInPipeline(); - - // If the image has been saved, then it is not modified any more... - if (status == Action::SUCCESS) { - input->setModified(false); - - auto it = find_if(resampleAction->getOutputComponents().crbegin(), resampleAction->getOutputComponents().crend(), [] (const Component* c) { return c->isTopLevel(); } ); - // Equivalent of - // ComponentList::reverse_iterator it = resampleAction->getOutputComponents().rbegin(); - // while (it != resampleAction->getOutputComponents().rend() && !(*it)->isTopLevel()) { - // it++; - // } - - Component* resampled = (*it); - - Action* otsuThreshold = Application::getAction("Otsu Threshold Filter"); - if (otsuThreshold != nullptr) { - otsuThreshold->setInputComponent(resampled); - status = otsuThreshold->applyInPipeline(); - - if (status == Action::SUCCESS) { - // if all goes well, don't show the intermediate result - delete resampled; - } - else { - pipelineStatus = PipelineActionStatus::OTSU_APPLY_ERROR; - } - } - else { - pipelineStatus = PipelineActionStatus::OTSU_NOT_FOUND; - } - } - else { - pipelineStatus = PipelineActionStatus::RESAMPLE_APPLY_ERROR; + if (pipelineStatus == OK) { + // if all goes well, don't show the intermediate result + delete resampled; } - - } - else { - pipelineStatus = PipelineActionStatus::RESAMPLE_NOT_FOUND; } switch (pipelineStatus) { case OTSU_APPLY_ERROR: - CAMITK_ERROR("\"Otsu Threshold Filter\" apply error:" + Action::getStatusAsString(status)); + CAMITK_ERROR("\"Otsu Threshold Filter\" apply error"); break; case OTSU_NOT_FOUND: CAMITK_ERROR("\"Otsu Threshold Filter\" action not found"); break; case RESAMPLE_APPLY_ERROR: - CAMITK_ERROR("\"Resample Factor\" apply error:" + Action::getStatusAsString(status)); + CAMITK_ERROR("\"Resample Factor\" apply error."); break; case RESAMPLE_NOT_FOUND: CAMITK_ERROR("\"Resample Factor\" action not found"); break; + case OK: + CAMITK_TRACE("Pipeline successful"); + break; } Application::refresh(); @@ -142,4 +107,93 @@ Action::ApplyStatus ActionsInActionAction::apply() { return ((pipelineStatus == OK) ? SUCCESS : ERROR); } +// --------------- getLastTopLevelOutputComponents ------------------- +Component* ActionsInActionAction::getLastTopLevelOutputComponents(const QString& actionName) { + + Component* lastOutputComponent = nullptr; + + Action* currentAction = Application::getAction(actionName); + + if (currentAction != nullptr) { +#if QT_VERSION >= QT_VERSION_CHECK(5, 6, 0) + // it Qt >= 5.6 crbegin() exist and we can use a lambda expression to find the first + // top level component, looking from the end to the beginning of the component list. + auto it = find_if(currentAction->getOutputComponents().crbegin(), + currentAction->getOutputComponents().crend(), + [](const Component * c) { + return c->isTopLevel(); + }); + // This is equivalent of: + // ComponentList::reverse_iterator it = currentAction->getOutputComponents().rbegin(); + // while (it != currentAction->getOutputComponents().rend() && !(*it)->isTopLevel()) { + // it++; + // } + lastOutputComponent = (*it); +#else + // In Qt < 5.6, too bad: crbegin(), crend(), rbegin() and rend() do not exist + unsigned int i = currentAction->getOutputComponents().size() - 1; + while (i > 0 && !currentAction->getOutputComponents().at(i)->isTopLevel()) { + i--; + } + if (i >= 0) { + lastOutputComponent = currentAction->getOutputComponents().at(i); + } +#endif + } + + return lastOutputComponent; +} + +// --------------- applyResample ------------------- +ActionsInActionAction::PipelineActionStatus ActionsInActionAction::applyResample(Component* input) { + Action* resampleAction = Application::getAction("Resample"); + + if (resampleAction == nullptr) { + return RESAMPLE_NOT_FOUND; + } + else { + // set the input + resampleAction->setInputComponent(input); + + // set the parameter + ImageComponent* inputImage = dynamic_cast(input); + // Note: as ActionsInActionAction takes ImageComponent as inputs, the dynamic_cast is + // guaranty to work perfectly well. + int* dims = inputImage->getImageData()->GetDimensions(); + double factor = property("Resample Factor").toDouble(); + resampleAction->setProperty("New Image X Dimension", int(dims[0]*factor)); + resampleAction->setProperty("New Image Y Dimension", int(dims[1]*factor)); + resampleAction->setProperty("New Image Z Dimension", std::max(1, int(dims[2]*factor))); + + // apply the action + Action::ApplyStatus status = resampleAction->applyInPipeline(); + + // reset the flag (just in case) + input->setModified(false); + + return (status == Action::SUCCESS) ? OK : RESAMPLE_APPLY_ERROR; + } + + +} + + +// --------------- applyOtsu ------------------- +ActionsInActionAction::PipelineActionStatus ActionsInActionAction::applyOtsu(Component* input) { + Action* otsuThreshold = Application::getAction("Otsu Threshold Filter"); + if (otsuThreshold == nullptr) { + return OTSU_NOT_FOUND; + } + else { + // set the input + otsuThreshold->setInputComponent(input); + + // nothing special to do on the action parameters + + // apply the action + Action::ApplyStatus status = otsuThreshold->applyInPipeline(); + + return (status == Action::SUCCESS) ? OK : OTSU_APPLY_ERROR; + } +} diff --git a/tutorials/actions/actionsinaction/ActionsInActionAction.h b/tutorials/actions/actionsinaction/ActionsInActionAction.h index 1afafb86..324dfa76 100644 --- a/tutorials/actions/actionsinaction/ActionsInActionAction.h +++ b/tutorials/actions/actionsinaction/ActionsInActionAction.h @@ -48,8 +48,8 @@ public slots: virtual ApplyStatus apply(); private: - // many things can happen during the execution of this action - enum PipelineActionStatus{ + /// many things can happen during the execution of this action + enum PipelineActionStatus { RESAMPLE_NOT_FOUND, OTSU_NOT_FOUND, RESAMPLE_APPLY_ERROR, @@ -57,6 +57,39 @@ private: OK, }; + /// Get the last toplevel output component of a given action + /// This is required in some rare specific pipeline execution environment + /// + /// Knowing that: + /// - Action::getOutputComponents() method returns all instanciated components during the apply() method + /// **as well** as all the unmodified components (unaware of if this modified components were modified + /// prior to the apply() method). + /// Note that the last instanciated component is at the end of the list. + /// - Action::getOutputComponent() method returns the first instanciated or modified component during the + /// apply(). + /// + /// There is a specific issue that arise when there are existing components marked as modified + /// **before** the start of the pipeline (e.g., when the action pipeline was run previously + /// and the user left the instanciated component untouched/unsaved or when the user modified + /// a component manually, outside the pipeline). + /// + /// The best way (for now) to deal with this issue is to simply get the last top-level component + /// in the ouput list. This will be the last instanciated top-level component during the apply() method. + /// Any components that were modified _before_ the apply() will therefore be ignore + /// + /// This is an issue to consider in the Action class. + /// We probably should list in Action::preprocessInPipeline() all the component that were marked modified + /// and ignore them in the output component list (that will not take into account the rare case were a + /// already modified component was modified **as well** during the apply(), but that seems a better + /// behaviour anyway. + /// + camitk::Component *getLastTopLevelOutputComponents(const QString & actionName); + + /// apply the resample action (modifying the parameter) + PipelineActionStatus applyResample(camitk::Component *input); + + /// apply the Otsu action + PipelineActionStatus applyOtsu(camitk::Component *input); }; #endif // ACTIONSINACTIONACTION_H -- GitLab From 416a81db6d7ce6dc97421afff507b17afcd01404 Mon Sep 17 00:00:00 2001 From: Emmanuel Promayon Date: Thu, 25 Jul 2019 10:17:00 +0200 Subject: [PATCH 04/12] FIXED help syntax --- imaging/actions/itkfilters/integration-testdata/asm-input.scxml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/imaging/actions/itkfilters/integration-testdata/asm-input.scxml b/imaging/actions/itkfilters/integration-testdata/asm-input.scxml index 1d586853..dec92b94 100644 --- a/imaging/actions/itkfilters/integration-testdata/asm-input.scxml +++ b/imaging/actions/itkfilters/integration-testdata/asm-input.scxml @@ -2,7 +2,7 @@ -