Skip to content

Commit

Permalink
BUG: Strip out the Alpha channel when creating the Image Geometry. (#827
Browse files Browse the repository at this point in the history
)

Signed-off-by: Michael Jackson <mike.jackson@bluequartz.net>
  • Loading branch information
imikejackson committed Feb 7, 2024
1 parent c83e9f2 commit 840edd6
Show file tree
Hide file tree
Showing 3 changed files with 94 additions and 26 deletions.
21 changes: 17 additions & 4 deletions src/Plugins/OrientationAnalysis/docs/WritePoleFigureFilter.md
Expand Up @@ -6,7 +6,20 @@ IO (Output)

## Description

This **Filter** creates a standard pole figure image for each **Ensemble** in a selected **Data Container** with an **Image Geometry**. The **Filter** uses Euler angles in radians and requires the crystal structures for each **Ensemble** array and the corresponding **Ensemble** Ids on the **Cells**. The **Filter** also optionally can use a *mask* array to determine which **Cells** are valid for the pole figure computation.
This **Filter** creates a standard crystallographic pole figure image for each **Ensemble** (phase)in a selected **Data Container**. The **Filter** uses Euler angles in radians and requires the crystal structures and material names for each **Ensemble** array and the corresponding **Ensemble** Ids on the **Cells**. The **Filter** also optionally can use a *mask* array to determine which **Cells** are valid for the pole figure computation.

In a practicale sense, this means that the following information is available to the filter:

- Cell Level

- Euler Angles (Float 32) ordered as sets of (phi1, Phi, phi2).
- Phases (Int32) This is the phase that each Euler angle belongs to
- Optional Mask(boolean or uint8) True/1 if the Euler angle should be included in the pole figure.

- Ensemble Level (Phase Information)

- Laue Class (UInt32)
- Material Names (String)

### Algorithm Choice

Expand All @@ -18,7 +31,7 @@ This **Filter** creates a standard pole figure image for each **Ensemble** in a

### Layout

The 3 pole figures can be laid out in a Square, Horizontal row or vertical column. Supporting informatio (including the color bar legend for color pole figures) will also be printed on the image.
The 3 pole figures can be laid out in a Square, Horizontal row or vertical column. Supporting information (including the color bar legend for color pole figures) will also be printed on the image.

| Lambert Projection | Discrete |
|--------------------|----------|
Expand All @@ -28,8 +41,8 @@ The 3 pole figures can be laid out in a Square, Horizontal row or vertical colum

## Example Pipelines

+ TxCopper_Exposed
+ TxCopper_Unexposed
- TxCopper_Exposed
- TxCopper_Unexposed

## License & Copyright

Expand Down
Expand Up @@ -362,21 +362,14 @@ Result<> WritePoleFigure::operator()()
// Find how many phases we have by getting the number of Crystal Structures
const size_t numPhases = crystalStructures.getNumberOfTuples();

// Loop over all the voxels gathering the Eulers for a specific phase into an array
// Create the Image Geometry that will serve as the final storage location for each
// pole figure. We are just giving it a default size for now, it will be resized
// further down the algorithm.
std::vector<usize> tupleShape = {1, static_cast<usize>(m_InputValues->ImageSize), static_cast<usize>(m_InputValues->ImageSize)};
auto& imageGeom = m_DataStructure.getDataRefAs<ImageGeom>(m_InputValues->OutputImageGeometryPath);
auto cellAttrMatPath = imageGeom.getCellDataPath();
imageGeom.setDimensions({static_cast<usize>(m_InputValues->ImageSize), static_cast<usize>(m_InputValues->ImageSize), 1});
imageGeom.getCellData()->resizeTuples(tupleShape);
for(size_t phase = 1; phase < numPhases; ++phase)
{
auto imageArrayPath = cellAttrMatPath.createChildPath(fmt::format("{}Phase_{}", m_InputValues->ImagePrefix, phase));
auto arrayCreationResult = nx::core::CreateArray<uint8>(m_DataStructure, tupleShape, {4ULL}, imageArrayPath, IDataAction::Mode::Execute);
if(arrayCreationResult.invalid())
{
return arrayCreationResult;
}
}

