Skip to content

Commit 6f19429

Browse files
shimatclaude
andcommitted
Add OpenCV 5 features module wrappers: ALIKED, DISK, LightGlue, ANNIndex, AffineFeature
Wraps the new and previously-unwrapped classes from the OpenCV 5 features module (formerly features2d): - ALIKED, DISK: deep-learning local feature detectors/descriptors (Feature2D, ONNX via dnn). Both expose Create (file path) and CreateFromMemory (buffer). - LightGlueMatcher: attention-based descriptor matcher (DescriptorMatcher) with SetPairInfo/ClearPairInfo. - ANNIndex: Annoy-based approximate nearest neighbor index (modern FLANN alternative) with add/build/knnSearch/save/load and ANNIndexDistance enum. - AffineFeature: affine-invariant wrapper over a backend Feature2D (ASIFT). DNN-dependent classes are guarded with HAVE_OPENCV_DNN in the native bridge. Model-path string entry points use the Windows/NotWindows marshaling split. Tests: AffineFeature and ANNIndex are exercised end-to-end; DISK/ALIKED/ LightGlue verify entry-point wiring (missing model -> OpenCVException) since their ONNX models are not committed. Full Features suite green (51 passed). Refs #1924. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
1 parent 89c3e9f commit 6f19429

16 files changed

