New Developer Features in GIMIAS 1 4 0

From user's Wiki!
Revision as of 07:16, 27 June 2012 by Msteghofer (Talk | contribs)

(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to: navigation, search

Factory Manager

GIMIAS Framework defines several interfaces. Is the responsability of the plugins to register implementations for these interfaces. For example, an implementation for DataEntityImpl interface. We developed a generic approach to manage all these interfaces.

Factory

Added two classes to manage factories in a generic way: BaseFactory and FactoryManager.

  • BaseFactory: Generic interface for all factories.
  • FactoryManager: Manages all instances of BaseFactory. This is the Registry role in the diagram
BaseFactory and FactoryManager UML class diagram

Example

Interface

As an example, you can find the interface "PortInvocation" and two implementations:

  • DirectPortInvocation: Default updater
  • WxPortInvocation: With multithreading capability using wxWidgets.
OutputUpdater interface and the two implementations

Interface implementations

The two implementations will be registered to the FactoryManager:

Core::FactoryManager::Register( PortInvocation::GetNameClass( ), DirectPortInvocation::Factory::New() );
Core::FactoryManager::Register( PortInvocation::GetNameClass( ), WxPortInvocation::Factory::New( ) );

To define the factories you need to call these macros inside the class definition:

  • For DefaultOutputUpdater:
coreDefineFactory( DirectPortInvocation );;
  • For WxOutputUpdater:
coreDefineFactoryClass( WxPortInvocation );
coreDefineFactoryTagsBegin( );
coreDefineFactoryAddTag( "multithreading", true );
coreDefineFactoryTagsEnd( );

Interface consumer

Another class will be the service consumer. The consumer can search a factory that implements the interaface "OutputUpdater" and that has multi threading capability:

blTagMap::Pointer properties = blTagMap::New( );
if ( GetMultithreading() )
{
  properties->AddTag( "multithreading", GetMultithreading() );
}
SmartPointerObject::Pointer object;
object = Core::FactoryManager::CreateInstance( PortInvocation::GetNameClass( ), properties );
PortInvocation::Pointer invocation = PortInvocation::SafeDownCast( object );

Multithreading

To add multi threading feature to GIMIAS, we decided to use the thread pool pattern and use the Boost implementation. Multi threading allows to execute processing tasks in background, while the main GUI thread can process GUI events.

Processor execution

This is the main UML class diagram

ProcessorManager.png

Two main classes have been added:

  • ProcessorManager: Executes Processors using thread pool pattern or current thread, depending on the configuration of the BaseProcessor
  • ProcessorExecutionQueue: A queue that executes Processors using thread pool pattern.
  • ProcessorThread: Execute a BaseProcessor using multi threading restrictions.

When a processor is added to the ProcessorManager using Execute() method, this is the sequence of operations:

  1. If multi threading is enabled:
    1. Creates a copy of the processor
    2. If copy fails, checks if the processor is already running. If it is, throw an exception
    3. Creates a ProcessorThread and adds it to the list
    4. Add the ProcessorThread to the ProcessorExecutionQueue
  2. If multi threading is not enabled:
    1. Executes the processor

By default all filters have the multi threading option disabled. If you want to enable it, you need to call:

ProcessingWidget::UpdateProcessor( true );

or:

GetProcessor()->SetMultithreading( multithreading );
Core::Runtime::Kernel::GetProcessorManager()->Execute( GetProcessor().GetPointer() );

Note:

  • To execute multiple instances of a processor, it needs to implement the copy function, otherwise, it can only be executed once. The only Processor that has Copy( ) function implemented is DynProcessor.

Events

The most critical point when adding multi threading is that "no more than one thread can call GUI functions". The generic approach is that all worker threads will send events to the main GUI thread to update GUI components. We defined a set of wxWidgets events that will be sent to the main GUI thread:

  • WxUpdatePortEvent: Updates the inputs before processing and the ouputs after processing. This code should be executed by main GUI thread because the observers of rendering data can update the render windows. When the input is updated, it will switch to the needed type for the processor.
  • WxUpdateCallbackEvent: Send progress, status, exception messages to GUI thread and abort processing if user click cancel button

This is the UML class diagram that shown the main classes related to the events management:

UML class diagram

When a filter is executed using multi threading, this is the sequence of performed steps:

  1. Update input data: BaseProcessor::GetProcessingData( ) is called. This function calls BaseFilterInputPort::UpdateInput( ), that creates a InputUpdater, initializes it, calls Update( ) and finally gets the data using GetData( ) method. The InputUpdater will create a new PortInvocation with multithreading capabilty or not, depending if multi threading has been enabled.
  2. Update processor: Processor is executed and generates new output data from input data
  3. Update output data: Output data is set as output of the processor and observers are notified. The function BaseProcessor::UpdateOutput( ) calls BaseFilterOutputPort::UpdateOutput( ), that creates a new OutputUpdater, initializes it and calls Update( ). OutputUpdater will create a new PortIncovation with multithreading capabilty or not, depending if multi threading has been enabled.

Note:

  • The updating of inputs and outputs will be done by the main GUI thread. You will see that the GUI is still blocked when adding the output to the rendering tree.

gmIO

Added progress update information for VTK and ITK readers and writers. Created two new classes: ITKImageWriter and ITKImageReader that only read and write ITK images. VTKImageDataWriter and VTKImageDataReader read only VTK images. This allows to configure progress callbacks easily.

ITKImageWriter and ITKImageReader will search valid file extensions over all registered factories for itkImageIOBase. However, some extensions cannot be rendered because the spacing information is missing.

  • Reader: .PIC, .pic, .mha, .mhd, .tif, .TIF, .tiff, .TIFF, .lsm, .LSM, .hdr, .img, .img.gz, .nia, .nii, .nii.gz, .hdr, .img, .img.gz, .jpg, .JPG, .jpeg, .JPEG, .tif, .TIF, .tiff, .TIFF, .bmp, .BMP, .PIC, .pic, .mha, .mhd, .tif, .TIF, .tiff, .TIFF, .lsm, .LSM, .hdr, .img, .img.gz, .nia, .nii, .nii.gz, .hdr, .img, .img.gz, .jpg, .JPG, .jpeg, .JPEG, .tif, .TIF, .tiff, .TIFF, .bmp, .BMP, .dcm
  • Writer: .pic, .mha, .mhd, .tif, .TIF, .tiff, .TIFF, .lsm, .LSM, .hdr, .img, .img.gz, .nia, .nii, .nii.gz, .hdr, .img, .img.gz, .jpg, .JPG, .jpeg, .JPEG, .tif, .TIF, .tiff, .TIFF, .bmp, .BMP, .dcm

DICOM images are written using ITKImageWriter and readed using DICOMFileReader. When an itk::Image is read from disk, the orientation is changed automatically to the default orientation of GIMIAS (ITK_COORDINATE_ORIENTATION_RAI).

Examples

ITK Processor

There's an example in the processor ResampleProcessor of the SandboxPlugin. Before executing the filter, you should add an observer to itk::ProgressEvent():

// Call the function 
typedef itk::ResampleImageFilter<ImageType,ImageType> ResampleImageFilterType;
ResampleImageFilterType::Pointer filter = ResampleImageFilterType::New( );
filter->SetInput( itkInputImage );
filter->SetTransform( m_ScaleTransform );
filter->SetOutputParametersFromImage( itkInputImage );
// Add observer
itk::MemberCommand<SandboxPlugin::ResampleProcessor>::Pointer command;
command = itk::MemberCommand<SandboxPlugin::ResampleProcessor>::New();
command->SetCallbackFunction( this, &ResampleProcessor::UpdateProgress );
filter->AddObserver( itk::ProgressEvent(), command );
// Update
filter->Update( );

The observer member function will update the UpdateCallback instance of the processor with the progress percentage and the cancellation of the processor:

void SandboxPlugin::ResampleProcessor::UpdateProgress(
	itk::Object *caller,
	const itk::EventObject& event)
{
	itk::ProcessObject *processObject = (itk::ProcessObject*)caller;
	if (typeid(event) == typeid(itk::ProgressEvent)) 
	{
		GetUpdateCallback()->SetProgress( processObject->GetProgress() );
		GetUpdateCallback()->Modified();

		if ( GetUpdateCallback()->GetAbortProcessing() )
		{
			processObject->AbortGenerateDataOn();
		}
	}
}

VTK processor

There's an example in the processor ShapeScaleProcessor of the SandboxPlugin. Before executing the filter, you should add an observer to ProgressEvent:

vtkSmartPointer<vtkCallbackCommand> progressCallback;
progressCallback = vtkSmartPointer<vtkCallbackCommand>::New();
progressCallback->SetCallback(ProgressFunction);
progressCallback->SetClientData( GetUpdateCallback().GetPointer() );
transformFilter->AddObserver( vtkCommand::ProgressEvent, progressCallback);

The progress function will update the UpdateCallback:

void SandboxPlugin::ShapeScaleProcessor::ProgressFunction(
	vtkObject* caller, long unsigned int eventId, void* clientData, void* callData)
{
	vtkAlgorithm* filter = static_cast<vtkAlgorithm*>(caller);
	Core::UpdateCallback* callback = static_cast<Core::UpdateCallback*> (clientData);
	callback->SetProgress( filter->GetProgress() );
	callback->Modified();

	filter->SetAbortExecute( callback->GetAbortProcessing() );
}


ITK Command Line Plugin

You can find this example in the thirdParty/SLICERAPPS. The Command Line Plugin name is Threshold.

To add progress to the reader, threshold and writer filters you need to add these lines:

itk::PluginFilterWatcher watchReader1(reader1, "Read Volume", CLPProcessInformation);
itk::PluginFilterWatcher watchFilter(filter, "Threshold image", CLPProcessInformation);
itk::PluginFilterWatcher watchWriter(writer, "Write Volume", CLPProcessInformation);

Multiple processing

Added a new feature that allows to execute a filter multiple times for 3D+T input data. For each time step, the filter will be executed.

To configure this option, there's a new button at the right of the input data selection control.

MultipleProcessing.png

Using this control, you can select the data you want to process.

Code changes

The configuration is stored on the BaseFilterInputPort class. There are two parameters:

  • UPDATE_ACCESS_MODE: Type of access when calling BaseFilter::Update()
    • UPDATE_ACCESS_SINGLE_TIME_STEP: Access a single time step (Default)
    • UPDATE_ACCESS_MULTIPLE_TIME_STEP: Access multiple time steps
  • SELECT_TIME_STEP_TYPE: Type of selection when using UPDATE_SINGLE_TIME_STEP
    • SELECT_ALL_TIME_STEPS: Select all time steps from input DataEntity
    • SELECT_SINGLE_TIME_STEP: Select single time step from input DataEntity (Default)

When BaseFilter::Update() function is called using ProcessingWidget::UpdateProcessor(), it will check the configuration of each input port. If the port is configured as single time step update access mode, depending on the selection and number of time steps of the input data, a loop iteration will be executed. For each loop iteration, a different time step will be automatically selected calling SetSelectedTimeStep().

This fetaure is available for BaseFilter class and Command Line Plugins.

Rendering

EDOM for rendering data

Use EDOM to store rendering data. Each rendering data is stored inside a DataEntity using a string as key.

DataEntity::Pointer GetRenderingData( const std::string& name );
void SetRenderingData( const std::string& name, DataEntity::Pointer dataEntity );

For example, to manage the CMGUI rendering data you need to call:

Core::DataEntity::Pointer renDataEntity = dataEntity->GetRenderingData( "CMGUI" );
dataEntity->SetRenderingData( "CMGUI", renDataEntity );

All MITK data has its own DataEntityImpl.

  • MitkContourImpl: Stores a mitk::Contour
  • MITKCuboidImpl: Stores a mitk::Cuboid
  • MitkImageImpl: Stores a mitk::Image
  • MitkPointSetImpl: Stores a mitk::PointSet
  • MitkSignalImpl: Stores a blMitk::Signal
  • MitkSurfaceImpl: Stores a mitk::Surface
  • MitkTransformImpl: Stores a mitk::Transform

To create an MITK data from any processing data you just need to call these code:

// Get rendering data
Core::DataEntity::Pointer renDataEntity = dataEntity->GetRenderingData( "MITK" );
if ( renDataEntity.IsNull() )
{
  renDataEntity = Core::DataEntity::New( Core::PointSetTypeId );
  renDataEntity->SwitchImplementation( typeid( mitk::PointSet::Pointer ) );
}

// Try to copy data ptr if the data type is the same
// Otherwise, try to copy the data
try
{
  renDataEntity->CopyDataPtr( dataEntity );
}
catch (...)
{
  renDataEntity->Copy( dataEntity, gmReferenceMemory );
}
dataEntity->SetRenderingData( "MITK", renDataEntity );


To retrieve the mitk::BaseData pointer, you need to call this function:

mitk::BaseData::Pointer renderingData;
renderingData = Core::RenDataFactory::GetBaseRenderingData( GetSelectedPointsDataEntity( ) );

Rendering tags

When a processor creates a new DataEntity and this data is added to the MITK rendering tree, the default rendering properties are applied. Now is possible to use Metadata to set specific rendering properties, like opacity or window level. When the DataEntity is added to the rendering tree, the metadata is scanned and all properties are converted to MITK. All the rendering properties stored in the metadata will be automatically stored in disk when the data is saved. Next time the data is opened, the properties will be restored.

To use this property, you should pass a blTagMap::Pointer to the UpdateOutput( ) function with the properties you want to apply.

MITK data

Valid rendering properties for metadata that will be converted to MITK properties are:

  • any property of type bool
  • any property of type int
  • any property of type float
  • any property of type double
  • string:
    • With the name "color": is a string like "0.3,0.6,0.7"
    • any property of type string
  • blTagMap::Pointer
    • With the name "levelwindow", all this child tags should be of type double:
      • "center"
      • "width"
      • "lowerWindowBound"
      • "upperWindowBound"
      • "rangeMin"
      • "rangeMax"
      • "defaultRangeMin"
      • "defaultRangeMax"

Signal data

Valid rendering properties for metadata that will be used in signal viewer are:

  • "color": std::string, like "0.3,0.5,0.9"
  • "width": int
  • "group": std::string, group name
  • "Legend": blTagMap::Pointer
    • "x position", int, position in pixels

Plugin provider

Loading plugins

When GIMIAS starts, it scans all available plugins in the plugins folder. Before, all plugins where loaded by default, so it wastes time loading unnecessary plugins. Now, the plugin name will be read from the plugin.xml file and only the configured plugins will be loaded.

Plugin provider

Now is possible to add extend GIMIAS with new plugin providers. A plugin provider provides plugins and loads them. PluginProvider class is the interface class.

Plugin providers class diagram

There are two default plugin providers:

  • FrontEndPluginProvider: Provides GIMIAS plugins, like DICOMPlugin
  • CommandLinePluginProvider: Provides command line plugins

You can add more plugin providers instances if you go to Preferences->Plugin configuration. For example, to add a new instance of command line plugin provider, you need to click the button "Import new plugins" and set these parameters:

Plugin providers configuration
Plugin providers configuration for 3D Slicer 4.0

The configuration of the plugin provider will be stored in the properties of the PluginProvider, under the tag name "Configuration".

Selected plugins

The selected plugins will be saved independently for each plugin provider because two plugin providers can use the same plugin name. The configuration of the selected plugins will be stored in the properties of the PluginProvider, under the tag name "Plugins".

Note that Profile is not used anymore, just for backwards compatibility.

SSH

Added SSH functionality that allows to connect to remote machines using SSH protocol and transfer files or execute commands.

You can read more here SSH Plugin

DICOM and PACS

DICOM Plugin

Moved the code that reads DICOM files from DicomWorkingAreaPanelWidget to Core::IO::DICOMFileReader. This allows to use the reader without DICOM plugin and avoid code duplication.

Another new feature is that when reading a DICOM folder, the time tag is choosen automatically using a file called DICOMTags.xml. You can modify the file and add new manufacturer, modality to select the time tag.


<?xml version="0" ?>
<tagmap size="5">
    <tag name="Default" type="class blSmartPointer<class blTagMap>">Unsupported value type
        <tagmap size="3">
            <tag name="CT" type="std::string">7005,1004:Percentage of full heartbeat</tag>
            <tag name="MR" type="std::string">0018,1060:Trigger Time</tag>
            <tag name="US" type="std::string">0020,0015:Phase Number</tag>
        </tagmap>
    </tag>
    <tag name="ge medical systems" type="class blSmartPointer<class blTagMap>">Unsupported value type
        <tagmap size="2">
            <tag name="CT" type="std::string">0008,0018:SOP Instance UID</tag>
            <tag name="MR" type="std::string">0018,1060:Trigger Time</tag>
        </tagmap>
    </tag>
    <tag name="philips medical systems" type="class blSmartPointer<class blTagMap>">Unsupported value type
        <tagmap size="2">
            <tag name="MR" type="std::string">2001,1008:Phase Number (dcmtk)</tag>
            <tag name="US" type="std::string">0020,0015:Phase Number</tag>
        </tagmap>
    </tag>
    <tag name="siemens" type="class blSmartPointer<class blTagMap>">Unsupported value type
        <tagmap size="2">
            <tag name="CT" type="std::string">0020,0012:Acquisition Number</tag>
            <tag name="MR" type="std::string">0018,1060:Trigger Time</tag>
        </tagmap>
    </tag>
    <tag name="toshiba" type="class blSmartPointer<class blTagMap>">Unsupported value type
        <tagmap size="1">
            <tag name="CT" type="std::string">7005,1004:Percentage of full heartbeat</tag>
        </tagmap>
    </tag>
</tagmap>

Added DICOM tags browser that allow to see all DICOM tags of a specific DICOM file and search for a specific tag.


DICOM Tags browser

DICOMRT

Now is possible to read DICOMRT studies. RTSTRUCT, RTDOSE can be read as images.

  • RTSTRUCT contains the structures defined by the user, like heart of lumb. These structures will be added to GIMIAS as a mask image. This allows to use ROI statistics processing tool to extract statistics from each structure, like volume, maximum dosis and minimum dosis
  • RTDOSE contains the quantity of dosis applied to the patient
DICOMRT

In this image you can see the initial CT image, several structures and the dosis image.

At the right you can see the ROI statistics panel widget. To compute the volume or min/max dose of a structure, you should select the dose image and the desired structure. Setting the check box "Export histogram", is possible to export the histogram inside the structure.

At the bottom you can see the signal viewer with the histograms of heart, left Lumb, tumor bed and tumor bed block. You can see that the dose of heart and lumb is low and the dose of tumor is high.

PACS

Added possibility to connect to ClearCanvas PACS server that you can install for free here.

CCLogo.png

You can query the database and retrieve a single serie.

Retrieve images from ClearCanvas server

gmCore Dependencies

MITK Plugin

Moved VTK dependent classes to MITKPlugin. This refactoring reduces unnecessary complexity of GIMIAS framework kernel libraries. This change will not affect the rest of the plugins because we kept the same class names and file names.

gmKernel

Removed dependency of gmKernel to wxWidgets. This change simplifies the complexity of gmKernel and will allow to reuse this library over other GUI libraries like Qt.

Moved the classes SplashScreen, StyleManager to gmWidgets library and created a subclass of Environment for wxWidgets called WxEnvironment.

To start the Kernel you need to implement two interfaces and pass it to the Initialize function:

  • Core::Widgets::BaseMainWindow
  • Core::Runtime::Environment

Development tools

Automated build

Started using CDash as an automated build system for GIMIAS. This is the link address to see all nightly builds and tests performed: GIMIAS Dash board

CDash.png

10 tests has been added:

  • gmKernelTests
  • MITKPluginTests
  • SshAPITests
  • BaseLibTests
  • BaseLibVTKTests
  • DcmAPITests
  • GIMIASLibTests
  • gmCommonObjectsTests
  • MeshLibTests
  • PacsAPITests

Visual Studio 2010

Now you can compile GIMIAS with VS2010 (if you use Express version, you need to configure Visual Studio executable in CSnake).

2010Products Hero.png

CSnake

Add a container solution with just one plugin

You can create a Visual Studio solution with just your plugin.

Signal Viewer plugin solution

To create this configuration you need to select the .py file of your plugin and use the parameter _includeInSolution:

signalViewerPlugin.AddProjects( [ gmCore, wxMathPlot ], _includeInSolution = False )

Added Plugins category

Added the "Plugins" category that allows to select all plugins

Automatic Updates

Added automatic updates feature. Now, you can update the version of any plugin or the GIMIAS kernel libraries. These fetaure allows to update the users with new version of your plugins and update the users with new plugins.

For this version of GIMIAS, the updates will keep compatibility with GIMIAS-1.4 version. There will be a new installer for GIMIAS-1.5.

Check for updates

When GIMIAS starts, it will check if new updates are available. You can modify the settings in the Preferences dialog.

Update preferences

When new updates are available, you will see a message:

New updates are available

You can click on the toolbar icon and a new window will be shown that allows you to select the desired packages.

Toolbar icon
Update dialog

You can see brief information about each package and select or deselect these. Once you have finished, you can download and install.

Download and Install

When the user clicks "Download + install", GIMIAS will be closed and the WebUpdate application will download and install the selected packages. Once it's finished, GIMIAS will be restarted again.

Advanced options

There are some advanced options:

Update advanced options

These are the advanced options:

  • You can choose the temporal folder where to download the selected packages
  • When your computer is inside a private network and you are using a proxy to access Internet, you need to configure the proxy settings
  • For specific GIMIAS package servers, you can specify the authentication settings
  • You can add more update sites. For example, you can configure your own server to update the users with new package updates

Web Updater

Automatic updates feature is based on a wxWidgets component called wxWebUpdate. You can find more information here: http://wxcode.sourceforge.net/docs/webupdate/index.html. The application has been extended for GIMIAS. One of the important extended features are:

  • Added compiler tag for package update
  • Allow to add multiple update sites
  • Avoid to use an xrc file for dialog and use wxGlade files converted to C++

You can find the application WebUpdate in the library TpExtLib. The application depends on two libraries: tpExtLibWxhttpEngine and tpExtLibWxWebUpdate.

Configuration files

There are two configuration files used by WebUpdate. The local configuration contains information about the local verion of each package. The remote XML script contains information about the new package version and what actions to take once it is downloaded.

Local Configuration file

You will find an example of local configuration file in the SVN: src\Apps\Gimias\resource\local.xml. This file contains the local version number of each plugin.

<?xml version="1.0" encoding="UTF-8"?>
<webupdate version="1.0">
  <appname>GIMIAS</appname>
  <appfile>gimias</appfile>
  <restart>0</restart>
  <savelog>1</savelog>
  <remoteuri>http://www.gimias.org/update/GIMIAS_1_4/UpdateServerScript.xml</remoteuri>
  <keywords></keywords>
  <local-package id="Core">
    <version>1.4.0</version>
  </local-package>
  <local-package id="CardioToolsPlugin">
    <version>1.4.0</version>
  </local-package>
</webupdate>

You can find the description of all tags here: [1]

Remote XML Script

You will find an example of remote XML script in the SVN: src\Apps\Gimias\resource\UpdateServerScript.xml. This file contains the information for each update for each package. This file should be stored in the server.

<?xml version="1.0"?>
<!DOCTYPE webupdate PUBLIC "-//WebUpdate//DTD WebUpdater 1.0//EN" "http://www.gimias.org/update/remote.dtd">

<webupdate version="1.0">

  <msg-update-available>Some updates for "$(appname)" are available</msg-update-available>
  <msg-update-notavailable>You have the most updated version of "$(appname)"...</msg-update-notavailable>
 
  <package id="DicomPlugin">
      <latest-version importance="high">1.4.1</latest-version>

      <latest-download>

        <uri>http://www.gimias.org/update/GIMIAS_1_4/DicomPlugin-$(latest-version).zip</uri>
        <md5>AD6AFC1583E0503C29BFF47393C0B83F</md5>
        <platform name="msw" arch="32" compiler="VS2010"/>

        <actions>
          <extract file="$(thisfile)" where="$(programdir)"/>
        </actions>

      </latest-download>

      <description>The package containing the binaries for DICOM Plugin in debug mode </description>
  </package>

</webupdate>

Description:

  • The field md5 is a security field that will be verified when the package is downloaded in the local computer. In Windows, you can generate a MD5 value for a zip package using this tool: File Checksum Integrity Verifier utility
fciv DicomPlugin-1.4.1.zip
  • platform: is used to specify the compatible platform for this package. This settings should match the platform of the local GIMIAS application.
  • actions: actions to perform on the downloaded package.

You can find the description of all tags here: [2]

XNAT

Added XNAT functionality that allows to connect to a remote XNAT database.

You can read more here XNAT Plugin

Web services

A new plugin has been added for execution of Web Services. You can read more here: WebServices Plugin.

Misc

Hide console window

In Windows, each time GIMIAS starts, the console window is shown by default. For this version of GIMIAS, in release mode, the console window will not be shown. To show the console window, you need to set a command line parameter when executing gimias.exe:

gimias.exe --consolewin

GIMIAS Extensions

Added a new repository with some plugin extensions for GIMIAS. The SVN URL is: https://svn.gimias.org/svn/toolkit/gimias/extensions/trunk

To compile it you need to configure this folder as a second project in CSnake.

For example, one of the plugin you can find in this repository is VMTKPlugin.

VMTK Plugin

Upgrade guide from GIMIAS 1.3 to GIMIAS 1.4

Processing window

Processor should not call SetState or any other GUI function. This function should be called by ProcessorManager when executing a processor. If the execution is made in the main GUI thread (no multithreading), it will show the processing window. Otherwise, it will use the status bar to show the progress.

Before
The process state is managed by each processor. Exceptions are controlled by try, catch, and throw statements.

BaseProcessor::SetState( Core::Runtime::APP_STATE_PROCESSING );

The widget calls the processor function Update() to execute it:

m_Processor->Update( );

After
The process state is controlled by the ProcessorManager, which is called on each widget with the inclusion of the next block of code, when Apply button is pressed by the user. Try statements are not necessary on processor methods.

// Set multithreading to false if you want to execute it directly by main GUI thread
GetProcessor( )->SetMultithreading( false );
Core::Runtime::Kernel::GetProcessorManager()->Execute( GetProcessor( ) );

BaseWindowFactory

There was a conflict in Windows with the name of the function CreateWindow( ) and it has been renamed to CreateBaseWindow( ).

ProgressTicket

Removed the class ProgressTicket. Now, it uses UpdateCallback to update the progress of an algorithm.

DataEntityImpl

Throw an exception when there's an error. Changed DataEntityImpl interface. The new interface affects these functions:

void DeepCopy( boost::any val, ImportMemoryManagementType mem = gmCopyMemory );
void DeepCopy( DataEntityImpl::Pointer input, ImportMemoryManagementType mem = gmCopyMemory );
virtual void SetData( blTagMap::Pointer tagMap, ImportMemoryManagementType mem = gmCopyMemory );
virtual void GetData( blTagMap::Pointer tagMap );
blTag::Pointer SafeFindTag( blTagMap::Pointer tagMap, const std::string &name );
virtual void SetAt( size_t pos, DataEntityImpl::Pointer data, ImportMemoryManagementType mem = gmCopyMemory );
virtual void SetDataPtr( boost::any val );

Use FactoryManager to register all implementations. DataEntityImplFactory has been removed and added DataEntityImplFactoryHelper that facilitates the search of a Factory and mantain the macros to create the factory. To register a factory you need to call Register( ) function:

FactoryManager::Register( DataEntityImpl::GetNameClass(), VtkPolyDataImpl::Factory::New() );

Each factory stores a set of tags that allow to search it:

  • "single" or "multiple": If the implementation is multiple over time or single time step
  • "DefaultDataEntityType": Default DataEntityType that will be set to the DataEntity if none is provided, like SurfaceMeshTypeId
  • "DataType": is a blTagMap that contains name of all valid data types that can manage this factory, like vtkPolyData*

The interface class DataEntityImpl has changed the function IsValidType() that now uses a string as parameter:

virtual bool IsValidType( const std::string &datatypename );

RenderingTreeMITK

Removed some code from RenderingTreeMITK:

  • RenderingTreeMITK::UpdateNode() has been removed
  • ApplyLUTToFirstScalarsVector has been moved to DataTreeMITKHelper
  • SetNodeDefaultProperties has been moved to RenDataBuilder

BaseWindowFactory

Removed GetName( ) function. Now it uses the function GetNameOfClass( ) defined using the macro coreClassNameMacro. The use of typeid( ) for the name of the class is not cross platform.

You need to change the member function GetName() for all classes derived from Core::BaseWindowFactory.

In Windows, all window names stored in the GIMIAS XML workflow file are like: "class Core::Widgets::MovieToolbar". These names are updated automatically to the new format: "MovieToolbarFactory". Note that the namespace has been removed from old name and can create conflicts with other class names.

blTagMap

Removed unsigned long Id from blTag. Now it uses the blTag::GetName( ) as ID. This simplifies the management of tags.

Before

blTag::Pointer tag = blTag::New( 0, "initialTimeStep", time );

After

blTag::Pointer tag = blTag::New( "initialTimeStep", time );


Plugin name

The old plugin loading strategy, was taking the name of the macro coreDefinePluginAddProfileMacro( ) to check plugin name. Now it takes the name from the plugin XML file.

To mantain backwards compatibility, you need to be sure that the plugin caption in the XML file matches the old one.

Note that you need to install the plugin.xml file into the binary folder, once it has been modified using the button "Install files to Build Folder" of CSnake.

Before

coreBeginDefinePluginMacro(SandboxPlugin::SandboxPlugin)
	coreDefinePluginAddProfileMacro("Sandbox")
coreEndDefinePluginMacro()
<?xml version="1.0" ?>
<plugin name="SandboxPlugin">
    <depends>
    </depends>
</plugin>


After

coreBeginDefinePluginMacro(SandboxPlugin::SandboxPlugin)
coreEndDefinePluginMacro()
<?xml version="1.0" ?>
<plugin name="SandboxPlugin" caption="Sandbox">
    <depends>
    </depends>
</plugin>

Widgets Class name and clinical workflow XML files

There was a bug in the previous version of GIMIAS because the name of class was set using the typeid operator. This name was used when storing the windows that will be loaded when using a clinical XML workflow file. In Linux the typeid operator is not using the same method than in Windows and this gives compatibility problems between XML files.

To fix this problem we use another method to set the class name for a class. Now it uses the string you pass to the macro coreClassNameMacro.

Before

// The name of the class in Windows is "class Core::Widgets::ImageInfoWidget".
// The name of the factory class in Windows is "class Core::Widgets::ImageInfoWidget".
coreDefineBaseWindowFactory( ImageInfoWidget )
coreClassNameMacro(ImageInfoWidget);

After

// The name of the class in Windows is "Core::Widgets::ImageInfoWidget".
// The name of the factory class in Windows is "Core::Widgets::ImageInfoWidgetFactory".
coreDefineBaseWindowFactory( Core::Widgets::ImageInfoWidget )
coreClassNameMacro(Core::Widgets::ImageInfoWidget);

Note that you need to specify the namespace in order to be compatible with the previous version of GIMIAS.

SignalViewerPlugin

Exported all classes so now these can be reused by other plugins.

Before

m_SignalWindow = new svSignalPlotWindow( NULL, NULL, this, wxID_ANY );
m_SignalWindow->AddSignal( 0, signal );

After

m_SignalWindow = new svSignalPlotWindow( this, wxID_ANY );
m_SignalWindow->AddSignal( signal );

Main window

To retrieve the main window you need to call the RuntimeGraphicalInterfacePointer and call the function GetMainWindow(). Now this function returns the base class BaseMainWindow. In order to use the wxWidgets class wxMitkCoreMainWindow, you need to cast it to wxWidgets.

Before

Core::Runtime::Kernel::RuntimeGraphicalInterfacePointer gIface;
gIface = Core::Runtime::Kernel::GetGraphicalInterface();
gIface->GetMainWindow()->Freeze();

After

Core::Runtime::Kernel::RuntimeGraphicalInterfacePointer gIface;
gIface = Core::Runtime::Kernel::GetGraphicalInterface();
Core::Widgets::BaseMainWindow* bmw = gIface->GetMainWindow();
Core::Widgets::wxMitkCoreMainWindow* mcmw = dynamic_cast<Core::Widgets::wxMitkCoreMainWindow*>( bmw );

Processor factories

Removed ProcessorFactories class. Now it uses the standard FactoryManager.

Before

#include "coreProcessorFactories.h"
#include "coreProcessorFactory.h"
Core::ProcessorFactories::Pointer factories;
factories = Core::Runtime::Kernel::GetProcessorFactories();
factories->RegisterFactory( ChangeOrientationProcessor::Factory::NewBase( ) );

After

#include "coreFactoryManager.h"
Core::FactoryManager::Register( Core::BaseProcessor::GetNameClass( ), ChangeOrientationProcessor::Factory::New( ) );
Core::FactoryManager::UnRegister( Core::FactoryManager::FindByInstanceClassName( ChangeOrientationProcessor::GetNameClass( ) );