Skip to content

Commit d64bbe3

Browse files
committed
Add ICP and Gray-code structured light bindings
1 parent 7a54684 commit d64bbe3

14 files changed

Lines changed: 944 additions & 2 deletions

File tree

cmake/opencv_build_options.cmake

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,8 +38,8 @@ set(BUILD_opencv_reg OFF CACHE BOOL "" FORCE)
3838
# OpenCV 5: calib3d was split and StereoMatcher/StereoBM/StereoSGBM now live in
3939
# the main "stereo" module, so it must be built (ximgproc/disparity_filter needs it).
4040
set(BUILD_opencv_stereo ON CACHE BOOL "" FORCE)
41-
set(BUILD_opencv_structured_light OFF CACHE BOOL "" FORCE)
42-
set(BUILD_opencv_surface_matching OFF CACHE BOOL "" FORCE)
41+
set(BUILD_opencv_structured_light ON CACHE BOOL "" FORCE)
42+
set(BUILD_opencv_surface_matching ON CACHE BOOL "" FORCE)
4343
set(BUILD_opencv_videostab OFF CACHE BOOL "" FORCE)
4444
set(BUILD_opencv_wechat_qrcode ON CACHE BOOL "" FORCE)
4545

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
using System.Runtime.CompilerServices;
2+
using System.Runtime.InteropServices;
3+
4+
#pragma warning disable 1591
5+
#pragma warning disable CA1401
6+
#pragma warning disable IDE1006
7+
8+
namespace OpenCvSharp.Internal;
9+
10+
static partial class NativeMethods
11+
{
12+
[LibraryImport(DllExtern), UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
13+
internal static partial ExceptionStatus structured_light_Ptr_GrayCodePattern_delete(IntPtr obj);
14+
15+
[LibraryImport(DllExtern), UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
16+
internal static partial ExceptionStatus structured_light_Ptr_GrayCodePattern_get(
17+
IntPtr obj,
18+
out IntPtr returnValue);
19+
20+
[LibraryImport(DllExtern), UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
21+
internal static partial ExceptionStatus structured_light_GrayCodePattern_create(
22+
int width,
23+
int height,
24+
out IntPtr returnValue);
25+
26+
[LibraryImport(DllExtern), UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
27+
internal static partial ExceptionStatus structured_light_StructuredLightPattern_generate(
28+
OpenCvSafeHandle obj,
29+
IntPtr patternImages,
30+
out int returnValue);
31+
32+
[LibraryImport(DllExtern), UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
33+
internal static partial ExceptionStatus structured_light_StructuredLightPattern_decode(
34+
OpenCvSafeHandle obj,
35+
[In] IntPtr[] patternImages,
36+
[In] int[] patternImageCounts,
37+
int cameraCount,
38+
IntPtr blackImages,
39+
IntPtr whiteImages,
40+
in OutputArrayProxy disparityMap,
41+
out int returnValue);
42+
43+
[LibraryImport(DllExtern), UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
44+
internal static partial ExceptionStatus structured_light_GrayCodePattern_getNumberOfPatternImages(
45+
OpenCvSafeHandle obj,
46+
out int returnValue);
47+
48+
[LibraryImport(DllExtern), UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
49+
internal static partial ExceptionStatus structured_light_GrayCodePattern_setWhiteThreshold(
50+
OpenCvSafeHandle obj,
51+
int value);
52+
53+
[LibraryImport(DllExtern), UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
54+
internal static partial ExceptionStatus structured_light_GrayCodePattern_setBlackThreshold(
55+
OpenCvSafeHandle obj,
56+
int value);
57+
58+
[LibraryImport(DllExtern), UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
59+
internal static partial ExceptionStatus structured_light_GrayCodePattern_getImagesForShadowMasks(
60+
OpenCvSafeHandle obj,
61+
in OutputArrayProxy blackImage,
62+
in OutputArrayProxy whiteImage);
63+
64+
[LibraryImport(DllExtern), UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
65+
internal static partial ExceptionStatus structured_light_GrayCodePattern_getProjectorPixel(
66+
OpenCvSafeHandle obj,
67+
IntPtr patternImages,
68+
int x,
69+
int y,
70+
out Point projectorPixel,
71+
out int returnValue);
72+
}
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
using System.Runtime.CompilerServices;
2+
using System.Runtime.InteropServices;
3+
4+
#pragma warning disable 1591
5+
#pragma warning disable CA1401
6+
#pragma warning disable IDE1006
7+
8+
namespace OpenCvSharp.Internal;
9+
10+
static partial class NativeMethods
11+
{
12+
[LibraryImport(DllExtern), UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
13+
internal static partial ExceptionStatus surface_matching_ICP_new1(out IntPtr returnValue);
14+
15+
[LibraryImport(DllExtern), UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
16+
internal static partial ExceptionStatus surface_matching_ICP_new2(
17+
int iterations,
18+
float tolerance,
19+
float rejectionScale,
20+
int numberOfLevels,
21+
out IntPtr returnValue);
22+
23+
[LibraryImport(DllExtern), UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
24+
internal static partial ExceptionStatus surface_matching_ICP_delete(IntPtr obj);
25+
26+
[LibraryImport(DllExtern), UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
27+
internal static partial ExceptionStatus surface_matching_ICP_registerModelToScene(
28+
OpenCvSafeHandle obj,
29+
in InputArrayProxy sourcePointCloud,
30+
in InputArrayProxy destinationPointCloud,
31+
out double residual,
32+
out IntPtr pose,
33+
out int returnValue);
34+
35+
[LibraryImport(DllExtern), UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
36+
internal static partial ExceptionStatus surface_matching_computeNormalsPC3d(
37+
in InputArrayProxy pointCloud,
38+
in OutputArrayProxy pointCloudWithNormals,
39+
int numberOfNeighbors,
40+
int flipViewpoint,
41+
Vec3f viewpoint,
42+
out int returnValue);
43+
}
Lines changed: 153 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,153 @@
1+
using OpenCvSharp.Internal;
2+
using OpenCvSharp.Internal.Vectors;
3+
4+
namespace OpenCvSharp.StructuredLight;
5+
6+
/// <summary>
7+
/// Gray-code structured-light pattern generator and decoder.
8+
/// </summary>
9+
public sealed class GrayCodePattern : StructuredLightPattern
10+
{
11+
private GrayCodePattern(IntPtr smartPtr, IntPtr rawPtr)
12+
: base(
13+
smartPtr,
14+
rawPtr,
15+
static p => NativeMethods.HandleException(
16+
NativeMethods.structured_light_Ptr_GrayCodePattern_delete(p)))
17+
{
18+
}
19+
20+
/// <summary>
21+
/// Creates a Gray-code pattern for a projector resolution.
22+
/// </summary>
23+
/// <param name="width">Projector width.</param>
24+
/// <param name="height">Projector height.</param>
25+
public static GrayCodePattern Create(int width = 1024, int height = 768)
26+
{
27+
ArgumentOutOfRangeException.ThrowIfNegativeOrZero(width);
28+
ArgumentOutOfRangeException.ThrowIfNegativeOrZero(height);
29+
30+
NativeMethods.HandleException(
31+
NativeMethods.structured_light_GrayCodePattern_create(
32+
width,
33+
height,
34+
out var smartPtr));
35+
NativeMethods.HandleException(
36+
NativeMethods.structured_light_Ptr_GrayCodePattern_get(
37+
smartPtr,
38+
out var rawPtr));
39+
return new GrayCodePattern(smartPtr, rawPtr);
40+
}
41+
42+
/// <summary>
43+
/// Gets the number of Gray-code images required for projection and decoding.
44+
/// </summary>
45+
public int GetNumberOfPatternImages()
46+
{
47+
ThrowIfDisposed();
48+
49+
NativeMethods.HandleException(
50+
NativeMethods.structured_light_GrayCodePattern_getNumberOfPatternImages(
51+
Handle,
52+
out var returnValue));
53+
GC.KeepAlive(this);
54+
return returnValue;
55+
}
56+
57+
/// <summary>
58+
/// Sets the minimum brightness difference between a pattern and its inverse.
59+
/// </summary>
60+
public void SetWhiteThreshold(int value)
61+
{
62+
ThrowIfDisposed();
63+
ArgumentOutOfRangeException.ThrowIfNegative(value);
64+
ArgumentOutOfRangeException.ThrowIfGreaterThan(value, 255);
65+
66+
NativeMethods.HandleException(
67+
NativeMethods.structured_light_GrayCodePattern_setWhiteThreshold(
68+
Handle,
69+
value));
70+
GC.KeepAlive(this);
71+
}
72+
73+
/// <summary>
74+
/// Sets the minimum brightness difference between white and black reference images.
75+
/// </summary>
76+
public void SetBlackThreshold(int value)
77+
{
78+
ThrowIfDisposed();
79+
ArgumentOutOfRangeException.ThrowIfNegative(value);
80+
ArgumentOutOfRangeException.ThrowIfGreaterThan(value, 255);
81+
82+
NativeMethods.HandleException(
83+
NativeMethods.structured_light_GrayCodePattern_setBlackThreshold(
84+
Handle,
85+
value));
86+
GC.KeepAlive(this);
87+
}
88+
89+
/// <summary>
90+
/// Generates black and white images used to compute shadow masks.
91+
/// </summary>
92+
public void GetImagesForShadowMasks(
93+
OutputArray blackImage,
94+
OutputArray whiteImage)
95+
{
96+
ThrowIfDisposed();
97+
98+
NativeMethods.HandleException(
99+
NativeMethods.structured_light_GrayCodePattern_getImagesForShadowMasks(
100+
Handle,
101+
blackImage.Proxy,
102+
whiteImage.Proxy));
103+
104+
GC.KeepAlive(this);
105+
GC.KeepAlive(blackImage.Source);
106+
GC.KeepAlive(whiteImage.Source);
107+
}
108+
109+
/// <summary>
110+
/// Decodes the projector coordinate corresponding to a camera pixel.
111+
/// </summary>
112+
/// <param name="patternImages">Captured Gray-code pattern sequence.</param>
113+
/// <param name="x">Camera pixel x coordinate.</param>
114+
/// <param name="y">Camera pixel y coordinate.</param>
115+
/// <param name="projectorPixel">Decoded projector coordinate.</param>
116+
/// <returns>True when the coordinate is decoded successfully.</returns>
117+
public bool TryGetProjectorPixel(
118+
IEnumerable<Mat> patternImages,
119+
int x,
120+
int y,
121+
out Point projectorPixel)
122+
{
123+
ThrowIfDisposed();
124+
ArgumentNullException.ThrowIfNull(patternImages);
125+
ArgumentOutOfRangeException.ThrowIfNegative(x);
126+
ArgumentOutOfRangeException.ThrowIfNegative(y);
127+
128+
var patternImageArray = patternImages.ToArray();
129+
if (patternImageArray.Length != GetNumberOfPatternImages())
130+
throw new ArgumentException(
131+
"The image sequence length does not match the Gray-code pattern.",
132+
nameof(patternImages));
133+
foreach (var image in patternImageArray)
134+
{
135+
ArgumentNullException.ThrowIfNull(image);
136+
image.ThrowIfDisposed();
137+
}
138+
139+
using var patternImageVector = new VectorOfMat(patternImageArray);
140+
NativeMethods.HandleException(
141+
NativeMethods.structured_light_GrayCodePattern_getProjectorPixel(
142+
Handle,
143+
patternImageVector.CvPtr,
144+
x,
145+
y,
146+
out projectorPixel,
147+
out var returnValue));
148+
149+
GC.KeepAlive(this);
150+
GC.KeepAlive(patternImageArray);
151+
return returnValue != 0;
152+
}
153+
}
Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
using OpenCvSharp.Internal;
2+
using OpenCvSharp.Internal.Util;
3+
using OpenCvSharp.Internal.Vectors;
4+
5+
namespace OpenCvSharp.StructuredLight;
6+
7+
/// <summary>
8+
/// Abstract base class for generating and decoding structured-light patterns.
9+
/// </summary>
10+
public abstract class StructuredLightPattern : Algorithm
11+
{
12+
/// <summary>
13+
/// Initializes a structured-light pattern wrapper.
14+
/// </summary>
15+
protected StructuredLightPattern(IntPtr smartPtr, IntPtr rawPtr, Action<IntPtr> release)
16+
: base(smartPtr, rawPtr, release)
17+
{
18+
}
19+
20+
/// <summary>
21+
/// Generates the pattern images to project.
22+
/// </summary>
23+
/// <returns>CV_8U images at projector resolution.</returns>
24+
public virtual Mat[] Generate()
25+
{
26+
ThrowIfDisposed();
27+
28+
using var patternImages = new VectorOfMat();
29+
NativeMethods.HandleException(
30+
NativeMethods.structured_light_StructuredLightPattern_generate(
31+
Handle,
32+
patternImages.CvPtr,
33+
out var returnValue));
34+
35+
GC.KeepAlive(this);
36+
if (returnValue == 0)
37+
throw new OpenCvSharpException("Structured-light pattern generation failed.");
38+
return patternImages.ToArray();
39+
}
40+
41+
/// <summary>
42+
/// Decodes structured-light images captured by a stereo camera pair.
43+
/// </summary>
44+
/// <param name="patternImages">Two sets of captured pattern images, one set for each camera.</param>
45+
/// <param name="disparityMap">Output CV_64F disparity map.</param>
46+
/// <param name="blackImages">Black reference image for each camera.</param>
47+
/// <param name="whiteImages">White reference image for each camera.</param>
48+
/// <returns>True when decoding succeeds.</returns>
49+
public virtual bool Decode(
50+
IEnumerable<IEnumerable<Mat>> patternImages,
51+
OutputArray disparityMap,
52+
IEnumerable<Mat> blackImages,
53+
IEnumerable<Mat> whiteImages)
54+
{
55+
ThrowIfDisposed();
56+
ArgumentNullException.ThrowIfNull(patternImages);
57+
ArgumentNullException.ThrowIfNull(blackImages);
58+
ArgumentNullException.ThrowIfNull(whiteImages);
59+
60+
var patterns = patternImages
61+
.Select(cameraImages => cameraImages?.ToArray() ??
62+
throw new ArgumentException("A camera image sequence is null.", nameof(patternImages)))
63+
.ToArray();
64+
if (patterns.Length != 2)
65+
throw new ArgumentException("Exactly two camera image sequences are required.", nameof(patternImages));
66+
if (patterns.Any(cameraImages => cameraImages.Length == 0))
67+
throw new ArgumentException("Camera image sequences must not be empty.", nameof(patternImages));
68+
if (patterns[0].Length != patterns[1].Length)
69+
throw new ArgumentException("Camera image sequences must have the same length.", nameof(patternImages));
70+
71+
var blackImageArray = blackImages.ToArray();
72+
var whiteImageArray = whiteImages.ToArray();
73+
if (blackImageArray.Length != patterns.Length)
74+
throw new ArgumentException("One black reference image is required for each camera.", nameof(blackImages));
75+
if (whiteImageArray.Length != patterns.Length)
76+
throw new ArgumentException("One white reference image is required for each camera.", nameof(whiteImages));
77+
78+
foreach (var image in patterns.SelectMany(static images => images)
79+
.Concat(blackImageArray)
80+
.Concat(whiteImageArray))
81+
{
82+
ArgumentNullException.ThrowIfNull(image);
83+
image.ThrowIfDisposed();
84+
}
85+
86+
var patternPointers = patterns
87+
.Select(cameraImages => cameraImages.Select(static image => image.CvPtr).ToArray())
88+
.ToArray();
89+
using var patternAddresses = new ArrayAddress2<IntPtr>(patternPointers);
90+
using var blackImageVector = new VectorOfMat(blackImageArray);
91+
using var whiteImageVector = new VectorOfMat(whiteImageArray);
92+
93+
NativeMethods.HandleException(
94+
NativeMethods.structured_light_StructuredLightPattern_decode(
95+
Handle,
96+
patternAddresses.GetPointer(),
97+
patternAddresses.GetDim2Lengths(),
98+
patternAddresses.GetDim1Length(),
99+
blackImageVector.CvPtr,
100+
whiteImageVector.CvPtr,
101+
disparityMap.Proxy,
102+
out var returnValue));
103+
104+
GC.KeepAlive(this);
105+
GC.KeepAlive(patterns);
106+
GC.KeepAlive(blackImageArray);
107+
GC.KeepAlive(whiteImageArray);
108+
GC.KeepAlive(disparityMap.Source);
109+
return returnValue != 0;
110+
}
111+
}

0 commit comments

Comments
 (0)