BUG: Resample Data (Image Geometry) sets the output length unit to Micrometer
维护者通常 4 天内回复
还没有人认领这个 Issue。
评估
调研方向
阅读 src/simplnx/Utilities/ImageProcessing/ImageGeometryResample.cpp,重点查看创建输出几何体的 PreflightImageGeometryResample(),以及 CreateImageGeometryAction 构造函数声明,以确认其默认单位。在 src/Plugins/SimplnxCore/test/ResampleImageGeomTest.cpp 中添加或使用提供的回归用例,并运行指定的 CTest 用例。当原位输出和新几何体输出都保留输入单位时,即完成;检查对 Read Image Stack 所述的影响。
由索引模型根据 Issue 内容生成。
描述
Problem
Resample Data (Image Geometry) sets the length unit of the resampled Image Geometry to Micrometer, whatever the input geometry's unit is. A Millimeter input becomes a Micrometer output, but the origin and spacing values stay in the input's units. So the output's physical size is wrong by the ratio between the two units.
- Expected: the resampled geometry has the input geometry's length unit, both when it is saved as a new geometry and when it replaces the input.
- Actual: the resampled geometry is always Micrometer.
- Commit:
developat 2eb6b878f6ff408d1454754d4c0e63edd676d7e0.
Preflight shows the problem: the Resampled Image Geometry Info value reports the input's unit, but the geometry that execute creates is Micrometer.
Steps to Reproduce
- Create an Image Geometry with dimensions 2 x 2 x 2, origin (0, 0, 0), spacing (1, 1, 1) and length unit Millimeter. Give it a Cell Attribute Matrix with one
int32array. - Run Resample Data (Image Geometry) with Resampling Mode set to Spacing and New Spacing (0.5, 0.5, 0.5). Run it once with Perform In Place off and once with it on.
- Look at the unit of the resampled geometry. It is Micrometer in both runs.
Cause
PreflightImageGeometryResample() builds the output geometry with CreateImageGeometryAction and does not pass a unit, so the action uses its default, Micrometer:
resultOutputActions.value().appendAction(std::make_unique<CreateImageGeometryAction>(
destImagePath, geometryDimensions, srcOrigin, CreateImageGeometryAction::OriginType{resolvedValues.Spacing[0], resolvedValues.Spacing[1], resolvedValues.Spacing[2]}, cellDataName));
The default comes from the constructor declaration:
The preflight value a few lines later uses the input's unit, so the two disagree:
In place, the action builds a temporary geometry with the same Micrometer default, and that geometry then replaces the input. The test below shows the same result for both modes.
Verification Unit Test
Add to src/Plugins/SimplnxCore/test/ResampleImageGeomTest.cpp:
TEST_CASE("SimplnxCore::ResampleImageGeom: Output keeps the input length unit", "[SimplnxCore][ResampleImageGeom]")
{
UnitTest::LoadPlugins();
const bool shouldRemoveOriginal = GENERATE(false, true);
CAPTURE(shouldRemoveOriginal);
DataStructure dataStructure;
const DataPath srcGeomPath({"Geom"});
const DataPath destGeomPath({"Geom Out"});
auto* geomPtr = ImageGeom::Create(dataStructure, "Geom");
REQUIRE(geomPtr != nullptr);
geomPtr->setDimensions({2, 2, 2});
geomPtr->setOrigin({0.0F, 0.0F, 0.0F});
geomPtr->setSpacing({1.0F, 1.0F, 1.0F});
geomPtr->setUnits(IGeometry::LengthUnit::Millimeter);
auto* cellAmPtr = AttributeMatrix::Create(dataStructure, "Cell Data", {2, 2, 2}, geomPtr->getId());
REQUIRE(cellAmPtr != nullptr);
geomPtr->setCellData(*cellAmPtr);
auto* dataPtr = Int32Array::Create(dataStructure, "Data", DataStoreUtilities::CreateDataStore<int32>(dataStructure, cellAmPtr->getDataPaths().front().createChildPath("Data"), cellAmPtr->getShape(), {1}),
cellAmPtr->getId());
REQUIRE(dataPtr != nullptr);
dataPtr->fill(7);
ResampleImageGeomFilter filter;
Arguments args;
args.insertOrAssign(ResampleImageGeomFilter::k_SelectedImageGeometryPath_Key, std::make_any<DataPath>(srcGeomPath));
args.insertOrAssign(ResampleImageGeomFilter::k_CreatedImageGeometry_Key, std::make_any<DataPath>(destGeomPath));
args.insertOrAssign(ResampleImageGeomFilter::k_RemoveOriginalGeometry_Key, std::make_any<bool>(shouldRemoveOriginal));
args.insertOrAssign(ResampleImageGeomFilter::k_Spacing_Key, std::make_any<std::vector<float32>>(std::vector<float32>{0.5F, 0.5F, 0.5F}));
auto preflightResult = filter.preflight(dataStructure, args);
SIMPLNX_RESULT_REQUIRE_VALID(preflightResult.outputActions);
auto executeResult = filter.execute(dataStructure, args);
SIMPLNX_RESULT_REQUIRE_VALID(executeResult.result);
// In place, the resampled geometry replaces the input at its path. Otherwise it is created at the new path.
const DataPath outputGeomPath = shouldRemoveOriginal ? srcGeomPath : destGeomPath;
const auto* outputGeomPtr = dataStructure.getDataAs<ImageGeom>(outputGeomPath);
REQUIRE(outputGeomPtr != nullptr);
CHECK(outputGeomPtr->getSpacing() == FloatVec3{0.5F, 0.5F, 0.5F});
CHECK(outputGeomPtr->getUnits() == IGeometry::LengthUnit::Millimeter);
}
Run only this test:
ctest -R "ResampleImageGeom: Output keeps the input length unit" --output-on-failure
Output at 2eb6b87 (simplnx-Rel, macOS arm64). In IGeometry::LengthUnit, 6 is Micrometer and 7 is Millimeter:
ResampleImageGeomTest.cpp:725: FAILED:
CHECK( outputGeomPtr->getUnits() == IGeometry::LengthUnit::Millimeter )
with expansion:
6 == 7
with message:
shouldRemoveOriginal := false
ResampleImageGeomTest.cpp:725: FAILED:
CHECK( outputGeomPtr->getUnits() == IGeometry::LengthUnit::Millimeter )
with expansion:
6 == 7
with message:
shouldRemoveOriginal := true
test cases: 1 | 0 passed | 1 failed
assertions: 17 | 15 passed | 2 failed
Proposed Fix
In PreflightImageGeometryResample() (src/simplnx/Utilities/ImageProcessing/ImageGeometryResample.cpp), pass the input geometry's unit to the action:
resultOutputActions.value().appendAction(std::make_unique<CreateImageGeometryAction>(
- destImagePath, geometryDimensions, srcOrigin, CreateImageGeometryAction::OriginType{resolvedValues.Spacing[0], resolvedValues.Spacing[1], resolvedValues.Spacing[2]}, cellDataName));
+ destImagePath, geometryDimensions, srcOrigin, CreateImageGeometryAction::OriginType{resolvedValues.Spacing[0], resolvedValues.Spacing[1], resolvedValues.Spacing[2]}, cellDataName,
+ srcImageGeom.getUnits()));
Read Image Stack also calls PreflightImageGeometryResample() when it resamples, so the change reaches that filter too. Check that its own unit handling still gives the unit it intends.
Notes
- Found while reviewing #1838, which fixes the unit of orientation-space and pole-figure geometries. #1784 is a separate unit problem: deep copies of geometries drop the unit.
- The effect on Read Image Stack comes from reading the code; it was not tested.
- 主要语言
- C++
- 星标
- 18
- 派生
- 15
- 平均合并
- 5 天 1 小时
- 30 天内合并 PR
- 22
环境准备
- 没有 Dockerfile 或 Docker Compose 文件
- 有 Pull Request 模板
- 阅读贡献指南
从这里开始
- 先读完整个 Issue,再读项目的贡献指南。
- 在 Issue 下留言说明你要接手 —— 这能避免两个人做同样的事。
- Fork 仓库,在一个分支上完成修改。
- 提交 Pull Request,并在描述里引用这个 Issue 编号。
BlueQuartzSoftware/simplnx 的其他 Issue
-
bug Filter
难度 2/5 1-3 小时 新手友好度 84/100
BlueQuartzSoftware/simplnx#1891 ·
维护者通常 4 天内回复
-
DOC: Compute Feature Phases Binary describes a per-Cell output, but writes one value per Feature未关闭documentation
难度 2/5 1-3 小时 新手友好度 76/100
BlueQuartzSoftware/simplnx#1886 ·
维护者通常 4 天内回复
-
bug Filter
难度 2/5 1-3 小时 新手友好度 78/100
BlueQuartzSoftware/simplnx#1860 · 1 条评论 ·
维护者通常 4 天内回复
-
documentation
难度 1/5 1-3 小时 新手友好度 88/100
BlueQuartzSoftware/simplnx#1847 ·
维护者通常 4 天内回复
-
documentation
难度 2/5 1-3 小时 新手友好度 68/100
BlueQuartzSoftware/simplnx#1846 ·
维护者通常 4 天内回复
查看 BlueQuartzSoftware/simplnx 的全部 Issue
相似的 Issue
-
bug priority: low
难度 2/5 1-3 小时 新手友好度 68/100
维护者通常 1 天内回复
-
难度 2/5 1-3 小时 新手友好度 86/100
chipsalliance/verible#2644 ·
-
难度 2/5 1-3 小时 新手友好度 74/100
grafana/pyroscope-dotnet#470 ·
维护者通常 1 天内回复
-
documentation P5 XS
难度 2/5 1-3 小时 新手友好度 72/100
open-astro/AlpacaBridge#1848 ·
维护者通常 1 天内回复
-
DiskANN AnnIterator returns unconverted IP distances after the graph walk runs out of candidates未关闭
难度 2/5 1-3 小时 新手友好度 75/100
zilliztech/knowhere#1933 ·
维护者通常 1 天内回复