Migration: InputArray/OutputArray/InputOutputArray to allocation-free ref structs (issue #1976 step 4 & 5) - #1989
Merged
Merged
Conversation
Merged
3 tasks done
shimat
force-pushed
the
feature/inputarray-migration-remaining-5x
branch
2 times, most recently
from
July 2, 2026 12:34
f754123 to
bcec38c
Compare
…ep 4) OutputArrayOfMatList/OutputArrayOfStructList<T> and their backing OutputArray(IEnumerable<Mat>) ctor / Create(List<Mat>) / Create<T>(List<T>) factories let a caller collect a variable-length Mat/struct result into a managed List<> after a native call. This is incompatible with turning OutputArray into a ref struct (a ref struct cannot be subclassed, and cannot itself hold a heap List<> across a call boundary the way these required), so it has to go before the final class -> ref struct flip. Every real variable-length Mat output (Split, FindContours, calibration rvecs/tvecs, etc.) already goes through the independent VectorOfMat SafeHandle type instead of OutputArray, so OutputArrayOfMatList had zero callers (and IsReady() unconditionally returned false for it, meaning AssignResult() would have thrown even if it were called - dead and already broken). OutputArrayOfStructList<T> had exactly one caller (SolveEquationTest.ByNormalArray, collecting Cv2.Solve's result into a List<double>); switched it to a Mat output, matching the sibling ByMat test and the only other Cv2.Solve overload. Drops the two now-unused native functions backing the Mat-list ctor/ read-back (core_OutputArray_new_byVectorOfMat, core_OutputArray_ getVectorOfMat) and their P/Invoke declarations. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Symmetric with the OutputArray cleanup above: InputArray's IEnumerable<Mat> ctor / Create(IEnumerable<Mat>) factory / explicit (InputArray)List<Mat> and (InputArray)Mat[] cast operators had zero callers. Every real "sequence of Mat" input (StereoCalibrate, Rectify3Collinear, CalibrateCamera, DrawContours, FastNlMeansDenoisingMulti, SaveMesh, etc.) already passes a raw Mat*[] pointer array or a VectorOfMat handle straight to the native extern, never by wrapping the sequence in an InputArray first. Drops the now-unused core_InputArray_new_byVectorOfMat native function and its P/Invoke declaration. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…step 5, phase A) Ports the class-based InputArray's remaining public factory surface onto the ref-struct InputArrayRef/OutputArrayRef/InputOutputArrayRef foundation, ahead of the module-by-module migration: - Create(Mat/UMat/MatExpr/Scalar/double) explicit-call parity with the existing implicit operators, plus the same for OutputArrayRef/ InputOutputArrayRef (Create(Mat)/Create(UMat)). - Create(IVec): dispatches onto the existing zero-alloc inline Vec operators instead of the class-based path's native _InputArray allocation - a net improvement, not just parity. - Create<T>(T[]/T[,]), with/without an explicit MatType: ported unchanged from InputArray.cs (materializes a Mat via Mat.FromPixelData, same as the class-based version - not allocation-free, matching the inherent cost of turning a managed array into a Mat). Investigation while porting found InputArray's ~25-method introspection surface (GetMat/GetMatVector/GetUMat/Kind/Dims/Cols/Rows/Size/Total/Type/ Depth/Channels/IsMat/IsUMat/IsVector/etc.) has zero internal callers and no test coverage anywhere in the repo - InputArray values are only ever built and immediately passed to a native call, never queried afterward. Not porting them to InputArrayRef; they get dropped along with the class at the final rename step, same as the already-removed OutputArray List<> scaffold. This also means the raw GCHandle-pinned native-vector constructors (InputArray(byte[]/short[]/.../double[])) don't need porting either - their only caller, Create(IVec), now goes through the zero-alloc inline path instead - so InputArrayRef needs no new native-handle-owning code path and no Dispose(). 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…step 5) First real (non-foundation) module converted to the ref-struct proxy types: QualityBase/QualityMSE/QualityPSNR/QualityGMSD/QualitySSIM/QualityBRISQUE. Pilot for the mechanical recipe that the rest of the ~226 files will follow: - InputArray/OutputArray -> InputArrayRef/OutputArrayRef in every public signature. - .ToInputProxy()/.ToOutputProxy() -> .Proxy. - Drop is-null guards and .ThrowIfDisposed()/.ThrowIfNotReady()/.Fix() - the ref struct's own implicit conversion operators already null-check and disposed-check the incoming Mat/UMat, and there is no write-back to flush (native writes straight through the Mat). - GC.KeepAlive(x) -> GC.KeepAlive(x.Source) (a ref struct can't convert to object, and x.Source is what actually needs keeping alive). - OutputArray? qualityMap (required-but-nullable) -> OutputArrayRef qualityMap = default (optional, skippable) - the null-conditional operators this displaces are no longer needed since default already means "not provided" (Kind == None, which native already treats as cv::noArray()). No native changes: every quality extern was already migrated to the ArrayProxy ABI, so this is a pure C#-side signature/body edit. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…UT/...) to InputArrayRef/OutputArrayRef (issue #1976 step 5, part 1) Converts CopyMakeBorder/Add/Subtract/Multiply/Divide/ScaleAdd/AddWeighted/ ConvertScaleAbs/LUT following the recipe established on the quality module. Also fixes the two ripple-effect call sites this broke (Cv2_core.cs's methods are called from all over the codebase, not just "core" files): Mat.LUT(InputArray) -> Mat.LUT(InputArrayRef), and a CoreTest.cs assertion using InputArray.Create(10.0) -> InputArrayRef.Create(10.0). 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…ance/Normalize/ReduceArgMax/ReduceArgMin/MinMaxLoc/MinMaxIdx) to InputArrayRef/OutputArrayRef (issue #1976 step 5, part 2) Fixes the ripple-effect call sites in Mat_CvMethods.cs (MeanStdDev/Norm/ Normalize/MinMaxLoc instance wrappers) and CoreTest.cs (NormVecb using InputArray.Create(vec) explicitly). 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…Channel/InsertChannel/Flip/Rotate/Repeat) to InputArrayRef/OutputArrayRef (issue #1976 step 5, part 3) Fixes the Mat_CvMethods.cs MinMaxIdx/InsertChannel instance wrappers this broke. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…oncat/BitwiseAnd/Or/Xor/Not/Absdiff/CopyTo/InRange/Compare/Min/Max) to InputArrayRef/OutputArrayRef (issue #1976 step 5, part 4) Fixes the Mat.InRange(InputArray,InputArray) instance wrapper this broke. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…rtToPolar/Phase/Magnitude/CheckRange/PatchNaNs/Gemm/MulTransposed/Transpose/Transform/PerspectiveTransform) to InputArrayRef/OutputArrayRef (issue #1976 step 5, part 5) Fixes the Mat_CvMethods.cs MulTransposed/Transform/PerspectiveTransform instance wrappers, and rewrites 3 InputArrayRefTest.cs parity tests that compared InputArrayRef.Create(...) against the class-based InputArray.Create (...) via Cv2.Transpose - that comparison is no longer possible now that Transpose itself takes InputArrayRef, so they compare against a directly -built Mat instead (equally rigorous, and no longer coupled to the soon-to-be-deleted class). Note: Cv2.Transpose is now byte-identical to the foundation's Cv2.TransposeRef (same signature, same native call) - cleanup (delete the redundant *Ref foundation methods once Add/CompleteSymm are migrated too) deferred to a dedicated pass so as not to disrupt the in-progress module sweep. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…eterminant/Trace/Invert/Solve/SolveLP/Sort/SortIdx) to InputArrayRef/OutputArrayRef (issue #1976 step 5, part 6) Fixes SolveEquationTest.ByNormalArray, which explicitly built InputArray/ OutputArray via Create(...) rather than relying on an implicit conversion. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…c/SolvePoly/Eigen/EigenNonSymmetric/CalcCovarMatrix/PCACompute/PCAComputeVar) to InputArrayRef/OutputArrayRef (issue #1976 step 5, part 7) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…oject/PCABackProject/SVDecomp/SVBackSubst/Mahalanobis/Dft/Idft/Dct/Idct) to InputArrayRef/OutputArrayRef (issue #1976 step 5, part 8) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…/Randn/RandShuffle/Kmeans) to InputArrayRef/OutputArrayRef (issue #1976 step 5, part 9) Completes the #region core.hpp block in Cv2_core.cs. Fixes the Mat_CvMethods.cs Randu/Randn instance wrappers. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…Zero/Mean/Sqrt/PCAComputeVar overload/Format) to InputArrayRef/OutputArrayRef (issue #1976 step 5, part 10) Completes the InputArrayRef/OutputArrayRef/InputOutputArrayRef conversion of Cv2_core.cs - these were missed by earlier batches (out of file order or in a second overload). Fixes the Mat.Mean(InputArray?) instance wrapper. Verified: zero remaining InputArray/OutputArray/InputOutputArray parameter declarations in Cv2_core.cs (only native extern function names containing those substrings remain, e.g. core_meanStdDev_OutputArray). 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…Dot) to InputArrayRef/OutputArrayRef (issue #1976 step 5, part 11) Mul() captures the InputArrayRef's proxy/source by value before handing off to MatExpr.FromExpr's deferred delegate, since ref structs cannot be captured by a closure.
These wrapped Cv2 static methods as Mat instance methods purely for chainable call syntax (e.g. mat.GaussianBlur(...).CvtColor(...)). The duplication doubled the maintenance surface of every Cv2 static method, and intermediate Mats produced mid-chain cannot be captured with `using`, so they leaked into non-deterministic GC-driven native cleanup -- the same class of problem InputArray/OutputArray ref-structification and the MatExpr lazy-tree rework were addressing elsewhere. Call sites now use Cv2.Xxx(...) directly. This is a breaking change for OpenCvSharp5.
…to InputArrayRef/OutputArrayRef (issue #1976 step 5, part 12)
…f/InputOutputArrayRef (issue #1976 step 5, part 13) Completes the "core残り" batch alongside Mat.cs/UMat.cs (parts 11-12).
…/OutputArrayRef/InputOutputArrayRef (issue #1976 step 5, part 14) Scripted the mechanical parts of this conversion (signature retyping, null-check/ThrowIfDisposed/ThrowIfNotReady/Fix() removal, ToXProxy()-> .Proxy, GC.KeepAlive(x)->GC.KeepAlive(x.Source)) since Cv2_imgproc.cs is the largest Cv2 static file; manually fixed the handful of cases the script couldn't infer: - CreateHanningWindow's dst is purely an output (native signature takes OutputArray, not InputOutputArray) despite the old class-based API exposing it as InputOutputArray via inheritance -- InputOutputArrayRef has no such inheritance, so the param is now OutputArrayRef. - Moments' InputArray constructor/helper migrated alongside its only caller (Cv2.Moments). - Call sites passing literal `null` for now non-nullable *Ref parameters (CalcHist mask, MorphologyEx element, EMD cost) updated to `default`. - ImgProcTest.Rectangle's InputOutputArray.Create(...) smoke coverage swapped for InputOutputArrayRef.Create(...).
…ef/InputOutputArrayRef (issue #1976 step 5, part 15) Scripted via migrate_step5.py (same tool used for Cv2_imgproc.cs). Only manual fixup needed: the two convenience Rodrigues(double[]/double[,]) overloads built explicit old-class InputArray.Create(...)/ OutputArray.Create(...) wrappers around their Mats before delegating to Rodrigues(InputArrayRef, OutputArrayRef, OutputArrayRef) -- simplified to pass the Mats directly, since Mat converts implicitly to the *Ref types.
…InputOutputArrayRef (issue #1976 step 5, part 16) Scripted via migrate_step5.py; no manual fixups needed this time.
…InputOutputArrayRef (issue #1976 step 5, part 17) Scripted via migrate_step5.py; fixed one null-literal call site (FindTransformECC's inputMask) to use default.
…ef/InputOutputArrayRef (issue #1976 step 5, part 18) Scripted via migrate_step5.py; no manual fixups needed.
…InputOutputArrayRef (issue #1976 step 5, part 19) Scripted via migrate_step5.py; no manual fixups needed. Completes the Cv2 static-file batch that followed the ArrayProxy ABI migration's module order (core/imgproc/geometry/calib/video/objdetect/stereo).
…tputArrayRef/InputOutputArrayRef (issue #1976 step 5, part 20) Covers every remaining class-based InputArray/OutputArray/ InputOutputArray consumer in src/OpenCvSharp/Modules and the nested Cv2.<Sub> facades (Aruco/Dnn/OptFlow/Detail/Text/XImgProc/XPhoto/Shape), completing issue #1976 Step 5's type migration across the whole library. Scripted via a generalized migrate_step5_v2.py that extends the Cv2 static-file script to instance methods and constructors (any access modifier, not just `public static`), since these files are mostly classes with instance members rather than Cv2 facade statics. Fixed two script bugs found along the way: - Missing \b word boundaries let e.g. "sum" match inside "sqsum". - The block-vs-expression-body scan treated any "=>" as an expression- bodied method terminator, including lambdas inside a constructor's `: base(..., x => ...)` initializer -- now tracks paren depth so only top-level terminators count. Manual fixups beyond the script: - FaceDetectorYN.Detect: replaced explicit `new InputArray(image)`/ `new OutputArray(faces)` wrapping with direct Mat-to-*Ref conversion (no owning wrapper needed anymore). - CharucoDetector: the cameraMatrix/distCoeffs "both null or both non-null" validation used `is null`, which doesn't apply to non- nullable ref structs -- rewritten to check Proxy.Kind against ArrayProxyKind.None. - A handful of leftover `if(x is null)` checks (no space after `if`, which the removal regex didn't match) caused CS0037 "null to non- nullable value type" -- removed. - XML doc <see cref="..."/> references to old signatures updated to match the new *Ref parameter types.
…ssue #1976 step 5, part 20 follow-up) - Null-literal arguments for now non-nullable *Ref parameters (mask/ element/etc.) changed to `default` across several test files. - Removed BarcodeDetectorTest.DetectAndDecode_NullImage_ThrowsArgumentNullException and WeChatQRCodeTest.DetectAndDecode_NullInput_ThrowsArgumentNullException: both asserted an ArgumentNullException from passing `null!` for an InputArrayRef parameter, which the type system now rejects at compile time instead -- the runtime check the test covered no longer exists.
…sue #1976 step 5, final cleanup part 1) All call sites were migrated to the *Ref ref structs in prior commits; this removes the now-dead classes. InOutArrayKind (and the KIND_SHIFT/ KIND_MASK constants that backed it) is deleted alongside InputArray: it only existed to support InputArray.Kind(), one of the ~25 introspection methods (GetMat/Empty/Type/IsMat/etc.) that turned out to have zero callers and zero tests when the *Ref types were designed, and so was never ported. Zero build fallout confirms every caller had already moved to InputArrayRef/OutputArrayRef/InputOutputArrayRef.
… cleanup part 2) TransposeRef/AddRef/CompleteSymmRef were the original proof-of-concept for the ref-struct design and call the exact same native functions as the now-migrated Cv2.Transpose/Add/CompleteSymm, making them pure duplicates. Rewired InputArrayRefTest.cs to exercise the same allocation-free and Create(...) factory behavior through the real, permanent Cv2 methods instead of the deleted PoC ones, so the zero-allocation regression coverage isn't lost.
…l names (issue #1976 step 5, final cleanup part 3) Mechanical rename now that the class-based InputArray/OutputArray/ InputOutputArray are gone and the names are free: InputArrayRef -> InputArray OutputArrayRef -> OutputArray InputOutputArrayRef -> InputOutputArray Single-pass whole-word regex per name (no placeholder/2-pass needed, unlike the earlier MatExprNode->MatExpr rename, since these three names don't overlap as substrings of each other and the target names weren't already in use). Renamed the defining file InputArrayRef.cs -> InputArray.cs and the test file/class InputArrayRefTest(.cs) -> InputArrayTest. Also dropped the ArrayProxyKind.RawInputArray/RawOutputArray/ RawInputOutputArray migration-scaffold enum values and rewrote the file-header comment in InputArray.cs: both were explicitly documented as "removed once the type flip is complete", which it now is. Nothing in the codebase ever constructed a proxy with those Kind values anymore (they existed solely so externs could wrap a class-based InputArray/OutputArray/InputOutputArray handle during the incremental ArrayProxy ABI migration). This completes issue #1976 step 5: InputArray/OutputArray/ InputOutputArray are now ref structs everywhere, matching their original class-based public API names.
shimat
force-pushed
the
feature/inputarray-migration-cleanup-5x
branch
from
July 2, 2026 12:45
7155b17 to
51a3786
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Stacked on #1988. Completes the class →
ref structflip forInputArray/OutputArray/InputOutputArray(issue #1976), the final step of the allocation-free array-proxy redesign whose ArrayProxy ABI foundation landed in #1980/#1983/#1986/#1987/#1988.OutputArrayOfMatList/OutputArrayOfStructList<T>, theInputArray(IEnumerable<Mat>)ctor/Createoverloads, and the corresponding nativecore_{Input,Output}Array_new_byVectorOfMat/core_OutputArray_getVectorOfMat) — all had zero callers once the actual vector-of-Mat paths turned out to go throughVectorOfMat(SafeHandle-based) instead.InputArray/OutputArray/InputOutputArrayconsumer across the library (Cv2 static facades,Mat/UMatinstance methods, and every class inModules/) from the old heap-allocated class to the allocation-free*Refref structs, then renamed the ref structs over the vacated names, deleted the old classes, and removed the now-superseded PoC scaffolding (Cv2_experimental.cs,ArrayProxyKind.Raw*).Mat_CvMethods.cs, the ~150Matinstance-method wrappers that just forwarded toCv2.Xxxfor chainable call syntax. They shared the same problem this whole redesign targets — intermediateMats produced mid-chain can't be captured withusing, so they leaked into non-deterministic GC-driven native cleanup — and duplicating everyCv2static method's signature for chaining wasn't worth maintaining. Callers now useCv2.Xxx(...)directly; this is a breaking change for OpenCvSharp5.Native (extern) side is untouched in this PR — the ArrayProxy ABI from the earlier stacked PRs already receives
InputArrayProxy/OutputArrayProxy/InputOutputArrayProxyby value/pointer, so this is purely a managed-side type change (class → ref struct) with no ABI impact.Test plan
OpenCvSharpbuilds clean (dotnet build -c Release), includingOpenCvSharp.GdipExtensions/OpenCvSharp.WpfExtensionsOpenCvSharp.Tests: 1238 passed / 27 skipped / 0 failedOpenCvSharp.Tests.Windows: 14 passed / 1 skipped