Lines changed: 1264 additions & 0 deletions
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
using System.Runtime.InteropServices;
2+
3+
#pragma warning disable 1591
4+
#pragma warning disable CA1401 // P/Invokes should not be visible
5+
#pragma warning disable CA2101 // Specify marshaling for P/Invoke string arguments
6+
#pragma warning disable IDE1006 // Naming style
7+
8+
namespace OpenCvSharp.Internal;
9+
10+
static partial class NativeMethods
11+
{
12+
// ReSharper disable InconsistentNaming
13+
14+
[DllImport(DllExtern, CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
15+
public static extern ExceptionStatus features_ANNIndex_create(int dim, int distType, out IntPtr returnValue);
16+
17+
[DllImport(DllExtern, CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
18+
public static extern ExceptionStatus features_Ptr_ANNIndex_get(IntPtr ptr, out IntPtr returnValue);
19+
20+
[DllImport(DllExtern, CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
21+
public static extern ExceptionStatus features_Ptr_ANNIndex_delete(IntPtr ptr);
22+
23+
[DllImport(DllExtern, CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
24+
public static extern ExceptionStatus features_ANNIndex_addItems(IntPtr obj, IntPtr features);
25+
26+
[DllImport(DllExtern, CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
27+
public static extern ExceptionStatus features_ANNIndex_build(IntPtr obj, int trees);
28+
29+
[DllImport(DllExtern, CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
30+
public static extern ExceptionStatus features_ANNIndex_knnSearch(
31+
IntPtr obj, IntPtr query, IntPtr indices, IntPtr dists, int knn, int search_k);
32+
33+
[DllImport(DllExtern, CallingConvention = CallingConvention.Cdecl, ExactSpelling = true, BestFitMapping = false, ThrowOnUnmappableChar = true,
34+
EntryPoint = "features_ANNIndex_save")]
35+
public static extern ExceptionStatus features_ANNIndex_save_NotWindows(
36+
IntPtr obj, [MarshalAs(StringUnmanagedTypeNotWindows)] string filename, int prefault);
37+
38+
[DllImport(DllExtern, CallingConvention = CallingConvention.Cdecl, ExactSpelling = true, BestFitMapping = false, ThrowOnUnmappableChar = true,
39+
EntryPoint = "features_ANNIndex_save")]
40+
public static extern ExceptionStatus features_ANNIndex_save_Windows(
41+
IntPtr obj, [MarshalAs(StringUnmanagedTypeWindows)] string filename, int prefault);
42+
43+
public static ExceptionStatus features_ANNIndex_save(IntPtr obj, string filename, int prefault)
44+
=> IsWindows()
45+
? features_ANNIndex_save_Windows(obj, filename, prefault)
46+
: features_ANNIndex_save_NotWindows(obj, filename, prefault);
47+
48+
[DllImport(DllExtern, CallingConvention = CallingConvention.Cdecl, ExactSpelling = true, BestFitMapping = false, ThrowOnUnmappableChar = true,
49+
EntryPoint = "features_ANNIndex_load")]
50+
public static extern ExceptionStatus features_ANNIndex_load_NotWindows(
51+
IntPtr obj, [MarshalAs(StringUnmanagedTypeNotWindows)] string filename, int prefault);
52+
53+
[DllImport(DllExtern, CallingConvention = CallingConvention.Cdecl, ExactSpelling = true, BestFitMapping = false, ThrowOnUnmappableChar = true,
54+
EntryPoint = "features_ANNIndex_load")]
55+
public static extern ExceptionStatus features_ANNIndex_load_Windows(
56+
IntPtr obj, [MarshalAs(StringUnmanagedTypeWindows)] string filename, int prefault);
57+
58+
public static ExceptionStatus features_ANNIndex_load(IntPtr obj, string filename, int prefault)
59+
=> IsWindows()
60+
? features_ANNIndex_load_Windows(obj, filename, prefault)
61+
: features_ANNIndex_load_NotWindows(obj, filename, prefault);
62+
63+
[DllImport(DllExtern, CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
64+
public static extern ExceptionStatus features_ANNIndex_getTreeNumber(IntPtr obj, out int returnValue);
65+
66+
[DllImport(DllExtern, CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
67+
public static extern ExceptionStatus features_ANNIndex_getItemNumber(IntPtr obj, out int returnValue);
68+
69+
[DllImport(DllExtern, CallingConvention = CallingConvention.Cdecl, ExactSpelling = true, BestFitMapping = false, ThrowOnUnmappableChar = true,
70+
EntryPoint = "features_ANNIndex_setOnDiskBuild")]
71+
public static extern ExceptionStatus features_ANNIndex_setOnDiskBuild_NotWindows(
72+
IntPtr obj, [MarshalAs(StringUnmanagedTypeNotWindows)] string filename, out int returnValue);
73+
74+
[DllImport(DllExtern, CallingConvention = CallingConvention.Cdecl, ExactSpelling = true, BestFitMapping = false, ThrowOnUnmappableChar = true,
75+
EntryPoint = "features_ANNIndex_setOnDiskBuild")]
76+
public static extern ExceptionStatus features_ANNIndex_setOnDiskBuild_Windows(
77+
IntPtr obj, [MarshalAs(StringUnmanagedTypeWindows)] string filename, out int returnValue);
78+
79+
public static ExceptionStatus features_ANNIndex_setOnDiskBuild(IntPtr obj, string filename, out int returnValue)
80+
=> IsWindows()
81+
? features_ANNIndex_setOnDiskBuild_Windows(obj, filename, out returnValue)
82+
: features_ANNIndex_setOnDiskBuild_NotWindows(obj, filename, out returnValue);
83+
84+
[DllImport(DllExtern, CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
85+
public static extern ExceptionStatus features_ANNIndex_setSeed(IntPtr obj, int seed);
86+
}

src/OpenCvSharp/Internal/PInvoke/NativeMethods/features/NativeMethods_features_DescriptorMatcher.cs

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,4 +116,40 @@ public static extern ExceptionStatus features_FlannBasedMatcher_add(
116116

117117
[DllImport(DllExtern, CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
118118
public static extern ExceptionStatus features_Ptr_FlannBasedMatcher_delete(IntPtr ptr);
119+
120+
#region LightGlueMatcher
121+
122+
[DllImport(DllExtern, CallingConvention = CallingConvention.Cdecl, ExactSpelling = true, BestFitMapping = false, ThrowOnUnmappableChar = true,
123+
EntryPoint = "features_LightGlueMatcher_create")]
124+
public static extern ExceptionStatus features_LightGlueMatcher_create_NotWindows(
125+
[MarshalAs(StringUnmanagedTypeNotWindows)] string modelPath, float scoreThreshold, int backend, int target, out IntPtr returnValue);
126+
127+
[DllImport(DllExtern, CallingConvention = CallingConvention.Cdecl, ExactSpelling = true, BestFitMapping = false, ThrowOnUnmappableChar = true,
128+
EntryPoint = "features_LightGlueMatcher_create")]
129+
public static extern ExceptionStatus features_LightGlueMatcher_create_Windows(
130+
[MarshalAs(StringUnmanagedTypeWindows)] string modelPath, float scoreThreshold, int backend, int target, out IntPtr returnValue);
131+
132+
public static ExceptionStatus features_LightGlueMatcher_create(string modelPath, float scoreThreshold, int backend, int target, out IntPtr returnValue)
133+
=> IsWindows()
134+
? features_LightGlueMatcher_create_Windows(modelPath, scoreThreshold, backend, target, out returnValue)
135+
: features_LightGlueMatcher_create_NotWindows(modelPath, scoreThreshold, backend, target, out returnValue);
136+
137+
[DllImport(DllExtern, CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
138+
public static extern ExceptionStatus features_LightGlueMatcher_create_buffer(
139+
byte[] modelData, IntPtr modelDataLength, float scoreThreshold, int backend, int target, out IntPtr returnValue);
140+
141+
[DllImport(DllExtern, CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
142+
public static extern ExceptionStatus features_LightGlueMatcher_setPairInfo(
143+
IntPtr obj, IntPtr queryKpts, IntPtr trainKpts, Size queryImageSize, Size trainImageSize);
144+
145+
[DllImport(DllExtern, CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
146+
public static extern ExceptionStatus features_LightGlueMatcher_clearPairInfo(IntPtr obj);
147+
148+
[DllImport(DllExtern, CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
149+
public static extern ExceptionStatus features_Ptr_LightGlueMatcher_get(IntPtr ptr, out IntPtr returnValue);
150+
151+
[DllImport(DllExtern, CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
152+
public static extern ExceptionStatus features_Ptr_LightGlueMatcher_delete(IntPtr ptr);
153+
154+
#endregion
119155
}

src/OpenCvSharp/Internal/PInvoke/NativeMethods/features/NativeMethods_features_Feature2D.cs

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -244,4 +244,87 @@ public static extern ExceptionStatus features_SimpleBlobDetector_create(
244244

245245
[DllImport(DllExtern, CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
246246
public static extern ExceptionStatus features_Ptr_Feature2D_get(IntPtr obj, out IntPtr returnValue);
247+
248+
#region AffineFeature
249+
250+
[DllImport(DllExtern, CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
251+
public static extern ExceptionStatus features_AffineFeature_create(
252+
IntPtr backend, int maxTilt, int minTilt, float tiltStep, float rotateStepBase, out IntPtr returnValue);
253+
254+
[DllImport(DllExtern, CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
255+
public static extern ExceptionStatus features_AffineFeature_setViewParams(
256+
IntPtr obj, float[] tilts, int tiltsLength, float[] rolls, int rollsLength);
257+
258+
[DllImport(DllExtern, CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
259+
public static extern ExceptionStatus features_AffineFeature_getViewParams(IntPtr obj, IntPtr tilts, IntPtr rolls);
260+
261+
[DllImport(DllExtern, CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
262+
public static extern ExceptionStatus features_Ptr_AffineFeature_delete(IntPtr ptr);
263+
264+
#endregion
265+
266+
#region DISK
267+
268+
[DllImport(DllExtern, CallingConvention = CallingConvention.Cdecl, ExactSpelling = true, BestFitMapping = false, ThrowOnUnmappableChar = true,
269+
EntryPoint = "features_DISK_create")]
270+
public static extern ExceptionStatus features_DISK_create_NotWindows(
271+
[MarshalAs(StringUnmanagedTypeNotWindows)] string modelPath, int maxKeypoints, float scoreThreshold, Size imageSize, int backendId, int targetId, out IntPtr returnValue);
272+
273+
[DllImport(DllExtern, CallingConvention = CallingConvention.Cdecl, ExactSpelling = true, BestFitMapping = false, ThrowOnUnmappableChar = true,
274+
EntryPoint = "features_DISK_create")]
275+
public static extern ExceptionStatus features_DISK_create_Windows(
276+
[MarshalAs(StringUnmanagedTypeWindows)] string modelPath, int maxKeypoints, float scoreThreshold, Size imageSize, int backendId, int targetId, out IntPtr returnValue);
277+
278+
public static ExceptionStatus features_DISK_create(string modelPath, int maxKeypoints, float scoreThreshold, Size imageSize, int backendId, int targetId, out IntPtr returnValue)
279+
=> IsWindows()
280+
? features_DISK_create_Windows(modelPath, maxKeypoints, scoreThreshold, imageSize, backendId, targetId, out returnValue)
281+
: features_DISK_create_NotWindows(modelPath, maxKeypoints, scoreThreshold, imageSize, backendId, targetId, out returnValue);
282+
283+
[DllImport(DllExtern, CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
284+
public static extern ExceptionStatus features_DISK_create_buffer(
285+
byte[] bufferModel, IntPtr bufferModelLength, int maxKeypoints, float scoreThreshold, Size imageSize, int backendId, int targetId, out IntPtr returnValue);
286+
287+
[DllImport(DllExtern, CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
288+
public static extern ExceptionStatus features_DISK_setMaxKeypoints(IntPtr obj, int maxKeypoints);
289+
[DllImport(DllExtern, CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
290+
public static extern ExceptionStatus features_DISK_getMaxKeypoints(IntPtr obj, out int returnValue);
291+
[DllImport(DllExtern, CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
292+
public static extern ExceptionStatus features_DISK_setScoreThreshold(IntPtr obj, float threshold);
293+
[DllImport(DllExtern, CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
294+
public static extern ExceptionStatus features_DISK_getScoreThreshold(IntPtr obj, out float returnValue);
295+
[DllImport(DllExtern, CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
296+
public static extern ExceptionStatus features_DISK_setImageSize(IntPtr obj, Size size);
297+
[DllImport(DllExtern, CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
298+
public static extern ExceptionStatus features_DISK_getImageSize(IntPtr obj, out Size returnValue);
299+
300+
[DllImport(DllExtern, CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
301+
public static extern ExceptionStatus features_Ptr_DISK_delete(IntPtr ptr);
302+
303+
#endregion
304+
305+
#region ALIKED
306+
307+
[DllImport(DllExtern, CallingConvention = CallingConvention.Cdecl, ExactSpelling = true, BestFitMapping = false, ThrowOnUnmappableChar = true,
308+
EntryPoint = "features_ALIKED_create")]
309+
public static extern ExceptionStatus features_ALIKED_create_NotWindows(
310+
[MarshalAs(StringUnmanagedTypeNotWindows)] string modelPath, Size inputSize, int normalizeDescriptors, int engine, int backend, int target, out IntPtr returnValue);
311+
312+
[DllImport(DllExtern, CallingConvention = CallingConvention.Cdecl, ExactSpelling = true, BestFitMapping = false, ThrowOnUnmappableChar = true,
313+
EntryPoint = "features_ALIKED_create")]
314+
public static extern ExceptionStatus features_ALIKED_create_Windows(
315+
[MarshalAs(StringUnmanagedTypeWindows)] string modelPath, Size inputSize, int normalizeDescriptors, int engine, int backend, int target, out IntPtr returnValue);
316+
317+
public static ExceptionStatus features_ALIKED_create(string modelPath, Size inputSize, int normalizeDescriptors, int engine, int backend, int target, out IntPtr returnValue)
318+
=> IsWindows()
319+
? features_ALIKED_create_Windows(modelPath, inputSize, normalizeDescriptors, engine, backend, target, out returnValue)
320+
: features_ALIKED_create_NotWindows(modelPath, inputSize, normalizeDescriptors, engine, backend, target, out returnValue);
321+
322+
[DllImport(DllExtern, CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
323+
public static extern ExceptionStatus features_ALIKED_create_buffer(
324+
byte[] modelData, IntPtr modelDataLength, Size inputSize, int normalizeDescriptors, int engine, int backend, int target, out IntPtr returnValue);
325+
326+
[DllImport(DllExtern, CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
327+
public static extern ExceptionStatus features_Ptr_ALIKED_delete(IntPtr ptr);
328+
329+
#endregion
247330
}
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
using OpenCvSharp.Internal;
2+
3+
namespace OpenCvSharp;
4+
5+
// ReSharper disable once InconsistentNaming
6+
/// <summary>
7+
/// ALIKED: deep learning based local feature detector and descriptor (OpenCV 5).
8+
/// Runs an ONNX model through the DNN module. Requires OpenCV built with the dnn module.
9+
/// </summary>
10+
public class ALIKED : Feature2D
11+
{
12+
/// <summary>
13+
/// Creates instance by raw pointer cv::ALIKED*
14+
/// </summary>
15+
private ALIKED(IntPtr smartPtr, IntPtr rawPtr)
16+
: base(smartPtr, rawPtr, p => NativeMethods.HandleException(NativeMethods.features_Ptr_ALIKED_delete(p)))
17+
{
18+
}
19+
20+
/// <summary>
21+
/// Creates ALIKED from a model file path.
22+
/// </summary>
23+
/// <param name="modelPath">Path to the ONNX model file.</param>
24+
/// <param name="inputSize">Input image size for the network. Default is 640x640.</param>
25+
/// <param name="normalizeDescriptors">Whether to L2-normalize descriptors. Default is true.</param>
26+
/// <param name="engine">DNN engine type (cv::dnn::EngineType). Default is 2 (ENGINE_NEW).</param>
27+
/// <param name="backend">DNN backend (see <see cref="Dnn.Backend"/>). Default is 0 (DEFAULT).</param>
28+
/// <param name="target">DNN target (see <see cref="Dnn.Target"/>). Default is 0 (CPU).</param>
29+
public static ALIKED Create(
30+
string modelPath, Size? inputSize = null, bool normalizeDescriptors = true,
31+
int engine = 2, int backend = 0, int target = 0)
32+
{
33+
if (modelPath is null)
34+
throw new ArgumentNullException(nameof(modelPath));
35+
var size = inputSize ?? new Size(640, 640);
36+
37+
NativeMethods.HandleException(
38+
NativeMethods.features_ALIKED_create(
39+
modelPath, size, normalizeDescriptors ? 1 : 0, engine, backend, target, out var smartPtr));
40+
NativeMethods.HandleException(
41+
NativeMethods.features_Ptr_Feature2D_get(smartPtr, out var rawPtr));
42+
return new ALIKED(smartPtr, rawPtr);
43+
}
44+
45+
/// <summary>
46+
/// Creates ALIKED from an in-memory ONNX model buffer.
47+
/// </summary>
48+
/// <param name="modelData">Buffer containing the model data.</param>
49+
/// <param name="inputSize">Input image size for the network. Default is 640x640.</param>
50+
/// <param name="normalizeDescriptors">Whether to L2-normalize descriptors. Default is true.</param>
51+
/// <param name="engine">DNN engine type (cv::dnn::EngineType). Default is 2 (ENGINE_NEW).</param>
52+
/// <param name="backend">DNN backend (see <see cref="Dnn.Backend"/>). Default is 0 (DEFAULT).</param>
53+
/// <param name="target">DNN target (see <see cref="Dnn.Target"/>). Default is 0 (CPU).</param>
54+
public static ALIKED CreateFromMemory(
55+
byte[] modelData, Size? inputSize = null, bool normalizeDescriptors = true,
56+
int engine = 2, int backend = 0, int target = 0)
57+
{
58+
if (modelData is null)
59+
throw new ArgumentNullException(nameof(modelData));
60+
var size = inputSize ?? new Size(640, 640);
61+
62+
NativeMethods.HandleException(
63+
NativeMethods.features_ALIKED_create_buffer(
64+
modelData, new IntPtr(modelData.Length), size, normalizeDescriptors ? 1 : 0, engine, backend, target, out var smartPtr));
65+
GC.KeepAlive(modelData);
66+
NativeMethods.HandleException(
67+
NativeMethods.features_Ptr_Feature2D_get(smartPtr, out var rawPtr));
68+
return new ALIKED(smartPtr, rawPtr);
69+
}
70+
}

0 commit comments

Comments
 (0)