selxSuperElastixFilter.hxx 11.7 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
/*=========================================================================
*
*  Copyright Leiden University Medical Center, Erasmus University Medical
*  Center and contributors
*
*  Licensed under the Apache License, Version 2.0 (the "License");
*  you may not use this file except in compliance with the License.
*  You may obtain a copy of the License at
*
*        http://www.apache.org/licenses/LICENSE-2.0.txt
*
*  Unless required by applicable law or agreed to in writing, software
*  distributed under the License is distributed on an "AS IS" BASIS,
*  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*  See the License for the specific language governing permissions and
*  limitations under the License.
*
*=========================================================================*/

20
21
22
23
#ifndef selxSuperElastixFilter_hxx
#define selxSuperElastixFilter_hxx

#include "selxSuperElastixFilter.h"
24
#include "selxRegisterComponentFactoriesByTypeList.h"
25

26
27
28
29
30
31
namespace selx
{
/**
 * ********************* Constructor *********************
 */

32
33
template< typename ComponentTypeList >
SuperElastixFilter< ComponentTypeList >
34
35
36
37
::SuperElastixFilter( void ) :
  m_InputConnectionModified( true ),
  m_OutputConnectionModified( true ),
  m_BlueprintConnectionModified( true ),
38
39
  m_IsConnected( false ),
  m_AllUniqueComponents( false )
40
{
41
  RegisterFactoriesByTypeList< ComponentTypeList >::Register();
42

43
  // Disable "Primary" as required input
44
  this->SetRequiredInputNames( {} );
45
46
47
48
} // end Constructor


/**
49
50
51
* ********************* GenerateOutputInformation *********************
*/

52
template< typename ComponentTypeList >
53
void
54
SuperElastixFilter< ComponentTypeList >
55
::GenerateOutputInformation()
56
{
57
58
59
60
  /*
  * Override the ProcessObject default implementation, since outputs may be different types and
  * therefore the output information must come from the sink components.
  */
61

62
  // TODO this method should be revised together with overlord->Configure(). A functionality
63
64
  // is foreseen in which the user may provide a minimalistic configuration (blueprint) and in
  // which other criteria to select a component are derived from neighboring components.
65
66
67
68
69
70
  // E.g. we could implement source nodes that provides ImageReaders of multiple dimensions.
  // At connecting these reader, the dimensionality is known from the data and the source
  // component can pass the dimensionality as an additional criterion to the component selectors
  // downstream. By (re-)configuring these, unique components may be selected and their requirements
  // are passed further down stream.
  // Eventually configuration boils down to a while loop that repeatedly tries to narrow down
71
  // the component selectors until no more unique components can be found.
72
  if (!this->m_Overlord)
73
  {
74
75
76
77
78
79
    if (!this->m_Blueprint)
    {
      itkExceptionMacro(<< "Setting a Blueprint is required first.")
    }

    this->m_Overlord = OverlordPointer(new Overlord(this->m_Blueprint->Get()));
80
    this->m_AllUniqueComponents = this->m_Overlord->Configure();
81
  }
82
83
84
85
  else if (this->m_BlueprintConnectionModified == true)
  {
//TODO: 
  }
86

87
  if( ( m_InputConnectionModified == true ) || ( this->m_BlueprintConnectionModified == true ) )
88
  {
89
90
91
    auto                             usedInputs = this->GetInputNames();
    Overlord::SourceInterfaceMapType sources    = this->m_Overlord->GetSourceInterfaces();
    for( const auto & nameAndInterface : sources )
92
    {
93
      auto foundIndex = std::find( usedInputs.begin(), usedInputs.end(), nameAndInterface.first );
94

95
      if( foundIndex == usedInputs.end() )
96
      {
97
98
        // or should we catch and rethrow nameAndInterface.second->SetMiniPipelineInput(this->GetInput(nameAndInterface.first)); ?
        itkExceptionMacro( << "SuperElastixFilter requires the input " "" << nameAndInterface.first << "" " for the Source Component with that name" )
99
100
      }

101
102
      nameAndInterface.second->SetMiniPipelineInput( this->GetInput( nameAndInterface.first ) );
      usedInputs.erase( foundIndex );
103
    }
104
    if( usedInputs.size() > 0 )
105
106
107
    {
      std::stringstream msg;
      msg << "These inputs are connected, but not used by any Source Component: " << std::endl;
108
      for( auto & unusedInput : usedInputs )
109
110
111
      {
        msg << unusedInput << std::endl;
      }
112
      itkExceptionMacro( << msg.str() )
113
114
115
    }
  }

116
  if( ( m_OutputConnectionModified == true ) || ( this->m_BlueprintConnectionModified == true ) )
117
  {
118
119
120
    auto                           usedOutputs = this->GetOutputNames();
    Overlord::SinkInterfaceMapType sinks       = this->m_Overlord->GetSinkInterfaces();
    for( const auto & nameAndInterface : sinks )
121
    {
122
      auto foundIndex = std::find( usedOutputs.begin(), usedOutputs.end(), nameAndInterface.first );
123

124
      if( foundIndex == usedOutputs.end() )
125
      {
126
127
        // or should we catch and rethrow nameAndInterface.second->SetMiniPipelineOutput(this->GetOutput(nameAndInterface.first)); ?
        itkExceptionMacro( << "SuperElastixFilter requires the output " "" << nameAndInterface.first << "" " for the Sink Component with that name" )
128
      }
129
130
131
132
      nameAndInterface.second->SetMiniPipelineOutput( this->GetOutput( nameAndInterface.first ) );
      usedOutputs.erase( foundIndex );
    }
    if( usedOutputs.size() > 0 )
133
134
135
    {
      std::stringstream msg;
      msg << "These outputs are connected, but not used by any Sink Component: " << std::endl;
136
      for( auto & unusedOutput : usedOutputs )
137
138
139
      {
        msg << unusedOutput << std::endl;
      }
140
      itkExceptionMacro( << msg.str() )
141
    }
142
143
  }

144
  if (this->m_AllUniqueComponents == false) // by setting inputs and outputs, settings could be derived to uniquely select the other components
145
  {
146
    this->m_AllUniqueComponents = this->m_Overlord->Configure();
147
148
  }

149
  if (this->m_AllUniqueComponents  && !this->m_IsConnected)
150
  {
151
152
    this->m_IsConnected = this->m_Overlord->ConnectComponents();
    std::cout << "Connecting Components: " << (this->m_IsConnected ? "succeeded" : "failed") << std::endl << std::endl;
153
154
  }

155
  if( ( m_OutputConnectionModified == true ) || ( this->m_BlueprintConnectionModified == true ) )
156
  {
157
    Overlord::SinkInterfaceMapType sinks = this->m_Overlord->GetSinkInterfaces();
158
159
160
161
162
163
164
    for( const auto & nameAndInterface : sinks )
    {
      // Update information: ask the mini pipeline what the size of the data will be
      nameAndInterface.second->GetMiniPipelineOutput()->UpdateOutputInformation();
      // Put the information into the Filter's output Objects by grafting
      this->GetOutput( nameAndInterface.first )->Graft( nameAndInterface.second->GetMiniPipelineOutput() );
    }
165
166
  }

167
  this->m_BlueprintConnectionModified = false;
168
169
  this->m_InputConnectionModified     = false;
  this->m_OutputConnectionModified    = false;
170
171
}

172

173
174
175
/**
 * ********************* GenerateData *********************
 */
176

177
template< typename ComponentTypeList >
178
void
179
SuperElastixFilter< ComponentTypeList >
180
::GenerateData( void )
181
{
182
  std::cout << "Executing Network:" << std::endl;
183
184
  // This calls controller components that take over the control flow if the itk pipeline is broken.
  this->m_Overlord->Execute();
185
186

  Overlord::SinkInterfaceMapType sinks = this->m_Overlord->GetSinkInterfaces();
187
  for( const auto & nameAndInterface : sinks )
188
  {
189
    // Here we force all output to be updated.
190
191
    // TODO: it might be desirable to leave parts of the mini pipeline 'outdated' for memory/speed reasons and only update if requested downsteam.
    nameAndInterface.second->GetMiniPipelineOutput()->Update();
192
    this->GetOutput( nameAndInterface.first )->Graft( nameAndInterface.second->GetMiniPipelineOutput() );
193
  }
194
195
}

196
197
198
199

template< typename ComponentTypeList >
typename SuperElastixFilter< ComponentTypeList >::AnyFileReaderType::Pointer
SuperElastixFilter< ComponentTypeList >
200
::GetInputFileReader(const DataObjectIdentifierType & inputName)
201
202
203
{
  //TODO: Before we can get the reader the Blueprint needs to set and applied in the overlord.
  // This is not like the itk pipeline philosophy
204
  if (!this->m_Overlord)
205
  {
206
207
208
209
210
    if (!this->m_Blueprint)
    {
      itkExceptionMacro(<< "Setting a Blueprint is required first.")
    }
    this->m_Overlord = OverlordPointer(new Overlord(this->m_Blueprint->Get()));
211
    this->m_AllUniqueComponents = this->m_Overlord->Configure();
212
  }
213
  if (!this->m_AllUniqueComponents)
214
215
  {
    itkExceptionMacro(<< "Blueprint was not sufficiently specified to build a network.")
216
  }
217
  return this->m_Overlord->GetInputFileReader( inputName );
218
219
}

220

221
222
223
template< typename ComponentTypeList >
typename SuperElastixFilter< ComponentTypeList >::AnyFileWriterType::Pointer
SuperElastixFilter< ComponentTypeList >
224
::GetOutputFileWriter( const DataObjectIdentifierType & outputName )
225
226
227
{
  //TODO: Before we can get the reader the Blueprint needs to set and applied in the overlord.
  // This is not like the itk pipeline philosophy
228
  if (!this->m_Overlord)
229
  {
230
231
232
233
    if (!this->m_Blueprint)
    {
      itkExceptionMacro(<< "Setting a Blueprint is required first.")
    }
234

235
    this->m_Overlord = OverlordPointer(new Overlord(this->m_Blueprint->Get()));
236
    this->m_AllUniqueComponents = this->m_Overlord->Configure();
237
  }
238
  if (!this->m_AllUniqueComponents)
239
240
241
  {
    itkExceptionMacro(<< "Blueprint was not sufficiently specified to build a network.")
  }
242

243
  return this->m_Overlord->GetOutputFileWriter( outputName );
244
245
}

246
247

template< typename ComponentTypeList >
248
void
249
SuperElastixFilter< ComponentTypeList >
250
::SetInput( const DataObjectIdentifierType & inputName, itk::DataObject * input )
251
{
252
  Superclass::SetInput( inputName, input );
253
  this->m_InputConnectionModified = true;
254
255
}

256

257
template< typename ComponentTypeList >
258
259
260
typename SuperElastixFilter< ComponentTypeList >::OutputDataType
* SuperElastixFilter< ComponentTypeList >
::GetOutput( const DataObjectIdentifierType &outputName )
261
{
262
263
  OutputDataType * output = Superclass::GetOutput( outputName );
  if( output != nullptr ) // if an output already exists, return it
264
265
266
  {
    return output;
  }
267
  else  // otherwise ask the sink component to initialize an output of the right type (the sink knows what type that is).
268
  {
269
    if (!this->m_Overlord)
270
    {
271
272
273
274
      if (!this->m_Blueprint) // to ask the sink it must be configured by a blueprint.
      {
        itkExceptionMacro(<< "Setting a Blueprint is required first.")
      }
275

276
      this->m_Overlord = OverlordPointer(new Overlord(this->m_Blueprint->Get()));
277
      this->m_AllUniqueComponents = this->m_Overlord->Configure();
278
279
      this->m_BlueprintConnectionModified = false;
    }
280
281
282
283
    typename OutputDataType::Pointer newOutput = this->m_Overlord->GetInitializedOutput( outputName );
    this->m_OutputConnectionModified           = true;

    Superclass::SetOutput( outputName, newOutput );
284
285
286

    return newOutput;
  }
287
288
}

289
290
template< typename ComponentTypeList >
template< typename ReturnType >
291
ReturnType *
292
SuperElastixFilter< ComponentTypeList >
293
::GetOutput( const DataObjectIdentifierType & outputName )
294
{
295
296
  OutputDataType * output = Superclass::GetOutput( outputName );
  if( output != nullptr ) // if an output already exists, return it
297
  {
298
299
    ReturnType * returnOutput = dynamic_cast< ReturnType * >( output );
    if( returnOutput != nullptr ) // if it is of the same type as requested before
300
301
302
    {
      return returnOutput;
    }
303
    itkExceptionMacro( << "Output " "" << outputName << "" " was requested before, but the ReturnTypes do not match" )
304
  }
305
  // Purposely not checking the outputName, but just create the requested&named data object in the filter.
Floris Berendsen's avatar
Floris Berendsen committed
306
  // When connecting the Sinks the selxFilter names and data types are checked.
FBerendsen's avatar
FBerendsen committed
307
  typename ReturnType::Pointer newOutput = ReturnType::New();
308
309
310

  Superclass::SetOutput( outputName, newOutput );

311
  this->m_OutputConnectionModified = true;
312
  return newOutput;
313
314
}

315

316
template< typename ComponentTypeList >
317
318
319
void
SuperElastixFilter< ComponentTypeList >
::Update( void )
320
321
322
323
324
{
  //this->SetPrimaryOutput()
  this->GenerateOutputInformation();
  this->GenerateData();
}
325
326
327
} // namespace elx

#endif // selxSuperElastixFilter_hxx