Skip to content

Commit

Permalink
ENH: ReadMHAFile-Allow user to transpose stored matrix and other UI i…
Browse files Browse the repository at this point in the history
…mprovements (#871)

Signed-off-by: Michael Jackson <[email protected]>
  • Loading branch information
imikejackson authored Mar 4, 2024
1 parent eb58825 commit f903464
Show file tree
Hide file tree
Showing 94 changed files with 718 additions and 562 deletions.
10 changes: 5 additions & 5 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ endif()
file(TO_CMAKE_PATH "${CMAKE_COMMAND}" CMAKE_COMMAND_NORM)

project(simplnx
VERSION 1.2.6
VERSION 1.2.7
DESCRIPTION "SIMPL Redesign"
HOMEPAGE_URL "https://github.com/bluequartzsoftware/simplnx"
LANGUAGES CXX
Expand Down Expand Up @@ -209,10 +209,10 @@ endif()
# Force HDF5 1.10 API
target_compile_definitions(simplnx PUBLIC "H5_USE_110_API")

target_compile_options( simplnx PUBLIC
$<$<CXX_COMPILER_ID:GNU,Clang,AppleClang,Intel>: "-ffp-contract=off">
)

target_compile_options(simplnx
PUBLIC
$<$<CXX_COMPILER_ID:GNU,Clang,AppleClang,Intel>:-ffp-contract=off>
)

if(SIMPLNX_ENABLE_MULTICORE)
target_compile_definitions(simplnx PUBLIC "SIMPLNX_ENABLE_MULTICORE")
Expand Down
2 changes: 1 addition & 1 deletion conda/meta.yaml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{% set name = "simplnx" %}
{% set version = "1.2.6" %}
{% set version = "1.2.7" %}

package:
name: {{ name|lower }}
Expand Down
2 changes: 1 addition & 1 deletion conda/recipe.yaml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
context:
version: "1.2.6"
version: "1.2.7"
name: simplnx

package:
Expand Down
4 changes: 2 additions & 2 deletions pipelines/PorosityAnalysis.d3dpipeline
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@
"pipeline": [
{
"args": {
"cell_data_name": "Optical Data",
"image_data_array_path": "ImageData",
"cell_attribute_matrix_name": "Optical Data",
"image_data_array_name": "ImageData",
"image_geometry_path": "RoboMet.3D Image Stack",
"image_transform_choice": 0,
"input_file_list_info": {
Expand Down
4 changes: 2 additions & 2 deletions src/Plugins/ITKImageProcessing/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -382,8 +382,8 @@ if(EXISTS "${DREAM3D_DATA_DIR}" AND SIMPLNX_DOWNLOAD_TEST_FILES)
endif()

download_test_data(DREAM3D_DATA_DIR ${DREAM3D_DATA_DIR}
ARCHIVE_NAME ITKMhaFileReaderTest_rev2.tar.gz
SHA512 82592ffb6904389da1475e9c57f0be724d29db22ba9c942fd02d08ce3f8c73dc39a245503e584e56ca4ea7554d63cf1db5fb46e6e426768020f1ca0542822d0a
ARCHIVE_NAME ITKMhaFileReaderTest_v3.tar.gz
SHA512 106bcf9cfb4b77e6feb9355d7df0d1772e59961961201d1efaf2288294b39b3642e25e88caea322be56bb7d1029cfccc5c7c3844e7bd1f25a0c9f6186a29415b
)
download_test_data(DREAM3D_DATA_DIR ${DREAM3D_DATA_DIR}
ARCHIVE_NAME Porosity_Image.tar.gz
Expand Down
20 changes: 19 additions & 1 deletion src/Plugins/ITKImageProcessing/docs/ITKMhaFileReader.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,25 @@ ITKImageProcessing (ITKImageProcessing)

## Description

Reads MHA images and their transformation matrices using ITK
Reads MHA images and the transformation matrix using ITK. Some select headers from the MHA file are read. If those headers are not available then the default values are used.

- Number of Dimensions
- Center of Rotation (0,0,0 is the default)
- Offset (used at the origin for the created Image Geometr: 0,0,0 is the default)
- Transformation Matrix

### Use of the Transformation Matrix Notes

There is an option to use the transpose of the Transformation Matrix. This can be useful if the stored transformation matrix is not the correct active transformation. If the determinant of the transformation matrix is NOT 1.0 (or really close) AND the user has selected to transpose the matrix a preflight error will be thrown. Using the *transpose* of the transformation matrix **only** works if the transformation matrix purely represents a rotation: Other affine transforms are **NOT** allowed such as shear, scale and translation.

#### Technical Discussion

A 4x4 transformation matrix that involves only rotation (and no translation, scaling, or other transformations), the transpose of the matrix is equivalent to its inverse. This property is specific to orthogonal matrices, which are matrices where the rows and columns form a set of orthonormal vectors. In the context of 3D graphics and transformations, a rotation matrix is a type of orthogonal matrix.

The key properties that make the transpose of a rotation matrix equivalent to its inverse are:

- **Preservation of Dot Product**: The rotation does not change the dot product between vectors, preserving angles and lengths.
- **Orthonormal Rows and Columns**: The rows (and columns) of a rotation matrix are orthonormal, meaning they have unit length and are perpendicular to each other. This property ensures that multiplying the matrix by its transpose results in the identity matrix, indicating that the transpose is indeed the inverse.

% Auto generated parameter table will be inserted here

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@
"pipeline": [
{
"args": {
"cell_data_name": "Cell Data",
"image_data_array_path": "ImageData",
"cell_attribute_matrix_name": "Cell Data",
"image_data_array_name": "ImageData",
"image_geometry_path": "ImageDataContainer",
"image_transform_choice": 0,
"input_file_list_info": {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@
"pipeline": [
{
"args": {
"cell_data_name": "Cell Data",
"image_data_array_path": "ImageData",
"cell_attribute_matrix_name": "Cell Data",
"image_data_array_name": "ImageData",
"image_geometry_path": "ImageDataContainer",
"image_transform_choice": 0,
"input_file_list_info": {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@ namespace cxItkImageReader
{

//------------------------------------------------------------------------------
Result<OutputActions> ReadImagePreflight(const std::string& fileName, DataPath imageGeomPath, std::string cellDataName, DataPath arrayPath, const ImageReaderOptions& imageReaderOptions)
Result<OutputActions> ReadImagePreflight(const std::string& fileName, DataPath imageGeomPath, const std::string& cellDataName, const std::string& arrayName,
const ImageReaderOptions& imageReaderOptions)
{
OutputActions actions;

Expand Down Expand Up @@ -73,7 +74,7 @@ Result<OutputActions> ReadImagePreflight(const std::string& fileName, DataPath i

actions.appendAction(std::make_unique<CreateImageGeometryAction>(std::move(imageGeomPath), std::move(dims), origin.toContainer<CreateImageGeometryAction::OriginType>(),
spacing.toContainer<CreateImageGeometryAction::SpacingType>(), cellDataName));
actions.appendAction(std::make_unique<CreateArrayAction>(*numericType, std::move(arrayDims), std::move(cDims), std::move(arrayPath)));
actions.appendAction(std::make_unique<CreateArrayAction>(*numericType, std::move(arrayDims), std::move(cDims), imageGeomPath.createChildPath(cellDataName).createChildPath(arrayName)));

} catch(const itk::ExceptionObject& err)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -182,9 +182,10 @@ struct ImageReaderOptions
* @param fileName
* @param imageGeomPath
* @param cellDataName
* @param arrayPath
* @param arrayName
* @return
*/
Result<OutputActions> ReadImagePreflight(const std::string& fileName, DataPath imageGeomPath, std::string cellDataName, DataPath arrayPath, const ImageReaderOptions& imageReaderOptions);
Result<OutputActions> ReadImagePreflight(const std::string& fileName, DataPath imageGeomPath, const std::string& cellDataName, const std::string& arrayName,
const ImageReaderOptions& imageReaderOptions);

} // namespace cxItkImageReader
Original file line number Diff line number Diff line change
Expand Up @@ -183,7 +183,7 @@ class IOHandler
Result<> outputResult = {};

auto* filterListPtr = Application::Instance()->getFilterList();
auto imageImportFilter = ITKImageReader();
// auto imageImportFilter = ITKImageReader();

for(const auto& bound : m_Cache.bounds)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -81,11 +81,11 @@ Parameters ITKImageReader::parameters() const
FileSystemPathParameter::ExtensionsType{{".png"}, {".tiff"}, {".tif"}, {".bmp"}, {".jpeg"}, {".jpg"}, {".nrrd"}, {".mha"}},
FileSystemPathParameter::PathType::InputFile, false));

params.insertSeparator(Parameters::Separator{"Created Cell Data"});
params.insertSeparator(Parameters::Separator{"Created Data Objects"});
params.insert(std::make_unique<DataGroupCreationParameter>(k_ImageGeometryPath_Key, "Created Image Geometry", "The path to the created Image Geometry", DataPath({"ImageDataContainer"})));
params.insert(std::make_unique<DataObjectNameParameter>(k_CellDataName_Key, "Cell Data Name", "The name of the created cell attribute matrix", ImageGeom::k_CellDataName));
params.insert(std::make_unique<ArrayCreationParameter>(k_ImageDataArrayPath_Key, "Created Image Data", "The path to the created image data array",
DataPath({"ImageDataContainer", ImageGeom::k_CellDataName, "ImageData"})));
params.insert(std::make_unique<DataObjectNameParameter>(k_CellDataName_Key, "Created Cell Attribute Matrix", "The name of the created cell attribute matrix", ImageGeom::k_CellDataName));
params.insert(std::make_unique<DataObjectNameParameter>(k_ImageDataArrayPath_Key, "Created Cell Data",
"The name of the created image data array. Will be stored in the created Cell Attribute Matrix", "ImageData"));

return params;
}
Expand All @@ -102,13 +102,13 @@ IFilter::PreflightResult ITKImageReader::preflightImpl(const DataStructure& data
{
auto fileName = filterArgs.value<fs::path>(k_FileName_Key);
auto imageGeomPath = filterArgs.value<DataPath>(k_ImageGeometryPath_Key);
auto cellDataName = filterArgs.value<std::string>(k_CellDataName_Key);
auto imageDataArrayPath = filterArgs.value<DataPath>(k_ImageDataArrayPath_Key);
auto cellDataName = filterArgs.value<DataObjectNameParameter::ValueType>(k_CellDataName_Key);
auto imageDataArrayName = filterArgs.value<DataObjectNameParameter::ValueType>(k_ImageDataArrayPath_Key);
auto shouldChangeOrigin = filterArgs.value<bool>(k_ChangeOrigin_Key);
auto shouldCenterOrigin = filterArgs.value<bool>(k_CenterOrigin_Key);
auto shouldChangeSpacing = filterArgs.value<bool>(k_ChangeSpacing_Key);
auto origin = filterArgs.value<std::vector<float64>>(k_Origin_Key);
auto spacing = filterArgs.value<std::vector<float64>>(k_Spacing_Key);
auto origin = filterArgs.value<VectorFloat64Parameter::ValueType>(k_Origin_Key);
auto spacing = filterArgs.value<VectorFloat64Parameter::ValueType>(k_Spacing_Key);

std::string fileNameString = fileName.string();

Expand All @@ -120,7 +120,7 @@ IFilter::PreflightResult ITKImageReader::preflightImpl(const DataStructure& data
imageReaderOptions.Origin = FloatVec3(static_cast<float32>(origin[0]), static_cast<float32>(origin[1]), static_cast<float32>(origin[2]));
imageReaderOptions.Spacing = FloatVec3(static_cast<float32>(spacing[0]), static_cast<float32>(spacing[1]), static_cast<float32>(spacing[2]));

Result<OutputActions> result = cxItkImageReader::ReadImagePreflight(fileNameString, imageGeomPath, cellDataName, imageDataArrayPath, imageReaderOptions);
Result<OutputActions> result = cxItkImageReader::ReadImagePreflight(fileNameString, imageGeomPath, cellDataName, imageDataArrayName, imageReaderOptions);

return {result};
}
Expand All @@ -131,12 +131,15 @@ Result<> ITKImageReader::executeImpl(DataStructure& dataStructure, const Argumen
{
auto fileName = filterArgs.value<FileSystemPathParameter::ValueType>(k_FileName_Key);
auto imageGeometryPath = filterArgs.value<DataPath>(k_ImageGeometryPath_Key);
auto imageDataArrayPath = filterArgs.value<DataPath>(k_ImageDataArrayPath_Key);
auto cellDataName = filterArgs.value<DataObjectNameParameter::ValueType>(k_CellDataName_Key);
auto imageDataArrayName = filterArgs.value<DataObjectNameParameter::ValueType>(k_ImageDataArrayPath_Key);
// auto shouldChangeOrigin = filterArgs.value<bool>(k_ChangeOrigin_Key);
// auto shouldCenterOrigin = filterArgs.value<bool>(k_CenterOrigin_Key);
// auto shouldChangeSpacing = filterArgs.value<bool>(k_ChangeSpacing_Key);
auto origin = filterArgs.value<std::vector<float64>>(k_Origin_Key);
auto spacing = filterArgs.value<std::vector<float64>>(k_Spacing_Key);
auto origin = filterArgs.value<VectorFloat64Parameter::ValueType>(k_Origin_Key);
auto spacing = filterArgs.value<VectorFloat64Parameter::ValueType>(k_Spacing_Key);

DataPath imageDataArrayPath = imageGeometryPath.createChildPath(cellDataName).createChildPath(imageDataArrayName);

const IDataArray* inputArrayPtr = dataStructure.getDataAs<IDataArray>(imageDataArrayPath);
if(!inputArrayPtr->getDataFormat().empty())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,9 @@ class ITKIMAGEPROCESSING_EXPORT ITKImageReader : public IFilter

// Parameter Keys
static inline constexpr StringLiteral k_FileName_Key = "file_name";
static inline constexpr StringLiteral k_ImageGeometryPath_Key = "geometry_path";
static inline constexpr StringLiteral k_ImageDataArrayPath_Key = "image_data_array_path";
static inline constexpr StringLiteral k_CellDataName_Key = "cell_data_name";
static inline constexpr StringLiteral k_ImageGeometryPath_Key = "created_geometry_path";
static inline constexpr StringLiteral k_ImageDataArrayPath_Key = "image_data_array_name";
static inline constexpr StringLiteral k_CellDataName_Key = "cell_attribute_matrix_name";

static inline constexpr StringLiteral k_LengthUnit_Key = "length_unit";

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -174,7 +174,7 @@ IFilter::PreflightResult ITKImportFijiMontageFilter::preflightImpl(const DataStr
imageImportArgs.insertOrAssign(ITKImageReader::k_FileName_Key, std::make_any<fs::path>(bound.Filepath));
imageImportArgs.insertOrAssign(ITKImageReader::k_ImageGeometryPath_Key, std::make_any<DataPath>(imageDataProxy.getParent().getParent()));
imageImportArgs.insertOrAssign(ITKImageReader::k_CellDataName_Key, std::make_any<std::string>(imageDataProxy.getParent().getTargetName()));
imageImportArgs.insertOrAssign(ITKImageReader::k_ImageDataArrayPath_Key, std::make_any<DataPath>(imageDataProxy));
imageImportArgs.insertOrAssign(ITKImageReader::k_ImageDataArrayPath_Key, std::make_any<std::string>(imageDataProxy.getTargetName()));

auto result = imageImportFilter.preflight(dataStructure, imageImportArgs, messageHandler, shouldCancel);
if(result.outputActions.invalid())
Expand Down
Loading

0 comments on commit f903464

Please sign in to comment.