OpenCvSharp5 wraps OpenCV 5.x and is published as a new package family (OpenCvSharp5.*) alongside the existing OpenCvSharp4 family (OpenCvSharp4.*, OpenCV 4.13.0). OpenCvSharp4 continues as a maintenance line, so you are not forced to migrate — but if you want OpenCV 5 features you will move to OpenCvSharp5 and the changes below apply.
Status: living document. The "OpenCV 5 API changes" section is filled in as modules are ported. See the OpenCV 5 tracking issue for current progress.
The single biggest decision behind OpenCvSharp5 is dropping every target framework except net8.0 (see §1). OpenCvSharp4 has to support .NET Framework 4.6.1, .NET Standard 2.0/2.1, and modern .NET all at once, which rules out a lot of runtime and language surface added since C# 8 — ref structs with relaxed rules, source-generated P/Invoke ([LibraryImport]), SafeHandle-first ownership, and so on. Once OpenCvSharp5 could assume .NET 8+, most of the rest of the redesign followed from that one decision:
InputArray/OutputArray/InputOutputArraybecameref structs (§2). In OpenCvSharp4, every implicit conversion (e.g.Cv2.Foo(mat, dst)) allocated a disposable native wrapper object; because the conversion is implicit, there is nowhere natural to put ausing, so these objects routinely leaked until the finalizer eventually ran. Theref structversion carries only a handle and a kind tag, lives on the stack, and has nothing to dispose in the first place.Matarithmetic (MatExpr) became a lazy, purely-managed expression tree (§2). Chained operators like255 - mat * 0.8used to allocate a nativecv::MatExprat every operator call; OpenCvSharp4 even shipped a dedicatedResourcesTrackerhelper class so users could track and dispose those anonymous intermediates by hand. OpenCvSharp5 builds the whole expression in managed memory and only touches native code once, when the expression is finally materialized into aMat—ResourcesTrackeris gone because there is nothing left for it to track.- P/Invoke moved from
[DllImport]to source-generated[LibraryImport], and most wrapper types now own their native handle through aSafeHandleinstead of manualGC.KeepAlivecalls scattered through the codebase. This is invisible from the C# call-site surface almost everywhere, but it is why a few previouslyIDisposable/CvObject-based helper types (e.g.FacemarkAAM.Params, §6) turned into plain POCOs — they no longer own a native handle that needs managing. - The managed API follows the OpenCV 5 / Python
cv2module layout more closely. Free-function facades likeCvDnn/CvArucobecame nestedCv2.Dnn/Cv2.Arucoclasses (§4), and C# namespaces track the upstream module reorganization (features2d→features,calib3dsplit intocalib/stereo/geometry/ptcloud, etc., §7).
None of this is change for its own sake — every item above is either a direct consequence of raising the minimum target framework, or a mechanical follow-through of an upstream OpenCV 5 change. But together they add up to a real one-time migration cost, bigger than a typical OpenCvSharp point release. The sections below are ordered roughly by how many call sites they are likely to touch, starting with the highest-impact ones.
The OpenCvSharp5 managed libraries target .NET 8+ only. Support for .NET Standard 2.0 / 2.1 and .NET Framework (net48) has been dropped.
OpenCvSharp5,OpenCvSharp5.GdipExtensions:net8.0OpenCvSharp5.WpfExtensions:net8.0-windows(net48 dropped)
If you need .NET Framework, Unity, or another pre-.NET 8 runtime, stay on the OpenCvSharp4 family, which keeps the netstandard2.0 / net48 targets.
In OpenCvSharp4, InputArray/OutputArray/InputOutputArray were ordinary classes wrapping a native cv::_InputArray/_OutputArray/_InputOutputArray handle (public class InputArray : CvObject, heap-allocated, IDisposable via the CvObject base). Every implicit conversion from Mat/Scalar/double/a fixed-length Vec/... allocated one of these on the heap and built a matching native object underneath it.
In OpenCvSharp5 they are readonly ref structs (issue #1976): a stack-only proxy carrying just a native handle and a kind tag (plus an inline payload for scalar/vector operands), built with zero heap allocation and requiring no disposal at all — see InputArray.cs.
This is transparent at the overwhelming majority of call sites — Cv2.Foo(mat, dst) compiles completely unchanged — but a few patterns do break:
nullno longer works for an optional array parameter — usedefault. Aref structcan't benull, so any call site that passed a literalnullfor an optional mask/array argument now needsdefault:The compiler error you'll get (// OpenCvSharp4 sift.DetectAndCompute(gray, null, out var keypoints, descriptors); // OpenCvSharp5 sift.DetectAndCompute(gray, default, out var keypoints, descriptors);
CS0037: cannot convert null to 'InputArray' because it's a non-nullable value type) doesn't point atdefaultas the fix, so it's worth knowing this in advance.- They can no longer be stored in a field, captured by a closure, or used across an
await/yieldboundary (standardref structconstraints). Build one at the call site instead of holding onto it; if you need the same array across several calls, keep the underlyingMat/UMataround and let the implicit conversion run again each time. OutputArray.Create(List<T>)was removed. OpenCvSharp4 let aList<T>act as a write-back output sink:Use a// OpenCvSharp4 var output = new List<byte>(); Cv2.Threshold(InputArray.Create(input), OutputArray.Create(output), t, max, ThresholdTypes.Binary);
Matoutput and read the array back withMat.GetArray<T>(out T[]):// OpenCvSharp5 using var output = new Mat(); Cv2.Threshold(InputArray.Create(input), output, t, max, ThresholdTypes.Binary); output.GetArray(out byte[] outputArr);
OpenCvSharp4's Mat arithmetic operators (+, -, *, /, T(), Inv(), ...) eagerly built a disposable MatExpr wrapping a native cv::MatExpr at every operator call. A chained expression like 255 - mat * 0.8 therefore produced multiple untracked native intermediates that a single using couldn't reach, so OpenCvSharp4 shipped a dedicated ResourcesTracker helper purely to track and dispose them:
// OpenCvSharp4
using var t = new ResourcesTracker();
Mat dst = t.T(255 - t.T(src * 0.8));In OpenCvSharp5, Mat arithmetic operators return a purely managed, lazily-evaluated MatExpr that holds no native resource — the expression tree is assembled entirely in managed memory, and the native cv::MatExpr chain is built (and immediately torn down) only once, when the expression is materialized into a Mat:
// OpenCvSharp5
Mat dst = 255 - src * 0.8;ResourcesTracker has been removed entirely — there is nothing left for it to track. Two follow-on effects:
MatExpris no longerIDisposable. Code that didusing var x = Mat.Zeros(...);orusing var t = a * b;now fails to compile (CS1674: 'MatExpr': type used in a using statement must be implicitly convertible to 'System.IDisposable') — just drop theusing. If your code declares the variable asMatrather thanvar(e.g.using (Mat img = Mat.Zeros(...))), it still compiles and still needs disposal, becauseMatExprhas an implicit conversion toMatthat materializes it there.- Fixed-size ops like
Cross()are unaffected — they still return a concreteMatdirectly, since OpenCV'scross()always produces one fixed-size result with no fusable expression node.
Mat used to have a large family of instance methods (mat.Circle(...), mat.Line(...), mat.Rectangle(...), mat.PutText(...), mat.CvtColor(...), mat.Threshold(...), mat.Resize(...), mat.SaveImage(...), mat.HoughCircles(...), mat.MinEnclosingCircle(...), and many more — over 2,300 lines in Mat_CvMethods.cs) that existed purely so Cv2 static methods could be called with chainable, fluent syntax. These have been removed entirely: the duplication doubled the maintenance surface of every Cv2 method, and intermediate Mats produced mid-chain couldn't be captured with using, leaking into non-deterministic GC-driven native cleanup — the same problem the MatExpr rework above solves for arithmetic operators.
This is likely the single most common break when porting existing OpenCvSharp4 code — more so than any of the renames in this guide — and the compile error gives no hint about what happened (CS1061: 'Mat' does not contain a definition for 'Circle'). The fix is mechanical: mat.Xxx(args) → Cv2.Xxx(mat, args) (insert the Mat as the first argument). Two subtleties:
- Methods that used to return a new
Mat(var gray = src.CvtColor(code);,var t = src.Threshold(...)) need an explicit destinationMatcreated first:// OpenCvSharp5 using var gray = new Mat(); Cv2.CvtColor(src, gray, code);
Mat.Resize(...)collides with an unrelated, still-existing instance method —Mat.Resize(int sz)/Mat.Resize(int sz, Scalar s), astd::vector-style row-count resize present since OpenCvSharp4. If you meant "resize this image" and get a "wrong number of arguments" error rather than "no such method", you wantCv2.Resize(src, dst, size, fx, fy, interpolation)— overload resolution won't suggest it for you.
The old OpenCvSharp.Extensions package mixed dependency-free helpers with GDI+ (System.Drawing) interop. It has been split:
| Before (OpenCvSharp4) | After (OpenCvSharp5) |
|---|---|
package OpenCvSharp4.Extensions |
package OpenCvSharp5.GdipExtensions |
using OpenCvSharp.Extensions; (for BitmapConverter) |
using OpenCvSharp.GdipExtensions; |
BitmapConverter (System.Drawing.Bitmap ⇄ Mat) now lives in OpenCvSharp5.GdipExtensions, namespace OpenCvSharp.GdipExtensions.
CvExtensions (the Mat.HoughLinesProbabilisticEx extension) moved into the core OpenCvSharp package. Its namespace is unchanged (OpenCvSharp.Extensions), so existing using OpenCvSharp.Extensions; keeps working — but you no longer need a separate extensions package for it.
The standalone free-function facade classes (CvDnn, CvAruco, …) have been replaced by nested static classes under Cv2, so the managed API mirrors the C++ sub-namespaces and the Python cv2 submodules (cv2.dnn.*, cv2.aruco.*, …):
| Before (OpenCvSharp4) | After (OpenCvSharp5) | C++ namespace |
|---|---|---|
CvDnn.ReadNetFromOnnx |
Cv2.Dnn.ReadNetFromONNX |
cv::dnn |
CvAruco.DetectMarkers |
Cv2.Aruco.DetectMarkers |
cv::aruco |
CvXImgProc.NiblackThreshold |
Cv2.XImgProc.NiblackThreshold |
cv::ximgproc |
CvXPhoto.Inpaint |
Cv2.XPhoto.Inpaint |
cv::xphoto |
CvOptFlow.CalcOpticalFlowSF |
Cv2.OptFlow.CalcOpticalFlowSF |
cv::optflow |
CvText.DetectTextSWT |
Cv2.Text.DetectTextSWT |
cv::text |
CvDetail.ComputeImageFeatures |
Cv2.Detail.ComputeImageFeatures |
cv::detail |
CvShape.CreateThinPlateSplineShapeTransformer |
Cv2.CreateThinPlateSplineShapeTransformer |
cv:: (no sub-namespace) |
The wrapper types (Net, ArucoDetector, the ximgproc filter classes, …) are unchanged and still live in their OpenCvSharp.<Sub> namespaces, so a typical call site only changes the static facade prefix:
using OpenCvSharp.Dnn; // Net / Model types (unchanged)
var net = Cv2.Dnn.ReadNetFromONNX("m.onnx"); // was: CvDnn.ReadNetFromOnnx(...)CvShape had no matching cv::shape sub-namespace (its members are Create… factories for types living directly in cv::), so it folds straight into Cv2. CvExtensions (the OpenCvSharp-specific Mat extension methods) is not part of this change and keeps its name and OpenCvSharp.Extensions namespace.
Aruco is more than a rename. Beyond the
CvAruco.Xxx→Cv2.Aruco.Xxxfacade swap,DetectMarkersitself moved from a static one-shot call to an instance method onArucoDetector, constructed once from aDictionary+DetectorParameters+RefineParametersand then reused — mirroring the OpenCV 5 C++ API shape (cv::aruco::ArucoDetector):// OpenCvSharp4 CvAruco.DetectMarkers(image, dictionary, out corners, out ids, detectorParameters, out rejected); // OpenCvSharp5 using var dictionary = Cv2.Aruco.GetPredefinedDictionary(PredefinedDictionaryType.Dict4X4_1000); using var detector = new ArucoDetector(dictionary, detectorParameters, new RefineParameters()); detector.DetectMarkers(image, out corners, out ids, out rejected);The
Arucotypes themselves (ArucoDetector,Dictionary,DetectorParameters) did not move — they are still inOpenCvSharp.Aruco, same as OpenCvSharp4.
Following the OpenCV 5 features2d/xfeatures2d reshuffle, several detector and Bag-of-Words classes changed namespace. This table is the result of a full sweep of every type in OpenCvSharp.Features2D (OpenCvSharp4) and the features/xfeatures2d modules (OpenCvSharp5), not just the ones that happened to surface through sample code:
| Class | Before (OpenCvSharp4) | After (OpenCvSharp5) |
|---|---|---|
SIFT |
OpenCvSharp.Features2D |
OpenCvSharp (the Features2D namespace no longer exists) |
BRISK |
OpenCvSharp |
OpenCvSharp.XFeatures2D |
KAZE |
OpenCvSharp |
OpenCvSharp.XFeatures2D |
AKAZE |
OpenCvSharp |
OpenCvSharp.XFeatures2D |
AgastFeatureDetector |
OpenCvSharp |
OpenCvSharp.XFeatures2D |
BOWTrainer / BOWKMeansTrainer / BOWImgDescriptorExtractor |
OpenCvSharp |
OpenCvSharp.XFeatures2D |
Everything else in these two modules keeps its OpenCvSharp4 namespace:
| Class | Namespace (unchanged) |
|---|---|
SURF, BriefDescriptorExtractor, FREAK, LATCH, LUCID, StarDetector |
OpenCvSharp.XFeatures2D |
ORB, FastFeatureDetector, MSER, GFTTDetector, SimpleBlobDetector, DenseFeatureDetector, Feature2D, DescriptorMatcher, BFMatcher, FlannBasedMatcher, KeyPointsFilter, KeyPoint, DMatch, AKAZEDescriptorType, KAZEDiffusivityType, ORBScoreType, FASTType, DrawMatchesFlags |
OpenCvSharp |
Note that AKAZEDescriptorType and KAZEDiffusivityType stay in OpenCvSharp even though the AKAZE/KAZE classes that use them moved to OpenCvSharp.XFeatures2D.
ALIKED, DISK, AffineFeature, LightGlueMatcher, and ANNIndex/ANNIndexDistance are new in OpenCvSharp5 (no OpenCvSharp4 equivalent) and live in OpenCvSharp.
These wrap OpenCV functions that were removed in OpenCV 5 (no replacement), so the managed methods are gone too:
| Removed | Notes / replacement |
|---|---|
OpenCvSharp.Extensions.Binarizer (Niblack/Sauvola/Bernsen/Nick/…) |
Use Cv2.XImgProc.NiblackThreshold (Niblack / Sauvola / Wolf / Nick) |
CvDnn.ReadNetFromDarknet, CvDnn.ReadNetFromCaffe, CvDnn.ReadNetFromTorch, CvDnn.ReadTorchBlob, CvDnn.ShrinkCaffeModel (and the Net.* equivalents) |
Darknet/Caffe/Torch parsers removed — export to ONNX and use Cv2.Dnn.ReadNetFromONNX |
Net.SetHalideScheduler |
Halide backend removed |
Cv2.ConvertFp16 |
Use Mat.ConvertTo to/from MatType.CV_16F |
Cv2.LogPolar, Cv2.LinearPolar |
Use Cv2.WarpPolar (WarpPolarMode.Linear / Log) |
Window.GetHandle(), Cv2.GetWindowHandle |
Legacy C API removed; no replacement |
Window.DisplayOverlay, Window.DisplayStatusBar |
Legacy GTK status-bar API removed; no replacement |
TrackerGOTURN |
Removed from OpenCV 5 |
The entire OpenCvSharp.Cuda namespace (GpuMat, Stream, DeviceInfo, …) |
Was already non-functional dead code in OpenCvSharp4 (guarded by a build symbol that was never actually defined) and has now been deleted outright. OpenCvSharp has never supported CUDA — see the main README. |
OutputArray.Create(List<T>) |
See §2 — use a Mat output and Mat.GetArray<T>(out T[]) instead |
OpenCvSharp.Internal.NativeMethods.LoadLibraries, TryPInvoke |
No replacement is normally needed: OpenCvSharp5 relies on the .NET runtime to resolve the native library on the first API call. For a nonstandard native library location, register NativeLibrary.SetDllImportResolver before that call; use a lightweight public API such as Cv2.GetVersionString() if eager validation is required. |
Caffe/Darknet has no runtime fallback, not just a removed convenience method. The generic dispatcher
Cv2.Dnn.ReadNet(model, config)still detects.caffemodel/.prototxt/.weights/.cfgextensions, but on a match it unconditionally throwscv::Exception: Caffe importer has been removed. Please use ONNX-converted models or use an older OpenCV version.(same for Darknet). Converting the model to ONNX is mandatory, not just recommended, for any code that used to load.caffemodel/Darknet models.
Caffe model support was dropped; these constructors now take ONNX model paths:
| Before (OpenCvSharp4) | After (OpenCvSharp5) |
|---|---|
new BarcodeDetector(superResolutionPrototxtPath, superResolutionCaffeModelPath) |
new BarcodeDetector(superResolutionModelPath = "") |
new WeChatQRCode(detectorPrototxt, detectorCaffe, srPrototxt, srCaffe) |
new WeChatQRCode(detectorModelPath = "", superResolutionModelPath = "") |
FacemarkAAM.Params / FacemarkLBF.Params are no longer IDisposable. They used to be native-handle-backed CvObject subclasses; they are now plain managed classes with the same property names and types (ModelFilename, M, N, NIter, Verbose, SaveModel, MaxM, MaxN, TextureMaxM, Scales, ...). Remove any using/.Dispose() call on them — everything else about constructing and using them is unchanged:
// OpenCvSharp4
using var p = new FacemarkAAM.Params { M = 20, N = 10 };
// OpenCvSharp5
var p = new FacemarkAAM.Params { M = 20, N = 10 };OpenCV 5 reorganizes modules and changes some APIs. The managed surface follows the C++ structure where reasonable, while the Cv2 facade is kept source-compatible where possible. This section is updated as modules are ported.
- Module reorganization (C++ build side):
calib3dsplit intocalib/stereo/geometry/ptcloud;features2drenamed tofeatures. Despite this, most C# call sites are unaffected —Cv2.CalibrateCamera,Cv2.FindChessboardCorners,Cv2.Rodrigues,StereoBM/StereoSGBMall keep their existing flatCv2/OpenCvSharplocations; only the internal source file layout moved.ml,gapi, and the HaarCascadeClassifier/HOGDescriptornative modules moved intoopencv_contribon the C++ build side only — the C#CascadeClassifier/HOGDescriptortypes stay in the sameOpenCvSharpnamespace and the same NuGet package, so nothing changes for callers. - New
MatTypedepth types:CV_16BF(bfloat16),CV_32U,CV_64U,CV_64S,CV_Bool. - DNN: a new inference engine is now the default (
EngineType.Autotries the new engine and falls back to the classic 4.x-style engine; passEngineType.Classicexplicitly to force the old behavior). The Darknet and Caffe model readers were removed — convert models to ONNX and useCv2.Dnn.ReadNetFromONNX. - Behavioral differences:
ResizewithINTER_NEARESTnow matches Pillow;WarpAffine/WarpPerspective/Remapinterpolation produces slightly different numeric output; the legacyHersheyFontstext rendering changed and a newFontFacetext API is available. bool+outarray signatures dropped in favor of a direct return value, but only where the single-item sibling had already diverged from the rawbool+by-ref-output C++ shape, so the multi-item method now matches it (empty array/buffer signals failure):Cv2.ImReadMulti(siblingCv2.ImReadreturnsMat),Cv2.ImDecodeMulti(siblingCv2.ImDecodereturnsMat),QRCodeDetector.DecodeMulti/QRCodeDetectorAruco.DecodeMulti(siblingDecodereturnsstring). Methods with no such sibling asymmetry —Cv2.ImEncode*,QRCodeDetector(Aruco).Detect/DetectMulti,Cv2.FindChessboardCorners/FindChessboardCornersSB/FindCirclesGrid,ObjectnessBING.ComputeSaliency,Facemark.Fit,FontFace.GetInstance,Cv2.CheckRange,VideoCapture.WaitAny— keep their originalbool+outshape, since it already mirrors the corresponding OpenCV C++ signature (bool func(..., vector<T>&, ...)) faithfully.
(Detailed managed signature changes will be added here per module as the port lands.)