3
3
#include " ITKImageProcessing/Common/ITKArrayHelper.hpp"
4
4
#include " ITKImageProcessing/Filters/ITKImageReader.hpp"
5
5
6
+ #include " simplnx/Common/TypesUtility.hpp"
6
7
#include " simplnx/Core/Application.hpp"
7
8
#include " simplnx/DataStructure/DataPath.hpp"
8
9
#include " simplnx/DataStructure/Geometry/ImageGeom.hpp"
@@ -57,6 +58,8 @@ const ChoicesParameter::ValueType k_FlipAboutYAxis = 2;
57
58
const Uuid k_SimplnxCorePluginId = *Uuid::FromString (" 05cc618b-781f-4ac0-b9ac-43f26ce1854f" );
58
59
const Uuid k_RotateSampleRefFrameFilterId = *Uuid::FromString (" d2451dc1-a5a1-4ac2-a64d-7991669dcffc" );
59
60
const FilterHandle k_RotateSampleRefFrameFilterHandle (k_RotateSampleRefFrameFilterId, k_SimplnxCorePluginId);
61
+ const Uuid k_ColorToGrayScaleFilterId = *Uuid::FromString (" d938a2aa-fee2-4db9-aa2f-2c34a9736580" );
62
+ const FilterHandle k_ColorToGrayScaleFilterHandle (k_ColorToGrayScaleFilterId, k_SimplnxCorePluginId);
60
63
61
64
// Make sure we can instantiate the RotateSampleRefFrame Filter
62
65
std::unique_ptr<IFilter> CreateRotateSampleRefFrameFilter ()
@@ -132,7 +135,8 @@ namespace cxITKImportImageStack
132
135
{
133
136
template <class T >
134
137
Result<> ReadImageStack (DataStructure& dataStructure, const DataPath& imageGeomPath, const std::string& cellDataName, const DataPath& imageDataPath, const std::vector<std::string>& files,
135
- ChoicesParameter::ValueType transformType, const IFilter::MessageHandler& messageHandler, const std::atomic_bool& shouldCancel)
138
+ ChoicesParameter::ValueType transformType, bool convertToGrayscale, VectorFloat32Parameter::ValueType luminosityValues, const IFilter::MessageHandler& messageHandler,
139
+ const std::atomic_bool& shouldCancel)
136
140
{
137
141
auto & imageGeom = dataStructure.getDataRefAs <ImageGeom>(imageGeomPath);
138
142
@@ -147,13 +151,21 @@ Result<> ReadImageStack(DataStructure& dataStructure, const DataPath& imageGeomP
147
151
// Variables for the progress Reporting
148
152
usize slice = 0 ;
149
153
154
+ auto * filterListPtr = Application::Instance ()->getFilterList ();
155
+
156
+ if (convertToGrayscale && !filterListPtr->containsPlugin (k_SimplnxCorePluginId))
157
+ {
158
+ return MakeErrorResult (-18542 , " SimplnxCore was not instantiated in this instance, so color to grayscale is not a valid option." );
159
+ }
160
+ auto grayScaleFilter = filterListPtr->createFilter (k_ColorToGrayScaleFilterHandle);
161
+ Result<> outputResult = {};
162
+
150
163
// Loop over all the files importing them one by one and copying the data into the data array
151
164
for (const auto & filePath : files)
152
165
{
153
166
messageHandler (IFilter::Message::Type::Info, fmt::format (" Importing: {}" , filePath));
154
167
155
168
DataStructure importedDataStructure;
156
-
157
169
{
158
170
// Create a sub-filter to read each image, although for preflight we are going to read the first image in the
159
171
// list and hope the rest are correct.
@@ -171,6 +183,50 @@ Result<> ReadImageStack(DataStructure& dataStructure, const DataPath& imageGeomP
171
183
return executeResult.result ;
172
184
}
173
185
}
186
+
187
+ // ======================= Convert to GrayScale Section ===================
188
+ bool validInputForGrayScaleConversion = importedDataStructure.getDataRefAs <IDataArray>(imageDataPath).getDataType () == DataType::uint8;
189
+ if (convertToGrayscale && validInputForGrayScaleConversion && nullptr != grayScaleFilter.get ())
190
+ {
191
+
192
+ // This same filter was used to preflight so as long as nothing changes on disk this really should work....
193
+ Arguments colorToGrayscaleArgs;
194
+ colorToGrayscaleArgs.insertOrAssign (" conversion_algorithm" , std::make_any<ChoicesParameter::ValueType>(0 ));
195
+ colorToGrayscaleArgs.insertOrAssign (" color_weights" , std::make_any<VectorFloat32Parameter::ValueType>(luminosityValues));
196
+ colorToGrayscaleArgs.insertOrAssign (" input_data_array_vector" , std::make_any<std::vector<DataPath>>(std::vector<DataPath>{imageDataPath}));
197
+ colorToGrayscaleArgs.insertOrAssign (" output_array_prefix" , std::make_any<std::string>(" gray" ));
198
+
199
+ // Run grayscale filter and process results and messages
200
+ auto result = grayScaleFilter->execute (importedDataStructure, colorToGrayscaleArgs).result ;
201
+ if (result.invalid ())
202
+ {
203
+ return result;
204
+ }
205
+
206
+ // deletion of non-grayscale array
207
+ DataObject::IdType id;
208
+ { // scoped for safety since this reference will be nonexistent in a moment
209
+ auto & oldArray = importedDataStructure.getDataRefAs <IDataArray>(imageDataPath);
210
+ id = oldArray.getId ();
211
+ }
212
+ importedDataStructure.removeData (id);
213
+
214
+ // rename grayscale array to reflect original
215
+ {
216
+ auto & gray = importedDataStructure.getDataRefAs <IDataArray>(imageDataPath.getParent ().createChildPath (" gray" + imageDataPath.getTargetName ()));
217
+ if (!gray.canRename (imageDataPath.getTargetName ()))
218
+ {
219
+ return MakeErrorResult (-64543 , fmt::format (" Unable to rename the internal grayscale array to {}" , imageDataPath.getTargetName ()));
220
+ }
221
+ gray.rename (imageDataPath.getTargetName ());
222
+ }
223
+ }
224
+ else if (convertToGrayscale && !validInputForGrayScaleConversion)
225
+ {
226
+ outputResult.warnings ().emplace_back (Warning{
227
+ -74320 , fmt::format (" The array ({}) resulting from reading the input image file is not a UInt8Array. The input image will not be converted to grayscale." , imageDataPath.getTargetName ())});
228
+ }
229
+
174
230
// Check the ImageGeometry of the imported Image matches the destination
175
231
const auto & importedImageGeom = importedDataStructure.getDataRefAs <ImageGeom>(imageGeomPath);
176
232
SizeVec3 importedDims = importedImageGeom.getDimensions ();
@@ -206,11 +262,11 @@ Result<> ReadImageStack(DataStructure& dataStructure, const DataPath& imageGeomP
206
262
// Check to see if the filter got canceled.
207
263
if (shouldCancel)
208
264
{
209
- return {} ;
265
+ return outputResult ;
210
266
}
211
267
}
212
268
213
- return {} ;
269
+ return outputResult ;
214
270
}
215
271
} // namespace cxITKImportImageStack
216
272
@@ -256,6 +312,10 @@ Parameters ITKImportImageStack::parameters() const
256
312
params.insert (std::make_unique<VectorFloat32Parameter>(k_Spacing_Key, " Spacing" , " The spacing of the 3D volume" , std::vector<float32>{1 .0F , 1 .0F , 1 .0F }, std::vector<std::string>{" X" , " y" , " Z" }));
257
313
params.insertLinkableParameter (std::make_unique<ChoicesParameter>(k_ImageTransformChoice_Key, " Optional Slice Operations" ,
258
314
" Operation that is performed on each slice. 0=None, 1=Flip about X, 2=Flip about Y" , 0 , k_SliceOperationChoices));
315
+ params.insertLinkableParameter (
316
+ std::make_unique<BoolParameter>(k_ConvertToGrayScale_Key, " Convert To GrayScale" , " The filter will show an error if the images are already in grayscale format" , false ));
317
+ params.insert (std::make_unique<VectorFloat32Parameter>(k_ColorWeights_Key, " Color Weighting" , " RGB weights for the grayscale conversion using the luminosity algorithm." ,
318
+ std::vector<float32>{0 .2125f , 0 .7154f , 0 .0721f }, std::vector<std::string>({" Red" , " Green" , " Blue" })));
259
319
260
320
params.insertSeparator (Parameters::Separator{" File List" });
261
321
params.insert (
@@ -266,6 +326,8 @@ Parameters ITKImportImageStack::parameters() const
266
326
params.insert (std::make_unique<DataObjectNameParameter>(k_CellDataName_Key, " Cell Data Name" , " The name of the created cell attribute matrix" , ImageGeom::k_CellDataName));
267
327
params.insert (std::make_unique<DataObjectNameParameter>(k_ImageDataArrayPath_Key, " Created Image Data" , " The path to the created image data array" , " ImageData" ));
268
328
329
+ params.linkParameters (k_ConvertToGrayScale_Key, k_ColorWeights_Key, true );
330
+
269
331
return params;
270
332
}
271
333
@@ -283,11 +345,17 @@ IFilter::PreflightResult ITKImportImageStack::preflightImpl(const DataStructure&
283
345
auto origin = filterArgs.value <VectorFloat32Parameter::ValueType>(k_Origin_Key);
284
346
auto spacing = filterArgs.value <VectorFloat32Parameter::ValueType>(k_Spacing_Key);
285
347
auto imageGeomPath = filterArgs.value <DataPath>(k_ImageGeometryPath_Key);
286
- auto imageDataName = filterArgs.value <DataObjectNameParameter::ValueType>(k_ImageDataArrayPath_Key);
348
+ auto pImageDataArrayNameValue = filterArgs.value <DataObjectNameParameter::ValueType>(k_ImageDataArrayPath_Key);
287
349
auto cellDataName = filterArgs.value <DataObjectNameParameter::ValueType>(k_CellDataName_Key);
288
350
auto imageTransformValue = filterArgs.value <ChoicesParameter::ValueType>(k_ImageTransformChoice_Key);
351
+ auto pConvertToGrayScaleValue = filterArgs.value <bool >(k_ConvertToGrayScale_Key);
352
+ auto pColorWeightsValue = filterArgs.value <VectorFloat32Parameter::ValueType>(k_ColorWeights_Key);
289
353
290
- const DataPath imageDataPath = imageGeomPath.createChildPath (cellDataName).createChildPath (imageDataName);
354
+ PreflightResult preflightResult;
355
+ nx::core::Result<OutputActions> resultOutputActions = {};
356
+ std::vector<PreflightValue> preflightUpdatedValues;
357
+
358
+ const DataPath imageDataPath = imageGeomPath.createChildPath (cellDataName).createChildPath (pImageDataArrayNameValue);
291
359
292
360
if (imageTransformValue != k_NoImageTransform)
293
361
{
@@ -314,7 +382,7 @@ IFilter::PreflightResult ITKImportImageStack::preflightImpl(const DataStructure&
314
382
imageReaderArgs.insertOrAssign (ITKImageReader::k_FileName_Key, std::make_any<fs::path>(files.at (0 )));
315
383
316
384
const ITKImageReader imageReader;
317
- PreflightResult imageReaderResult = imageReader.preflight (dataStructure, imageReaderArgs, messageHandler);
385
+ PreflightResult imageReaderResult = imageReader.preflight (dataStructure, imageReaderArgs, messageHandler, shouldCancel );
318
386
if (imageReaderResult.outputActions .invalid ())
319
387
{
320
388
return imageReaderResult;
@@ -326,15 +394,15 @@ IFilter::PreflightResult ITKImportImageStack::preflightImpl(const DataStructure&
326
394
const auto * createImageGeomActionPtr = dynamic_cast <const CreateImageGeometryAction*>(action0Ptr);
327
395
if (createImageGeomActionPtr == nullptr )
328
396
{
329
- throw std::runtime_error (" ITKImportImageStack : Expected CreateImageGeometryAction at index 0" );
397
+ throw std::runtime_error (fmt::format ( " {} : Expected CreateImageGeometryAction at index 0" , this -> humanName ()) );
330
398
}
331
399
332
400
// The second action should be the array creation
333
401
const IDataAction* action1Ptr = imageReaderResult.outputActions .value ().actions .at (1 ).get ();
334
402
const auto * createArrayActionPtr = dynamic_cast <const CreateArrayAction*>(action1Ptr);
335
403
if (createArrayActionPtr == nullptr )
336
404
{
337
- throw std::runtime_error (" ITKImportImageStack : Expected CreateArrayAction at index 1" );
405
+ throw std::runtime_error (fmt::format ( " {} : Expected CreateArrayAction at index 1" , this -> humanName ()) );
338
406
}
339
407
340
408
// X Y Z
@@ -344,11 +412,36 @@ IFilter::PreflightResult ITKImportImageStack::preflightImpl(const DataStructure&
344
412
// Z Y X
345
413
const std::vector<usize> arrayDims (dims.crbegin (), dims.crend ());
346
414
347
- OutputActions outputActions;
348
- outputActions.appendAction (std::make_unique<CreateImageGeometryAction>(std::move (imageGeomPath), std::move (dims), std::move (origin), std::move (spacing), cellDataName));
349
- outputActions.appendAction (std::make_unique<CreateArrayAction>(createArrayActionPtr->type (), arrayDims, createArrayActionPtr->componentDims (), imageDataPath));
415
+ // OutputActions outputActions;
416
+ resultOutputActions.value ().appendAction (std::make_unique<CreateImageGeometryAction>(std::move (imageGeomPath), std::move (dims), std::move (origin), std::move (spacing), cellDataName));
417
+
418
+ if (createArrayActionPtr->type () != DataType::uint8 && pConvertToGrayScaleValue)
419
+ {
420
+ return MakePreflightErrorResult (-23504 , fmt::format (" The input DataType is {} which cannot be converted to grayscale. Please turn off the 'Convert To Grayscale' option." ,
421
+ nx::core::DataTypeToString (createArrayActionPtr->type ())));
422
+ }
423
+
424
+ if (pConvertToGrayScaleValue)
425
+ {
426
+ auto * filterListPtr = Application::Instance ()->getFilterList ();
427
+ if (!filterListPtr->containsPlugin (k_SimplnxCorePluginId))
428
+ {
429
+ return MakePreflightErrorResult (-23501 , " Color to GrayScale conversion is disabled because the 'SimplnxCore' plugin was not loaded." );
430
+ }
431
+ auto grayScaleFilter = filterListPtr->createFilter (k_ColorToGrayScaleFilterHandle);
432
+ if (nullptr == grayScaleFilter.get ())
433
+ {
434
+ return MakePreflightErrorResult (-23502 , " Color to GrayScale conversion is disabled because the 'Color to GrayScale' filter is missing from the SimplnxCore plugin." );
435
+ }
436
+ resultOutputActions.value ().appendAction (std::make_unique<CreateArrayAction>(createArrayActionPtr->type (), arrayDims, std::vector<size_t >{1ULL }, imageDataPath));
437
+ }
438
+ else
439
+ {
440
+ resultOutputActions.value ().appendAction (std::make_unique<CreateArrayAction>(createArrayActionPtr->type (), arrayDims, createArrayActionPtr->componentDims (), imageDataPath));
441
+ }
350
442
351
- return {std::move (outputActions)};
443
+ // Return both the resultOutputActions and the preflightUpdatedValues via std::move()
444
+ return {std::move (resultOutputActions), std::move (preflightUpdatedValues)};
352
445
}
353
446
354
447
// ------------------------------------------------------------------------------
@@ -362,6 +455,8 @@ Result<> ITKImportImageStack::executeImpl(DataStructure& dataStructure, const Ar
362
455
auto imageDataName = filterArgs.value <DataObjectNameParameter::ValueType>(k_ImageDataArrayPath_Key);
363
456
auto cellDataName = filterArgs.value <DataObjectNameParameter::ValueType>(k_CellDataName_Key);
364
457
auto imageTransformValue = filterArgs.value <ChoicesParameter::ValueType>(k_ImageTransformChoice_Key);
458
+ auto convertToGrayScaleValue = filterArgs.value <bool >(k_ConvertToGrayScale_Key);
459
+ auto colorWeightsValue = filterArgs.value <VectorFloat32Parameter::ValueType>(k_ColorWeights_Key);
365
460
366
461
const DataPath imageDataPath = imageGeomPath.createChildPath (cellDataName).createChildPath (imageDataName);
367
462
@@ -384,43 +479,53 @@ Result<> ITKImportImageStack::executeImpl(DataStructure& dataStructure, const Ar
384
479
switch (*numericType)
385
480
{
386
481
case NumericType::uint8: {
387
- readResult = cxITKImportImageStack::ReadImageStack<uint8>(dataStructure, imageGeomPath, cellDataName, imageDataPath, files, imageTransformValue, messageHandler, shouldCancel);
482
+ readResult = cxITKImportImageStack::ReadImageStack<uint8>(dataStructure, imageGeomPath, cellDataName, imageDataPath, files, imageTransformValue, convertToGrayScaleValue, colorWeightsValue,
483
+ messageHandler, shouldCancel);
388
484
break ;
389
485
}
390
486
case NumericType::int8: {
391
- readResult = cxITKImportImageStack::ReadImageStack<int8>(dataStructure, imageGeomPath, cellDataName, imageDataPath, files, imageTransformValue, messageHandler, shouldCancel);
487
+ readResult = cxITKImportImageStack::ReadImageStack<int8>(dataStructure, imageGeomPath, cellDataName, imageDataPath, files, imageTransformValue, convertToGrayScaleValue, colorWeightsValue,
488
+ messageHandler, shouldCancel);
392
489
break ;
393
490
}
394
491
case NumericType::uint16: {
395
- readResult = cxITKImportImageStack::ReadImageStack<uint16>(dataStructure, imageGeomPath, cellDataName, imageDataPath, files, imageTransformValue, messageHandler, shouldCancel);
492
+ readResult = cxITKImportImageStack::ReadImageStack<uint16>(dataStructure, imageGeomPath, cellDataName, imageDataPath, files, imageTransformValue, convertToGrayScaleValue, colorWeightsValue,
493
+ messageHandler, shouldCancel);
396
494
break ;
397
495
}
398
496
case NumericType::int16: {
399
- readResult = cxITKImportImageStack::ReadImageStack<int16>(dataStructure, imageGeomPath, cellDataName, imageDataPath, files, imageTransformValue, messageHandler, shouldCancel);
497
+ readResult = cxITKImportImageStack::ReadImageStack<int16>(dataStructure, imageGeomPath, cellDataName, imageDataPath, files, imageTransformValue, convertToGrayScaleValue, colorWeightsValue,
498
+ messageHandler, shouldCancel);
400
499
break ;
401
500
}
402
501
case NumericType::uint32: {
403
- readResult = cxITKImportImageStack::ReadImageStack<uint32>(dataStructure, imageGeomPath, cellDataName, imageDataPath, files, imageTransformValue, messageHandler, shouldCancel);
502
+ readResult = cxITKImportImageStack::ReadImageStack<uint32>(dataStructure, imageGeomPath, cellDataName, imageDataPath, files, imageTransformValue, convertToGrayScaleValue, colorWeightsValue,
503
+ messageHandler, shouldCancel);
404
504
break ;
405
505
}
406
506
case NumericType::int32: {
407
- readResult = cxITKImportImageStack::ReadImageStack<int32>(dataStructure, imageGeomPath, cellDataName, imageDataPath, files, imageTransformValue, messageHandler, shouldCancel);
507
+ readResult = cxITKImportImageStack::ReadImageStack<int32>(dataStructure, imageGeomPath, cellDataName, imageDataPath, files, imageTransformValue, convertToGrayScaleValue, colorWeightsValue,
508
+ messageHandler, shouldCancel);
408
509
break ;
409
510
}
410
511
case NumericType::uint64: {
411
- readResult = cxITKImportImageStack::ReadImageStack<uint64>(dataStructure, imageGeomPath, cellDataName, imageDataPath, files, imageTransformValue, messageHandler, shouldCancel);
512
+ readResult = cxITKImportImageStack::ReadImageStack<uint64>(dataStructure, imageGeomPath, cellDataName, imageDataPath, files, imageTransformValue, convertToGrayScaleValue, colorWeightsValue,
513
+ messageHandler, shouldCancel);
412
514
break ;
413
515
}
414
516
case NumericType::int64: {
415
- readResult = cxITKImportImageStack::ReadImageStack<int64>(dataStructure, imageGeomPath, cellDataName, imageDataPath, files, imageTransformValue, messageHandler, shouldCancel);
517
+ readResult = cxITKImportImageStack::ReadImageStack<int64>(dataStructure, imageGeomPath, cellDataName, imageDataPath, files, imageTransformValue, convertToGrayScaleValue, colorWeightsValue,
518
+ messageHandler, shouldCancel);
416
519
break ;
417
520
}
418
521
case NumericType::float32: {
419
- readResult = cxITKImportImageStack::ReadImageStack<float32>(dataStructure, imageGeomPath, cellDataName, imageDataPath, files, imageTransformValue, messageHandler, shouldCancel);
522
+ readResult = cxITKImportImageStack::ReadImageStack<float32>(dataStructure, imageGeomPath, cellDataName, imageDataPath, files, imageTransformValue, convertToGrayScaleValue, colorWeightsValue,
523
+ messageHandler, shouldCancel);
420
524
break ;
421
525
}
422
526
case NumericType::float64: {
423
- readResult = cxITKImportImageStack::ReadImageStack<float64>(dataStructure, imageGeomPath, cellDataName, imageDataPath, files, imageTransformValue, messageHandler, shouldCancel);
527
+ readResult = cxITKImportImageStack::ReadImageStack<float64>(dataStructure, imageGeomPath, cellDataName, imageDataPath, files, imageTransformValue, convertToGrayScaleValue, colorWeightsValue,
528
+ messageHandler, shouldCancel);
424
529
break ;
425
530
}
426
531
default : {
0 commit comments