Commit d46cbc8e authored by Emmanuel Promayon's avatar Emmanuel Promayon

Merge branch 'feature/call-actions-in-action' into 'develop'

Feature/call actions in action

See merge request !117
parents 7a06f9cb 64a6f44b
......@@ -27,25 +27,26 @@ camitk_disable_tests(TESTS action-itkfilters-level1-6
action-itkfilters-level1-8
action-itkfilters-level1-14
REASON " Test failure due to ITK exception.
For now disable these tests until this can be taken care of in the save() method
For input file: biorad.pic, cthead1.lsm, mini-complex-slow1.nrrd
When applying action Gradient Magnitude With Smoothing, the following exception is raised:
\"camitk-testcomponents aborted by std exception: /usr/include/ITK-4.10/itkRecursiveSeparableImageFilter.hxx:245:
itk::ERROR: RecursiveGaussianImageFilter(0x55b3b21bc700): The number of pixels along direction 2 is less than 4. This filter requires a minimum of four pixels along the dimension to be processed...\""
For now disable these tests until this can be taken care of in the save() method
For input file: biorad.pic, cthead1.lsm, mini-complex-slow1.nrrd
When applying action Gradient Magnitude With Smoothing, the following exception is raised:
\"camitk-testcomponents aborted by std exception: /usr/include/ITK-4.10/itkRecursiveSeparableImageFilter.hxx:245:
itk::ERROR: RecursiveGaussianImageFilter(0x55b3b21bc700): The number of pixels along direction 2 is less than 4.
This filter requires a minimum of four pixels along the dimension to be processed...\""
)
camitk_tests_requirement(TESTS action-itkfilters-integration-test
REQUIRES "NOT WIN32"
REASON "WIN32 OpenGL failure on a VM
This test will always fail when run inside a VM due to OpenGL crash.
This test will pass when run directly from a physical windows machine."
This test will always fail when run inside a VM due to OpenGL crash.
This test will pass when run directly from a physical windows machine."
)
camitk_tests_requirement(TESTS action-itkfilters-integration-test
REQUIRES "${VTK_VERSION} VERSION_EQUAL 6.3"
REASON "VTK version is not equals than 6.3
The default test files are written using the currently supported version of VTK (VTK 6.3).
This test will therefore fail when comparing the input to the output if another version of VTK is used.
E.g., Ubuntu 16.04 LTS comes with VTK 6.2 and Debian buster comes with VTK 7.1
Although everything should work well on both OS, this test will unconditionnaly fail."
The default test files are written using the currently supported version of VTK (VTK 6.3).
This test will therefore fail when comparing the input to the output if another version of VTK is used.
E.g., Ubuntu 16.04 LTS comes with VTK 6.2 and Debian buster comes with VTK 7.1
Although everything should work well on both OS, this test will unconditionnaly fail."
)
......@@ -2,7 +2,7 @@
<state id="Initialize">
<onentry>
<camitk:onState>
<camitk:description><![CDATA[This pipeline will process several actions on the input component(s): <br /> <ul><li>Open</li><li>Otsu Threshold Filter</li><li>Morphological Operators</li><li>Save As</li><li>Save As</li><li>Connected Components Classification</li><li>Save As</li><li>Manual Threshold Filter</li><li>Save As</li><li>Morphological Operators</li><li>Save As</li><li>Morphological Operators</li><li>Save As</li><li>Close</li><li>Close</li><li>Close</li><li>Close</li><li>Close</li><li>Close</li><li>Reconstruction</li><li>Save As</li><li>Save As</li><li>Close</li><li>Reconstruction</li><li>Save As</li><li>Close</li><li>Reconstruction</li><li>Save As</li><li>Save As</li><li>Clean Mesh</li><li>Save As</li>]]></camitk:description>
<camitk:description><![CDATA[This pipeline will process several actions on the input component(s): <br /> <ul><li>Open</li><li>Otsu Threshold Filter</li><li>Morphological Operators</li><li>Save As</li><li>Save As</li><li>Connected Components Classification</li><li>Save As</li><li>Manual Threshold Filter</li><li>Save As</li><li>Morphological Operators</li><li>Save As</li><li>Morphological Operators</li><li>Save As</li><li>Close</li><li>Close</li><li>Close</li><li>Close</li><li>Close</li><li>Close</li><li>Reconstruction</li><li>Save As</li><li>Save As</li><li>Close</li><li>Reconstruction</li><li>Save As</li><li>Close</li><li>Reconstruction</li><li>Save As</li><li>Save As</li><li>Clean Mesh</li><li>Save As</li></ul>]]></camitk:description>
</camitk:onState>
</onentry>
<transition target="Open Input Image" event="Next"/>
......
......@@ -18,11 +18,12 @@ camitk_disable_tests(TESTS component-itkimage-level3-3
component-itkimage-level3-19
component-itkimage-level3-20
REASON " Test failure due to ITK exception.
For now disable these tests until this can be dealt with (if possible)
For input file: biorad.pic, cthead1.lsm, BigEndian.spr, LittleEndianZ.hdr and LittleEndianZ.img.gz
When applying action Gradient Magnitude With Smoothing, the following exception is raised:
\"camitk-testcomponents aborted by std exception: /usr/include/ITK-4.10/itkRecursiveSeparableImageFilter.hxx:245:
itk::ERROR: RecursiveGaussianImageFilter(0x55b3b21bc700): The number of pixels along direction 2 is less than 4. This filter requires a minimum of four pixels along the dimension to be processed...\""
For now disable these tests until this can be dealt with (if possible)
For input file: biorad.pic, cthead1.lsm, BigEndian.spr, LittleEndianZ.hdr and LittleEndianZ.img.gz
When applying action Gradient Magnitude With Smoothing, the following exception is raised:
\"camitk-testcomponents aborted by std exception: /usr/include/ITK-4.10/itkRecursiveSeparableImageFilter.hxx:245:
itk::ERROR: RecursiveGaussianImageFilter(0x55b3b21bc700): The number of pixels along direction 2 is less than 4.
This filter requires a minimum of four pixels along the dimension to be processed...\""
)
# TODO confirm diagnostic: check why BigEndianCompressed.img.gz, LittleEndianCompressed.img.gz and minimal.nii.gz does not work on Windows (maybe because of double suffix .im.gz?)
......@@ -31,7 +32,7 @@ camitk_tests_requirement(TESTS component-itkimage-level3-6
component-itkimage-level3-28
REQUIRES "NOT WIN32"
REASON "WIN32 failure (probably) due to double suffix .im.gz or .nii.gz
For input file: BigEndianCompressed.img.gz, LittleEndianCompressed.img.gz and minimal.nii.gz"
For input file: BigEndianCompressed.img.gz, LittleEndianCompressed.img.gz and minimal.nii.gz"
)
camitk_tests_requirement(TESTS component-itkimage-level3-1
......@@ -45,8 +46,8 @@ camitk_tests_requirement(TESTS component-itkimage-level3-1
component-itkimage-level3-28
REQUIRES "${ITK_VERSION} VERSION_LESS 4.12"
REASON " ITK version is greater than 4.11
The default test files are written using the currently supported version of ITK (ITK 4.11).
This test will therefore fail when comparing the input to the output if another version of ITK is used."
The default test files are written using the currently supported version of ITK (ITK 4.11).
This test will therefore fail when comparing the input to the output if another version of ITK is used."
)
# Additional test for SDK actions: this will add auto test for the given actions using the itkimage component
......
......@@ -27,6 +27,8 @@
#include <Application.h>
#include <Property.h>
#include <Log.h>
// Vtk includes
#include <vtkCallbackCommand.h>
#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<int>::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<int>::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<int>::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<vtkImageData> 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();
}
......@@ -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)
......
......@@ -16,15 +16,15 @@ set(SHIBOKEN_CAMITK_SDK_PATH ${SHIBOKEN_CAMITK_SDK_PATH}:${CMAKE_CURRENT_SOURCE_
camitk_tests_requirement(TESTS action-reconstruction-integration-test
REQUIRES "${VTK_VERSION} VERSION_EQUAL 6.3"
REASON "VTK version is not equals than 6.3
The default test files are written using the currently supported version of VTK (VTK 6.3).
This test will therefore fail when comparing the input to the output if another version of VTK is used.
E.g., Ubuntu 16.04 LTS comes with VTK 6.2 and Debian buster comes with VTK 7.1
Although everything should work well on both OS, this test will unconditionnaly fail."
The default test files are written using the currently supported version of VTK (VTK 6.3).
This test will therefore fail when comparing the input to the output if another version of VTK is used.
E.g., Ubuntu 16.04 LTS comes with VTK 6.2 and Debian buster comes with VTK 7.1
Although everything should work well on both OS, this test will unconditionnaly fail."
)
camitk_tests_requirement(TESTS action-reconstruction-integration-test
REQUIRES "NOT WIN32"
REASON "WIN32 OpenGL failure on a VM
This test will always fail when run inside a VM due to OpenGL crash.
This test will pass when run directly from a physical windows machine."
This test will always fail when run inside a VM due to OpenGL crash.
This test will pass when run directly from a physical windows machine."
)
......@@ -22,11 +22,11 @@ set -e
# values to check
initTestData() {
# fill test data
extensionCount=( [4.2]=41 [4.1]=41 [4.0]=31 )
extensionCount=( [4.2]=42 [4.1]=41 [4.0]=31 )
componentExtensionCount=( [4.2]=14 [4.1]=14 [4.0]=14 )
actionExtensionCount=( [4.2]=27 [4.1]=27 [4.0]=27 )
actionExtensionCount=( [4.2]=28 [4.1]=27 [4.0]=27 )
fileExtensionCount=( [4.2]=37 [4.1]=37 [4.0]=37 )
actionCount=( [4.2]=105 [4.1]=105 [4.0]=105 )
actionCount=( [4.2]=106 [4.1]=105 [4.0]=105 )
# fill release date
releaseDate=( [4.2]="not yet released, current development version" \
......
......@@ -59,7 +59,8 @@ if(XSD_FOUND)
endif()
if(NOT XSD_FIND_QUIETLY)
message(STATUS "Found XSD executable: ${XSD_EXECUTABLE} version ${XSD_VERSION}")
string(REGEX REPLACE "\n$" "" XSD_VERSION_WITHOUT_TRAILING_NEWLINE "${XSD_VERSION}")
message(STATUS "Found XSD executable: ${XSD_EXECUTABLE} version ${XSD_VERSION_WITHOUT_TRAILING_NEWLINE}")
endif()
else()
if(XSD_FIND_REQUIRED)
......
#!
#! @ingroup group_sdk_cmake_camitk_test
#!
#! camitk_disable_tests is a macro to disable one or more tests unconditionally.
#! camitk_disable_tests is a macro to unconditionally disable one or more tests.
#!
#! It encapsulates CMake set_tests_properties in order to manage version older that CMake 3.9
#! and to force the developper to give a (good) reason for bypassing tests.
......@@ -47,18 +47,22 @@ macro(camitk_disable_tests)
set(multiValueArgs TESTS)
cmake_parse_arguments(CAMITK_DISABLE_TESTS "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
set(DISABLE_MESSAGE "")
# CMake < 3.9 did not have the DISABLED test property. Use WILL_FAIL instead
foreach(TEST_NAME ${CAMITK_DISABLE_TESTS_TESTS})
# CMake < 3.9 did not have the DISABLED test property. Use WILL_FAIL instead
if(${CMAKE_VERSION} VERSION_LESS "3.9")
set(DISABLE_MESSAGE ${DISABLE_MESSAGE} " Unconditionally disabling test ${TEST_NAME} (using WILL_FAIL property)\n")
set_tests_properties(${TEST_NAME} PROPERTIES WILL_FAIL true)
else()
set(DISABLE_MESSAGE ${DISABLE_MESSAGE} " Unconditionally disabling test ${TEST_NAME}\n")
set_tests_properties(${TEST_NAME} PROPERTIES DISABLED true)
endif()
endforeach()
set(DISABLE_MESSAGE ${DISABLE_MESSAGE} " Reason: ${CAMITK_DISABLE_TESTS_REASON}\n")
message(AUTHOR_WARNING ${DISABLE_MESSAGE})
# message to the developper
string(REPLACE ";" " " CAMITK_DISABLE_TESTS_TESTS_STRING "${CAMITK_DISABLE_TESTS_TESTS}")
set(DISABLE_MESSAGE "Unconditionally disabling tests:\n Disabled tests: ${CAMITK_DISABLE_TESTS_TESTS_STRING}\n")
if(${CMAKE_VERSION} VERSION_LESS "3.9")
set(DISABLE_MESSAGE ${DISABLE_MESSAGE} " (using WILL_FAIL property because CMake < 3.9)\n")
endif()
set(DISABLE_MESSAGE ${DISABLE_MESSAGE} " Reason: ${CAMITK_DISABLE_TESTS_REASON}")
message(STATUS ${DISABLE_MESSAGE})
endmacro()
......@@ -168,24 +168,24 @@ macro(camitk_tests_requirement)
endif()
# -- disable tests if the requirement is not met
set(DISABLE_MESSAGE "")
string(REPLACE ";" " " CAMITK_TESTS_REQUIREMENT_TESTS_STRING "${CAMITK_TESTS_REQUIREMENT_TESTS}")
if(NOT ${REQUIRES_EVAL})
foreach(TEST_NAME ${CAMITK_TESTS_REQUIREMENT_TESTS})
set(DISABLE_MESSAGE ${DISABLE_MESSAGE} " Disabling test ${TEST_NAME}")
# CMake < 3.9 did not have the DISABLED test property. Use WILL_FAIL instead
if(${CMAKE_VERSION} VERSION_LESS "3.9")
set(DISABLE_MESSAGE ${DISABLE_MESSAGE} " (using WILL_FAIL property)")
set_tests_properties(${TEST_NAME} PROPERTIES WILL_FAIL true)
else()
set_tests_properties(${TEST_NAME} PROPERTIES DISABLED true)
endif()
set(DISABLE_MESSAGE ${DISABLE_MESSAGE} "\n")
endforeach()
set(DISABLE_MESSAGE ${DISABLE_MESSAGE} " Reason: ${CAMITK_TESTS_REQUIREMENT_REASON}\n")
set(DISABLE_MESSAGE ${DISABLE_MESSAGE} " Requirement statement unmet: ${CAMITK_TESTS_REQUIREMENT_REQUIRES}.\n")
message(AUTHOR_WARNING ${DISABLE_MESSAGE})
set(DISABLE_MESSAGE "Test requirement \"${CAMITK_TESTS_REQUIREMENT_REQUIRES}\": Failed\n Disabled tests: ${CAMITK_TESTS_REQUIREMENT_TESTS_STRING}\n")
if(${CMAKE_VERSION} VERSION_LESS "3.9")
set(DISABLE_MESSAGE ${DISABLE_MESSAGE} " (using WILL_FAIL property because CMake < 3.9)\n")
endif()
set(DISABLE_MESSAGE ${DISABLE_MESSAGE} " Reason: ${CAMITK_TESTS_REQUIREMENT_REASON}\n")
set(DISABLE_MESSAGE ${DISABLE_MESSAGE} " Requirement statement unmet: ${CAMITK_TESTS_REQUIREMENT_REQUIRES}")
message(STATUS ${DISABLE_MESSAGE})
else()
string(REPLACE ";" " " CAMITK_TESTS_REQUIREMENT_TESTS_STRING "${CAMITK_TESTS_REQUIREMENT_TESTS}")
message(STATUS "Test requirement \"${CAMITK_TESTS_REQUIREMENT_REQUIRES}\": Passed\n Enabled tests: ${CAMITK_TESTS_REQUIREMENT_TESTS_STRING}")
endif()
endmacro()
......@@ -38,8 +38,8 @@ camitk_tests_requirement(TESTS component-vtkmesh-level3-1
component-vtkmesh-level3-16
REQUIRES "${VTK_VERSION} VERSION_EQUAL 6.3"
REASON "VTK version is not equals than 6.3
The default test files are written using the currently supported version of VTK (VTK 6.3).
This test will therefore fail when comparing the input to the output if another version of VTK is used.
E.g., Ubuntu 16.04 LTS comes with VTK 6.2 and Debian buster comes with VTK 7.1
Although everything should work well on both OS, this test will unconditionnaly fail."
The default test files are written using the currently supported version of VTK (VTK 6.3).
This test will therefore fail when comparing the input to the output if another version of VTK is used.
E.g., Ubuntu 16.04 LTS comes with VTK 6.2 and Debian buster comes with VTK 7.1
Although everything should work well on both OS, this test will unconditionnaly fail."
)
......@@ -46,7 +46,7 @@ public:
/// Method returning the action extension descrption
virtual QString getDescription() {
return "Extensions for Biopolis developers";
return "Compute the average voxels value in the neiborhoods of the selected voxel";
};
/// initialize all the actions
......
# Call CamiTK CMake Macro to define the action
camitk_extension(ACTION_EXTENSION
# Auto test will not work as this action needs two other action
# and auto test are isolating the action it takes.
# But integration test will perform all the required tests.
ENABLE_INTEGRATION_TEST
)
camitk_tests_requirement(TESTS action-hardcodedpipeline-integration-test
REQUIRES "NOT WIN32"
REASON "WIN32 OpenGL failure on a VM
This test will always fail when run inside a VM due to OpenGL crash.
This test will pass when run directly from a physical windows machine."
)
/*****************************************************************************
* $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 <http://www.gnu.org/licenses/>.
*
* $CAMITK_LICENCE_END$
****************************************************************************/
#include "HardCodedPipelineAction.h"
// CamiTK
#include <Application.h>
#include <Property.h>
#include <Log.h>
// Qt
#include <QVector3D>
using namespace camitk;
// --------------- Constructor -------------------
HardCodedPipelineAction::HardCodedPipelineAction(ActionExtension* extension) : Action(extension) {
// Setting name, description and input component
setName(tr("Hard-Coded Action Pipeline"));
setDescription("Demonstrate how to call one or more actions from another action (that is, implement a hard-coded action pipeline without modifying anything of the called actions).");
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 Resample action"), tr("factor")));
}
// --------------- destructor -------------------
HardCodedPipelineAction::~HardCodedPipelineAction() {
// Do not do anything yet.
// Delete stuff if you create stuff
// (except if you use smart pointers of course !!)
}
// --------------- apply -------------------
Action::ApplyStatus HardCodedPipelineAction::apply() {
PipelineActionStatus pipelineStatus = PipelineActionStatus::OK;
//
// Hard-coded Pipeline starts here
// 1. apply Resample action, and check that everything is ok
// 2. apply Otsu threshold
// if everything is alright, remove the intermediate results and force the application refresh
// 1. apply the resample action (first action in the pipeline) on the last selected component
pipelineStatus = applyResample(getTargets().last());
// If the image has been saved, then it is not modified any more...
if (pipelineStatus == OK) {
// 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");
// 2. apply the Otsu action (second action in the pipeline)
pipelineStatus = applyOtsu(resampled);
if (pipelineStatus == OK) {
// if all goes well, don't show the intermediate result
delete resampled;
}
}
switch (pipelineStatus) {
case OTSU_APPLY_ERROR:
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.");
break;
case RESAMPLE_NOT_FOUND:
CAMITK_ERROR("\"Resample Factor\" action not found");
break;
case OK:
CAMITK_TRACE("Pipeline successful");
break;
}
Application::refresh();
return ((pipelineStatus == OK) ? SUCCESS : ERROR);
}
// --------------- getLastTopLevelOutputComponents -------------------
Component* HardCodedPipelineAction::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 the 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 -------------------
HardCodedPipelineAction::PipelineActionStatus HardCodedPipelineAction::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<ImageComponent*>(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 -------------------
HardCodedPipelineAction::PipelineActionStatus HardCodedPipelineAction::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;
}
}
/*****************************************************************************
* $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 <http://www.gnu.org/licenses/>.
*
* $CAMITK_LICENCE_END$
****************************************************************************/
#ifndef ACTIONSINACTIONACTION_H
#define ACTIONSINACTIONACTION_H
#include <Action.h>
#include <ImageComponent.h>
class HardCodedPipelineAction : public camitk::Action {
Q_OBJECT
public:
/// Default Constructor
HardCodedPipelineAction(camitk::ActionExtension*);
/// Default Destructor
virtual ~HardCodedPipelineAction();
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,
};
/// 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
/*****************************************************************************
* $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.