|
| 1 | +#include "MapPointCloudToRegularGrid.hpp" |
| 2 | + |
| 3 | +#include "simplnx/DataStructure/Geometry/ImageGeom.hpp" |
| 4 | +#include "simplnx/DataStructure/Geometry/VertexGeom.hpp" |
| 5 | +#include "simplnx/Utilities/MaskCompareUtilities.hpp" |
| 6 | + |
| 7 | +using namespace nx::core; |
| 8 | + |
| 9 | +namespace |
| 10 | +{ |
| 11 | +constexpr nx::core::StringLiteral k_SilentMode = "Silent"; |
| 12 | +constexpr nx::core::StringLiteral k_WarningMode = "Warning with Count"; |
| 13 | +constexpr nx::core::StringLiteral k_ErrorMode = "Error at First Instance"; |
| 14 | +const nx::core::ChoicesParameter::ValueType k_SilentModeIndex = 0; |
| 15 | +const nx::core::ChoicesParameter::ValueType k_WarningModeIndex = 1; |
| 16 | +const nx::core::ChoicesParameter::ValueType k_ErrorModeIndex = 2; |
| 17 | + |
| 18 | +constexpr int64 k_MaskCompareInvalid = -2605; |
| 19 | +constexpr int64 k_ErrorOutOfBounds = -2607; |
| 20 | +constexpr int64 k_WarningOutOfBounds = -2608; |
| 21 | +constexpr int64 k_InvalidHandlingValue = -2609; |
| 22 | + |
| 23 | +template <bool UseSilent, bool UseWarning, bool UseError> |
| 24 | +struct OutOfBoundsType |
| 25 | +{ |
| 26 | + // Compile time checks for bounding, no runtime overhead |
| 27 | + static_assert((UseSilent && !UseWarning && !UseError) || (!UseSilent && UseWarning && !UseError) || (!UseSilent && !UseWarning && UseError), |
| 28 | + "struct `OutOfBoundsType` can only have one true bool in its instantiation"); |
| 29 | + |
| 30 | + static constexpr bool UsingSilent = UseSilent; |
| 31 | + static constexpr bool UsingWarning = UseWarning; |
| 32 | + static constexpr bool UsingError = UseError; |
| 33 | +}; |
| 34 | + |
| 35 | +using SilentType = OutOfBoundsType<true, false, false>; |
| 36 | +using WarningType = OutOfBoundsType<false, true, false>; |
| 37 | +using ErrorType = OutOfBoundsType<false, false, true>; |
| 38 | + |
| 39 | +template <class OutOfBoundsType = SilentType, bool UseMask = false> |
| 40 | +Result<> ProcessVertices(const IFilter::MessageHandler& messageHandler, const VertexGeom& vertices, const ImageGeom& image, UInt64AbstractDataStore& voxelIndices, |
| 41 | + const std::unique_ptr<MaskCompareUtilities::MaskCompare>& maskCompare, uint64 outOfBoundsValue) |
| 42 | +{ |
| 43 | + // Out of Bounds Counter |
| 44 | + usize count = 0; |
| 45 | + |
| 46 | + // Execution |
| 47 | + usize numVerts = vertices.getNumberOfVertices(); |
| 48 | + auto start = std::chrono::steady_clock::now(); |
| 49 | + for(int64 i = 0; i < numVerts; i++) |
| 50 | + { |
| 51 | + if constexpr(UseMask) |
| 52 | + { |
| 53 | + if(!maskCompare->isTrue(i)) |
| 54 | + { |
| 55 | + continue; |
| 56 | + } |
| 57 | + } |
| 58 | + |
| 59 | + auto coords = vertices.getVertexCoordinate(i); |
| 60 | + const auto indexResult = image.getIndex(coords[0], coords[1], coords[2]); |
| 61 | + if(indexResult.has_value()) |
| 62 | + { |
| 63 | + voxelIndices[i] = indexResult.value(); |
| 64 | + } |
| 65 | + else |
| 66 | + { |
| 67 | + if constexpr(OutOfBoundsType::UsingError) |
| 68 | + { |
| 69 | + BoundingBox3Df imageBounds = image.getBoundingBoxf(); |
| 70 | + const Point3Df& minPoint = imageBounds.getMinPoint(); |
| 71 | + const Point3Df& maxPoint = imageBounds.getMaxPoint(); |
| 72 | + return MakeErrorResult( |
| 73 | + k_ErrorOutOfBounds, |
| 74 | + fmt::format("Out of bounds value encountered.\nVertex Index: {}\nVertex Coordinates [X,Y,Z]: [{},{},{}]\nImage Coordinate Bounds:\nX: {} to {}\nY: {} to {}\nZ: {} to {}", i, coords[0], |
| 75 | + coords[1], coords[2], minPoint.getX(), maxPoint.getX(), minPoint.getY(), maxPoint.getY(), minPoint.getZ(), maxPoint.getZ())); |
| 76 | + } |
| 77 | + |
| 78 | + // Out of bounds value |
| 79 | + voxelIndices[i] = outOfBoundsValue; |
| 80 | + count++; |
| 81 | + } |
| 82 | + |
| 83 | + auto now = std::chrono::steady_clock::now(); |
| 84 | + if(std::chrono::duration_cast<std::chrono::milliseconds>(now - start).count() > 1000) |
| 85 | + { |
| 86 | + messageHandler(fmt::format("Computing Point Cloud Voxel Indices || {}% Completed", static_cast<int64>((static_cast<float32>(i) / numVerts) * 100.0f))); |
| 87 | + start = now; |
| 88 | + } |
| 89 | + } |
| 90 | + |
| 91 | + if constexpr(OutOfBoundsType::UsingWarning) |
| 92 | + { |
| 93 | + if(count > 0) |
| 94 | + { |
| 95 | + return MakeWarningVoidResult(k_WarningOutOfBounds, fmt::format("Mapping Complete. Number of value outside image bounds: {}", count)); |
| 96 | + } |
| 97 | + } |
| 98 | + |
| 99 | + return {}; |
| 100 | +} |
| 101 | +} // namespace |
| 102 | + |
| 103 | +// ----------------------------------------------------------------------------- |
| 104 | +MapPointCloudToRegularGrid::MapPointCloudToRegularGrid(DataStructure& dataStructure, const IFilter::MessageHandler& mesgHandler, const std::atomic_bool& shouldCancel, |
| 105 | + MapPointCloudToRegularGridInputValues* inputValues) |
| 106 | +: m_DataStructure(dataStructure) |
| 107 | +, m_InputValues(inputValues) |
| 108 | +, m_ShouldCancel(shouldCancel) |
| 109 | +, m_MessageHandler(mesgHandler) |
| 110 | +{ |
| 111 | +} |
| 112 | + |
| 113 | +// ----------------------------------------------------------------------------- |
| 114 | +MapPointCloudToRegularGrid::~MapPointCloudToRegularGrid() noexcept = default; |
| 115 | + |
| 116 | +// ----------------------------------------------------------------------------- |
| 117 | +void MapPointCloudToRegularGrid::updateProgress(const std::string& message) |
| 118 | +{ |
| 119 | + m_MessageHandler(IFilter::Message::Type::Info, message); |
| 120 | +} |
| 121 | + |
| 122 | +// ----------------------------------------------------------------------------- |
| 123 | +const std::atomic_bool& MapPointCloudToRegularGrid::getCancel() |
| 124 | +{ |
| 125 | + return m_ShouldCancel; |
| 126 | +} |
| 127 | + |
| 128 | +// ----------------------------------------------------------------------------- |
| 129 | +Result<> MapPointCloudToRegularGrid::operator()() |
| 130 | +{ |
| 131 | + // Get the target image as a pointer |
| 132 | + const auto& image = m_DataStructure.getDataRefAs<ImageGeom>(m_InputValues->ImageGeomPath); |
| 133 | + |
| 134 | + // Create the Mask |
| 135 | + std::unique_ptr<MaskCompareUtilities::MaskCompare> maskCompare; |
| 136 | + try |
| 137 | + { |
| 138 | + maskCompare = MaskCompareUtilities::InstantiateMaskCompare(m_DataStructure, m_InputValues->MaskArrayPath); |
| 139 | + } catch(const std::out_of_range& exception) |
| 140 | + { |
| 141 | + // This really should NOT be happening as the path was verified during preflight BUT we may be calling this from |
| 142 | + // somewhere else that is NOT going through the normal nx::core::IFilter API of Preflight and Execute |
| 143 | + std::string message = fmt::format("Mask Array DataPath does not exist or is not of the correct type (Bool | UInt8) {}", m_InputValues->MaskArrayPath.toString()); |
| 144 | + return MakeErrorResult(k_MaskCompareInvalid, message); |
| 145 | + } |
| 146 | + |
| 147 | + // Cache all the needed objects for ::ProcessVertices |
| 148 | + const auto& vertices = m_DataStructure.getDataRefAs<VertexGeom>(m_InputValues->VertexGeomPath); |
| 149 | + auto& voxelIndices = m_DataStructure.getDataAs<UInt64Array>(m_InputValues->VoxelIndicesPath)->getDataStoreRef(); |
| 150 | + |
| 151 | + // Execute the correct ::ProcessVertices, else error out |
| 152 | + if(m_InputValues->UseMask) |
| 153 | + { |
| 154 | + switch(m_InputValues->OutOfBoundsHandling) |
| 155 | + { |
| 156 | + case k_SilentModeIndex: { |
| 157 | + return ProcessVertices<SilentType, true>(m_MessageHandler, vertices, image, voxelIndices, maskCompare, m_InputValues->OutOfBoundsValue); |
| 158 | + } |
| 159 | + case k_WarningModeIndex: { |
| 160 | + return ProcessVertices<WarningType, true>(m_MessageHandler, vertices, image, voxelIndices, maskCompare, m_InputValues->OutOfBoundsValue); |
| 161 | + } |
| 162 | + case k_ErrorModeIndex: { |
| 163 | + return ProcessVertices<ErrorType, true>(m_MessageHandler, vertices, image, voxelIndices, maskCompare, m_InputValues->OutOfBoundsValue); |
| 164 | + } |
| 165 | + default: { |
| 166 | + return MakeErrorResult(k_InvalidHandlingValue, fmt::format("Unexpected Out of Bounds Handing Option. Received : {}. Expected: {} ({}), {} ({}), {} ({})", m_InputValues->OutOfBoundsHandling, |
| 167 | + k_SilentMode, k_SilentModeIndex, k_WarningMode, k_WarningModeIndex, k_ErrorMode, k_ErrorModeIndex)); |
| 168 | + } |
| 169 | + } |
| 170 | + } |
| 171 | + else |
| 172 | + { |
| 173 | + switch(m_InputValues->OutOfBoundsHandling) |
| 174 | + { |
| 175 | + case k_SilentModeIndex: { |
| 176 | + return ProcessVertices<SilentType, false>(m_MessageHandler, vertices, image, voxelIndices, maskCompare, m_InputValues->OutOfBoundsValue); |
| 177 | + } |
| 178 | + case k_WarningModeIndex: { |
| 179 | + return ProcessVertices<WarningType, false>(m_MessageHandler, vertices, image, voxelIndices, maskCompare, m_InputValues->OutOfBoundsValue); |
| 180 | + } |
| 181 | + case k_ErrorModeIndex: { |
| 182 | + return ProcessVertices<ErrorType, false>(m_MessageHandler, vertices, image, voxelIndices, maskCompare, m_InputValues->OutOfBoundsValue); |
| 183 | + } |
| 184 | + default: { |
| 185 | + return MakeErrorResult(k_InvalidHandlingValue, fmt::format("Unexpected Out of Bounds Handing Option. Received : {}. Expected: {} ({}), {} ({}), {} ({})", m_InputValues->OutOfBoundsHandling, |
| 186 | + k_SilentMode, k_SilentModeIndex, k_WarningMode, k_WarningModeIndex, k_ErrorMode, k_ErrorModeIndex)); |
| 187 | + } |
| 188 | + } |
| 189 | + } |
| 190 | +} |
0 commit comments