Skip to content

Commit 8c1cd34

Browse files
authored
Merge pull request #1850 from shimat/ximgproc_EdgeDrawing
Add ximgproc EdgeDrawing
2 parents f0d9186 + 9e6d83e commit 8c1cd34

12 files changed

Lines changed: 1034 additions & 1 deletion

File tree

.github/copilot-instructions.md

Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,3 +61,126 @@ In agent mode, do **not** use display commands that require user input (e.g., `m
6161
- PowerShell: `Select-Object -First N`, `Out-String`, `Write-Output`
6262
- Git: pass `-P` or `--no-pager`, or pipe to `Out-String`; e.g. `git --no-pager diff`
6363
- Use `cat` for displaying file contents.
64+
65+
## Adding a new OpenCV class wrapper
66+
67+
> **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).
68+
69+
Follow this checklist when wrapping a new `cv::SomeClass : cv::Algorithm` class:
70+
71+
### Files to create
72+
73+
| File | Location |
74+
|---|---|
75+
| `<module>_SomeClass.h` | `src/OpenCvSharpExtern/` |
76+
| `SomeClass.cs` | `src/OpenCvSharp/Modules/<module>/` |
77+
| `NativeMethods_<module>_SomeClass.cs` | `src/OpenCvSharp/Internal/PInvoke/NativeMethods/<module>/` |
78+
| `SomeClassTest.cs` | `test/OpenCvSharp.Tests/<module>/` |
79+
| `Enum/SomeEnum.cs` (if needed) | same module folder |
80+
| `VectorOfVecXy.cs` (if needed) | `src/OpenCvSharp/Internal/Vectors/` |
81+
82+
### Files to modify
83+
84+
| File | Change |
85+
|---|---|
86+
| `<module>.cpp` | Add `#include "<module>_SomeClass.h"` |
87+
| `std_vector.h` | Add `#pragma region cv::VecXy` block if new vector type needed |
88+
| `NativeMethods_stdvector.cs` | Add corresponding P/Invoke region if new vector type needed |
89+
| `Cv<Module>.cs` | Add `public static SomeClass CreateSomeClass()` factory method |
90+
91+
### C++ extern pattern (<module>_SomeClass.h)
92+
93+
```cpp
94+
#pragma once
95+
#ifndef NO_CONTRIB
96+
#include "include_opencv.h"
97+
98+
CVAPI(ExceptionStatus) <module>_Ptr_SomeClass_delete(cv::Ptr<cv::<module>::SomeClass> *obj)
99+
{ BEGIN_WRAP delete obj; END_WRAP }
100+
101+
CVAPI(ExceptionStatus) <module>_Ptr_SomeClass_get(
102+
cv::Ptr<cv::<module>::SomeClass> *ptr, cv::<module>::SomeClass **returnValue)
103+
{ BEGIN_WRAP *returnValue = ptr->get(); END_WRAP }
104+
105+
CVAPI(ExceptionStatus) <module>_createSomeClass(
106+
cv::Ptr<cv::<module>::SomeClass> **returnValue)
107+
{
108+
BEGIN_WRAP
109+
const auto ptr = cv::<module>::createSomeClass();
110+
*returnValue = new cv::Ptr<cv::<module>::SomeClass>(ptr);
111+
END_WRAP
112+
}
113+
// ... method bindings ...
114+
#endif // NO_CONTRIB
115+
```
116+
117+
### C# class pattern (SomeClass.cs)
118+
119+
```csharp
120+
public class SomeClass : Algorithm
121+
{
122+
private SomeClass(IntPtr smartPtr, IntPtr rawPtr)
123+
: base(smartPtr, rawPtr, p => NativeMethods.HandleException(
124+
NativeMethods.<module>_Ptr_SomeClass_delete(p))) { }
125+
126+
public static SomeClass Create()
127+
{
128+
NativeMethods.HandleException(NativeMethods.<module>_createSomeClass(out var smartPtr));
129+
NativeMethods.HandleException(NativeMethods.<module>_Ptr_SomeClass_get(smartPtr, out var rawPtr));
130+
return new SomeClass(smartPtr, rawPtr);
131+
}
132+
133+
public virtual void SomeMethod(InputArray src) {
134+
ThrowIfDisposed();
135+
if (src is null) throw new ArgumentNullException(nameof(src));
136+
src.ThrowIfDisposed();
137+
NativeMethods.HandleException(NativeMethods.<module>_SomeClass_someMethod(RawPtr, src.CvPtr));
138+
GC.KeepAlive(this);
139+
GC.KeepAlive(src);
140+
}
141+
142+
// OutputArray methods: call dst.Fix() after the P/Invoke call
143+
// std::vector return methods: use VectorOfXxx, wrap in using, call .ToArray()
144+
}
145+
```
146+
147+
### Params struct pattern (P/Invoke-compatible)
148+
149+
When the C++ class has a `Params` struct with `bool` fields, define a flat C struct with `int` for booleans and convert in `getParams`/`setParams`:
150+
151+
```cpp
152+
// C++ side
153+
struct CvSomeClassParams { int SomeBool; /* other fields */ };
154+
155+
CVAPI(ExceptionStatus) <module>_SomeClass_getParams(obj, CvSomeClassParams* out) {
156+
BEGIN_WRAP out->SomeBool = obj->params.SomeBool ? 1 : 0; END_WRAP }
157+
CVAPI(ExceptionStatus) <module>_SomeClass_setParams(obj, CvSomeClassParams* p) {
158+
BEGIN_WRAP cv::<module>::SomeClass::Params q; q.SomeBool = p->SomeBool != 0; obj->setParams(q); END_WRAP }
159+
```
160+
161+
```csharp
162+
// C# side — [MarshalAs(UnmanagedType.Bool)] makes bool marshal as 4-byte BOOL matching int in C
163+
[StructLayout(LayoutKind.Sequential)]
164+
public struct SomeClassParams {
165+
[MarshalAs(UnmanagedType.Bool)] public bool SomeBool;
166+
// other fields...
167+
}
168+
// P/Invoke: out SomeClassParams / ref SomeClassParams
169+
```
170+
171+
### Namespace access note
172+
173+
From `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.
174+
175+
### std::vector return values
176+
177+
- `std::vector<std::vector<Point>>``VectorOfVectorPoint` (already exists)
178+
- `std::vector<int>``VectorOfInt32` (already exists)
179+
- `std::vector<Vec4f>``VectorOfVec4f` (already exists)
180+
- `std::vector<Vec6d>``VectorOfVec6d` (added in EdgeDrawing PR)
181+
- New vector types: add `#pragma region` in `std_vector.h`, `#region` in `NativeMethods_stdvector.cs`, and create `VectorOfXxx.cs`
182+
183+
### EdgeDrawing as reference implementation
184+
185+
See `src/OpenCvSharpExtern/ximgproc_EdgeDrawing.h`, `src/OpenCvSharp/Modules/ximgproc/EdgeDrawing.cs` for a complete example covering: factory, OutputArray methods, std::vector methods, nested Params struct with bool fields, and VectorOfVec6d.
186+

src/OpenCvSharp/Internal/PInvoke/NativeMethods/NativeMethods_stdvector.cs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,16 @@ static partial class NativeMethods
125125
[DllImport(DllExtern, CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
126126
public static extern void vector_Vec6f_delete(IntPtr vector);
127127
#endregion
128+
#region cv::Vec6d
129+
[DllImport(DllExtern, CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
130+
public static extern IntPtr vector_Vec6d_new1();
131+
[DllImport(DllExtern, CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
132+
public static extern nuint vector_Vec6d_getSize(IntPtr vector);
133+
[DllImport(DllExtern, CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
134+
public static extern IntPtr vector_Vec6d_getPointer(IntPtr vector);
135+
[DllImport(DllExtern, CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
136+
public static extern void vector_Vec6d_delete(IntPtr vector);
137+
#endregion
128138
#region cv::Point2i
129139
[DllImport(DllExtern, CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
130140
public static extern IntPtr vector_Point2i_new1();
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
using System.Runtime.InteropServices;
2+
3+
// ReSharper disable InconsistentNaming
4+
5+
#pragma warning disable 1591
6+
#pragma warning disable CA1401 // P/Invokes should not be visible
7+
#pragma warning disable IDE1006 // Naming style
8+
9+
namespace OpenCvSharp.Internal;
10+
11+
/// <summary>
12+
/// Blittable P/Invoke representation of <c>EdgeDrawing::Params</c>.
13+
/// Bool fields use <c>int</c> (0/1) to match the C++ CvEdgeDrawingParams struct layout exactly.
14+
/// This type is internal; use <c>OpenCvSharp.XImgProc.EdgeDrawingParams</c> in consumer code.
15+
/// </summary>
16+
[StructLayout(LayoutKind.Sequential)]
17+
public struct CvEdgeDrawingParams
18+
{
19+
public int PFmode;
20+
public int EdgeDetectionOperator;
21+
public int GradientThresholdValue;
22+
public int AnchorThresholdValue;
23+
public int ScanInterval;
24+
public int MinPathLength;
25+
public float Sigma;
26+
public int SumFlag;
27+
public int NFAValidation;
28+
public int MinLineLength;
29+
public double MaxDistanceBetweenTwoLines;
30+
public double LineFitErrorThreshold;
31+
public double MaxErrorThreshold;
32+
}
33+
34+
static partial class NativeMethods
35+
{
36+
[DllImport(DllExtern, CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
37+
public static extern ExceptionStatus ximgproc_Ptr_EdgeDrawing_delete(IntPtr obj);
38+
39+
[DllImport(DllExtern, CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
40+
public static extern ExceptionStatus ximgproc_Ptr_EdgeDrawing_get(IntPtr ptr, out IntPtr returnValue);
41+
42+
[DllImport(DllExtern, CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
43+
public static extern ExceptionStatus ximgproc_createEdgeDrawing(out IntPtr returnValue);
44+
45+
[DllImport(DllExtern, CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
46+
public static extern ExceptionStatus ximgproc_EdgeDrawing_detectEdges(IntPtr obj, IntPtr src);
47+
48+
[DllImport(DllExtern, CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
49+
public static extern ExceptionStatus ximgproc_EdgeDrawing_getEdgeImage(IntPtr obj, IntPtr dst);
50+
51+
[DllImport(DllExtern, CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
52+
public static extern ExceptionStatus ximgproc_EdgeDrawing_getGradientImage(IntPtr obj, IntPtr dst);
53+
54+
[DllImport(DllExtern, CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
55+
public static extern ExceptionStatus ximgproc_EdgeDrawing_getSegments(IntPtr obj, IntPtr returnValue);
56+
57+
[DllImport(DllExtern, CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
58+
public static extern ExceptionStatus ximgproc_EdgeDrawing_getSegmentIndicesOfLines(IntPtr obj, IntPtr returnValue);
59+
60+
[DllImport(DllExtern, CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
61+
public static extern ExceptionStatus ximgproc_EdgeDrawing_detectLines(IntPtr obj, IntPtr lines);
62+
63+
[DllImport(DllExtern, CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
64+
public static extern ExceptionStatus ximgproc_EdgeDrawing_detectLines_vector(IntPtr obj, IntPtr lines);
65+
66+
[DllImport(DllExtern, CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
67+
public static extern ExceptionStatus ximgproc_EdgeDrawing_detectEllipses(IntPtr obj, IntPtr ellipses);
68+
69+
[DllImport(DllExtern, CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
70+
public static extern ExceptionStatus ximgproc_EdgeDrawing_detectEllipses_vector(IntPtr obj, IntPtr ellipses);
71+
72+
[DllImport(DllExtern, CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
73+
public static extern ExceptionStatus ximgproc_EdgeDrawing_Params_default(out CvEdgeDrawingParams returnValue);
74+
75+
[DllImport(DllExtern, CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
76+
public static extern ExceptionStatus ximgproc_EdgeDrawing_getParams(IntPtr obj, out CvEdgeDrawingParams returnValue);
77+
78+
[DllImport(DllExtern, CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
79+
public static extern ExceptionStatus ximgproc_EdgeDrawing_setParams(IntPtr obj, ref CvEdgeDrawingParams parameters);
80+
}
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
using System.Runtime.InteropServices;
2+
using OpenCvSharp.Internal.Util;
3+
4+
namespace OpenCvSharp.Internal.Vectors;
5+
6+
/// <summary>
7+
/// std::vector&lt;cv::Vec6d&gt;
8+
/// </summary>
9+
// ReSharper disable once InconsistentNaming
10+
internal sealed class VectorOfVec6d : CvObject, IStdVector<Vec6d>
11+
{
12+
/// <summary>
13+
/// Constructor
14+
/// </summary>
15+
public VectorOfVec6d()
16+
{
17+
var p = NativeMethods.vector_Vec6d_new1();
18+
SetSafeHandle(new OpenCvPtrSafeHandle(p, ownsHandle: false, releaseAction: null));
19+
}
20+
21+
/// <summary>
22+
/// Releases unmanaged resources
23+
/// </summary>
24+
protected override void DisposeUnmanaged()
25+
{
26+
NativeMethods.vector_Vec6d_delete(CvPtr);
27+
base.DisposeUnmanaged();
28+
}
29+
30+
/// <summary>
31+
/// vector.size()
32+
/// </summary>
33+
public int Size
34+
{
35+
get
36+
{
37+
var res = NativeMethods.vector_Vec6d_getSize(CvPtr);
38+
GC.KeepAlive(this);
39+
return (int)res;
40+
}
41+
}
42+
43+
/// <summary>
44+
/// &amp;vector[0]
45+
/// </summary>
46+
public IntPtr ElemPtr
47+
{
48+
get
49+
{
50+
var res = NativeMethods.vector_Vec6d_getPointer(CvPtr);
51+
GC.KeepAlive(this);
52+
return res;
53+
}
54+
}
55+
56+
/// <summary>
57+
/// Converts std::vector to managed array
58+
/// </summary>
59+
public Vec6d[] ToArray()
60+
{
61+
return ToArray<Vec6d>();
62+
}
63+
64+
/// <summary>
65+
/// Converts std::vector to managed array
66+
/// </summary>
67+
/// <typeparam name="T">structure whose size equals sizeof(double)*6</typeparam>
68+
public T[] ToArray<T>() where T : unmanaged
69+
{
70+
var typeSize = Marshal.SizeOf<T>();
71+
if (typeSize != sizeof(double) * 6)
72+
throw new OpenCvSharpException($"Unsupported type '{typeof(T)}'");
73+
74+
var arySize = Size;
75+
if (arySize == 0)
76+
return [];
77+
78+
var dst = new T[arySize];
79+
using (var dstPtr = new ArrayAddress1<T>(dst))
80+
{
81+
long bytesToCopy = typeSize * dst.Length;
82+
unsafe
83+
{
84+
Buffer.MemoryCopy(ElemPtr.ToPointer(), dstPtr.Pointer.ToPointer(), bytesToCopy, bytesToCopy);
85+
}
86+
}
87+
GC.KeepAlive(this);
88+
return dst;
89+
}
90+
}

src/OpenCvSharp/Modules/ximgproc/CvXImgProc.cs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -641,6 +641,19 @@ public static void GradientDericheX(InputArray op, OutputArray dst, double alpha
641641

642642
#endregion
643643

644+
#region edge_drawing.hpp
645+
646+
/// <summary>
647+
/// Creates a smart pointer to an EdgeDrawing object and initializes it.
648+
/// </summary>
649+
/// <returns>EdgeDrawing instance</returns>
650+
public static EdgeDrawing CreateEdgeDrawing()
651+
{
652+
return EdgeDrawing.Create();
653+
}
654+
655+
#endregion
656+
644657
#region edgeboxes.hpp
645658

646659
/// <summary>

0 commit comments

Comments
 (0)