Skip to content

Commit

Permalink
FILT: Add Oxford Channel 5 Binary file reader.
Browse files Browse the repository at this point in the history
- Requires EbsdLib 1.0.34 or higher
- Filter requires BOTh .cpr and .crc files to be present
- Needs a unit test

Signed-off-by: Michael Jackson <[email protected]>
  • Loading branch information
imikejackson committed Nov 6, 2024
1 parent 382861a commit 42dc0f8
Show file tree
Hide file tree
Showing 8 changed files with 709 additions and 0 deletions.
2 changes: 2 additions & 0 deletions src/Plugins/OrientationAnalysis/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ set(FilterList
MergeTwinsFilter
NeighborOrientationCorrelationFilter
ReadAngDataFilter
ReadChannel5DataFilter
ReadCtfDataFilter
ReadEnsembleInfoFilter
ReadGrainMapper3DFilter
Expand Down Expand Up @@ -194,6 +195,7 @@ set(filter_algorithms
MergeTwins
NeighborOrientationCorrelation
ReadAngData
ReadChannel5Data
ReadCtfData
ReadEnsembleInfo
ReadGrainMapper3D
Expand Down
Empty file.
Original file line number Diff line number Diff line change
@@ -0,0 +1,185 @@
#include "ReadChannel5Data.hpp"

#include "simplnx/Common/RgbColor.hpp"
#include "simplnx/DataStructure/DataArray.hpp"
#include "simplnx/DataStructure/Geometry/ImageGeom.hpp"
#include "simplnx/DataStructure/StringArray.hpp"
#include "simplnx/Utilities/Math/MatrixMath.hpp"
#include "simplnx/Utilities/StringUtilities.hpp"

#include "EbsdLib/Core/Orientation.hpp"

using namespace nx::core;

using FloatVec3Type = std::vector<float>;

namespace
{
template <typename T>
void copyRawData(const ReadChannel5DataInputValues* m_InputValues, size_t numElements, DataStructure& m_DataStructure, CprReader& m_Reader, const std::string& name, DataPath& dataArrayPath)
{
using ArrayType = DataArray<T>;
auto& dataRef = m_DataStructure.getDataRefAs<ArrayType>(dataArrayPath);
auto* dataStorePtr = dataRef.getDataStore();

const nonstd::span<T> rawDataPtr(reinterpret_cast<T*>(m_Reader.getPointerByName(name)), numElements);
// std::copy(rawDataPtr.begin(), rawDataPtr.end(), dataStorePtr->begin() + offset);
for(size_t idx = 0; idx < numElements; idx++)
{
dataStorePtr->setValue(idx, rawDataPtr[idx]);
}
}

} // namespace

// -----------------------------------------------------------------------------
ReadChannel5Data::ReadChannel5Data(DataStructure& dataStructure, const IFilter::MessageHandler& msgHandler, const std::atomic_bool& shouldCancel, ReadChannel5DataInputValues* inputValues)
: m_DataStructure(dataStructure)
, m_MessageHandler(msgHandler)
, m_ShouldCancel(shouldCancel)
, m_InputValues(inputValues)
{
}

// -----------------------------------------------------------------------------
ReadChannel5Data::~ReadChannel5Data() noexcept = default;

// -----------------------------------------------------------------------------
Result<> ReadChannel5Data::operator()()
{
CprReader reader;
reader.setFileName(m_InputValues->InputFile.string());
const int32_t err = reader.readFile();
if(err < 0)
{
return MakeErrorResult(reader.getErrorCode(), reader.getErrorMessage());
}

const auto result = loadMaterialInfo(&reader);
if(result.first < 0)
{
return MakeErrorResult(result.first, result.second);
}

copyRawEbsdData(&reader);

return {};
}

// -----------------------------------------------------------------------------
std::pair<int32, std::string> ReadChannel5Data::loadMaterialInfo(CprReader* reader) const
{
const std::vector<CtfPhase::Pointer> phases = reader->getPhaseVector();
if(phases.empty())
{
return {reader->getErrorCode(), reader->getErrorMessage()};
}

const DataPath CellEnsembleAttributeMatrixPath = m_InputValues->DataContainerName.createChildPath(m_InputValues->CellEnsembleAttributeMatrixName);

auto& crystalStructures = m_DataStructure.getDataRefAs<UInt32Array>(CellEnsembleAttributeMatrixPath.createChildPath(EbsdLib::CtfFile::CrystalStructures));

auto& materialNames = m_DataStructure.getDataRefAs<StringArray>(CellEnsembleAttributeMatrixPath.createChildPath(EbsdLib::CtfFile::MaterialName));

auto& latticeConstants = m_DataStructure.getDataRefAs<Float32Array>(CellEnsembleAttributeMatrixPath.createChildPath(EbsdLib::CtfFile::LatticeConstants));

const std::string k_InvalidPhase = "Invalid Phase";

// Initialize the zero'th element to unknowns. The other elements will
// be filled in based on values from the data file
crystalStructures[0] = EbsdLib::CrystalStructure::UnknownCrystalStructure;
materialNames[0] = k_InvalidPhase;

for(size_t i = 0; i < 6; i++)
{
latticeConstants.getDataStoreRef().setComponent(0, i, 0.0F);
}

for(const CtfPhase::Pointer& phase : phases)
{
const int32_t phaseID = phase->getPhaseIndex();
crystalStructures[phaseID] = phase->determineOrientationOpsIndex();
std::string materialName = phase->getMaterialName();
materialName = nx::core::StringUtilities::replace(materialName, "MaterialName", "");
materialName = nx::core::StringUtilities::trimmed(materialName);
materialNames[phaseID] = materialName;

std::vector<float> lattConst = phase->getLatticeConstants();

for(size_t i = 0; i < 6; i++)
{
latticeConstants.getDataStoreRef().setComponent(phaseID, i, lattConst[i]);
}
}
return {0, ""};
}

// -----------------------------------------------------------------------------
void ReadChannel5Data::copyRawEbsdData(CprReader* reader) const
{
const DataPath cellAttributeMatrixPath = m_InputValues->DataContainerName.createChildPath(m_InputValues->CellAttributeMatrixName);

std::vector<size_t> cDims = {1};

const auto& imageGeom = m_DataStructure.getDataRefAs<ImageGeom>(m_InputValues->DataContainerName);
const size_t totalCells = imageGeom.getNumberOfCells();

// Prepare the Cell Attribute Matrix with the correct number of tuples based on the total Cells being read from the file.
std::vector<size_t> tDims = {imageGeom.getNumXCells(), imageGeom.getNumYCells(), imageGeom.getNumZCells()};

std::vector<EbsdLib::CrcDataParser> fieldParsers = reader->createFieldParsers(m_InputValues->InputFile.string());
for(const auto& parser : fieldParsers)
{
std::string fieldName = parser.FieldDefinition.FieldName;
DataPath dataArrayPath = cellAttributeMatrixPath.createChildPath(fieldName);
// if(m_DataStructure.getData(dataArrayPath) != nullptr)
// {
// std::cout << fieldName << " Exists in Data Structure\n";
// }

if(parser.FieldDefinition.numericType == EbsdLib::NumericTypes::Type::Int32)
{
copyRawData<int32_t>(m_InputValues, totalCells, m_DataStructure, *reader, fieldName, dataArrayPath);
}
else if(parser.FieldDefinition.numericType == EbsdLib::NumericTypes::Type::Float)
{
copyRawData<float32>(m_InputValues, totalCells, m_DataStructure, *reader, fieldName, dataArrayPath);
}
else if(parser.FieldDefinition.numericType == EbsdLib::NumericTypes::Type::UInt8)
{
copyRawData<uint8_t>(m_InputValues, totalCells, m_DataStructure, *reader, fieldName, dataArrayPath);
}
}

// Copy the data from the 'Phase' array into the 'Phases' array
if(m_InputValues->CreateCompatibleArrays)
{
auto& targetArray = m_DataStructure.getDataRefAs<Int32Array>(cellAttributeMatrixPath.createChildPath(EbsdLib::CtfFile::Phases));
auto* phasePtr = reinterpret_cast<uint8_t*>(reader->getPointerByName(EbsdLib::Ctf::Phase));
for(size_t i = 0; i < totalCells; i++)
{
// if(phasePtr[i] < 1)
// {
// phasePtr[i] = 1;
// }
targetArray[i] = phasePtr[i];
}
}

// Condense the Euler Angles from 3 separate arrays into a single 1x3 array
if(m_InputValues->CreateCompatibleArrays)
{
const auto* fComp0 = reinterpret_cast<float*>(reader->getPointerByName(EbsdLib::Ctf::phi1));
const auto* fComp1 = reinterpret_cast<float*>(reader->getPointerByName(EbsdLib::Ctf::Phi));
const auto* fComp2 = reinterpret_cast<float*>(reader->getPointerByName(EbsdLib::Ctf::phi2));
cDims[0] = 3;

auto& cellEulerAngles = m_DataStructure.getDataRefAs<Float32Array>(cellAttributeMatrixPath.createChildPath(EbsdLib::CtfFile::EulerAngles));
for(size_t i = 0; i < totalCells; i++)
{
cellEulerAngles[3 * i] = fComp0[i];
cellEulerAngles[3 * i + 1] = fComp1[i];
cellEulerAngles[3 * i + 2] = fComp2[i];
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
#pragma once

#include "OrientationAnalysis/OrientationAnalysis_export.hpp"

#include "simplnx/DataStructure/DataArray.hpp"
#include "simplnx/DataStructure/DataPath.hpp"
#include "simplnx/DataStructure/DataStructure.hpp"
#include "simplnx/DataStructure/IDataArray.hpp"
#include "simplnx/Filter/IFilter.hpp"
#include "simplnx/Parameters/FileSystemPathParameter.hpp"

#include "EbsdLib/IO/HKL/CprReader.h"

#include <filesystem>
namespace fs = std::filesystem;

namespace nx::core
{

struct ORIENTATIONANALYSIS_EXPORT ReadChannel5DataInputValues
{
FileSystemPathParameter::ValueType InputFile;
DataPath DataContainerName;
std::string CellAttributeMatrixName;
std::string CellEnsembleAttributeMatrixName;
bool EdaxHexagonalAlignment;
bool CreateCompatibleArrays;
};

struct ORIENTATIONANALYSIS_EXPORT Ang_Private_Data
{
std::array<size_t, 3> dims = {0, 0, 0};
std::array<float, 3> resolution = {0.0F, 0.0F, 0.0F};
std::array<float, 3> origin = {0.0F, 0.0F, 0.0F};
std::vector<CtfPhase::Pointer> phases;
int32_t units = 0;
};

/**
* @brief The ReadChannel5DataPrivate class is a private implementation of the ReadChannel5Data class
*/
class ORIENTATIONANALYSIS_EXPORT ReadChannel5DataPrivate
{
public:
ReadChannel5DataPrivate() = default;
~ReadChannel5DataPrivate() = default;

ReadChannel5DataPrivate(const ReadChannel5DataPrivate&) = delete; // Copy Constructor Not Implemented
ReadChannel5DataPrivate(ReadChannel5DataPrivate&&) = delete; // Move Constructor Not Implemented
ReadChannel5DataPrivate& operator=(const ReadChannel5DataPrivate&) = delete; // Copy Assignment Not Implemented
ReadChannel5DataPrivate& operator=(ReadChannel5DataPrivate&&) = delete; // Move Assignment Not Implemented

Ang_Private_Data m_Data;

std::string m_InputFile_Cache;
fs::file_time_type m_TimeStamp_Cache;
};

/**
* @class ReadChannel5Data
* @brief This filter will read a single .ang file into a new Image Geometry, allowing the immediate use of Filters on the data instead of having to generate the intermediate
* .h5ebsd file.
*/
class ORIENTATIONANALYSIS_EXPORT ReadChannel5Data
{
public:
ReadChannel5Data(DataStructure& dataStructure, const IFilter::MessageHandler& msgHandler, const std::atomic_bool& shouldCancel, ReadChannel5DataInputValues* inputValues);
~ReadChannel5Data() noexcept;

ReadChannel5Data(const ReadChannel5Data&) = delete; // Copy Constructor Not Implemented
ReadChannel5Data(ReadChannel5Data&&) = delete; // Move Constructor Not Implemented
ReadChannel5Data& operator=(const ReadChannel5Data&) = delete; // Copy Assignment Not Implemented
ReadChannel5Data& operator=(ReadChannel5Data&&) = delete; // Move Assignment Not Implemented

Result<> operator()();

private:
DataStructure& m_DataStructure;
const IFilter::MessageHandler& m_MessageHandler;
const std::atomic_bool& m_ShouldCancel;
const ReadChannel5DataInputValues* m_InputValues = nullptr;

/**
* @brief
* @param reader
* @return Error code.
*/
std::pair<int32, std::string> loadMaterialInfo(CprReader* reader) const;

/**
* @brief
* @param reader
*/
void copyRawEbsdData(CprReader* reader) const;
};

} // namespace nx::core
Loading

0 comments on commit 42dc0f8

Please sign in to comment.