{"owner":"shimat","repo":"opencvsharp","hasSkills":true,"totalSkillsCount":6,"totalTokensCount":8054,"categories":["claude-rule","root-instruction","copilot-instructions","mcp-config","marketplace","plugin-manifest"],"hasMcp":true,"mcpConfig":{"mcpServers":{"opencvsharp":{"command":"npx","args":["-y","@modelcontextprotocol/server-opencvsharp"]}}},"found":["CLAUDE.md","AGENTS.md",".github/copilot-instructions.md",".cursor/mcp.json",".claude-plugin/marketplace.json",".claude-plugin/plugin.json"],"skills":{"CLAUDE.md":"See `.github/copilot-instructions.md` for development instructions.\n","AGENTS.md":"See `.github/copilot-instructions.md` for development instructions.\n",".github/copilot-instructions.md":"# Copilot Instructions\n\n## File encoding\n\nAll text files in this repository use **UTF-8 without a BOM**. This applies to `.cs`, `.csproj`, `.yml`, `.md`, `.json`, `.cpp`, `.h`, and every other text file — there are no exceptions.\n\n- `.editorconfig` declares `charset = utf-8` (no BOM) for all files, so editors and `dotnet format` enforce this automatically.\n- The `Write` / `create_file` tool already produces UTF-8 without a BOM, so just create and edit files normally — no encoding fix-up step is needed.\n- Do **not** add a BOM, and do not save as ANSI, Shift-JIS, or UTF-16. The repository is English-only (ASCII), and the C++ build sets the UTF-8 source charset, so MSVC reads the sources correctly without a BOM.\n- **All repository content is English**, regardless of what language a request or discussion happens in: code, comments, identifiers, commit messages, PR/issue titles and descriptions, and every file under `docs/`. This holds even when the person or agent driving the change is working with the maintainer in a different language.\n\n## NuGet README sync\n\nWhen editing the root `README.md`, also update the NuGet-specific README files accordingly.\n\n| File | Target packages |\n|---|---|\n| `packaging/nuget/README.managed.md` | `OpenCvSharp5`, `OpenCvSharp5.Windows`, `OpenCvSharp5.Windows.Slim`, `OpenCvSharp5.GdipExtensions`, `OpenCvSharp5.WpfExtensions`, `OpenCvSharp5.AvaloniaExtensions` |\n| `packaging/nuget/README.runtime.md` | `OpenCvSharp5.runtime.*`, `OpenCvSharp5.official.runtime.*` (all native runtime packages) |\n\n> The `main`-branch packages are the `OpenCvSharp5.*` family (OpenCV 5.x). An identically-structured `OpenCvSharp4.*` family (OpenCV 4.13.0, frozen on the `4.x` branch) is also published for .NET Framework / pre-.NET 8 consumers — see the branch note under \"Versioning and release process\".\n\nThe NuGet READMEs are a subset of the top-level README and consist of the following sections:\n\n- Overview and supported platforms\n- Installation instructions (Quick Start)\n- Requirements\n- Slim profile module coverage table (`README.managed.md` only)\n- Code usage examples (`README.managed.md` only)\n- Links (GitHub, Samples, API Docs, Issue Tracker)\n\nDo **not** include CI badges, Docker instructions, build instructions, or donation links in the NuGet READMEs.\n\n## Markdown authoring\n\nDo not hard-wrap Markdown prose at a fixed character width in the middle of a sentence. Write each paragraph, and each list item, as a single unbroken line in the source, and let the renderer/editor soft-wrap it for display. Only start a new line for a genuinely new block: the next list item, a new paragraph, a heading, a code fence, etc. This applies to every Markdown file in the repo (READMEs, `docs/*.md`, this file) as well as PR/issue/commit-message bodies that render as Markdown — manually wrapping mid-sentence makes diffs noisy (a small wording edit reflows the whole paragraph) and reads as pointlessly rigid formatting. When editing an existing hard-wrapped file, prefer reflowing the touched paragraphs to single lines rather than preserving the old wrap width.\n\n## Agent mode — terminal commands\n\nIn agent mode, do **not** use display commands that require user input (e.g., `more`, `less` without options). Use non-interactive alternatives instead:\n- PowerShell: `Select-Object -First N`, `Out-String`, `Write-Output`\n- Git: pass `-P` or `--no-pager`, or pipe to `Out-String`; e.g. `git --no-pager diff`\n- Use `cat` for displaying file contents.\n\n## Adding a new OpenCV class wrapper\n\n> **Scope**: This checklist covers `cv::SomeClass : cv::Algorithm` subclasses. OpenCV also has classes that do **not** inherit from `Algorithm` — those may follow different ownership and lifetime patterns (see existing non-Algorithm wrappers such as `BackgroundSubtractor` or classes in `core/` for reference).\n\nFollow this checklist when wrapping a new `cv::SomeClass : cv::Algorithm` class:\n\n### Files to create\n\n| File | Location |\n|---|---|\n| `<module>_SomeClass.h` | `src/OpenCvSharpExtern/` |\n| `SomeClass.cs` | `src/OpenCvSharp/Modules/<module>/` |\n| `NativeMethods_<module>_SomeClass.cs` | `src/OpenCvSharp/Internal/PInvoke/NativeMethods/<module>/` |\n| `SomeClassTest.cs` | `test/OpenCvSharp.Tests/<module>/` |\n| `Enum/SomeEnum.cs` (if needed) | same module folder |\n| `VectorOfVecXy.cs` (only if the element type is non-blittable/nested — blittable types use `StdVector<T>` directly, no new file needed) | `src/OpenCvSharp/Internal/Vectors/` |\n\n### Files to modify\n\n| File | Change |\n|---|---|\n| `<module>.cpp` | Add `#include \"<module>_SomeClass.h\"` |\n| `std_vector.h` | Add `#pragma region cv::VecXy` block if new vector type needed |\n| `NativeMethods_stdvector.cs` | Add corresponding P/Invoke region if new vector type needed |\n| `Cv<Module>.cs` | Add `public static SomeClass CreateSomeClass()` factory method |\n\n### C++ extern pattern (<module>_SomeClass.h)\n\n```cpp\n#pragma once\n#ifndef NO_CONTRIB\n#include \"include_opencv.h\"\n\nCVAPI(ExceptionStatus) <module>_Ptr_SomeClass_delete(cv::Ptr<cv::<module>::SomeClass> *obj)\n{ BEGIN_WRAP delete obj; END_WRAP }\n\nCVAPI(ExceptionStatus) <module>_Ptr_SomeClass_get(\n    cv::Ptr<cv::<module>::SomeClass> *ptr, cv::<module>::SomeClass **returnValue)\n{ BEGIN_WRAP *returnValue = ptr->get(); END_WRAP }\n\nCVAPI(ExceptionStatus) <module>_createSomeClass(\n    cv::Ptr<cv::<module>::SomeClass> **returnValue)\n{\n    BEGIN_WRAP\n    const auto ptr = cv::<module>::createSomeClass();\n    *returnValue = new cv::Ptr<cv::<module>::SomeClass>(ptr);\n    END_WRAP\n}\n// ... method bindings ...\n#endif // NO_CONTRIB\n```\n\n### C# class pattern (SomeClass.cs)\n\n```csharp\npublic class SomeClass : Algorithm\n{\n    private SomeClass(IntPtr smartPtr, IntPtr rawPtr)\n        : base(smartPtr, rawPtr, p => NativeMethods.HandleException(\n            NativeMethods.<module>_Ptr_SomeClass_delete(p))) { }\n\n    public static SomeClass Create()\n    {\n        NativeMethods.HandleException(NativeMethods.<module>_createSomeClass(out var smartPtr));\n        NativeMethods.HandleException(NativeMethods.<module>_Ptr_SomeClass_get(smartPtr, out var rawPtr));\n        return new SomeClass(smartPtr, rawPtr);\n    }\n\n    public virtual void SomeMethod(InputArray src) {\n        ThrowIfDisposed();\n        ArgumentNullException.ThrowIfNull(src);\n        src.ThrowIfDisposed();\n        NativeMethods.HandleException(NativeMethods.<module>_SomeClass_someMethod(RawPtr, src.CvPtr));\n        GC.KeepAlive(this);\n        GC.KeepAlive(src);\n    }\n\n    // OutputArray methods: call dst.Fix() after the P/Invoke call\n    // std::vector return methods: use StdVector<T> for blittable T (VectorOfXxx only for\n    // non-blittable/nested element types), wrap in using, call .ToArray()\n}\n```\n\n### Argument validation\n\nUse the BCL `ArgumentNullException.ThrowIfNull(x)` / `ArgumentOutOfRangeException.ThrowIfNegative(x)` / `.ThrowIfGreaterThan(x, max)` / etc. helpers instead of a manual `if (x is null) throw ...` or `if (x < 0) throw ...`. `CvObject.ThrowIfDisposed()` itself is implemented as `ObjectDisposedException.ThrowIf(IsDisposed, this)`. A few patterns are deliberately **not** converted to `ThrowIfNull`:\n\n- A ctor-chain initializer (`: this(...)`, `: base(...)`) or expression-bodied member (`=>`) that does `x ?? throw new ArgumentNullException(nameof(x))` — `ThrowIfNull` returns `void`, so it can't be used as part of an expression; keep the `?? throw` form there.\n- `if (string.IsNullOrEmpty(x)) throw new ArgumentNullException(...)` (filenames, extensions, etc.) — this intentionally raises `ArgumentNullException` for *both* a null and an empty string. Don't \"upgrade\" it to `ArgumentException.ThrowIfNullOrEmpty`, which throws `ArgumentException` for the empty-string case — that's a behavior change, not a mechanical rewrite.\n- A cast-based unsigned-comparison bounds check (`(uint)row >= (uint)Rows`) can still use `ThrowIfGreaterThanOrEqual((uint)row, (uint)Rows, nameof(row))`, but pass `nameof(row)` explicitly — `CallerArgumentExpression` would otherwise infer the parameter name as the literal cast expression text.\n\n### Params struct pattern (P/Invoke-compatible)\n\nWhen the C++ class has a `Params` struct with `bool` fields, define a flat C struct with `int` for booleans (not a marshaled `bool` — no `[MarshalAs(UnmanagedType.Bool)]`) and convert in `getParams`/`setParams`:\n\n```cpp\n// C++ side\nstruct CvSomeClassParams { int SomeBool; /* other fields */ };\n\nCVAPI(ExceptionStatus) <module>_SomeClass_getParams(obj, CvSomeClassParams* out) {\n    BEGIN_WRAP out->SomeBool = obj->params.SomeBool ? 1 : 0; END_WRAP }\nCVAPI(ExceptionStatus) <module>_SomeClass_setParams(obj, CvSomeClassParams* p) {\n    BEGIN_WRAP cv::<module>::SomeClass::Params q; q.SomeBool = p->SomeBool != 0; obj->setParams(q); END_WRAP }\n```\n\n```csharp\n// C# side — the P/Invoke-facing struct keeps SomeBool as a plain int (blittable, matches the C struct 1:1)\n[StructLayout(LayoutKind.Sequential)]\ninternal struct CvSomeClassParams {\n    public int SomeBool;\n    // other fields...\n}\n// The public-facing Params wrapper exposes a `bool SomeBool` property that converts\n// manually (`value != 0` on get, `value ? 1 : 0` on set) — see EdgeDrawingParams /\n// SimpleBlobDetector.Params for real examples.\n// P/Invoke: out CvSomeClassParams / ref CvSomeClassParams\n```\n\n### One-shot config classes: getAll/setAll pattern\n\nIf a `Params`/`Settings`-style class has behavior (mutated after construction and read by a live algorithm), keep individual native getter/setter P/Invoke calls per field as above. But if it's a pure **one-shot config** — constructed, handed to a factory/algorithm, and never mutated afterward (verify this against the OpenCV source before applying) — prefer folding all the scalar fields into a single blittable POD struct with one `getAll`/`setAll` P/Invoke pair instead of one call per field: a native temporary is created only at construction/read/write time, and the C# side becomes a plain managed data class with no native handle at all. `EdgeDrawingParams`, `SimpleBlobDetector.Params`, and `FacemarkLBF`/`FacemarkAAM`'s `Params` are reference implementations of this pattern. `Mat`/`string`/vector-typed fields still need their own separate P/Invoke argument (they don't fit in a blittable struct).\n\n### Namespace access note\n\nFrom `namespace OpenCvSharp.Internal`, types in `namespace OpenCvSharp` are directly visible (outer scope rule). Types in a sub-namespace such as `namespace OpenCvSharp.XImgProc` are NOT — add an explicit `using` directive in the NativeMethods file when referencing structs defined there.\n\n### POD value types crossing the extern boundary\n\nGeneral-purpose primitive value types passed across the P/Invoke boundary (`Point`/`Size`/`Rect`/`Scalar`/`Range`/`RotatedRect`/`KeyPoint`/`DMatch`/`Vec2b`..`Vec6d`, etc.) live under `namespace interop` with a clean name (no `MyCv*`/`CvVec*` prefix). Convert to/from the corresponding `cv::` type with the `OCS_INTEROP_BITCAST(InteropType, cv::Type)` macro (`std::bit_cast` plus a `static_assert` on size/layout match) rather than a hand-written field-by-field converter — the `static_assert` catches layout drift at compile time. Two exceptions:\n\n- `cv::Scalar`/`cv::Vec*` (and `Moments`) are **not** trivially copyable (`cv::Matx`-derived, user-defined copy/move ops) and can't go through `bit_cast` — they keep an explicit converter. Geometry types (`Point_`/`Size_`/`Rect_`/`RotatedRect`/`KeyPoint`/`DMatch`/`Range`/`TermCriteria`) *are* trivially copyable and use `bit_cast`.\n- Module-specific param/config structs (`aruco_DetectorParameters`, `ml_DTrees_*`, `*_Params`, `CvEdgeDrawingParams`, etc.) stay with their `module_` prefix and are **not** moved into `interop::` — the prefix is a meaningful namespace (which feature the struct belongs to), and folding it into the generic `interop::` bucket would obscure that.\n\n### Common P/Invoke pitfalls\n\n- **`cv::Ptr<T>*` vs. raw `T*` confusion.** `CvPtrObject.Handle` (used by most property getters/setters) returns a raw `T*`, not the smart pointer — a native binding declared as `cv::Ptr<T>* obj` with `(*obj)->getXxx()` silently misinterprets the raw pointer's vtable as a `cv::Ptr`'s internal layout and reliably crashes (access violation) the first time it's called, not at compile time. Match the parameter type to what the C# side actually passes (`Handle` → raw `T*` parameter with `obj->getXxx()`); this bit `BackgroundSubtractorGMG`/`MOG`'s property accessors and went unnoticed because no test exercised a round trip.\n- **Don't change a marshaling attribute (`[MarshalAs(UnmanagedType.LPStr)]` vs. `LPUTF8Str`) without checking every native code path that consumes it.** Different OpenCV backends decode filename strings differently (e.g. `cap_msmf.cpp` uses `MultiByteToWideChar(CP_ACP, ...)`, i.e. it expects ANSI, not UTF-8) — switching the attribute to \"fix\" one call site can silently corrupt non-ASCII paths on a different backend. This kind of regression doesn't show up in a plain build/test run; it only surfaces when you actually exercise the affected path with non-ASCII input.\n- **Write one real get/set (or call/verify) round trip before copying an existing property/method pattern to a new class.** Don't trust an existing, untested binding as a copy-source just because it compiles — verify it actually works first.\n\n### Free-function facade naming: `Cv2` mirrors `cv::`\n\nFree functions are exposed as static methods on `Cv2`, mirroring the C++ namespace structure:\n\n- `Cv2` = `cv::` — e.g. `Cv2.CvtColor` ↔ `cv::cvtColor`.\n- `Cv2.<Sub>` (a nested `public static partial class` under `Cv2`) = `cv::<sub>::` — e.g. `Cv2.Dnn.ReadNetFromONNX` ↔ `cv::dnn::readNetFromONNX`, matching the Python `cv2.dnn.*` submodules. In scope: `Dnn`, `Aruco`, `XImgProc`, `XPhoto`, `OptFlow`, `Text`, `Detail`, `Shape`, `VideoIORegistry`.\n\nPlace each submodule facade in its module folder as `Cv2.<Sub>.cs` (e.g. `Modules/dnn/Cv2.Dnn.cs`). The file uses `namespace OpenCvSharp;` and `using OpenCvSharp.<Sub>;` (the wrapper *types* stay in their `OpenCvSharp.<Sub>` namespaces). Where a sub-namespace type name collides with one in `OpenCvSharp` root (e.g. `InpaintTypes`), fully-qualify it in the signature (`OpenCvSharp.XPhoto.InpaintTypes`). Functions that live directly in `cv::` (no sub-namespace, e.g. the shape `Create…` factories) go on `Cv2` itself, not a nested class. The `CvExtensions` *extension methods* (`OpenCvSharp.Extensions`) are not part of this and keep their name.\n\n### std::vector return values\n\n- For a blittable/primitive element type (`int`, `byte`, `float`, `double`, `Point`, `Point2f`, `Vec4i`, `Vec4f`, `Vec6d`, ...), use the generic `StdVector<T>` directly — do **not** create a dedicated `VectorOfXxx` class. E.g. `new StdVector<Point2f>()`, `new StdVector<byte>()`, `new StdVector<Vec4i>()`.\n- Only create a dedicated `VectorOfXxx.cs` for element types that aren't blittable or need custom marshaling: nested vectors (`std::vector<std::vector<Point>>` → `VectorOfVectorPoint`), `Mat` (`VectorOfMat`), `String` (`VectorOfString`), or other non-trivial types (`VectorOfKeyLine`, `VectorOfDTreesNode`, ...).\n- New dedicated vector types: add `#pragma region` in `std_vector.h`, `#region` in `NativeMethods_stdvector.cs`, and create `VectorOfXxx.cs`.\n\n### EdgeDrawing as reference implementation\n\nSee `src/OpenCvSharpExtern/ximgproc_EdgeDrawing.h`, `src/OpenCvSharp/Modules/ximgproc/EdgeDrawing.cs` for a complete example covering: factory, OutputArray methods, `StdVector<T>`-returning methods (`StdVector<Vec6d>` in `DetectEllipses()`, `StdVector<Vec4f>` in `DetectLines()`, `StdVector<int>` in `GetSegmentIndicesOfLines()`), a nested `VectorOfVectorPoint`-returning method, and a Params struct with `int`-backed bool fields (`EdgeDrawingParams`).\n\n## Enum member naming\n\nC++ enum members are `ALL_CAPS_WITH_UNDERSCORES` (e.g. `IMWRITE_JPEG_QUALITY`, `NORM_L2SQR`). When wrapping them as a C# enum, **default to PascalCase**, stripping the module/family prefix (`IMWRITE_JPEG_QUALITY` → `JpegQuality`). Keep the original casing (or a near-verbatim form) only when one of these narrow exceptions applies:\n\n1. **The value itself is an unbreakable acronym/abbreviation, standing alone (nothing else glued onto it).** Test: read it aloud — do you spell out the letters (like an acronym) rather than pronounce it as a word? If so, PascalCasing it (`Exif`, `L2Sqr`) destroys the acronym for no benefit. Keep it verbatim (`EXIF`, `L2SQR`). Precedent: `NormTypes.INF`/`L1`/`L2SQR`, `ImageMetadataType.EXIF`/`XMP`/`ICCP`/`CICP`.\n2. **The value has no natural word boundaries — it's an opaque code, not a phrase.** Test: does inserting word breaks add any information, or does it just capitalize letters arbitrarily? If the latter, keep it verbatim. Precedent: `ColorConversionCodes.BGR2GRAY` (whole body kept verbatim, only the `COLOR_` prefix stripped), `FASTType.TYPE_5_8`.\n3. **The prefix identifies a specific vendor/backend and is semantically load-bearing.** Test: does stripping the prefix delete information a reader needs (which camera SDK/backend this property belongs to)? If so, keep the prefix verbatim; the suffix can still be PascalCased normally. Precedent: `VideoCaptureProperties`' `XI_`/`OPENNI_`/`PVAPI_`/`GIGA_`/`IOS_`-prefixed members.\n\nWhen none of the three applies, PascalCase — don't keep a verbatim name \"to be safe.\" The exceptions are narrow and should be justifiable by one of the tests above, not a default.\n\n**Compounds win over exception 1.** The \"standalone\" qualifier in exception 1 matters: once an acronym is glued to another word/acronym to form one compound member name, PascalCase the whole compound rather than leaving the acronym in caps — an ALL-CAPS-then-lowercase transition mid-identifier (`TIFFResUnit`, `CCITTRle`) is harder to read than a clean compound (`TiffResUnit`, `CcittRle`). This is why the pre-existing `ImwriteFlags.TiffResUnit`/`JpegQuality`/`PngCompression`/`PamTupleType` PascalCase acronyms that would otherwise qualify for exception 1 in isolation — they're prefixes in a compound, not standalone values. Same reasoning covers `ImwriteTiffCompressionFlags.CcittRle`/`CcittFax3` and the standalone-but-left-PascalCase `Lzw`/`Jbig`/`Dcs`/`Rle` siblings in that same enum (and its `ImwriteEXRCompressionFlags`/`ImwriteHDRCompressionFlags` cousins) — treating every acronym-derived compression-scheme name as ordinary vocabulary, consistently within the enum, beats selectively all-capping only some of them.\n\n**Enum type names are a separate question — mirror the C++ type identifier verbatim, don't run it through this heuristic.** OpenCV's own C++ type names are inconsistently cased across acronyms (`ImwriteJPEGSamplingFactorParams`, `ImwriteEXRTypeFlags`, `ImwritePNGFlags`, `ImwritePAMFlags`, `ImwriteWEBPLosslessMode`, `ImwriteHDRCompressionFlags`, `ImwriteBMPCompressionFlags`, `ImwriteGIFCompressionFlags` are capitalized, but `ImwriteTiffCompressionFlags`/`ImwriteTiffPredictorFlags`/`ImwriteTiffResolutionUnitFlags` use `Tiff`) — that inconsistency is inherited straight from `opencv2/imgcodecs.hpp`, not introduced on the C# side. Keep mirroring the C++ type name 1:1 regardless: it keeps the C# type grep-able against OpenCV's own headers/docs, which matters more than internal cosmetic consistency for a type name. This has always been this codebase's practice (`ImwriteEXRTypeFlags`/`ImwritePNGFlags`/`ImwritePAMFlags` pre-date this rule and already mirror C++ verbatim).\n\n## Raw array parameters and native mutation\n\nA `T[]` (or `T[,]`) parameter that native code mutates in place — via a pinned `fixed` pointer, or by passing the array directly to a P/Invoke signature typed as a raw pointer/array — looks identical at the call site to a pure read-only input array. No `[In]`/`[Out]` attribute is needed to make this mutation happen: for blittable element types, default P/Invoke array marshaling already pins the managed array and hands native code a pointer into that same memory, so writes are visible to the caller regardless of any attribute. Since the signature alone can't distinguish the two cases, the convention here is doc wording, not a wrapper type or attribute:\n\n- If native code both reads a raw array parameter's incoming contents and writes a new result back into the same array (e.g. an initial guess that gets refined in place), its `<param>` doc must start with **\"Input/output\"** (e.g. `Input/output vector of distortion coefficients...`). Don't document it as \"Output\" alone in this case — that hides the fact that the caller's pre-existing values feed into the call.\n- If native code only writes into the array (the caller just pre-allocates it for sizing, and its incoming contents are never read), document it as **\"Output ...\"** — but it must say so; an empty `<param>` or a vague, non-committal description (e.g. \"Pointer to the destination array\") leaves the reader unable to tell it apart from a pure input. Either way — read-and-write or write-only — the array must never be documented as if it were a plain input with no mutation signal at all.\n- If a parameter is genuinely a pure input (native code only reads it, never writes), keep a plain description with no \"Input/output\"/\"Output\" wording, and prefer `IEnumerable<T>` over `T[]` to remove the ambiguity altogether — unless it sits directly next to a `T[][]` parameter pinned via `ArrayAddress2<T>` in the same signature, in which case keep it as `T[]` for visual consistency with its `T[][]` sibling (see `Aruco.RefineDetectedMarkers`'s `detectedIds`).\n- `InputOutputArray`/`OutputArray`-typed parameters already signal mutation through their type and don't strictly need this doc convention, though matching \"Input/output\"/\"Output\" wording there is still accurate and preferred.\n\nSee [issue #2036](https://github.com/shimat/opencvsharp/issues/2036) for the background discussion and known instances (`Cv2.CalibrateCamera`'s `cameraMatrix`/`distCoeffs`, `Cv2.Find4QuadCornerSubpix`'s `corners`).\n\n## Repository structure\n\nOpenCvSharp is organized as:\n\n- **`src/OpenCvSharp/`** — managed C# wrapper (P/Invoke, types, extension methods). Built against `net8.0` (`netstandard2.0`/`netstandard2.1`/`net4x` support was dropped); consumers on net8.0 or above (net9.0, net10.0, ...) can reference it.\n- **`src/OpenCvSharpExtern/`** — thin C++ bridge called by the C# side via P/Invoke.\n- **`src/OpenCvSharp.WpfExtensions/`, `src/OpenCvSharp.GdipExtensions/`, `src/OpenCvSharp.AvaloniaExtensions/`** — optional UI-toolkit interop packages (`Mat`/`BitmapSource`/`Bitmap`/`WriteableBitmap` conversions) consumers reference separately from the core package; each ships as its own `OpenCvSharp5.*Extensions` NuGet package.\n- **`packaging/nuget/`** — NuGet packaging projects (runtime packages that bundle the native DLLs).\n\n## Native ABI and breaking-change policy\n\n`main` is on OpenCV 5.x, a deliberately breaking migration phase: neither the native `OpenCvSharpExtern` ABI nor the public managed API is a compatibility constraint on this branch. When a P/Invoke signature, a native struct layout, or a managed method signature is awkward only because it's mirroring an old/incidental shape, feel free to change **both** the native (`.cpp`/`.h`) and managed sides together to reach the most natural, most efficient design — don't contort the managed side just to avoid touching `OpenCvSharpExtern`. Rebuild and verify with `cmake --build src/build --config Release --target OpenCvSharpExtern` after any native-side change. That said, prefer the smallest change that gets a natural design: if a managed-only fix (e.g. exposing a multi-dimensional native array as a `Span` instead of adding a new native entry point) is just as natural, it's the lower-risk choice — don't change the native ABI merely because \"5.x allows it.\"\n\nBefore proposing to delete legacy/unused-looking code (custom loaders, old P/Invoke wrappers, etc.), verify — don't just assume unused: (1) identify the concrete use case the code was solving, (2) judge whether that use case can still plausibly occur for some consumer, (3) if so, check whether the .NET BCL or a language feature already covers the same need in a more general way (e.g. a custom DLL-loading shim vs. `System.Runtime.InteropServices.NativeLibrary`). If (3) holds, the custom code is reinventing something the platform already provides and removing it is justified; if you skip this check, a deletion can break a real but niche use case (e.g. a plugin host) that the code was quietly serving.\n\nDon't add lazy/deferred computation to a previously native-resource-free convenience type (e.g. a plain result class) if it means that type now has to hold a native handle and become `IDisposable`. Callers of a convenience API generally don't expect to have to think about disposal at all; a modest performance win from avoiding one copy is usually not worth breaking that expectation. If a genuine hot path needs the efficiency, first check whether an *eager* computation with a targeted zero-copy view for the specific expensive step (comparison, rendering, ...) gets most of the benefit without changing the type's disposal contract. If an `IDisposable`-introducing change really is the best option, present the trade-off (e.g. as two labeled alternatives) before implementing it rather than committing to it unilaterally.\n\n## Versioning and release process\n\nSee `docs/release-process.md` for the full release workflow.\n\n**Key rule: do not edit version numbers in source files for routine releases.** The NuGet package version is passed via `-p:Version=...` at pack time in CI. `AssemblyVersion` and `FileVersion` are hardcoded to `5.0.0.0` in `src/Directory.Build.props` and must not be changed for patch or minor OpenCV upgrades — only bump the major component on a breaking API change or OpenCV major-version upgrade. `InformationalVersion` is set automatically by the SDK to the full NuGet version string.\n\n> **Branch note:** `main` targets OpenCV 5.x and publishes the `OpenCvSharp5` package family (there is no separate `5.x` branch — `main` is the OpenCV-5 development branch). The `4.x` branch is frozen on OpenCV 4.13.0 (`OpenCvSharp4` family, `AssemblyVersion 4.0.0.0`); cut 4.x patches from there.\n\n## Native DLL loading\n\nThere is no custom native-library loader. Since `src/OpenCvSharp/` requires net8.0 or above, a consumer can never be a .NET Framework (net4x) project (net8.0+-only assemblies aren't referenceable from net4x), so the old `WindowsLibraryLoader`/`Win32Api` classes and their `dll/x64`-folder probing were unreachable and were removed. Native library resolution is handled entirely by the .NET runtime's default probing via the `runtimes/{rid}/native/` layout that the `OpenCvSharp5.runtime.*` NuGet packages provide. If an app needs to load the native DLL from a nonstandard location (e.g. a plugin host that doesn't process `deps.json`), it can call `System.Runtime.InteropServices.NativeLibrary.Load(path)` (or `NativeLibrary.SetDllImportResolver`) itself before the first OpenCvSharp call — no OpenCvSharp-side API is needed for this.\n\n## Pull request conventions\n\nThis repo has no `PULL_REQUEST_TEMPLATE.md`, so there's no fixed checklist to fill in — but that doesn't mean invent your own heading structure (`### Description`, `### Changes`, etc.). Look at a few recently merged PRs' bodies first and match their shape: a short plain-prose summary (no invented headings), optionally followed by a `## Test plan` checklist when the change needs one. Keep it terse — a large, sectioned write-up reads as noticeably more \"AI-generated\" than the plain style this repo's own history uses.\n\nWhen an agent creates a pull request or issue, it must inspect the repository's existing labels and apply at least one relevant label whenever a semantically appropriate label exists. If no existing label applies, explicitly report that instead of silently leaving the item unlabeled. Whenever an agent creates a pull request in this repository, it must also assign the pull request to `@shimat`. Verify both labels and assignees after creation before reporting the item as complete.\n\n## Issue backlog\n\n`docs/issue-backlog.md` tracks actionable issues identified from closed/stale GitHub issues. Update the checkboxes as items are resolved.\n",".cursor/mcp.json":"# Model Context Protocol (MCP) Configuration\n\nPath: `.cursor/mcp.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/shimat/opencvsharp/HEAD/.cursor/mcp.json)",".claude-plugin/marketplace.json":"# Claude Plugin Marketplace Catalog\n\nPath: `.claude-plugin/marketplace.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/shimat/opencvsharp/HEAD/.claude-plugin/marketplace.json)",".claude-plugin/plugin.json":"# claude-plugin Plugin Manifest\n\nPath: `.claude-plugin/plugin.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/shimat/opencvsharp/HEAD/.claude-plugin/plugin.json)"},"files":{"CLAUDE.md":"See `.github/copilot-instructions.md` for development instructions.\n","AGENTS.md":"See `.github/copilot-instructions.md` for development instructions.\n",".github/copilot-instructions.md":"# Copilot Instructions\n\n## File encoding\n\nAll text files in this repository use **UTF-8 without a BOM**. This applies to `.cs`, `.csproj`, `.yml`, `.md`, `.json`, `.cpp`, `.h`, and every other text file — there are no exceptions.\n\n- `.editorconfig` declares `charset = utf-8` (no BOM) for all files, so editors and `dotnet format` enforce this automatically.\n- The `Write` / `create_file` tool already produces UTF-8 without a BOM, so just create and edit files normally — no encoding fix-up step is needed.\n- Do **not** add a BOM, and do not save as ANSI, Shift-JIS, or UTF-16. The repository is English-only (ASCII), and the C++ build sets the UTF-8 source charset, so MSVC reads the sources correctly without a BOM.\n- **All repository content is English**, regardless of what language a request or discussion happens in: code, comments, identifiers, commit messages, PR/issue titles and descriptions, and every file under `docs/`. This holds even when the person or agent driving the change is working with the maintainer in a different language.\n\n## NuGet README sync\n\nWhen editing the root `README.md`, also update the NuGet-specific README files accordingly.\n\n| File | Target packages |\n|---|---|\n| `packaging/nuget/README.managed.md` | `OpenCvSharp5`, `OpenCvSharp5.Windows`, `OpenCvSharp5.Windows.Slim`, `OpenCvSharp5.GdipExtensions`, `OpenCvSharp5.WpfExtensions`, `OpenCvSharp5.AvaloniaExtensions` |\n| `packaging/nuget/README.runtime.md` | `OpenCvSharp5.runtime.*`, `OpenCvSharp5.official.runtime.*` (all native runtime packages) |\n\n> The `main`-branch packages are the `OpenCvSharp5.*` family (OpenCV 5.x). An identically-structured `OpenCvSharp4.*` family (OpenCV 4.13.0, frozen on the `4.x` branch) is also published for .NET Framework / pre-.NET 8 consumers — see the branch note under \"Versioning and release process\".\n\nThe NuGet READMEs are a subset of the top-level README and consist of the following sections:\n\n- Overview and supported platforms\n- Installation instructions (Quick Start)\n- Requirements\n- Slim profile module coverage table (`README.managed.md` only)\n- Code usage examples (`README.managed.md` only)\n- Links (GitHub, Samples, API Docs, Issue Tracker)\n\nDo **not** include CI badges, Docker instructions, build instructions, or donation links in the NuGet READMEs.\n\n## Markdown authoring\n\nDo not hard-wrap Markdown prose at a fixed character width in the middle of a sentence. Write each paragraph, and each list item, as a single unbroken line in the source, and let the renderer/editor soft-wrap it for display. Only start a new line for a genuinely new block: the next list item, a new paragraph, a heading, a code fence, etc. This applies to every Markdown file in the repo (READMEs, `docs/*.md`, this file) as well as PR/issue/commit-message bodies that render as Markdown — manually wrapping mid-sentence makes diffs noisy (a small wording edit reflows the whole paragraph) and reads as pointlessly rigid formatting. When editing an existing hard-wrapped file, prefer reflowing the touched paragraphs to single lines rather than preserving the old wrap width.\n\n## Agent mode — terminal commands\n\nIn agent mode, do **not** use display commands that require user input (e.g., `more`, `less` without options). Use non-interactive alternatives instead:\n- PowerShell: `Select-Object -First N`, `Out-String`, `Write-Output`\n- Git: pass `-P` or `--no-pager`, or pipe to `Out-String`; e.g. `git --no-pager diff`\n- Use `cat` for displaying file contents.\n\n## Adding a new OpenCV class wrapper\n\n> **Scope**: This checklist covers `cv::SomeClass : cv::Algorithm` subclasses. OpenCV also has classes that do **not** inherit from `Algorithm` — those may follow different ownership and lifetime patterns (see existing non-Algorithm wrappers such as `BackgroundSubtractor` or classes in `core/` for reference).\n\nFollow this checklist when wrapping a new `cv::SomeClass : cv::Algorithm` class:\n\n### Files to create\n\n| File | Location |\n|---|---|\n| `<module>_SomeClass.h` | `src/OpenCvSharpExtern/` |\n| `SomeClass.cs` | `src/OpenCvSharp/Modules/<module>/` |\n| `NativeMethods_<module>_SomeClass.cs` | `src/OpenCvSharp/Internal/PInvoke/NativeMethods/<module>/` |\n| `SomeClassTest.cs` | `test/OpenCvSharp.Tests/<module>/` |\n| `Enum/SomeEnum.cs` (if needed) | same module folder |\n| `VectorOfVecXy.cs` (only if the element type is non-blittable/nested — blittable types use `StdVector<T>` directly, no new file needed) | `src/OpenCvSharp/Internal/Vectors/` |\n\n### Files to modify\n\n| File | Change |\n|---|---|\n| `<module>.cpp` | Add `#include \"<module>_SomeClass.h\"` |\n| `std_vector.h` | Add `#pragma region cv::VecXy` block if new vector type needed |\n| `NativeMethods_stdvector.cs` | Add corresponding P/Invoke region if new vector type needed |\n| `Cv<Module>.cs` | Add `public static SomeClass CreateSomeClass()` factory method |\n\n### C++ extern pattern (<module>_SomeClass.h)\n\n```cpp\n#pragma once\n#ifndef NO_CONTRIB\n#include \"include_opencv.h\"\n\nCVAPI(ExceptionStatus) <module>_Ptr_SomeClass_delete(cv::Ptr<cv::<module>::SomeClass> *obj)\n{ BEGIN_WRAP delete obj; END_WRAP }\n\nCVAPI(ExceptionStatus) <module>_Ptr_SomeClass_get(\n    cv::Ptr<cv::<module>::SomeClass> *ptr, cv::<module>::SomeClass **returnValue)\n{ BEGIN_WRAP *returnValue = ptr->get(); END_WRAP }\n\nCVAPI(ExceptionStatus) <module>_createSomeClass(\n    cv::Ptr<cv::<module>::SomeClass> **returnValue)\n{\n    BEGIN_WRAP\n    const auto ptr = cv::<module>::createSomeClass();\n    *returnValue = new cv::Ptr<cv::<module>::SomeClass>(ptr);\n    END_WRAP\n}\n// ... method bindings ...\n#endif // NO_CONTRIB\n```\n\n### C# class pattern (SomeClass.cs)\n\n```csharp\npublic class SomeClass : Algorithm\n{\n    private SomeClass(IntPtr smartPtr, IntPtr rawPtr)\n        : base(smartPtr, rawPtr, p => NativeMethods.HandleException(\n            NativeMethods.<module>_Ptr_SomeClass_delete(p))) { }\n\n    public static SomeClass Create()\n    {\n        NativeMethods.HandleException(NativeMethods.<module>_createSomeClass(out var smartPtr));\n        NativeMethods.HandleException(NativeMethods.<module>_Ptr_SomeClass_get(smartPtr, out var rawPtr));\n        return new SomeClass(smartPtr, rawPtr);\n    }\n\n    public virtual void SomeMethod(InputArray src) {\n        ThrowIfDisposed();\n        ArgumentNullException.ThrowIfNull(src);\n        src.ThrowIfDisposed();\n        NativeMethods.HandleException(NativeMethods.<module>_SomeClass_someMethod(RawPtr, src.CvPtr));\n        GC.KeepAlive(this);\n        GC.KeepAlive(src);\n    }\n\n    // OutputArray methods: call dst.Fix() after the P/Invoke call\n    // std::vector return methods: use StdVector<T> for blittable T (VectorOfXxx only for\n    // non-blittable/nested element types), wrap in using, call .ToArray()\n}\n```\n\n### Argument validation\n\nUse the BCL `ArgumentNullException.ThrowIfNull(x)` / `ArgumentOutOfRangeException.ThrowIfNegative(x)` / `.ThrowIfGreaterThan(x, max)` / etc. helpers instead of a manual `if (x is null) throw ...` or `if (x < 0) throw ...`. `CvObject.ThrowIfDisposed()` itself is implemented as `ObjectDisposedException.ThrowIf(IsDisposed, this)`. A few patterns are deliberately **not** converted to `ThrowIfNull`:\n\n- A ctor-chain initializer (`: this(...)`, `: base(...)`) or expression-bodied member (`=>`) that does `x ?? throw new ArgumentNullException(nameof(x))` — `ThrowIfNull` returns `void`, so it can't be used as part of an expression; keep the `?? throw` form there.\n- `if (string.IsNullOrEmpty(x)) throw new ArgumentNullException(...)` (filenames, extensions, etc.) — this intentionally raises `ArgumentNullException` for *both* a null and an empty string. Don't \"upgrade\" it to `ArgumentException.ThrowIfNullOrEmpty`, which throws `ArgumentException` for the empty-string case — that's a behavior change, not a mechanical rewrite.\n- A cast-based unsigned-comparison bounds check (`(uint)row >= (uint)Rows`) can still use `ThrowIfGreaterThanOrEqual((uint)row, (uint)Rows, nameof(row))`, but pass `nameof(row)` explicitly — `CallerArgumentExpression` would otherwise infer the parameter name as the literal cast expression text.\n\n### Params struct pattern (P/Invoke-compatible)\n\nWhen the C++ class has a `Params` struct with `bool` fields, define a flat C struct with `int` for booleans (not a marshaled `bool` — no `[MarshalAs(UnmanagedType.Bool)]`) and convert in `getParams`/`setParams`:\n\n```cpp\n// C++ side\nstruct CvSomeClassParams { int SomeBool; /* other fields */ };\n\nCVAPI(ExceptionStatus) <module>_SomeClass_getParams(obj, CvSomeClassParams* out) {\n    BEGIN_WRAP out->SomeBool = obj->params.SomeBool ? 1 : 0; END_WRAP }\nCVAPI(ExceptionStatus) <module>_SomeClass_setParams(obj, CvSomeClassParams* p) {\n    BEGIN_WRAP cv::<module>::SomeClass::Params q; q.SomeBool = p->SomeBool != 0; obj->setParams(q); END_WRAP }\n```\n\n```csharp\n// C# side — the P/Invoke-facing struct keeps SomeBool as a plain int (blittable, matches the C struct 1:1)\n[StructLayout(LayoutKind.Sequential)]\ninternal struct CvSomeClassParams {\n    public int SomeBool;\n    // other fields...\n}\n// The public-facing Params wrapper exposes a `bool SomeBool` property that converts\n// manually (`value != 0` on get, `value ? 1 : 0` on set) — see EdgeDrawingParams /\n// SimpleBlobDetector.Params for real examples.\n// P/Invoke: out CvSomeClassParams / ref CvSomeClassParams\n```\n\n### One-shot config classes: getAll/setAll pattern\n\nIf a `Params`/`Settings`-style class has behavior (mutated after construction and read by a live algorithm), keep individual native getter/setter P/Invoke calls per field as above. But if it's a pure **one-shot config** — constructed, handed to a factory/algorithm, and never mutated afterward (verify this against the OpenCV source before applying) — prefer folding all the scalar fields into a single blittable POD struct with one `getAll`/`setAll` P/Invoke pair instead of one call per field: a native temporary is created only at construction/read/write time, and the C# side becomes a plain managed data class with no native handle at all. `EdgeDrawingParams`, `SimpleBlobDetector.Params`, and `FacemarkLBF`/`FacemarkAAM`'s `Params` are reference implementations of this pattern. `Mat`/`string`/vector-typed fields still need their own separate P/Invoke argument (they don't fit in a blittable struct).\n\n### Namespace access note\n\nFrom `namespace OpenCvSharp.Internal`, types in `namespace OpenCvSharp` are directly visible (outer scope rule). Types in a sub-namespace such as `namespace OpenCvSharp.XImgProc` are NOT — add an explicit `using` directive in the NativeMethods file when referencing structs defined there.\n\n### POD value types crossing the extern boundary\n\nGeneral-purpose primitive value types passed across the P/Invoke boundary (`Point`/`Size`/`Rect`/`Scalar`/`Range`/`RotatedRect`/`KeyPoint`/`DMatch`/`Vec2b`..`Vec6d`, etc.) live under `namespace interop` with a clean name (no `MyCv*`/`CvVec*` prefix). Convert to/from the corresponding `cv::` type with the `OCS_INTEROP_BITCAST(InteropType, cv::Type)` macro (`std::bit_cast` plus a `static_assert` on size/layout match) rather than a hand-written field-by-field converter — the `static_assert` catches layout drift at compile time. Two exceptions:\n\n- `cv::Scalar`/`cv::Vec*` (and `Moments`) are **not** trivially copyable (`cv::Matx`-derived, user-defined copy/move ops) and can't go through `bit_cast` — they keep an explicit converter. Geometry types (`Point_`/`Size_`/`Rect_`/`RotatedRect`/`KeyPoint`/`DMatch`/`Range`/`TermCriteria`) *are* trivially copyable and use `bit_cast`.\n- Module-specific param/config structs (`aruco_DetectorParameters`, `ml_DTrees_*`, `*_Params`, `CvEdgeDrawingParams`, etc.) stay with their `module_` prefix and are **not** moved into `interop::` — the prefix is a meaningful namespace (which feature the struct belongs to), and folding it into the generic `interop::` bucket would obscure that.\n\n### Common P/Invoke pitfalls\n\n- **`cv::Ptr<T>*` vs. raw `T*` confusion.** `CvPtrObject.Handle` (used by most property getters/setters) returns a raw `T*`, not the smart pointer — a native binding declared as `cv::Ptr<T>* obj` with `(*obj)->getXxx()` silently misinterprets the raw pointer's vtable as a `cv::Ptr`'s internal layout and reliably crashes (access violation) the first time it's called, not at compile time. Match the parameter type to what the C# side actually passes (`Handle` → raw `T*` parameter with `obj->getXxx()`); this bit `BackgroundSubtractorGMG`/`MOG`'s property accessors and went unnoticed because no test exercised a round trip.\n- **Don't change a marshaling attribute (`[MarshalAs(UnmanagedType.LPStr)]` vs. `LPUTF8Str`) without checking every native code path that consumes it.** Different OpenCV backends decode filename strings differently (e.g. `cap_msmf.cpp` uses `MultiByteToWideChar(CP_ACP, ...)`, i.e. it expects ANSI, not UTF-8) — switching the attribute to \"fix\" one call site can silently corrupt non-ASCII paths on a different backend. This kind of regression doesn't show up in a plain build/test run; it only surfaces when you actually exercise the affected path with non-ASCII input.\n- **Write one real get/set (or call/verify) round trip before copying an existing property/method pattern to a new class.** Don't trust an existing, untested binding as a copy-source just because it compiles — verify it actually works first.\n\n### Free-function facade naming: `Cv2` mirrors `cv::`\n\nFree functions are exposed as static methods on `Cv2`, mirroring the C++ namespace structure:\n\n- `Cv2` = `cv::` — e.g. `Cv2.CvtColor` ↔ `cv::cvtColor`.\n- `Cv2.<Sub>` (a nested `public static partial class` under `Cv2`) = `cv::<sub>::` — e.g. `Cv2.Dnn.ReadNetFromONNX` ↔ `cv::dnn::readNetFromONNX`, matching the Python `cv2.dnn.*` submodules. In scope: `Dnn`, `Aruco`, `XImgProc`, `XPhoto`, `OptFlow`, `Text`, `Detail`, `Shape`, `VideoIORegistry`.\n\nPlace each submodule facade in its module folder as `Cv2.<Sub>.cs` (e.g. `Modules/dnn/Cv2.Dnn.cs`). The file uses `namespace OpenCvSharp;` and `using OpenCvSharp.<Sub>;` (the wrapper *types* stay in their `OpenCvSharp.<Sub>` namespaces). Where a sub-namespace type name collides with one in `OpenCvSharp` root (e.g. `InpaintTypes`), fully-qualify it in the signature (`OpenCvSharp.XPhoto.InpaintTypes`). Functions that live directly in `cv::` (no sub-namespace, e.g. the shape `Create…` factories) go on `Cv2` itself, not a nested class. The `CvExtensions` *extension methods* (`OpenCvSharp.Extensions`) are not part of this and keep their name.\n\n### std::vector return values\n\n- For a blittable/primitive element type (`int`, `byte`, `float`, `double`, `Point`, `Point2f`, `Vec4i`, `Vec4f`, `Vec6d`, ...), use the generic `StdVector<T>` directly — do **not** create a dedicated `VectorOfXxx` class. E.g. `new StdVector<Point2f>()`, `new StdVector<byte>()`, `new StdVector<Vec4i>()`.\n- Only create a dedicated `VectorOfXxx.cs` for element types that aren't blittable or need custom marshaling: nested vectors (`std::vector<std::vector<Point>>` → `VectorOfVectorPoint`), `Mat` (`VectorOfMat`), `String` (`VectorOfString`), or other non-trivial types (`VectorOfKeyLine`, `VectorOfDTreesNode`, ...).\n- New dedicated vector types: add `#pragma region` in `std_vector.h`, `#region` in `NativeMethods_stdvector.cs`, and create `VectorOfXxx.cs`.\n\n### EdgeDrawing as reference implementation\n\nSee `src/OpenCvSharpExtern/ximgproc_EdgeDrawing.h`, `src/OpenCvSharp/Modules/ximgproc/EdgeDrawing.cs` for a complete example covering: factory, OutputArray methods, `StdVector<T>`-returning methods (`StdVector<Vec6d>` in `DetectEllipses()`, `StdVector<Vec4f>` in `DetectLines()`, `StdVector<int>` in `GetSegmentIndicesOfLines()`), a nested `VectorOfVectorPoint`-returning method, and a Params struct with `int`-backed bool fields (`EdgeDrawingParams`).\n\n## Enum member naming\n\nC++ enum members are `ALL_CAPS_WITH_UNDERSCORES` (e.g. `IMWRITE_JPEG_QUALITY`, `NORM_L2SQR`). When wrapping them as a C# enum, **default to PascalCase**, stripping the module/family prefix (`IMWRITE_JPEG_QUALITY` → `JpegQuality`). Keep the original casing (or a near-verbatim form) only when one of these narrow exceptions applies:\n\n1. **The value itself is an unbreakable acronym/abbreviation, standing alone (nothing else glued onto it).** Test: read it aloud — do you spell out the letters (like an acronym) rather than pronounce it as a word? If so, PascalCasing it (`Exif`, `L2Sqr`) destroys the acronym for no benefit. Keep it verbatim (`EXIF`, `L2SQR`). Precedent: `NormTypes.INF`/`L1`/`L2SQR`, `ImageMetadataType.EXIF`/`XMP`/`ICCP`/`CICP`.\n2. **The value has no natural word boundaries — it's an opaque code, not a phrase.** Test: does inserting word breaks add any information, or does it just capitalize letters arbitrarily? If the latter, keep it verbatim. Precedent: `ColorConversionCodes.BGR2GRAY` (whole body kept verbatim, only the `COLOR_` prefix stripped), `FASTType.TYPE_5_8`.\n3. **The prefix identifies a specific vendor/backend and is semantically load-bearing.** Test: does stripping the prefix delete information a reader needs (which camera SDK/backend this property belongs to)? If so, keep the prefix verbatim; the suffix can still be PascalCased normally. Precedent: `VideoCaptureProperties`' `XI_`/`OPENNI_`/`PVAPI_`/`GIGA_`/`IOS_`-prefixed members.\n\nWhen none of the three applies, PascalCase — don't keep a verbatim name \"to be safe.\" The exceptions are narrow and should be justifiable by one of the tests above, not a default.\n\n**Compounds win over exception 1.** The \"standalone\" qualifier in exception 1 matters: once an acronym is glued to another word/acronym to form one compound member name, PascalCase the whole compound rather than leaving the acronym in caps — an ALL-CAPS-then-lowercase transition mid-identifier (`TIFFResUnit`, `CCITTRle`) is harder to read than a clean compound (`TiffResUnit`, `CcittRle`). This is why the pre-existing `ImwriteFlags.TiffResUnit`/`JpegQuality`/`PngCompression`/`PamTupleType` PascalCase acronyms that would otherwise qualify for exception 1 in isolation — they're prefixes in a compound, not standalone values. Same reasoning covers `ImwriteTiffCompressionFlags.CcittRle`/`CcittFax3` and the standalone-but-left-PascalCase `Lzw`/`Jbig`/`Dcs`/`Rle` siblings in that same enum (and its `ImwriteEXRCompressionFlags`/`ImwriteHDRCompressionFlags` cousins) — treating every acronym-derived compression-scheme name as ordinary vocabulary, consistently within the enum, beats selectively all-capping only some of them.\n\n**Enum type names are a separate question — mirror the C++ type identifier verbatim, don't run it through this heuristic.** OpenCV's own C++ type names are inconsistently cased across acronyms (`ImwriteJPEGSamplingFactorParams`, `ImwriteEXRTypeFlags`, `ImwritePNGFlags`, `ImwritePAMFlags`, `ImwriteWEBPLosslessMode`, `ImwriteHDRCompressionFlags`, `ImwriteBMPCompressionFlags`, `ImwriteGIFCompressionFlags` are capitalized, but `ImwriteTiffCompressionFlags`/`ImwriteTiffPredictorFlags`/`ImwriteTiffResolutionUnitFlags` use `Tiff`) — that inconsistency is inherited straight from `opencv2/imgcodecs.hpp`, not introduced on the C# side. Keep mirroring the C++ type name 1:1 regardless: it keeps the C# type grep-able against OpenCV's own headers/docs, which matters more than internal cosmetic consistency for a type name. This has always been this codebase's practice (`ImwriteEXRTypeFlags`/`ImwritePNGFlags`/`ImwritePAMFlags` pre-date this rule and already mirror C++ verbatim).\n\n## Raw array parameters and native mutation\n\nA `T[]` (or `T[,]`) parameter that native code mutates in place — via a pinned `fixed` pointer, or by passing the array directly to a P/Invoke signature typed as a raw pointer/array — looks identical at the call site to a pure read-only input array. No `[In]`/`[Out]` attribute is needed to make this mutation happen: for blittable element types, default P/Invoke array marshaling already pins the managed array and hands native code a pointer into that same memory, so writes are visible to the caller regardless of any attribute. Since the signature alone can't distinguish the two cases, the convention here is doc wording, not a wrapper type or attribute:\n\n- If native code both reads a raw array parameter's incoming contents and writes a new result back into the same array (e.g. an initial guess that gets refined in place), its `<param>` doc must start with **\"Input/output\"** (e.g. `Input/output vector of distortion coefficients...`). Don't document it as \"Output\" alone in this case — that hides the fact that the caller's pre-existing values feed into the call.\n- If native code only writes into the array (the caller just pre-allocates it for sizing, and its incoming contents are never read), document it as **\"Output ...\"** — but it must say so; an empty `<param>` or a vague, non-committal description (e.g. \"Pointer to the destination array\") leaves the reader unable to tell it apart from a pure input. Either way — read-and-write or write-only — the array must never be documented as if it were a plain input with no mutation signal at all.\n- If a parameter is genuinely a pure input (native code only reads it, never writes), keep a plain description with no \"Input/output\"/\"Output\" wording, and prefer `IEnumerable<T>` over `T[]` to remove the ambiguity altogether — unless it sits directly next to a `T[][]` parameter pinned via `ArrayAddress2<T>` in the same signature, in which case keep it as `T[]` for visual consistency with its `T[][]` sibling (see `Aruco.RefineDetectedMarkers`'s `detectedIds`).\n- `InputOutputArray`/`OutputArray`-typed parameters already signal mutation through their type and don't strictly need this doc convention, though matching \"Input/output\"/\"Output\" wording there is still accurate and preferred.\n\nSee [issue #2036](https://github.com/shimat/opencvsharp/issues/2036) for the background discussion and known instances (`Cv2.CalibrateCamera`'s `cameraMatrix`/`distCoeffs`, `Cv2.Find4QuadCornerSubpix`'s `corners`).\n\n## Repository structure\n\nOpenCvSharp is organized as:\n\n- **`src/OpenCvSharp/`** — managed C# wrapper (P/Invoke, types, extension methods). Built against `net8.0` (`netstandard2.0`/`netstandard2.1`/`net4x` support was dropped); consumers on net8.0 or above (net9.0, net10.0, ...) can reference it.\n- **`src/OpenCvSharpExtern/`** — thin C++ bridge called by the C# side via P/Invoke.\n- **`src/OpenCvSharp.WpfExtensions/`, `src/OpenCvSharp.GdipExtensions/`, `src/OpenCvSharp.AvaloniaExtensions/`** — optional UI-toolkit interop packages (`Mat`/`BitmapSource`/`Bitmap`/`WriteableBitmap` conversions) consumers reference separately from the core package; each ships as its own `OpenCvSharp5.*Extensions` NuGet package.\n- **`packaging/nuget/`** — NuGet packaging projects (runtime packages that bundle the native DLLs).\n\n## Native ABI and breaking-change policy\n\n`main` is on OpenCV 5.x, a deliberately breaking migration phase: neither the native `OpenCvSharpExtern` ABI nor the public managed API is a compatibility constraint on this branch. When a P/Invoke signature, a native struct layout, or a managed method signature is awkward only because it's mirroring an old/incidental shape, feel free to change **both** the native (`.cpp`/`.h`) and managed sides together to reach the most natural, most efficient design — don't contort the managed side just to avoid touching `OpenCvSharpExtern`. Rebuild and verify with `cmake --build src/build --config Release --target OpenCvSharpExtern` after any native-side change. That said, prefer the smallest change that gets a natural design: if a managed-only fix (e.g. exposing a multi-dimensional native array as a `Span` instead of adding a new native entry point) is just as natural, it's the lower-risk choice — don't change the native ABI merely because \"5.x allows it.\"\n\nBefore proposing to delete legacy/unused-looking code (custom loaders, old P/Invoke wrappers, etc.), verify — don't just assume unused: (1) identify the concrete use case the code was solving, (2) judge whether that use case can still plausibly occur for some consumer, (3) if so, check whether the .NET BCL or a language feature already covers the same need in a more general way (e.g. a custom DLL-loading shim vs. `System.Runtime.InteropServices.NativeLibrary`). If (3) holds, the custom code is reinventing something the platform already provides and removing it is justified; if you skip this check, a deletion can break a real but niche use case (e.g. a plugin host) that the code was quietly serving.\n\nDon't add lazy/deferred computation to a previously native-resource-free convenience type (e.g. a plain result class) if it means that type now has to hold a native handle and become `IDisposable`. Callers of a convenience API generally don't expect to have to think about disposal at all; a modest performance win from avoiding one copy is usually not worth breaking that expectation. If a genuine hot path needs the efficiency, first check whether an *eager* computation with a targeted zero-copy view for the specific expensive step (comparison, rendering, ...) gets most of the benefit without changing the type's disposal contract. If an `IDisposable`-introducing change really is the best option, present the trade-off (e.g. as two labeled alternatives) before implementing it rather than committing to it unilaterally.\n\n## Versioning and release process\n\nSee `docs/release-process.md` for the full release workflow.\n\n**Key rule: do not edit version numbers in source files for routine releases.** The NuGet package version is passed via `-p:Version=...` at pack time in CI. `AssemblyVersion` and `FileVersion` are hardcoded to `5.0.0.0` in `src/Directory.Build.props` and must not be changed for patch or minor OpenCV upgrades — only bump the major component on a breaking API change or OpenCV major-version upgrade. `InformationalVersion` is set automatically by the SDK to the full NuGet version string.\n\n> **Branch note:** `main` targets OpenCV 5.x and publishes the `OpenCvSharp5` package family (there is no separate `5.x` branch — `main` is the OpenCV-5 development branch). The `4.x` branch is frozen on OpenCV 4.13.0 (`OpenCvSharp4` family, `AssemblyVersion 4.0.0.0`); cut 4.x patches from there.\n\n## Native DLL loading\n\nThere is no custom native-library loader. Since `src/OpenCvSharp/` requires net8.0 or above, a consumer can never be a .NET Framework (net4x) project (net8.0+-only assemblies aren't referenceable from net4x), so the old `WindowsLibraryLoader`/`Win32Api` classes and their `dll/x64`-folder probing were unreachable and were removed. Native library resolution is handled entirely by the .NET runtime's default probing via the `runtimes/{rid}/native/` layout that the `OpenCvSharp5.runtime.*` NuGet packages provide. If an app needs to load the native DLL from a nonstandard location (e.g. a plugin host that doesn't process `deps.json`), it can call `System.Runtime.InteropServices.NativeLibrary.Load(path)` (or `NativeLibrary.SetDllImportResolver`) itself before the first OpenCvSharp call — no OpenCvSharp-side API is needed for this.\n\n## Pull request conventions\n\nThis repo has no `PULL_REQUEST_TEMPLATE.md`, so there's no fixed checklist to fill in — but that doesn't mean invent your own heading structure (`### Description`, `### Changes`, etc.). Look at a few recently merged PRs' bodies first and match their shape: a short plain-prose summary (no invented headings), optionally followed by a `## Test plan` checklist when the change needs one. Keep it terse — a large, sectioned write-up reads as noticeably more \"AI-generated\" than the plain style this repo's own history uses.\n\nWhen an agent creates a pull request or issue, it must inspect the repository's existing labels and apply at least one relevant label whenever a semantically appropriate label exists. If no existing label applies, explicitly report that instead of silently leaving the item unlabeled. Whenever an agent creates a pull request in this repository, it must also assign the pull request to `@shimat`. Verify both labels and assignees after creation before reporting the item as complete.\n\n## Issue backlog\n\n`docs/issue-backlog.md` tracks actionable issues identified from closed/stale GitHub issues. Update the checkboxes as items are resolved.\n",".cursor/mcp.json":"# Model Context Protocol (MCP) Configuration\n\nPath: `.cursor/mcp.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/shimat/opencvsharp/HEAD/.cursor/mcp.json)",".claude-plugin/marketplace.json":"# Claude Plugin Marketplace Catalog\n\nPath: `.claude-plugin/marketplace.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/shimat/opencvsharp/HEAD/.claude-plugin/marketplace.json)",".claude-plugin/plugin.json":"# claude-plugin Plugin Manifest\n\nPath: `.claude-plugin/plugin.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/shimat/opencvsharp/HEAD/.claude-plugin/plugin.json)"},"items":[{"name":"AGENTS.md","path":"AGENTS.md","rawUrl":"https://raw.githubusercontent.com/shimat/opencvsharp/HEAD/AGENTS.md","title":"AI Agent Protocol & Instructions","category":"root-instruction","format":"markdown","content":"See `.github/copilot-instructions.md` for development instructions.\n","isInternal":false,"tokens":13,"sizeBytes":68},{"name":"CLAUDE.md","path":"CLAUDE.md","rawUrl":"https://raw.githubusercontent.com/shimat/opencvsharp/HEAD/CLAUDE.md","title":"Claude Agent Guidelines & System Prompt","category":"claude-rule","format":"markdown","content":"See `.github/copilot-instructions.md` for development instructions.\n","isInternal":false,"tokens":13,"sizeBytes":68},{"name":"copilot-instructions.md","path":".github/copilot-instructions.md","rawUrl":"https://raw.githubusercontent.com/shimat/opencvsharp/HEAD/.github/copilot-instructions.md","title":"GitHub Copilot Instructions","category":"copilot-instructions","format":"markdown","content":"# Copilot Instructions\n\n## File encoding\n\nAll text files in this repository use **UTF-8 without a BOM**. This applies to `.cs`, `.csproj`, `.yml`, `.md`, `.json`, `.cpp`, `.h`, and every other text file — there are no exceptions.\n\n- `.editorconfig` declares `charset = utf-8` (no BOM) for all files, so editors and `dotnet format` enforce this automatically.\n- The `Write` / `create_file` tool already produces UTF-8 without a BOM, so just create and edit files normally — no encoding fix-up step is needed.\n- Do **not** add a BOM, and do not save as ANSI, Shift-JIS, or UTF-16. The repository is English-only (ASCII), and the C++ build sets the UTF-8 source charset, so MSVC reads the sources correctly without a BOM.\n- **All repository content is English**, regardless of what language a request or discussion happens in: code, comments, identifiers, commit messages, PR/issue titles and descriptions, and every file under `docs/`. This holds even when the person or agent driving the change is working with the maintainer in a different language.\n\n## NuGet README sync\n\nWhen editing the root `README.md`, also update the NuGet-specific README files accordingly.\n\n| File | Target packages |\n|---|---|\n| `packaging/nuget/README.managed.md` | `OpenCvSharp5`, `OpenCvSharp5.Windows`, `OpenCvSharp5.Windows.Slim`, `OpenCvSharp5.GdipExtensions`, `OpenCvSharp5.WpfExtensions`, `OpenCvSharp5.AvaloniaExtensions` |\n| `packaging/nuget/README.runtime.md` | `OpenCvSharp5.runtime.*`, `OpenCvSharp5.official.runtime.*` (all native runtime packages) |\n\n> The `main`-branch packages are the `OpenCvSharp5.*` family (OpenCV 5.x). An identically-structured `OpenCvSharp4.*` family (OpenCV 4.13.0, frozen on the `4.x` branch) is also published for .NET Framework / pre-.NET 8 consumers — see the branch note under \"Versioning and release process\".\n\nThe NuGet READMEs are a subset of the top-level README and consist of the following sections:\n\n- Overview and supported platforms\n- Installation instructions (Quick Start)\n- Requirements\n- Slim profile module coverage table (`README.managed.md` only)\n- Code usage examples (`README.managed.md` only)\n- Links (GitHub, Samples, API Docs, Issue Tracker)\n\nDo **not** include CI badges, Docker instructions, build instructions, or donation links in the NuGet READMEs.\n\n## Markdown authoring\n\nDo not hard-wrap Markdown prose at a fixed character width in the middle of a sentence. Write each paragraph, and each list item, as a single unbroken line in the source, and let the renderer/editor soft-wrap it for display. Only start a new line for a genuinely new block: the next list item, a new paragraph, a heading, a code fence, etc. This applies to every Markdown file in the repo (READMEs, `docs/*.md`, this file) as well as PR/issue/commit-message bodies that render as Markdown — manually wrapping mid-sentence makes diffs noisy (a small wording edit reflows the whole paragraph) and reads as pointlessly rigid formatting. When editing an existing hard-wrapped file, prefer reflowing the touched paragraphs to single lines rather than preserving the old wrap width.\n\n## Agent mode — terminal commands\n\nIn agent mode, do **not** use display commands that require user input (e.g., `more`, `less` without options). Use non-interactive alternatives instead:\n- PowerShell: `Select-Object -First N`, `Out-String`, `Write-Output`\n- Git: pass `-P` or `--no-pager`, or pipe to `Out-String`; e.g. `git --no-pager diff`\n- Use `cat` for displaying file contents.\n\n## Adding a new OpenCV class wrapper\n\n> **Scope**: This checklist covers `cv::SomeClass : cv::Algorithm` subclasses. OpenCV also has classes that do **not** inherit from `Algorithm` — those may follow different ownership and lifetime patterns (see existing non-Algorithm wrappers such as `BackgroundSubtractor` or classes in `core/` for reference).\n\nFollow this checklist when wrapping a new `cv::SomeClass : cv::Algorithm` class:\n\n### Files to create\n\n| File | Location |\n|---|---|\n| `<module>_SomeClass.h` | `src/OpenCvSharpExtern/` |\n| `SomeClass.cs` | `src/OpenCvSharp/Modules/<module>/` |\n| `NativeMethods_<module>_SomeClass.cs` | `src/OpenCvSharp/Internal/PInvoke/NativeMethods/<module>/` |\n| `SomeClassTest.cs` | `test/OpenCvSharp.Tests/<module>/` |\n| `Enum/SomeEnum.cs` (if needed) | same module folder |\n| `VectorOfVecXy.cs` (only if the element type is non-blittable/nested — blittable types use `StdVector<T>` directly, no new file needed) | `src/OpenCvSharp/Internal/Vectors/` |\n\n### Files to modify\n\n| File | Change |\n|---|---|\n| `<module>.cpp` | Add `#include \"<module>_SomeClass.h\"` |\n| `std_vector.h` | Add `#pragma region cv::VecXy` block if new vector type needed |\n| `NativeMethods_stdvector.cs` | Add corresponding P/Invoke region if new vector type needed |\n| `Cv<Module>.cs` | Add `public static SomeClass CreateSomeClass()` factory method |\n\n### C++ extern pattern (<module>_SomeClass.h)\n\n```cpp\n#pragma once\n#ifndef NO_CONTRIB\n#include \"include_opencv.h\"\n\nCVAPI(ExceptionStatus) <module>_Ptr_SomeClass_delete(cv::Ptr<cv::<module>::SomeClass> *obj)\n{ BEGIN_WRAP delete obj; END_WRAP }\n\nCVAPI(ExceptionStatus) <module>_Ptr_SomeClass_get(\n    cv::Ptr<cv::<module>::SomeClass> *ptr, cv::<module>::SomeClass **returnValue)\n{ BEGIN_WRAP *returnValue = ptr->get(); END_WRAP }\n\nCVAPI(ExceptionStatus) <module>_createSomeClass(\n    cv::Ptr<cv::<module>::SomeClass> **returnValue)\n{\n    BEGIN_WRAP\n    const auto ptr = cv::<module>::createSomeClass();\n    *returnValue = new cv::Ptr<cv::<module>::SomeClass>(ptr);\n    END_WRAP\n}\n// ... method bindings ...\n#endif // NO_CONTRIB\n```\n\n### C# class pattern (SomeClass.cs)\n\n```csharp\npublic class SomeClass : Algorithm\n{\n    private SomeClass(IntPtr smartPtr, IntPtr rawPtr)\n        : base(smartPtr, rawPtr, p => NativeMethods.HandleException(\n            NativeMethods.<module>_Ptr_SomeClass_delete(p))) { }\n\n    public static SomeClass Create()\n    {\n        NativeMethods.HandleException(NativeMethods.<module>_createSomeClass(out var smartPtr));\n        NativeMethods.HandleException(NativeMethods.<module>_Ptr_SomeClass_get(smartPtr, out var rawPtr));\n        return new SomeClass(smartPtr, rawPtr);\n    }\n\n    public virtual void SomeMethod(InputArray src) {\n        ThrowIfDisposed();\n        ArgumentNullException.ThrowIfNull(src);\n        src.ThrowIfDisposed();\n        NativeMethods.HandleException(NativeMethods.<module>_SomeClass_someMethod(RawPtr, src.CvPtr));\n        GC.KeepAlive(this);\n        GC.KeepAlive(src);\n    }\n\n    // OutputArray methods: call dst.Fix() after the P/Invoke call\n    // std::vector return methods: use StdVector<T> for blittable T (VectorOfXxx only for\n    // non-blittable/nested element types), wrap in using, call .ToArray()\n}\n```\n\n### Argument validation\n\nUse the BCL `ArgumentNullException.ThrowIfNull(x)` / `ArgumentOutOfRangeException.ThrowIfNegative(x)` / `.ThrowIfGreaterThan(x, max)` / etc. helpers instead of a manual `if (x is null) throw ...` or `if (x < 0) throw ...`. `CvObject.ThrowIfDisposed()` itself is implemented as `ObjectDisposedException.ThrowIf(IsDisposed, this)`. A few patterns are deliberately **not** converted to `ThrowIfNull`:\n\n- A ctor-chain initializer (`: this(...)`, `: base(...)`) or expression-bodied member (`=>`) that does `x ?? throw new ArgumentNullException(nameof(x))` — `ThrowIfNull` returns `void`, so it can't be used as part of an expression; keep the `?? throw` form there.\n- `if (string.IsNullOrEmpty(x)) throw new ArgumentNullException(...)` (filenames, extensions, etc.) — this intentionally raises `ArgumentNullException` for *both* a null and an empty string. Don't \"upgrade\" it to `ArgumentException.ThrowIfNullOrEmpty`, which throws `ArgumentException` for the empty-string case — that's a behavior change, not a mechanical rewrite.\n- A cast-based unsigned-comparison bounds check (`(uint)row >= (uint)Rows`) can still use `ThrowIfGreaterThanOrEqual((uint)row, (uint)Rows, nameof(row))`, but pass `nameof(row)` explicitly — `CallerArgumentExpression` would otherwise infer the parameter name as the literal cast expression text.\n\n### Params struct pattern (P/Invoke-compatible)\n\nWhen the C++ class has a `Params` struct with `bool` fields, define a flat C struct with `int` for booleans (not a marshaled `bool` — no `[MarshalAs(UnmanagedType.Bool)]`) and convert in `getParams`/`setParams`:\n\n```cpp\n// C++ side\nstruct CvSomeClassParams { int SomeBool; /* other fields */ };\n\nCVAPI(ExceptionStatus) <module>_SomeClass_getParams(obj, CvSomeClassParams* out) {\n    BEGIN_WRAP out->SomeBool = obj->params.SomeBool ? 1 : 0; END_WRAP }\nCVAPI(ExceptionStatus) <module>_SomeClass_setParams(obj, CvSomeClassParams* p) {\n    BEGIN_WRAP cv::<module>::SomeClass::Params q; q.SomeBool = p->SomeBool != 0; obj->setParams(q); END_WRAP }\n```\n\n```csharp\n// C# side — the P/Invoke-facing struct keeps SomeBool as a plain int (blittable, matches the C struct 1:1)\n[StructLayout(LayoutKind.Sequential)]\ninternal struct CvSomeClassParams {\n    public int SomeBool;\n    // other fields...\n}\n// The public-facing Params wrapper exposes a `bool SomeBool` property that converts\n// manually (`value != 0` on get, `value ? 1 : 0` on set) — see EdgeDrawingParams /\n// SimpleBlobDetector.Params for real examples.\n// P/Invoke: out CvSomeClassParams / ref CvSomeClassParams\n```\n\n### One-shot config classes: getAll/setAll pattern\n\nIf a `Params`/`Settings`-style class has behavior (mutated after construction and read by a live algorithm), keep individual native getter/setter P/Invoke calls per field as above. But if it's a pure **one-shot config** — constructed, handed to a factory/algorithm, and never mutated afterward (verify this against the OpenCV source before applying) — prefer folding all the scalar fields into a single blittable POD struct with one `getAll`/`setAll` P/Invoke pair instead of one call per field: a native temporary is created only at construction/read/write time, and the C# side becomes a plain managed data class with no native handle at all. `EdgeDrawingParams`, `SimpleBlobDetector.Params`, and `FacemarkLBF`/`FacemarkAAM`'s `Params` are reference implementations of this pattern. `Mat`/`string`/vector-typed fields still need their own separate P/Invoke argument (they don't fit in a blittable struct).\n\n### Namespace access note\n\nFrom `namespace OpenCvSharp.Internal`, types in `namespace OpenCvSharp` are directly visible (outer scope rule). Types in a sub-namespace such as `namespace OpenCvSharp.XImgProc` are NOT — add an explicit `using` directive in the NativeMethods file when referencing structs defined there.\n\n### POD value types crossing the extern boundary\n\nGeneral-purpose primitive value types passed across the P/Invoke boundary (`Point`/`Size`/`Rect`/`Scalar`/`Range`/`RotatedRect`/`KeyPoint`/`DMatch`/`Vec2b`..`Vec6d`, etc.) live under `namespace interop` with a clean name (no `MyCv*`/`CvVec*` prefix). Convert to/from the corresponding `cv::` type with the `OCS_INTEROP_BITCAST(InteropType, cv::Type)` macro (`std::bit_cast` plus a `static_assert` on size/layout match) rather than a hand-written field-by-field converter — the `static_assert` catches layout drift at compile time. Two exceptions:\n\n- `cv::Scalar`/`cv::Vec*` (and `Moments`) are **not** trivially copyable (`cv::Matx`-derived, user-defined copy/move ops) and can't go through `bit_cast` — they keep an explicit converter. Geometry types (`Point_`/`Size_`/`Rect_`/`RotatedRect`/`KeyPoint`/`DMatch`/`Range`/`TermCriteria`) *are* trivially copyable and use `bit_cast`.\n- Module-specific param/config structs (`aruco_DetectorParameters`, `ml_DTrees_*`, `*_Params`, `CvEdgeDrawingParams`, etc.) stay with their `module_` prefix and are **not** moved into `interop::` — the prefix is a meaningful namespace (which feature the struct belongs to), and folding it into the generic `interop::` bucket would obscure that.\n\n### Common P/Invoke pitfalls\n\n- **`cv::Ptr<T>*` vs. raw `T*` confusion.** `CvPtrObject.Handle` (used by most property getters/setters) returns a raw `T*`, not the smart pointer — a native binding declared as `cv::Ptr<T>* obj` with `(*obj)->getXxx()` silently misinterprets the raw pointer's vtable as a `cv::Ptr`'s internal layout and reliably crashes (access violation) the first time it's called, not at compile time. Match the parameter type to what the C# side actually passes (`Handle` → raw `T*` parameter with `obj->getXxx()`); this bit `BackgroundSubtractorGMG`/`MOG`'s property accessors and went unnoticed because no test exercised a round trip.\n- **Don't change a marshaling attribute (`[MarshalAs(UnmanagedType.LPStr)]` vs. `LPUTF8Str`) without checking every native code path that consumes it.** Different OpenCV backends decode filename strings differently (e.g. `cap_msmf.cpp` uses `MultiByteToWideChar(CP_ACP, ...)`, i.e. it expects ANSI, not UTF-8) — switching the attribute to \"fix\" one call site can silently corrupt non-ASCII paths on a different backend. This kind of regression doesn't show up in a plain build/test run; it only surfaces when you actually exercise the affected path with non-ASCII input.\n- **Write one real get/set (or call/verify) round trip before copying an existing property/method pattern to a new class.** Don't trust an existing, untested binding as a copy-source just because it compiles — verify it actually works first.\n\n### Free-function facade naming: `Cv2` mirrors `cv::`\n\nFree functions are exposed as static methods on `Cv2`, mirroring the C++ namespace structure:\n\n- `Cv2` = `cv::` — e.g. `Cv2.CvtColor` ↔ `cv::cvtColor`.\n- `Cv2.<Sub>` (a nested `public static partial class` under `Cv2`) = `cv::<sub>::` — e.g. `Cv2.Dnn.ReadNetFromONNX` ↔ `cv::dnn::readNetFromONNX`, matching the Python `cv2.dnn.*` submodules. In scope: `Dnn`, `Aruco`, `XImgProc`, `XPhoto`, `OptFlow`, `Text`, `Detail`, `Shape`, `VideoIORegistry`.\n\nPlace each submodule facade in its module folder as `Cv2.<Sub>.cs` (e.g. `Modules/dnn/Cv2.Dnn.cs`). The file uses `namespace OpenCvSharp;` and `using OpenCvSharp.<Sub>;` (the wrapper *types* stay in their `OpenCvSharp.<Sub>` namespaces). Where a sub-namespace type name collides with one in `OpenCvSharp` root (e.g. `InpaintTypes`), fully-qualify it in the signature (`OpenCvSharp.XPhoto.InpaintTypes`). Functions that live directly in `cv::` (no sub-namespace, e.g. the shape `Create…` factories) go on `Cv2` itself, not a nested class. The `CvExtensions` *extension methods* (`OpenCvSharp.Extensions`) are not part of this and keep their name.\n\n### std::vector return values\n\n- For a blittable/primitive element type (`int`, `byte`, `float`, `double`, `Point`, `Point2f`, `Vec4i`, `Vec4f`, `Vec6d`, ...), use the generic `StdVector<T>` directly — do **not** create a dedicated `VectorOfXxx` class. E.g. `new StdVector<Point2f>()`, `new StdVector<byte>()`, `new StdVector<Vec4i>()`.\n- Only create a dedicated `VectorOfXxx.cs` for element types that aren't blittable or need custom marshaling: nested vectors (`std::vector<std::vector<Point>>` → `VectorOfVectorPoint`), `Mat` (`VectorOfMat`), `String` (`VectorOfString`), or other non-trivial types (`VectorOfKeyLine`, `VectorOfDTreesNode`, ...).\n- New dedicated vector types: add `#pragma region` in `std_vector.h`, `#region` in `NativeMethods_stdvector.cs`, and create `VectorOfXxx.cs`.\n\n### EdgeDrawing as reference implementation\n\nSee `src/OpenCvSharpExtern/ximgproc_EdgeDrawing.h`, `src/OpenCvSharp/Modules/ximgproc/EdgeDrawing.cs` for a complete example covering: factory, OutputArray methods, `StdVector<T>`-returning methods (`StdVector<Vec6d>` in `DetectEllipses()`, `StdVector<Vec4f>` in `DetectLines()`, `StdVector<int>` in `GetSegmentIndicesOfLines()`), a nested `VectorOfVectorPoint`-returning method, and a Params struct with `int`-backed bool fields (`EdgeDrawingParams`).\n\n## Enum member naming\n\nC++ enum members are `ALL_CAPS_WITH_UNDERSCORES` (e.g. `IMWRITE_JPEG_QUALITY`, `NORM_L2SQR`). When wrapping them as a C# enum, **default to PascalCase**, stripping the module/family prefix (`IMWRITE_JPEG_QUALITY` → `JpegQuality`). Keep the original casing (or a near-verbatim form) only when one of these narrow exceptions applies:\n\n1. **The value itself is an unbreakable acronym/abbreviation, standing alone (nothing else glued onto it).** Test: read it aloud — do you spell out the letters (like an acronym) rather than pronounce it as a word? If so, PascalCasing it (`Exif`, `L2Sqr`) destroys the acronym for no benefit. Keep it verbatim (`EXIF`, `L2SQR`). Precedent: `NormTypes.INF`/`L1`/`L2SQR`, `ImageMetadataType.EXIF`/`XMP`/`ICCP`/`CICP`.\n2. **The value has no natural word boundaries — it's an opaque code, not a phrase.** Test: does inserting word breaks add any information, or does it just capitalize letters arbitrarily? If the latter, keep it verbatim. Precedent: `ColorConversionCodes.BGR2GRAY` (whole body kept verbatim, only the `COLOR_` prefix stripped), `FASTType.TYPE_5_8`.\n3. **The prefix identifies a specific vendor/backend and is semantically load-bearing.** Test: does stripping the prefix delete information a reader needs (which camera SDK/backend this property belongs to)? If so, keep the prefix verbatim; the suffix can still be PascalCased normally. Precedent: `VideoCaptureProperties`' `XI_`/`OPENNI_`/`PVAPI_`/`GIGA_`/`IOS_`-prefixed members.\n\nWhen none of the three applies, PascalCase — don't keep a verbatim name \"to be safe.\" The exceptions are narrow and should be justifiable by one of the tests above, not a default.\n\n**Compounds win over exception 1.** The \"standalone\" qualifier in exception 1 matters: once an acronym is glued to another word/acronym to form one compound member name, PascalCase the whole compound rather than leaving the acronym in caps — an ALL-CAPS-then-lowercase transition mid-identifier (`TIFFResUnit`, `CCITTRle`) is harder to read than a clean compound (`TiffResUnit`, `CcittRle`). This is why the pre-existing `ImwriteFlags.TiffResUnit`/`JpegQuality`/`PngCompression`/`PamTupleType` PascalCase acronyms that would otherwise qualify for exception 1 in isolation — they're prefixes in a compound, not standalone values. Same reasoning covers `ImwriteTiffCompressionFlags.CcittRle`/`CcittFax3` and the standalone-but-left-PascalCase `Lzw`/`Jbig`/`Dcs`/`Rle` siblings in that same enum (and its `ImwriteEXRCompressionFlags`/`ImwriteHDRCompressionFlags` cousins) — treating every acronym-derived compression-scheme name as ordinary vocabulary, consistently within the enum, beats selectively all-capping only some of them.\n\n**Enum type names are a separate question — mirror the C++ type identifier verbatim, don't run it through this heuristic.** OpenCV's own C++ type names are inconsistently cased across acronyms (`ImwriteJPEGSamplingFactorParams`, `ImwriteEXRTypeFlags`, `ImwritePNGFlags`, `ImwritePAMFlags`, `ImwriteWEBPLosslessMode`, `ImwriteHDRCompressionFlags`, `ImwriteBMPCompressionFlags`, `ImwriteGIFCompressionFlags` are capitalized, but `ImwriteTiffCompressionFlags`/`ImwriteTiffPredictorFlags`/`ImwriteTiffResolutionUnitFlags` use `Tiff`) — that inconsistency is inherited straight from `opencv2/imgcodecs.hpp`, not introduced on the C# side. Keep mirroring the C++ type name 1:1 regardless: it keeps the C# type grep-able against OpenCV's own headers/docs, which matters more than internal cosmetic consistency for a type name. This has always been this codebase's practice (`ImwriteEXRTypeFlags`/`ImwritePNGFlags`/`ImwritePAMFlags` pre-date this rule and already mirror C++ verbatim).\n\n## Raw array parameters and native mutation\n\nA `T[]` (or `T[,]`) parameter that native code mutates in place — via a pinned `fixed` pointer, or by passing the array directly to a P/Invoke signature typed as a raw pointer/array — looks identical at the call site to a pure read-only input array. No `[In]`/`[Out]` attribute is needed to make this mutation happen: for blittable element types, default P/Invoke array marshaling already pins the managed array and hands native code a pointer into that same memory, so writes are visible to the caller regardless of any attribute. Since the signature alone can't distinguish the two cases, the convention here is doc wording, not a wrapper type or attribute:\n\n- If native code both reads a raw array parameter's incoming contents and writes a new result back into the same array (e.g. an initial guess that gets refined in place), its `<param>` doc must start with **\"Input/output\"** (e.g. `Input/output vector of distortion coefficients...`). Don't document it as \"Output\" alone in this case — that hides the fact that the caller's pre-existing values feed into the call.\n- If native code only writes into the array (the caller just pre-allocates it for sizing, and its incoming contents are never read), document it as **\"Output ...\"** — but it must say so; an empty `<param>` or a vague, non-committal description (e.g. \"Pointer to the destination array\") leaves the reader unable to tell it apart from a pure input. Either way — read-and-write or write-only — the array must never be documented as if it were a plain input with no mutation signal at all.\n- If a parameter is genuinely a pure input (native code only reads it, never writes), keep a plain description with no \"Input/output\"/\"Output\" wording, and prefer `IEnumerable<T>` over `T[]` to remove the ambiguity altogether — unless it sits directly next to a `T[][]` parameter pinned via `ArrayAddress2<T>` in the same signature, in which case keep it as `T[]` for visual consistency with its `T[][]` sibling (see `Aruco.RefineDetectedMarkers`'s `detectedIds`).\n- `InputOutputArray`/`OutputArray`-typed parameters already signal mutation through their type and don't strictly need this doc convention, though matching \"Input/output\"/\"Output\" wording there is still accurate and preferred.\n\nSee [issue #2036](https://github.com/shimat/opencvsharp/issues/2036) for the background discussion and known instances (`Cv2.CalibrateCamera`'s `cameraMatrix`/`distCoeffs`, `Cv2.Find4QuadCornerSubpix`'s `corners`).\n\n## Repository structure\n\nOpenCvSharp is organized as:\n\n- **`src/OpenCvSharp/`** — managed C# wrapper (P/Invoke, types, extension methods). Built against `net8.0` (`netstandard2.0`/`netstandard2.1`/`net4x` support was dropped); consumers on net8.0 or above (net9.0, net10.0, ...) can reference it.\n- **`src/OpenCvSharpExtern/`** — thin C++ bridge called by the C# side via P/Invoke.\n- **`src/OpenCvSharp.WpfExtensions/`, `src/OpenCvSharp.GdipExtensions/`, `src/OpenCvSharp.AvaloniaExtensions/`** — optional UI-toolkit interop packages (`Mat`/`BitmapSource`/`Bitmap`/`WriteableBitmap` conversions) consumers reference separately from the core package; each ships as its own `OpenCvSharp5.*Extensions` NuGet package.\n- **`packaging/nuget/`** — NuGet packaging projects (runtime packages that bundle the native DLLs).\n\n## Native ABI and breaking-change policy\n\n`main` is on OpenCV 5.x, a deliberately breaking migration phase: neither the native `OpenCvSharpExtern` ABI nor the public managed API is a compatibility constraint on this branch. When a P/Invoke signature, a native struct layout, or a managed method signature is awkward only because it's mirroring an old/incidental shape, feel free to change **both** the native (`.cpp`/`.h`) and managed sides together to reach the most natural, most efficient design — don't contort the managed side just to avoid touching `OpenCvSharpExtern`. Rebuild and verify with `cmake --build src/build --config Release --target OpenCvSharpExtern` after any native-side change. That said, prefer the smallest change that gets a natural design: if a managed-only fix (e.g. exposing a multi-dimensional native array as a `Span` instead of adding a new native entry point) is just as natural, it's the lower-risk choice — don't change the native ABI merely because \"5.x allows it.\"\n\nBefore proposing to delete legacy/unused-looking code (custom loaders, old P/Invoke wrappers, etc.), verify — don't just assume unused: (1) identify the concrete use case the code was solving, (2) judge whether that use case can still plausibly occur for some consumer, (3) if so, check whether the .NET BCL or a language feature already covers the same need in a more general way (e.g. a custom DLL-loading shim vs. `System.Runtime.InteropServices.NativeLibrary`). If (3) holds, the custom code is reinventing something the platform already provides and removing it is justified; if you skip this check, a deletion can break a real but niche use case (e.g. a plugin host) that the code was quietly serving.\n\nDon't add lazy/deferred computation to a previously native-resource-free convenience type (e.g. a plain result class) if it means that type now has to hold a native handle and become `IDisposable`. Callers of a convenience API generally don't expect to have to think about disposal at all; a modest performance win from avoiding one copy is usually not worth breaking that expectation. If a genuine hot path needs the efficiency, first check whether an *eager* computation with a targeted zero-copy view for the specific expensive step (comparison, rendering, ...) gets most of the benefit without changing the type's disposal contract. If an `IDisposable`-introducing change really is the best option, present the trade-off (e.g. as two labeled alternatives) before implementing it rather than committing to it unilaterally.\n\n## Versioning and release process\n\nSee `docs/release-process.md` for the full release workflow.\n\n**Key rule: do not edit version numbers in source files for routine releases.** The NuGet package version is passed via `-p:Version=...` at pack time in CI. `AssemblyVersion` and `FileVersion` are hardcoded to `5.0.0.0` in `src/Directory.Build.props` and must not be changed for patch or minor OpenCV upgrades — only bump the major component on a breaking API change or OpenCV major-version upgrade. `InformationalVersion` is set automatically by the SDK to the full NuGet version string.\n\n> **Branch note:** `main` targets OpenCV 5.x and publishes the `OpenCvSharp5` package family (there is no separate `5.x` branch — `main` is the OpenCV-5 development branch). The `4.x` branch is frozen on OpenCV 4.13.0 (`OpenCvSharp4` family, `AssemblyVersion 4.0.0.0`); cut 4.x patches from there.\n\n## Native DLL loading\n\nThere is no custom native-library loader. Since `src/OpenCvSharp/` requires net8.0 or above, a consumer can never be a .NET Framework (net4x) project (net8.0+-only assemblies aren't referenceable from net4x), so the old `WindowsLibraryLoader`/`Win32Api` classes and their `dll/x64`-folder probing were unreachable and were removed. Native library resolution is handled entirely by the .NET runtime's default probing via the `runtimes/{rid}/native/` layout that the `OpenCvSharp5.runtime.*` NuGet packages provide. If an app needs to load the native DLL from a nonstandard location (e.g. a plugin host that doesn't process `deps.json`), it can call `System.Runtime.InteropServices.NativeLibrary.Load(path)` (or `NativeLibrary.SetDllImportResolver`) itself before the first OpenCvSharp call — no OpenCvSharp-side API is needed for this.\n\n## Pull request conventions\n\nThis repo has no `PULL_REQUEST_TEMPLATE.md`, so there's no fixed checklist to fill in — but that doesn't mean invent your own heading structure (`### Description`, `### Changes`, etc.). Look at a few recently merged PRs' bodies first and match their shape: a short plain-prose summary (no invented headings), optionally followed by a `## Test plan` checklist when the change needs one. Keep it terse — a large, sectioned write-up reads as noticeably more \"AI-generated\" than the plain style this repo's own history uses.\n\nWhen an agent creates a pull request or issue, it must inspect the repository's existing labels and apply at least one relevant label whenever a semantically appropriate label exists. If no existing label applies, explicitly report that instead of silently leaving the item unlabeled. Whenever an agent creates a pull request in this repository, it must also assign the pull request to `@shimat`. Verify both labels and assignees after creation before reporting the item as complete.\n\n## Issue backlog\n\n`docs/issue-backlog.md` tracks actionable issues identified from closed/stale GitHub issues. Update the checkboxes as items are resolved.\n","isInternal":false,"tokens":7128,"sizeBytes":28369},{"name":"marketplace.json","path":".claude-plugin/marketplace.json","rawUrl":"https://raw.githubusercontent.com/shimat/opencvsharp/HEAD/.claude-plugin/marketplace.json","title":"Claude Plugin Marketplace Catalog","category":"marketplace","format":"json","content":"# Claude Plugin Marketplace Catalog\n\nPath: `.claude-plugin/marketplace.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/shimat/opencvsharp/HEAD/.claude-plugin/marketplace.json)","tokens":300,"sizeBytes":197},{"name":"plugin.json","path":".claude-plugin/plugin.json","rawUrl":"https://raw.githubusercontent.com/shimat/opencvsharp/HEAD/.claude-plugin/plugin.json","title":"claude-plugin Plugin Manifest","category":"plugin-manifest","format":"json","content":"# claude-plugin Plugin Manifest\n\nPath: `.claude-plugin/plugin.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/shimat/opencvsharp/HEAD/.claude-plugin/plugin.json)","tokens":300,"sizeBytes":183},{"name":"mcp.json","path":".cursor/mcp.json","rawUrl":"https://raw.githubusercontent.com/shimat/opencvsharp/HEAD/.cursor/mcp.json","title":"Model Context Protocol (MCP) Configuration","category":"mcp-config","format":"json","content":"# Model Context Protocol (MCP) Configuration\n\nPath: `.cursor/mcp.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/shimat/opencvsharp/HEAD/.cursor/mcp.json)","tokens":300,"sizeBytes":176}],"systemPromptSnippet":"<yakaai_skills repo=\"shimat/opencvsharp\">\n<!-- File: AGENTS.md (Tokens: ~13 | Category: root-instruction) -->\nSee `.github/copilot-instructions.md` for development instructions.\n\n<!-- File: CLAUDE.md (Tokens: ~13 | Category: claude-rule) -->\nSee `.github/copilot-instructions.md` for development instructions.\n\n<!-- File: .github/copilot-instructions.md (Tokens: ~7128 | Category: copilot-instructions) -->\n# Copilot Instructions\n\n## File encoding\n\nAll text files in this repository use **UTF-8 without a BOM**. This applies to `.cs`, `.csproj`, `.yml`, `.md`, `.json`, `.cpp`, `.h`, and every other text file — there are no exceptions.\n\n- `.editorconfig` declares `charset = utf-8` (no BOM) for all files, so editors and `dotnet format` enforce this automatically.\n- The `Write` / `create_file` tool already produces UTF-8 without a BOM, so just create and edit files normally — no encoding fix-up step is needed.\n- Do **not** add a BOM, and do not save as ANSI, Shift-JIS, or UTF-16. The repository is English-only (ASCII), and the C++ build sets the UTF-8 source charset, so MSVC reads the sources correctly without a BOM.\n- **All repository content is English**, regardless of what language a request or discussion happens in: code, comments, identifiers, commit messages, PR/issue titles and descriptions, and every file under `docs/`. This holds even when the person or agent driving the change is working with the maintainer in a different language.\n\n## NuGet README sync\n\nWhen editing the root `README.md`, also update the NuGet-specific README files accordingly.\n\n| File | Target packages |\n|---|---|\n| `packaging/nuget/README.managed.md` | `OpenCvSharp5`, `OpenCvSharp5.Windows`, `OpenCvSharp5.Windows.Slim`, `OpenCvSharp5.GdipExtensions`, `OpenCvSharp5.WpfExtensions`, `OpenCvSharp5.AvaloniaExtensions` |\n| `packaging/nuget/README.runtime.md` | `OpenCvSharp5.runtime.*`, `OpenCvSharp5.official.runtime.*` (all native runtime packages) |\n\n> The `main`-branch packages are the `OpenCvSharp5.*` family (OpenCV 5.x). An identically-structured `OpenCvSharp4.*` family (OpenCV 4.13.0, frozen on the `4.x` branch) is also published for .NET Framework / pre-.NET 8 consumers — see the branch note under \"Versioning and release process\".\n\nThe NuGet READMEs are a subset of the top-level README and consist of the following sections:\n\n- Overview and supported platforms\n- Installation instructions (Quick Start)\n- Requirements\n- Slim profile module coverage table (`README.managed.md` only)\n- Code usage examples (`README.managed.md` only)\n- Links (GitHub, Samples, API Docs, Issue Tracker)\n\nDo **not** include CI badges, Docker instructions, build instructions, or donation links in the NuGet READMEs.\n\n## Markdown authoring\n\nDo not hard-wrap Markdown prose at a fixed character width in the middle of a sentence. Write each paragraph, and each list item, as a single unbroken line in the source, and let the renderer/editor soft-wrap it for display. Only start a new line for a genuinely new block: the next list item, a new paragraph, a heading, a code fence, etc. This applies to every Markdown file in the repo (READMEs, `docs/*.md`, this file) as well as PR/issue/commit-message bodies that render as Markdown — manually wrapping mid-sentence makes diffs noisy (a small wording edit reflows the whole paragraph) and reads as pointlessly rigid formatting. When editing an existing hard-wrapped file, prefer reflowing the touched paragraphs to single lines rather than preserving the old wrap width.\n\n## Agent mode — terminal commands\n\nIn agent mode, do **not** use display commands that require user input (e.g., `more`, `less` without options). Use non-interactive alternatives instead:\n- PowerShell: `Select-Object -First N`, `Out-String`, `Write-Output`\n- Git: pass `-P` or `--no-pager`, or pipe to `Out-String`; e.g. `git --no-pager diff`\n- Use `cat` for displaying file contents.\n\n## Adding a new OpenCV class wrapper\n\n> **Scope**: This checklist covers `cv::SomeClass : cv::Algorithm` subclasses. OpenCV also has classes that do **not** inherit from `Algorithm` — those may follow different ownership and lifetime patterns (see existing non-Algorithm wrappers such as `BackgroundSubtractor` or classes in `core/` for reference).\n\nFollow this checklist when wrapping a new `cv::SomeClass : cv::Algorithm` class:\n\n### Files to create\n\n| File | Location |\n|---|---|\n| `<module>_SomeClass.h` | `src/OpenCvSharpExtern/` |\n| `SomeClass.cs` | `src/OpenCvSharp/Modules/<module>/` |\n| `NativeMethods_<module>_SomeClass.cs` | `src/OpenCvSharp/Internal/PInvoke/NativeMethods/<module>/` |\n| `SomeClassTest.cs` | `test/OpenCvSharp.Tests/<module>/` |\n| `Enum/SomeEnum.cs` (if needed) | same module folder |\n| `VectorOfVecXy.cs` (only if the element type is non-blittable/nested — blittable types use `StdVector<T>` directly, no new file needed) | `src/OpenCvSharp/Internal/Vectors/` |\n\n### Files to modify\n\n| File | Change |\n|---|---|\n| `<module>.cpp` | Add `#include \"<module>_SomeClass.h\"` |\n| `std_vector.h` | Add `#pragma region cv::VecXy` block if new vector type needed |\n| `NativeMethods_stdvector.cs` | Add corresponding P/Invoke region if new vector type needed |\n| `Cv<Module>.cs` | Add `public static SomeClass CreateSomeClass()` factory method |\n\n### C++ extern pattern (<module>_SomeClass.h)\n\n```cpp\n#pragma once\n#ifndef NO_CONTRIB\n#include \"include_opencv.h\"\n\nCVAPI(ExceptionStatus) <module>_Ptr_SomeClass_delete(cv::Ptr<cv::<module>::SomeClass> *obj)\n{ BEGIN_WRAP delete obj; END_WRAP }\n\nCVAPI(ExceptionStatus) <module>_Ptr_SomeClass_get(\n    cv::Ptr<cv::<module>::SomeClass> *ptr, cv::<module>::SomeClass **returnValue)\n{ BEGIN_WRAP *returnValue = ptr->get(); END_WRAP }\n\nCVAPI(ExceptionStatus) <module>_createSomeClass(\n    cv::Ptr<cv::<module>::SomeClass> **returnValue)\n{\n    BEGIN_WRAP\n    const auto ptr = cv::<module>::createSomeClass();\n    *returnValue = new cv::Ptr<cv::<module>::SomeClass>(ptr);\n    END_WRAP\n}\n// ... method bindings ...\n#endif // NO_CONTRIB\n```\n\n### C# class pattern (SomeClass.cs)\n\n```csharp\npublic class SomeClass : Algorithm\n{\n    private SomeClass(IntPtr smartPtr, IntPtr rawPtr)\n        : base(smartPtr, rawPtr, p => NativeMethods.HandleException(\n            NativeMethods.<module>_Ptr_SomeClass_delete(p))) { }\n\n    public static SomeClass Create()\n    {\n        NativeMethods.HandleException(NativeMethods.<module>_createSomeClass(out var smartPtr));\n        NativeMethods.HandleException(NativeMethods.<module>_Ptr_SomeClass_get(smartPtr, out var rawPtr));\n        return new SomeClass(smartPtr, rawPtr);\n    }\n\n    public virtual void SomeMethod(InputArray src) {\n        ThrowIfDisposed();\n        ArgumentNullException.ThrowIfNull(src);\n        src.ThrowIfDisposed();\n        NativeMethods.HandleException(NativeMethods.<module>_SomeClass_someMethod(RawPtr, src.CvPtr));\n        GC.KeepAlive(this);\n        GC.KeepAlive(src);\n    }\n\n    // OutputArray methods: call dst.Fix() after the P/Invoke call\n    // std::vector return methods: use StdVector<T> for blittable T (VectorOfXxx only for\n    // non-blittable/nested element types), wrap in using, call .ToArray()\n}\n```\n\n### Argument validation\n\nUse the BCL `ArgumentNullException.ThrowIfNull(x)` / `ArgumentOutOfRangeException.ThrowIfNegative(x)` / `.ThrowIfGreaterThan(x, max)` / etc. helpers instead of a manual `if (x is null) throw ...` or `if (x < 0) throw ...`. `CvObject.ThrowIfDisposed()` itself is implemented as `ObjectDisposedException.ThrowIf(IsDisposed, this)`. A few patterns are deliberately **not** converted to `ThrowIfNull`:\n\n- A ctor-chain initializer (`: this(...)`, `: base(...)`) or expression-bodied member (`=>`) that does `x ?? throw new ArgumentNullException(nameof(x))` — `ThrowIfNull` returns `void`, so it can't be used as part of an expression; keep the `?? throw` form there.\n- `if (string.IsNullOrEmpty(x)) throw new ArgumentNullException(...)` (filenames, extensions, etc.) — this intentionally raises `ArgumentNullException` for *both* a null and an empty string. Don't \"upgrade\" it to `ArgumentException.ThrowIfNullOrEmpty`, which throws `ArgumentException` for the empty-string case — that's a behavior change, not a mechanical rewrite.\n- A cast-based unsigned-comparison bounds check (`(uint)row >= (uint)Rows`) can still use `ThrowIfGreaterThanOrEqual((uint)row, (uint)Rows, nameof(row))`, but pass `nameof(row)` explicitly — `CallerArgumentExpression` would otherwise infer the parameter name as the literal cast expression text.\n\n### Params struct pattern (P/Invoke-compatible)\n\nWhen the C++ class has a `Params` struct with `bool` fields, define a flat C struct with `int` for booleans (not a marshaled `bool` — no `[MarshalAs(UnmanagedType.Bool)]`) and convert in `getParams`/`setParams`:\n\n```cpp\n// C++ side\nstruct CvSomeClassParams { int SomeBool; /* other fields */ };\n\nCVAPI(ExceptionStatus) <module>_SomeClass_getParams(obj, CvSomeClassParams* out) {\n    BEGIN_WRAP out->SomeBool = obj->params.SomeBool ? 1 : 0; END_WRAP }\nCVAPI(ExceptionStatus) <module>_SomeClass_setParams(obj, CvSomeClassParams* p) {\n    BEGIN_WRAP cv::<module>::SomeClass::Params q; q.SomeBool = p->SomeBool != 0; obj->setParams(q); END_WRAP }\n```\n\n```csharp\n// C# side — the P/Invoke-facing struct keeps SomeBool as a plain int (blittable, matches the C struct 1:1)\n[StructLayout(LayoutKind.Sequential)]\ninternal struct CvSomeClassParams {\n    public int SomeBool;\n    // other fields...\n}\n// The public-facing Params wrapper exposes a `bool SomeBool` property that converts\n// manually (`value != 0` on get, `value ? 1 : 0` on set) — see EdgeDrawingParams /\n// SimpleBlobDetector.Params for real examples.\n// P/Invoke: out CvSomeClassParams / ref CvSomeClassParams\n```\n\n### One-shot config classes: getAll/setAll pattern\n\nIf a `Params`/`Settings`-style class has behavior (mutated after construction and read by a live algorithm), keep individual native getter/setter P/Invoke calls per field as above. But if it's a pure **one-shot config** — constructed, handed to a factory/algorithm, and never mutated afterward (verify this against the OpenCV source before applying) — prefer folding all the scalar fields into a single blittable POD struct with one `getAll`/`setAll` P/Invoke pair instead of one call per field: a native temporary is created only at construction/read/write time, and the C# side becomes a plain managed data class with no native handle at all. `EdgeDrawingParams`, `SimpleBlobDetector.Params`, and `FacemarkLBF`/`FacemarkAAM`'s `Params` are reference implementations of this pattern. `Mat`/`string`/vector-typed fields still need their own separate P/Invoke argument (they don't fit in a blittable struct).\n\n### Namespace access note\n\nFrom `namespace OpenCvSharp.Internal`, types in `namespace OpenCvSharp` are directly visible (outer scope rule). Types in a sub-namespace such as `namespace OpenCvSharp.XImgProc` are NOT — add an explicit `using` directive in the NativeMethods file when referencing structs defined there.\n\n### POD value types crossing the extern boundary\n\nGeneral-purpose primitive value types passed across the P/Invoke boundary (`Point`/`Size`/`Rect`/`Scalar`/`Range`/`RotatedRect`/`KeyPoint`/`DMatch`/`Vec2b`..`Vec6d`, etc.) live under `namespace interop` with a clean name (no `MyCv*`/`CvVec*` prefix). Convert to/from the corresponding `cv::` type with the `OCS_INTEROP_BITCAST(InteropType, cv::Type)` macro (`std::bit_cast` plus a `static_assert` on size/layout match) rather than a hand-written field-by-field converter — the `static_assert` catches layout drift at compile time. Two exceptions:\n\n- `cv::Scalar`/`cv::Vec*` (and `Moments`) are **not** trivially copyable (`cv::Matx`-derived, user-defined copy/move ops) and can't go through `bit_cast` — they keep an explicit converter. Geometry types (`Point_`/`Size_`/`Rect_`/`RotatedRect`/`KeyPoint`/`DMatch`/`Range`/`TermCriteria`) *are* trivially copyable and use `bit_cast`.\n- Module-specific param/config structs (`aruco_DetectorParameters`, `ml_DTrees_*`, `*_Params`, `CvEdgeDrawingParams`, etc.) stay with their `module_` prefix and are **not** moved into `interop::` — the prefix is a meaningful namespace (which feature the struct belongs to), and folding it into the generic `interop::` bucket would obscure that.\n\n### Common P/Invoke pitfalls\n\n- **`cv::Ptr<T>*` vs. raw `T*` confusion.** `CvPtrObject.Handle` (used by most property getters/setters) returns a raw `T*`, not the smart pointer — a native binding declared as `cv::Ptr<T>* obj` with `(*obj)->getXxx()` silently misinterprets the raw pointer's vtable as a `cv::Ptr`'s internal layout and reliably crashes (access violation) the first time it's called, not at compile time. Match the parameter type to what the C# side actually passes (`Handle` → raw `T*` parameter with `obj->getXxx()`); this bit `BackgroundSubtractorGMG`/`MOG`'s property accessors and went unnoticed because no test exercised a round trip.\n- **Don't change a marshaling attribute (`[MarshalAs(UnmanagedType.LPStr)]` vs. `LPUTF8Str`) without checking every native code path that consumes it.** Different OpenCV backends decode filename strings differently (e.g. `cap_msmf.cpp` uses `MultiByteToWideChar(CP_ACP, ...)`, i.e. it expects ANSI, not UTF-8) — switching the attribute to \"fix\" one call site can silently corrupt non-ASCII paths on a different backend. This kind of regression doesn't show up in a plain build/test run; it only surfaces when you actually exercise the affected path with non-ASCII input.\n- **Write one real get/set (or call/verify) round trip before copying an existing property/method pattern to a new class.** Don't trust an existing, untested binding as a copy-source just because it compiles — verify it actually works first.\n\n### Free-function facade naming: `Cv2` mirrors `cv::`\n\nFree functions are exposed as static methods on `Cv2`, mirroring the C++ namespace structure:\n\n- `Cv2` = `cv::` — e.g. `Cv2.CvtColor` ↔ `cv::cvtColor`.\n- `Cv2.<Sub>` (a nested `public static partial class` under `Cv2`) = `cv::<sub>::` — e.g. `Cv2.Dnn.ReadNetFromONNX` ↔ `cv::dnn::readNetFromONNX`, matching the Python `cv2.dnn.*` submodules. In scope: `Dnn`, `Aruco`, `XImgProc`, `XPhoto`, `OptFlow`, `Text`, `Detail`, `Shape`, `VideoIORegistry`.\n\nPlace each submodule facade in its module folder as `Cv2.<Sub>.cs` (e.g. `Modules/dnn/Cv2.Dnn.cs`). The file uses `namespace OpenCvSharp;` and `using OpenCvSharp.<Sub>;` (the wrapper *types* stay in their `OpenCvSharp.<Sub>` namespaces). Where a sub-namespace type name collides with one in `OpenCvSharp` root (e.g. `InpaintTypes`), fully-qualify it in the signature (`OpenCvSharp.XPhoto.InpaintTypes`). Functions that live directly in `cv::` (no sub-namespace, e.g. the shape `Create…` factories) go on `Cv2` itself, not a nested class. The `CvExtensions` *extension methods* (`OpenCvSharp.Extensions`) are not part of this and keep their name.\n\n### std::vector return values\n\n- For a blittable/primitive element type (`int`, `byte`, `float`, `double`, `Point`, `Point2f`, `Vec4i`, `Vec4f`, `Vec6d`, ...), use the generic `StdVector<T>` directly — do **not** create a dedicated `VectorOfXxx` class. E.g. `new StdVector<Point2f>()`, `new StdVector<byte>()`, `new StdVector<Vec4i>()`.\n- Only create a dedicated `VectorOfXxx.cs` for element types that aren't blittable or need custom marshaling: nested vectors (`std::vector<std::vector<Point>>` → `VectorOfVectorPoint`), `Mat` (`VectorOfMat`), `String` (`VectorOfString`), or other non-trivial types (`VectorOfKeyLine`, `VectorOfDTreesNode`, ...).\n- New dedicated vector types: add `#pragma region` in `std_vector.h`, `#region` in `NativeMethods_stdvector.cs`, and create `VectorOfXxx.cs`.\n\n### EdgeDrawing as reference implementation\n\nSee `src/OpenCvSharpExtern/ximgproc_EdgeDrawing.h`, `src/OpenCvSharp/Modules/ximgproc/EdgeDrawing.cs` for a complete example covering: factory, OutputArray methods, `StdVector<T>`-returning methods (`StdVector<Vec6d>` in `DetectEllipses()`, `StdVector<Vec4f>` in `DetectLines()`, `StdVector<int>` in `GetSegmentIndicesOfLines()`), a nested `VectorOfVectorPoint`-returning method, and a Params struct with `int`-backed bool fields (`EdgeDrawingParams`).\n\n## Enum member naming\n\nC++ enum members are `ALL_CAPS_WITH_UNDERSCORES` (e.g. `IMWRITE_JPEG_QUALITY`, `NORM_L2SQR`). When wrapping them as a C# enum, **default to PascalCase**, stripping the module/family prefix (`IMWRITE_JPEG_QUALITY` → `JpegQuality`). Keep the original casing (or a near-verbatim form) only when one of these narrow exceptions applies:\n\n1. **The value itself is an unbreakable acronym/abbreviation, standing alone (nothing else glued onto it).** Test: read it aloud — do you spell out the letters (like an acronym) rather than pronounce it as a word? If so, PascalCasing it (`Exif`, `L2Sqr`) destroys the acronym for no benefit. Keep it verbatim (`EXIF`, `L2SQR`). Precedent: `NormTypes.INF`/`L1`/`L2SQR`, `ImageMetadataType.EXIF`/`XMP`/`ICCP`/`CICP`.\n2. **The value has no natural word boundaries — it's an opaque code, not a phrase.** Test: does inserting word breaks add any information, or does it just capitalize letters arbitrarily? If the latter, keep it verbatim. Precedent: `ColorConversionCodes.BGR2GRAY` (whole body kept verbatim, only the `COLOR_` prefix stripped), `FASTType.TYPE_5_8`.\n3. **The prefix identifies a specific vendor/backend and is semantically load-bearing.** Test: does stripping the prefix delete information a reader needs (which camera SDK/backend this property belongs to)? If so, keep the prefix verbatim; the suffix can still be PascalCased normally. Precedent: `VideoCaptureProperties`' `XI_`/`OPENNI_`/`PVAPI_`/`GIGA_`/`IOS_`-prefixed members.\n\nWhen none of the three applies, PascalCase — don't keep a verbatim name \"to be safe.\" The exceptions are narrow and should be justifiable by one of the tests above, not a default.\n\n**Compounds win over exception 1.** The \"standalone\" qualifier in exception 1 matters: once an acronym is glued to another word/acronym to form one compound member name, PascalCase the whole compound rather than leaving the acronym in caps — an ALL-CAPS-then-lowercase transition mid-identifier (`TIFFResUnit`, `CCITTRle`) is harder to read than a clean compound (`TiffResUnit`, `CcittRle`). This is why the pre-existing `ImwriteFlags.TiffResUnit`/`JpegQuality`/`PngCompression`/`PamTupleType` PascalCase acronyms that would otherwise qualify for exception 1 in isolation — they're prefixes in a compound, not standalone values. Same reasoning covers `ImwriteTiffCompressionFlags.CcittRle`/`CcittFax3` and the standalone-but-left-PascalCase `Lzw`/`Jbig`/`Dcs`/`Rle` siblings in that same enum (and its `ImwriteEXRCompressionFlags`/`ImwriteHDRCompressionFlags` cousins) — treating every acronym-derived compression-scheme name as ordinary vocabulary, consistently within the enum, beats selectively all-capping only some of them.\n\n**Enum type names are a separate question — mirror the C++ type identifier verbatim, don't run it through this heuristic.** OpenCV's own C++ type names are inconsistently cased across acronyms (`ImwriteJPEGSamplingFactorParams`, `ImwriteEXRTypeFlags`, `ImwritePNGFlags`, `ImwritePAMFlags`, `ImwriteWEBPLosslessMode`, `ImwriteHDRCompressionFlags`, `ImwriteBMPCompressionFlags`, `ImwriteGIFCompressionFlags` are capitalized, but `ImwriteTiffCompressionFlags`/`ImwriteTiffPredictorFlags`/`ImwriteTiffResolutionUnitFlags` use `Tiff`) — that inconsistency is inherited straight from `opencv2/imgcodecs.hpp`, not introduced on the C# side. Keep mirroring the C++ type name 1:1 regardless: it keeps the C# type grep-able against OpenCV's own headers/docs, which matters more than internal cosmetic consistency for a type name. This has always been this codebase's practice (`ImwriteEXRTypeFlags`/`ImwritePNGFlags`/`ImwritePAMFlags` pre-date this rule and already mirror C++ verbatim).\n\n## Raw array parameters and native mutation\n\nA `T[]` (or `T[,]`) parameter that native code mutates in place — via a pinned `fixed` pointer, or by passing the array directly to a P/Invoke signature typed as a raw pointer/array — looks identical at the call site to a pure read-only input array. No `[In]`/`[Out]` attribute is needed to make this mutation happen: for blittable element types, default P/Invoke array marshaling already pins the managed array and hands native code a pointer into that same memory, so writes are visible to the caller regardless of any attribute. Since the signature alone can't distinguish the two cases, the convention here is doc wording, not a wrapper type or attribute:\n\n- If native code both reads a raw array parameter's incoming contents and writes a new result back into the same array (e.g. an initial guess that gets refined in place), its `<param>` doc must start with **\"Input/output\"** (e.g. `Input/output vector of distortion coefficients...`). Don't document it as \"Output\" alone in this case — that hides the fact that the caller's pre-existing values feed into the call.\n- If native code only writes into the array (the caller just pre-allocates it for sizing, and its incoming contents are never read), document it as **\"Output ...\"** — but it must say so; an empty `<param>` or a vague, non-committal description (e.g. \"Pointer to the destination array\") leaves the reader unable to tell it apart from a pure input. Either way — read-and-write or write-only — the array must never be documented as if it were a plain input with no mutation signal at all.\n- If a parameter is genuinely a pure input (native code only reads it, never writes), keep a plain description with no \"Input/output\"/\"Output\" wording, and prefer `IEnumerable<T>` over `T[]` to remove the ambiguity altogether — unless it sits directly next to a `T[][]` parameter pinned via `ArrayAddress2<T>` in the same signature, in which case keep it as `T[]` for visual consistency with its `T[][]` sibling (see `Aruco.RefineDetectedMarkers`'s `detectedIds`).\n- `InputOutputArray`/`OutputArray`-typed parameters already signal mutation through their type and don't strictly need this doc convention, though matching \"Input/output\"/\"Output\" wording there is still accurate and preferred.\n\nSee [issue #2036](https://github.com/shimat/opencvsharp/issues/2036) for the background discussion and known instances (`Cv2.CalibrateCamera`'s `cameraMatrix`/`distCoeffs`, `Cv2.Find4QuadCornerSubpix`'s `corners`).\n\n## Repository structure\n\nOpenCvSharp is organized as:\n\n- **`src/OpenCvSharp/`** — managed C# wrapper (P/Invoke, types, extension methods). Built against `net8.0` (`netstandard2.0`/`netstandard2.1`/`net4x` support was dropped); consumers on net8.0 or above (net9.0, net10.0, ...) can reference it.\n- **`src/OpenCvSharpExtern/`** — thin C++ bridge called by the C# side via P/Invoke.\n- **`src/OpenCvSharp.WpfExtensions/`, `src/OpenCvSharp.GdipExtensions/`, `src/OpenCvSharp.AvaloniaExtensions/`** — optional UI-toolkit interop packages (`Mat`/`BitmapSource`/`Bitmap`/`WriteableBitmap` conversions) consumers reference separately from the core package; each ships as its own `OpenCvSharp5.*Extensions` NuGet package.\n- **`packaging/nuget/`** — NuGet packaging projects (runtime packages that bundle the native DLLs).\n\n## Native ABI and breaking-change policy\n\n`main` is on OpenCV 5.x, a deliberately breaking migration phase: neither the native `OpenCvSharpExtern` ABI nor the public managed API is a compatibility constraint on this branch. When a P/Invoke signature, a native struct layout, or a managed method signature is awkward only because it's mirroring an old/incidental shape, feel free to change **both** the native (`.cpp`/`.h`) and managed sides together to reach the most natural, most efficient design — don't contort the managed side just to avoid touching `OpenCvSharpExtern`. Rebuild and verify with `cmake --build src/build --config Release --target OpenCvSharpExtern` after any native-side change. That said, prefer the smallest change that gets a natural design: if a managed-only fix (e.g. exposing a multi-dimensional native array as a `Span` instead of adding a new native entry point) is just as natural, it's the lower-risk choice — don't change the native ABI merely because \"5.x allows it.\"\n\nBefore proposing to delete legacy/unused-looking code (custom loaders, old P/Invoke wrappers, etc.), verify — don't just assume unused: (1) identify the concrete use case the code was solving, (2) judge whether that use case can still plausibly occur for some consumer, (3) if so, check whether the .NET BCL or a language feature already covers the same need in a more general way (e.g. a custom DLL-loading shim vs. `System.Runtime.InteropServices.NativeLibrary`). If (3) holds, the custom code is reinventing something the platform already provides and removing it is justified; if you skip this check, a deletion can break a real but niche use case (e.g. a plugin host) that the code was quietly serving.\n\nDon't add lazy/deferred computation to a previously native-resource-free convenience type (e.g. a plain result class) if it means that type now has to hold a native handle and become `IDisposable`. Callers of a convenience API generally don't expect to have to think about disposal at all; a modest performance win from avoiding one copy is usually not worth breaking that expectation. If a genuine hot path needs the efficiency, first check whether an *eager* computation with a targeted zero-copy view for the specific expensive step (comparison, rendering, ...) gets most of the benefit without changing the type's disposal contract. If an `IDisposable`-introducing change really is the best option, present the trade-off (e.g. as two labeled alternatives) before implementing it rather than committing to it unilaterally.\n\n## Versioning and release process\n\nSee `docs/release-process.md` for the full release workflow.\n\n**Key rule: do not edit version numbers in source files for routine releases.** The NuGet package version is passed via `-p:Version=...` at pack time in CI. `AssemblyVersion` and `FileVersion` are hardcoded to `5.0.0.0` in `src/Directory.Build.props` and must not be changed for patch or minor OpenCV upgrades — only bump the major component on a breaking API change or OpenCV major-version upgrade. `InformationalVersion` is set automatically by the SDK to the full NuGet version string.\n\n> **Branch note:** `main` targets OpenCV 5.x and publishes the `OpenCvSharp5` package family (there is no separate `5.x` branch — `main` is the OpenCV-5 development branch). The `4.x` branch is frozen on OpenCV 4.13.0 (`OpenCvSharp4` family, `AssemblyVersion 4.0.0.0`); cut 4.x patches from there.\n\n## Native DLL loading\n\nThere is no custom native-library loader. Since `src/OpenCvSharp/` requires net8.0 or above, a consumer can never be a .NET Framework (net4x) project (net8.0+-only assemblies aren't referenceable from net4x), so the old `WindowsLibraryLoader`/`Win32Api` classes and their `dll/x64`-folder probing were unreachable and were removed. Native library resolution is handled entirely by the .NET runtime's default probing via the `runtimes/{rid}/native/` layout that the `OpenCvSharp5.runtime.*` NuGet packages provide. If an app needs to load the native DLL from a nonstandard location (e.g. a plugin host that doesn't process `deps.json`), it can call `System.Runtime.InteropServices.NativeLibrary.Load(path)` (or `NativeLibrary.SetDllImportResolver`) itself before the first OpenCvSharp call — no OpenCvSharp-side API is needed for this.\n\n## Pull request conventions\n\nThis repo has no `PULL_REQUEST_TEMPLATE.md`, so there's no fixed checklist to fill in — but that doesn't mean invent your own heading structure (`### Description`, `### Changes`, etc.). Look at a few recently merged PRs' bodies first and match their shape: a short plain-prose summary (no invented headings), optionally followed by a `## Test plan` checklist when the change needs one. Keep it terse — a large, sectioned write-up reads as noticeably more \"AI-generated\" than the plain style this repo's own history uses.\n\nWhen an agent creates a pull request or issue, it must inspect the repository's existing labels and apply at least one relevant label whenever a semantically appropriate label exists. If no existing label applies, explicitly report that instead of silently leaving the item unlabeled. Whenever an agent creates a pull request in this repository, it must also assign the pull request to `@shimat`. Verify both labels and assignees after creation before reporting the item as complete.\n\n## Issue backlog\n\n`docs/issue-backlog.md` tracks actionable issues identified from closed/stale GitHub issues. Update the checkboxes as items are resolved.\n\n<!-- File: .claude-plugin/marketplace.json (Tokens: ~300 | Category: marketplace) -->\n# Claude Plugin Marketplace Catalog\n\nPath: `.claude-plugin/marketplace.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/shimat/opencvsharp/HEAD/.claude-plugin/marketplace.json)\n\n<!-- File: .claude-plugin/plugin.json (Tokens: ~300 | Category: plugin-manifest) -->\n# claude-plugin Plugin Manifest\n\nPath: `.claude-plugin/plugin.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/shimat/opencvsharp/HEAD/.claude-plugin/plugin.json)\n\n<!-- File: .cursor/mcp.json (Tokens: ~300 | Category: mcp-config) -->\n# Model Context Protocol (MCP) Configuration\n\nPath: `.cursor/mcp.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/shimat/opencvsharp/HEAD/.cursor/mcp.json)\n</yakaai_skills>"}