// Loop over all the voxels gathering the Eulers for a specific phase into an array
for(size_t phase = 1; phase < numPhases; ++phase)
Expand Down Expand Up @@ -666,22 +659,43 @@ Result<> WritePoleFigure::operator()()
}

// Fetch the rendered RGBA pixels from the entire canvas.
std::vector<unsigned char> image(static_cast<size_t>(pageHeight * pageWidth * 4));
context.get_image_data(image.data(), pageWidth, pageHeight, pageWidth * 4, 0, 0);
std::vector<unsigned char> rgbaCanvasImage(static_cast<size_t>(pageHeight * pageWidth * 4));
context.get_image_data(rgbaCanvasImage.data(), pageWidth, pageHeight, pageWidth * 4, 0, 0);
if(m_InputValues->SaveAsImageGeometry)
{
// Ensure the final Image Geometry is sized correctly.
imageGeom.setDimensions({static_cast<usize>(pageWidth), static_cast<usize>(pageHeight), 1});
imageGeom.getCellData()->resizeTuples({1, static_cast<usize>(pageHeight), static_cast<usize>(pageWidth)});
tupleShape[0] = 1;
tupleShape[1] = pageHeight;
tupleShape[2] = pageWidth;
// Create an output array to hold the RGB formatted color image
auto imageArrayPath = cellAttrMatPath.createChildPath(fmt::format("{}{}", m_InputValues->ImagePrefix, phase));
auto arrayCreationResult = nx::core::CreateArray<uint8>(m_DataStructure, tupleShape, {3ULL}, imageArrayPath, IDataAction::Mode::Execute);
if(arrayCreationResult.invalid())
{
return arrayCreationResult;
}

auto imageArrayPath = cellAttrMatPath.createChildPath(fmt::format("{}Phase_{}", m_InputValues->ImagePrefix, phase));
// Get a reference to the RGB final array and then copy ONLY the RGB pixels from the
// canvas RGBA data.
auto& imageData = m_DataStructure.getDataRefAs<UInt8Array>(imageArrayPath);
std::copy(image.begin(), image.end(), imageData.begin());

imageData.fill(0);
size_t tupleCount = pageHeight * pageWidth;
for(size_t t = 0; t < tupleCount; t++)
{
imageData[t * 3 + 0] = rgbaCanvasImage[t * 4 + 0];
imageData[t * 3 + 1] = rgbaCanvasImage[t * 4 + 1];
imageData[t * 3 + 2] = rgbaCanvasImage[t * 4 + 2];
}
}

