Repository navigation
Conversation
|
@ntustison could you take a look when you have a moment? This came out of the N4 review on #7075. |
|
@greptileai review |
[Medium impact] The change affects N4 bias correction when global data release is enabled. The buffer changes appear sound, but the existing image-iteration requirement must be satisfied before merging. Findings
|
| filter->Update(); | ||
|
|
||
| const N4ReleaseImageType * output = filter->GetOutput(); | ||
| return { output->GetBufferPointer(), output->GetBufferPointer() + output->GetBufferedRegion().GetNumberOfPixels() }; |
There was a problem hiding this comment.
Output copy skips image iterators
The new output copy builds a vector from raw buffer pointers. The project's image iteration standard says, “Use iterators, not raw buffer access.” Build the returned vector from image iterators to satisfy this repository requirement before merging.
Context Used: AGENTS.md (source)
Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!
hjmjohnson
left a comment
There was a problem hiding this comment.
Thanks. The root-cause list checks out: every input that is reused after a release is now passed as a view, the fresh view before RefineControlPointLattice is there, and the test fails on main (reasoned from the code: subtracter2 reads the released newLogBiasField). The test throws in the first iteration's subtracter2, not "at the second iteration" as the body says.
Requesting changes for one item, which is Greptile's P1 as well: the view in the public ReconstructBiasField changes its behavior for callers outside the filter (inline).
Non-blocking:
MakeInputViewis a template defined in the.h; the module layout puts template bodies in the.hxx.- The test compares a flagged run with an unflagged one, with
UseMaskLabelat its default. The fixed-baseline release-flag test #7075 deferred to this PR can follow once both merge. - The commit body's sentences "The output is unchanged. A GoogleTest runs N4 ..." restate the diff.
- The PR body still ends with the "Generated with Claude Code" footer; the AI disclosure in
<details>is enough (non-blocking).
This review ran as /code-review max (Standards and Spec axes) on Opus 5.5. Once these are addressed I will re-check, then request a Greptile review and mark the PR ready.
| using BSplineReconstructerType = BSplineControlPointImageFilter<BiasFieldControlPointLatticeType, ScalarImageType>; | ||
| auto reconstructer = BSplineReconstructerType::New(); | ||
| reconstructer->SetInput(controlPointLattice); | ||
| reconstructer->SetInput(MakeInputView(controlPointLattice)); |
There was a problem hiding this comment.
Should-fix: ReconstructBiasField is public. Wrapping the caller's lattice in a graft-based view here means a lattice that is an un-updated pipeline output (a reader's GetOutput() before Update()) is no longer brought up to date: the view has no source, and Graft copies regions and the pixel container but not the producer. That is a silent behavior change on a public method. The only in-tree caller is line 607, so the view can move there and leave the public method as on main:
| reconstructer->SetInput(MakeInputView(controlPointLattice)); | |
| reconstructer->SetInput(controlPointLattice); |
and at line 607: this->ReconstructBiasField(MakeInputView(this->m_LogBiasFieldControlPointLattice.GetPointer())).
There was a problem hiding this comment.
Done in 4a1014a, as suggested: the public ReconstructBiasField sets the caller's lattice as its input again, exactly as on main, and the only in-tree caller passes MakeInputView(this->m_LogBiasFieldControlPointLattice.GetPointer()). Removing that call-site view makes GlobalReleaseDataFlagDoesNotChangeOutput fail, so the test still covers it.
1d7d6f3 to
4a1014a
Compare
|
Thanks @hjmjohnson. Force-pushed 4a1014a. Besides the inline item: |
4a1014a to
38dc67e
Compare
…aFlag N4 passes the control point lattice, which it reuses later and returns from GetLogBiasFieldControlPointLattice, as the input of its internal B-spline reconstruction and refinement. With DataObject::GlobalReleaseDataFlag on, those filters released it after their update, and the next use threw "outside of buffered region". Pass a view that grafts the lattice instead, so a release frees only the view. MakeBiasFieldReconstructer, which every reconstruction uses, makes the view, and the refinement gets a fresh one.
38dc67e to
32fc255
Compare
|
Rebased onto |
Make N4BiasFieldCorrectionImageFilter run with
DataObject::GlobalReleaseDataFlagon; onmainit throws "outside of buffered region" when the control point lattice is read after a reconstruction. The output is unchanged.Root cause
N4 passes the control point lattice, which it reuses later, as an input of internal filters. After each internal update,
ProcessObject::ReleaseInputs()callsReleaseData()on those inputs whenShouldIReleaseData()is true, which is always the case with the global flag. The lattice is the input of the B-spline reconstructers (throughMakeBiasFieldReconstructer, for every iteration's field, the final field andReconstructBiasField), and is read again by the adder, byRefineControlPointLatticeand by callers ofGetLogBiasFieldControlPointLattice.Before #7072, #7075 and #7085 merged, the log input, the sharpened image and the bias field estimates went through internal subtract filters too;
mainnow computes those differences in place, so only the lattice is left.Found while answering a review on #7075.
Fix
A private helper,
MakeInputView(image), returns a new image that grafts the given one, so it shares the buffer.MakeBiasFieldReconstructer, which every reconstruction uses, passes such a view instead of the lattice itself. A release then initializes only the view, and the original keeps its buffer. BeforeRefineControlPointLattice, the reconstructer gets a fresh view, because the refinement reads its input after the update. No values or summation orders change.Test results
N4BiasFieldCorrectionImageFilter.GlobalReleaseDataFlagDoesNotChangeOutput: N4 on a 40 x 40 image with a bias field, a mask and a graded confidence image (2 levels x 4 iterations), normally and under a guard that turns the global flag on and restores it, compared withEXPECT_EQ. It throws onmainand passes with this PR.mainwith PERF: Build N4 B-spline fit points once instead of in each iteration #7075 and PERF: Reuse N4 iteration buffers to cut per-iteration allocations #7085:ctest -R 'BSpline|N4|BiasCorrection'passes 42/42, and the build adds no warnings. Without the view inMakeBiasFieldReconstructer, the new GTest throws.AI assistance