|
3 | 3 | #include "OrientationAnalysisTestUtils.hpp" |
4 | 4 |
|
5 | 5 | #include "simplnx/Core/Application.hpp" |
| 6 | +#include "simplnx/DataStructure/AttributeMatrix.hpp" |
| 7 | +#include "simplnx/DataStructure/Geometry/ImageGeom.hpp" |
6 | 8 | #include "simplnx/Parameters/ArraySelectionParameter.hpp" |
7 | 9 | #include "simplnx/Parameters/BoolParameter.hpp" |
8 | 10 | #include "simplnx/Parameters/Dream3dImportParameter.hpp" |
9 | 11 | #include "simplnx/Parameters/GeometrySelectionParameter.hpp" |
| 12 | +#include "simplnx/Parameters/MultiArraySelectionParameter.hpp" |
10 | 13 | #include "simplnx/UnitTest/UnitTestCommon.hpp" |
11 | 14 |
|
12 | 15 | #include <catch2/catch.hpp> |
13 | 16 |
|
| 17 | +#include <cmath> |
14 | 18 | #include <filesystem> |
15 | 19 |
|
16 | 20 | namespace fs = std::filesystem; |
@@ -196,3 +200,108 @@ TEST_CASE("OrientationAnalysis::NeighborOrientationCorrelationFilter: Small IN10 |
196 | 200 |
|
197 | 201 | UnitTest::CheckArraysInheritTupleDims(dataStructure, SmallIn100::k_TupleCheckIgnoredPaths); |
198 | 202 | } |
| 203 | + |
| 204 | +TEST_CASE("OrientationAnalysis::NeighborOrientationCorrelationFilter: Benchmark 200x200x200", "[OrientationAnalysis][NeighborOrientationCorrelationFilter][Benchmark]") |
| 205 | +{ |
| 206 | + UnitTest::LoadPlugins(); |
| 207 | + // 200x200x200, Quats (float32, 4-comp) => 200*200*4*4 = 640,000 bytes/slice |
| 208 | + const UnitTest::PreferencesSentinel prefsSentinel("Zarr", 640000, true); |
| 209 | + |
| 210 | + constexpr usize kDimX = 200; |
| 211 | + constexpr usize kDimY = 200; |
| 212 | + constexpr usize kDimZ = 200; |
| 213 | + constexpr usize kTotalVoxels = kDimX * kDimY * kDimZ; |
| 214 | + const ShapeType cellTupleShape = {kDimZ, kDimY, kDimX}; |
| 215 | + const auto benchmarkFile = fs::path(fmt::format("{}/neighbor_orientation_correlation_benchmark.dream3d", unit_test::k_BinaryTestOutputDir)); |
| 216 | + |
| 217 | + // Stage 1: Build data programmatically and write to .dream3d |
| 218 | + { |
| 219 | + DataStructure buildDS; |
| 220 | + auto* imageGeom = ImageGeom::Create(buildDS, "Image Geometry"); |
| 221 | + imageGeom->setDimensions({kDimX, kDimY, kDimZ}); |
| 222 | + imageGeom->setSpacing({1.0f, 1.0f, 1.0f}); |
| 223 | + imageGeom->setOrigin({0.0f, 0.0f, 0.0f}); |
| 224 | + |
| 225 | + auto* cellAM = AttributeMatrix::Create(buildDS, "Cell Data", cellTupleShape, imageGeom->getId()); |
| 226 | + imageGeom->setCellData(*cellAM); |
| 227 | + |
| 228 | + // Quaternions: block-based orientations with noise at boundaries |
| 229 | + auto* quatsArray = UnitTest::CreateTestDataArray<float32>(buildDS, "Quats", cellTupleShape, {4}, cellAM->getId()); |
| 230 | + auto& quatsStore = quatsArray->getDataStoreRef(); |
| 231 | + |
| 232 | + // Phases: all phase 1 (cubic) |
| 233 | + auto* phasesArray = UnitTest::CreateTestDataArray<int32>(buildDS, "Phases", cellTupleShape, {1}, cellAM->getId()); |
| 234 | + auto& phasesStore = phasesArray->getDataStoreRef(); |
| 235 | + |
| 236 | + // Confidence Index: low at block boundaries, high inside blocks |
| 237 | + auto* ciArray = UnitTest::CreateTestDataArray<float32>(buildDS, "Confidence Index", cellTupleShape, {1}, cellAM->getId()); |
| 238 | + auto& ciStore = ciArray->getDataStoreRef(); |
| 239 | + |
| 240 | + constexpr usize kBlockSize = 25; |
| 241 | + constexpr usize kBlocksPerDim = kDimX / kBlockSize; |
| 242 | + for(usize z = 0; z < kDimZ; z++) |
| 243 | + { |
| 244 | + for(usize y = 0; y < kDimY; y++) |
| 245 | + { |
| 246 | + for(usize x = 0; x < kDimX; x++) |
| 247 | + { |
| 248 | + const usize idx = z * kDimX * kDimY + y * kDimX + x; |
| 249 | + phasesStore[idx] = 1; |
| 250 | + |
| 251 | + // Block-based orientation: each 25^3 block gets a distinct quaternion |
| 252 | + usize bx = x / kBlockSize; |
| 253 | + usize by = y / kBlockSize; |
| 254 | + usize bz = z / kBlockSize; |
| 255 | + float32 angle = static_cast<float32>(bz * kBlocksPerDim * kBlocksPerDim + by * kBlocksPerDim + bx) * 0.1f; |
| 256 | + float32 sinHalf = std::sin(angle * 0.5f); |
| 257 | + float32 cosHalf = std::cos(angle * 0.5f); |
| 258 | + |
| 259 | + const usize qIdx = idx * 4; |
| 260 | + quatsStore[qIdx] = cosHalf; |
| 261 | + quatsStore[qIdx + 1] = sinHalf * 0.577350269f; // 1/sqrt(3) |
| 262 | + quatsStore[qIdx + 2] = sinHalf * 0.577350269f; |
| 263 | + quatsStore[qIdx + 3] = sinHalf * 0.577350269f; |
| 264 | + |
| 265 | + // Low CI at block boundaries (~10% of voxels), high CI inside |
| 266 | + bool isBoundary = (x % kBlockSize == 0) || (y % kBlockSize == 0) || (z % kBlockSize == 0); |
| 267 | + bool isNoisy = ((x * 7 + y * 13 + z * 29) % 10 == 0); |
| 268 | + ciStore[idx] = (isBoundary || isNoisy) ? 0.05f : 0.9f; |
| 269 | + } |
| 270 | + } |
| 271 | + } |
| 272 | + |
| 273 | + // Ensemble data: Crystal Structures (1 entry for phase 0 = unknown, 1 entry for phase 1 = cubic) |
| 274 | + auto* ensembleAM = AttributeMatrix::Create(buildDS, "Ensemble Data", {2}, imageGeom->getId()); |
| 275 | + auto* crystalStructuresArray = UnitTest::CreateTestDataArray<uint32>(buildDS, "CrystalStructures", {2}, {1}, ensembleAM->getId()); |
| 276 | + auto& crystalStructuresStore = crystalStructuresArray->getDataStoreRef(); |
| 277 | + crystalStructuresStore[0] = 999; // Unknown |
| 278 | + crystalStructuresStore[1] = 1; // Cubic-High (m-3m) |
| 279 | + |
| 280 | + UnitTest::WriteTestDataStructure(buildDS, benchmarkFile); |
| 281 | + } |
| 282 | + |
| 283 | + // Stage 2: Reload (arrays become ZarrStore in OOC) and run filter |
| 284 | + DataStructure dataStructure = UnitTest::LoadDataStructure(benchmarkFile); |
| 285 | + |
| 286 | + { |
| 287 | + const NeighborOrientationCorrelationFilter filter; |
| 288 | + Arguments args; |
| 289 | + |
| 290 | + args.insertOrAssign(NeighborOrientationCorrelationFilter::k_ImageGeometryPath_Key, std::make_any<DataPath>(DataPath({"Image Geometry"}))); |
| 291 | + args.insertOrAssign(NeighborOrientationCorrelationFilter::k_MinConfidence_Key, std::make_any<float32>(0.2f)); |
| 292 | + args.insertOrAssign(NeighborOrientationCorrelationFilter::k_MisorientationTolerance_Key, std::make_any<float32>(5.0f)); |
| 293 | + args.insertOrAssign(NeighborOrientationCorrelationFilter::k_Level_Key, std::make_any<int32>(2)); |
| 294 | + args.insertOrAssign(NeighborOrientationCorrelationFilter::k_CorrelationArrayPath_Key, std::make_any<DataPath>(DataPath({"Image Geometry", "Cell Data", "Confidence Index"}))); |
| 295 | + args.insertOrAssign(NeighborOrientationCorrelationFilter::k_CellPhasesArrayPath_Key, std::make_any<DataPath>(DataPath({"Image Geometry", "Cell Data", "Phases"}))); |
| 296 | + args.insertOrAssign(NeighborOrientationCorrelationFilter::k_QuatsArrayPath_Key, std::make_any<DataPath>(DataPath({"Image Geometry", "Cell Data", "Quats"}))); |
| 297 | + args.insertOrAssign(NeighborOrientationCorrelationFilter::k_CrystalStructuresArrayPath_Key, std::make_any<DataPath>(DataPath({"Image Geometry", "Ensemble Data", "CrystalStructures"}))); |
| 298 | + args.insertOrAssign(NeighborOrientationCorrelationFilter::k_IgnoredDataArrayPaths_Key, std::make_any<MultiArraySelectionParameter::ValueType>(MultiArraySelectionParameter::ValueType{})); |
| 299 | + |
| 300 | + auto preflightResult = filter.preflight(dataStructure, args); |
| 301 | + SIMPLNX_RESULT_REQUIRE_VALID(preflightResult.outputActions); |
| 302 | + auto executeResult = filter.execute(dataStructure, args, nullptr, IFilter::MessageHandler{[](const IFilter::Message& message) { fmt::print("{}\n", message.message); }}); |
| 303 | + SIMPLNX_RESULT_REQUIRE_VALID(executeResult.result); |
| 304 | + } |
| 305 | + |
| 306 | + fs::remove(benchmarkFile); |
| 307 | +} |
0 commit comments