// Write out the full RGBA data
if(m_InputValues->WriteImageToDisk)
{
const std::string filename = fmt::format("{}/{}Phase_{}.tiff", m_InputValues->OutputPath.string(), m_InputValues->ImagePrefix, phase);
auto result = TiffWriter::WriteImage(filename, pageWidth, pageHeight, 4, image.data());
const std::string filename = fmt::format("{}/{}{}.tiff", m_InputValues->OutputPath.string(), m_InputValues->ImagePrefix, phase);
auto result = TiffWriter::WriteImage(filename, pageWidth, pageHeight, 4, rgbaCanvasImage.data());
if(result.first < 0)
{
return MakeErrorResult(-53900, fmt::format("Error writing pole figure image '{}' to disk.\n Error Code from Tiff Writer: {}\n Message: {}", filename, result.first, result.second));
Expand Down
53 changes: 47 additions & 6 deletions src/Plugins/OrientationAnalysis/test/WritePoleFigureTest.cpp
Expand Up @@ -21,8 +21,43 @@ using namespace nx::core::UnitTest;
namespace
{
const std::string k_ImagePrefix("fw-ar-IF1-aptr12-corr Discrete Pole Figure");

template <typename T>
void CompareComponentsOfArrays(const DataStructure& dataStructure, const DataPath& exemplaryDataPath, const DataPath& computedPath, usize compIndex)
{
// DataPath exemplaryDataPath = featureGroup.createChildPath("SurfaceFeatures");
REQUIRE_NOTHROW(dataStructure.getDataRefAs<DataArray<T>>(exemplaryDataPath));
REQUIRE_NOTHROW(dataStructure.getDataRefAs<DataArray<T>>(computedPath));

const auto& exemplaryDataArray = dataStructure.getDataRefAs<DataArray<T>>(exemplaryDataPath);
const auto& generatedDataArray = dataStructure.getDataRefAs<DataArray<T>>(computedPath);
REQUIRE(generatedDataArray.getNumberOfTuples() == exemplaryDataArray.getNumberOfTuples());

usize exemplaryNumComp = exemplaryDataArray.getNumberOfComponents();
usize generatedNumComp = generatedDataArray.getNumberOfComponents();

REQUIRE(exemplaryNumComp == 4);
REQUIRE(generatedNumComp == 3);

REQUIRE(compIndex < exemplaryNumComp);
REQUIRE(compIndex < generatedNumComp);

INFO(fmt::format("Bad Comparison\n Input Data Array:'{}'\n Output DataArray: '{}'", exemplaryDataPath.toString(), computedPath.toString()));

usize start = 0;
usize numTuples = exemplaryDataArray.getNumberOfTuples();
for(usize i = start; i < numTuples; i++)
{
auto oldVal = exemplaryDataArray[i * exemplaryNumComp + compIndex];
auto newVal = generatedDataArray[i * generatedNumComp + compIndex];
INFO(fmt::format("Index: {} Comp: {}", i, compIndex));

REQUIRE(oldVal == newVal);
}
}

} // namespace

TEST_CASE("OrientationAnalysis::WritePoleFigureFilter-1", "[OrientationAnalysis][WritePoleFigureFilter]")
{
Application::GetOrCreateInstance()->loadPlugins(unit_test::k_BuildDir.view(), true);
Expand Down Expand Up @@ -51,7 +86,7 @@ TEST_CASE("OrientationAnalysis::WritePoleFigureFilter-1", "[OrientationAnalysis]
args.insertOrAssign(WritePoleFigureFilter::k_UseMask_Key, std::make_any<bool>(false));
args.insertOrAssign(WritePoleFigureFilter::k_ImageGeometryPath_Key, std::make_any<DataPath>(DataPath({"fw-ar-IF1-aptr12-corr Discrete Pole Figure [CALCULATED]"})));

DataPath calculatedImageData({"fw-ar-IF1-aptr12-corr Discrete Pole Figure [CALCULATED]", "CellData", fmt::format("{}Phase_{}", k_ImagePrefix, 1)});
DataPath calculatedImageData({"fw-ar-IF1-aptr12-corr Discrete Pole Figure [CALCULATED]", "CellData", fmt::format("{}{}", k_ImagePrefix, 1)});
DataPath exemplarImageData({"fw-ar-IF1-aptr12-corr Discrete Pole Figure", "CellData", "Image"});

args.insertOrAssign(WritePoleFigureFilter::k_CellEulerAnglesArrayPath_Key, std::make_any<DataPath>(DataPath({"fw-ar-IF1-aptr12-corr", "Cell Data", "EulerAngles"})));
Expand All @@ -71,7 +106,9 @@ TEST_CASE("OrientationAnalysis::WritePoleFigureFilter-1", "[OrientationAnalysis]
WriteTestDataStructure(dataStructure, fmt::format("{}/write_pole_figure-1.dream3d", unit_test::k_BinaryTestOutputDir));
#endif

CompareArrays<uint8>(dataStructure, exemplarImageData, calculatedImageData);
CompareComponentsOfArrays<uint8>(dataStructure, exemplarImageData, calculatedImageData, 0);
CompareComponentsOfArrays<uint8>(dataStructure, exemplarImageData, calculatedImageData, 1);
CompareComponentsOfArrays<uint8>(dataStructure, exemplarImageData, calculatedImageData, 2);
}

TEST_CASE("OrientationAnalysis::WritePoleFigureFilter-2", "[OrientationAnalysis][WritePoleFigureFilter]")
Expand Down Expand Up @@ -102,7 +139,7 @@ TEST_CASE("OrientationAnalysis::WritePoleFigureFilter-2", "[OrientationAnalysis]
args.insertOrAssign(WritePoleFigureFilter::k_UseMask_Key, std::make_any<bool>(true));
args.insertOrAssign(WritePoleFigureFilter::k_ImageGeometryPath_Key, std::make_any<DataPath>(DataPath({"fw-ar-IF1-aptr12-corr Discrete Pole Figure Masked [CALCULATED]"})));

DataPath calculatedImageData({"fw-ar-IF1-aptr12-corr Discrete Pole Figure Masked [CALCULATED]", "CellData", fmt::format("{}Phase_{}", k_ImagePrefix, 1)});
DataPath calculatedImageData({"fw-ar-IF1-aptr12-corr Discrete Pole Figure Masked [CALCULATED]", "CellData", fmt::format("{}{}", k_ImagePrefix, 1)});
DataPath exemplarImageData({"fw-ar-IF1-aptr12-corr Discrete Pole Figure Masked", "CellData", "Image"});

args.insertOrAssign(WritePoleFigureFilter::k_CellEulerAnglesArrayPath_Key, std::make_any<DataPath>(DataPath({"fw-ar-IF1-aptr12-corr", "Cell Data", "EulerAngles"})));
Expand All @@ -123,7 +160,9 @@ TEST_CASE("OrientationAnalysis::WritePoleFigureFilter-2", "[OrientationAnalysis]
WriteTestDataStructure(dataStructure, fmt::format("{}/write_pole_figure-2.dream3d", unit_test::k_BinaryTestOutputDir));
#endif

CompareArrays<uint8>(dataStructure, exemplarImageData, calculatedImageData);
CompareComponentsOfArrays<uint8>(dataStructure, exemplarImageData, calculatedImageData, 0);
CompareComponentsOfArrays<uint8>(dataStructure, exemplarImageData, calculatedImageData, 1);
CompareComponentsOfArrays<uint8>(dataStructure, exemplarImageData, calculatedImageData, 2);
}

TEST_CASE("OrientationAnalysis::WritePoleFigureFilter-3", "[OrientationAnalysis][WritePoleFigureFilter]")
Expand Down Expand Up @@ -154,7 +193,7 @@ TEST_CASE("OrientationAnalysis::WritePoleFigureFilter-3", "[OrientationAnalysis]
args.insertOrAssign(WritePoleFigureFilter::k_UseMask_Key, std::make_any<bool>(true));
args.insertOrAssign(WritePoleFigureFilter::k_ImageGeometryPath_Key, std::make_any<DataPath>(DataPath({"fw-ar-IF1-aptr12-corr Discrete Pole Figure Masked Color [CALCULATED]"})));

DataPath calculatedImageData({"fw-ar-IF1-aptr12-corr Discrete Pole Figure Masked Color [CALCULATED]", "CellData", fmt::format("{}Phase_{}", k_ImagePrefix, 1)});
DataPath calculatedImageData({"fw-ar-IF1-aptr12-corr Discrete Pole Figure Masked Color [CALCULATED]", "CellData", fmt::format("{}{}", k_ImagePrefix, 1)});
DataPath exemplarImageData({"fw-ar-IF1-aptr12-corr Discrete Pole Figure Masked Color", "CellData", "Image"});

args.insertOrAssign(WritePoleFigureFilter::k_CellEulerAnglesArrayPath_Key, std::make_any<DataPath>(DataPath({"fw-ar-IF1-aptr12-corr", "Cell Data", "EulerAngles"})));
Expand All @@ -175,5 +214,7 @@ TEST_CASE("OrientationAnalysis::WritePoleFigureFilter-3", "[OrientationAnalysis]
WriteTestDataStructure(dataStructure, fmt::format("{}/write_pole_figure-3.dream3d", unit_test::k_BinaryTestOutputDir));
#endif

CompareArrays<uint8>(dataStructure, exemplarImageData, calculatedImageData);
CompareComponentsOfArrays<uint8>(dataStructure, exemplarImageData, calculatedImageData, 0);
CompareComponentsOfArrays<uint8>(dataStructure, exemplarImageData, calculatedImageData, 1);
CompareComponentsOfArrays<uint8>(dataStructure, exemplarImageData, calculatedImageData, 2);
}

0 comments on commit 840edd6

Please sign in to comment.