From 5108520865a131872ece5095c4e051a12f82f0e0 Mon Sep 17 00:00:00 2001 From: okahiro1125 Date: Fri, 13 Jun 2025 19:59:21 +0900 Subject: [PATCH] Add DetectCharucoBoard and related functions --- .../NativeMethods/NativeMethods_aruco.cs | 18 +++ src/OpenCvSharp/Modules/aruco/CvAruco.cs | 133 ++++++++++++++++++ src/OpenCvSharpExtern/aruco.h | 71 ++++++++++ 3 files changed, 222 insertions(+) diff --git a/src/OpenCvSharp/Internal/PInvoke/NativeMethods/NativeMethods_aruco.cs b/src/OpenCvSharp/Internal/PInvoke/NativeMethods/NativeMethods_aruco.cs index 37f2c8d15..6cc036ba5 100644 --- a/src/OpenCvSharp/Internal/PInvoke/NativeMethods/NativeMethods_aruco.cs +++ b/src/OpenCvSharp/Internal/PInvoke/NativeMethods/NativeMethods_aruco.cs @@ -48,6 +48,24 @@ public static extern ExceptionStatus aruco_drawDetectedDiamonds( [MarshalAs(UnmanagedType.LPArray)] IntPtr[] corners, int cornerSize1, int[] contoursSize2, IntPtr ids, Scalar borderColor); + [Pure, DllImport(DllExtern, CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)] + public static extern ExceptionStatus aruco_detectCharucoBoard( + IntPtr image, + int squaresX, int squaresY, float squareLength, float markerLength, int arucoDictId, + IntPtr charucoCorners, IntPtr charucoIds, IntPtr markerCorners, IntPtr markerIds); + + [Pure, DllImport(DllExtern, CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)] + public static extern ExceptionStatus aruco_interpolateCornersCharuco( + IntPtr image, + int squaresX, int squaresY, float squareLength, float markerLength, int arucoDictId, + [MarshalAs(UnmanagedType.LPArray)] IntPtr[] markerCorners, int markerCornersSize1, int[] markerCornersSize2, IntPtr markerIds, + IntPtr charucoCorners, IntPtr charucoIds); + + [Pure, DllImport(DllExtern, CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)] + public static extern ExceptionStatus aruco_drawDetectedCornersCharuco( + IntPtr image, + IntPtr corners, IntPtr ids, Scalar cornerColor); + #region Dictionary [Pure, DllImport(DllExtern, CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)] diff --git a/src/OpenCvSharp/Modules/aruco/CvAruco.cs b/src/OpenCvSharp/Modules/aruco/CvAruco.cs index 7cfa6460b..750ec4808 100644 --- a/src/OpenCvSharp/Modules/aruco/CvAruco.cs +++ b/src/OpenCvSharp/Modules/aruco/CvAruco.cs @@ -296,4 +296,137 @@ public static void DrawDetectedDiamonds(InputArray image, GC.KeepAlive(image); } + + /// + /// Detect ChArUco Board. + /// + /// input image necessary for corner refinement. + /// number of chessboard squares in x directions. + /// number of chessboard squares in y directions. + /// chessboard square side length (normally in meters). + /// marker side length (same unit than squareLength). + /// dictionary of markers indicating the type of markers. + /// output list of detected charuco corners. + /// ids of the charucos in charucoCorners. + /// output list of detected marker corners. + /// ids of the corners in markerCorners. + public static void DetectCharucoBoard(InputArray image, int squaresX, int squaresY, float squareLength, float markerLength, + PredefinedDictionaryName arucoDictId, + out Point2f[] charucoCorners, out int[] charucoIds, + out Point2f[][] markerCorners, out int[] markerIds) + { + if (image is null) + throw new ArgumentNullException(nameof(image)); + + image.ThrowIfDisposed(); + + using var charucoCornersVec = new VectorOfPoint2f(); + using var charucoIdsVec = new VectorOfInt32(); + using var markerCornersVec = new VectorOfVectorPoint2f(); + using var markerIdsVec = new VectorOfInt32(); + + NativeMethods.HandleException( + NativeMethods.aruco_detectCharucoBoard( + image.CvPtr, + squaresX, squaresY, squareLength, markerLength, (int)arucoDictId, + charucoCornersVec.CvPtr, charucoIdsVec.CvPtr, + markerCornersVec.CvPtr, markerIdsVec.CvPtr + )); + + charucoCorners = charucoCornersVec.ToArray(); + charucoIds = charucoIdsVec.ToArray(); + markerCorners = markerCornersVec.ToArray(); + markerIds = markerIdsVec.ToArray(); + + GC.KeepAlive(image); + } + + /// input image necessary for corner refinement. + /// number of chessboard squares in x directions. + /// number of chessboard squares in y directions. + /// chessboard square side length (normally in meters). + /// marker side length (same unit than squareLength). + /// dictionary of markers indicating the type of markers. + /// list of detected marker corners. + /// ids of the corners in markerCorners. + /// output list of detected charuco corners. + /// ids of the charucos in charucoCorners. + public static void InterpolateCornersCharuco(InputArray image, + int squaresX, int squaresY, float squareLength, float markerLength, PredefinedDictionaryName arucoDictId, + Point2f[][] markerCorners, IEnumerable markerIds, + out Point2f[] charucoCorners, out int[] charucoIds) + { + if (image is null) + throw new ArgumentNullException(nameof(image)); + if (markerCorners is null) + throw new ArgumentNullException(nameof(markerCorners)); + if (markerIds is null) + throw new ArgumentNullException(nameof(markerIds)); + + image.ThrowIfDisposed(); + + using var markerCornersAddress = new ArrayAddress2(markerCorners); + using var markerIdsVec = new VectorOfInt32(markerIds); + using var charucoCornersVec = new VectorOfPoint2f(); + using var charucoIdsVec = new VectorOfInt32(); + + NativeMethods.HandleException( + NativeMethods.aruco_interpolateCornersCharuco( + image.CvPtr, + squaresX, squaresY, squareLength, markerLength, (int)arucoDictId, + markerCornersAddress.GetPointer(), markerCornersAddress.GetDim1Length(), markerCornersAddress.GetDim2Lengths(), markerIdsVec.CvPtr, + charucoCornersVec.CvPtr, charucoIdsVec.CvPtr) + ); + + charucoCorners = charucoCornersVec.ToArray(); + charucoIds = charucoIdsVec.ToArray(); + + GC.KeepAlive(image); + } + + /// + /// Draw a set of detected ChArUco Diamond markers. + /// + /// input/output image. It must have 1 or 3 channels. The number of channels is not altered. + /// vector of detected charuco corners. + /// list of identifiers for each corner in charucoCorners. + public static void DrawDetectedCornersCharuco(InputArray image, Point2f[] charucoCorners, IEnumerable? charucoIds = null) + { + DrawDetectedCornersCharuco(image, charucoCorners, charucoIds, new Scalar(0, 0, 255)); + } + + /// + /// Draw a set of detected ChArUco Diamond markers. + /// + /// input/output image. It must have 1 or 3 channels. The number of channels is not altered. + /// vector of detected charuco corners. + /// list of identifiers for each corner in charucoCorners. + /// color of the square surrounding each corner. + public static void DrawDetectedCornersCharuco(InputArray image, + Point2f[] charucoCorners, IEnumerable? charucoIds, Scalar cornerColor) + { + if (image is null) + throw new ArgumentNullException(nameof(image)); + if (charucoCorners is null) + throw new ArgumentNullException(nameof(charucoCorners)); + + using var charucoCornersVec = new VectorOfPoint2f(charucoCorners); + + if (charucoIds is null) + { + NativeMethods.HandleException( + NativeMethods.aruco_drawDetectedCornersCharuco(image.CvPtr, + charucoCornersVec.CvPtr, IntPtr.Zero, cornerColor)); + } + else + { + using var ids = new VectorOfInt32(charucoIds); + + NativeMethods.HandleException( + NativeMethods.aruco_drawDetectedCornersCharuco(image.CvPtr, + charucoCornersVec.CvPtr, ids.CvPtr, cornerColor)); + } + + GC.KeepAlive(image); + } } diff --git a/src/OpenCvSharpExtern/aruco.h b/src/OpenCvSharpExtern/aruco.h index 2660116cb..61aaf0e7a 100644 --- a/src/OpenCvSharpExtern/aruco.h +++ b/src/OpenCvSharpExtern/aruco.h @@ -301,3 +301,74 @@ CVAPI(ExceptionStatus) aruco_drawDetectedDiamonds( cv::aruco::drawDetectedDiamonds(*image, cornerVec, idArray, cpp(borderColor)); END_WRAP } + +CVAPI(ExceptionStatus) aruco_detectCharucoBoard( + cv::_InputArray* image, + int squaresX, + int squaresY, + float squareLength, + float markerLength, + int arucoDictId, + std::vector* charucoCorners, + std::vector* charucoIds, + std::vector< std::vector >* markerCorners, + std::vector* markerIds + ) +{ + BEGIN_WRAP + cv::aruco::Dictionary dictionary = cv::aruco::getPredefinedDictionary(arucoDictId); + cv::aruco::CharucoBoard charucoBoard(cv::Size(squaresX, squaresY), squareLength, markerLength, dictionary); + cv::aruco::CharucoDetector detector(charucoBoard); + cv::Mat charucoCorners_mat, charucoIds_mat; + //detector.detectBoard(*image, *charucoCorners, *charucoIds, *markerCorners, *markerIds); + detector.detectBoard(*image, charucoCorners_mat, charucoIds_mat, *markerCorners, *markerIds); + for (int i = 0; i < charucoCorners_mat.rows; ++i) + { + charucoCorners->push_back(charucoCorners_mat.at(i, 0)); + charucoIds->push_back(charucoIds_mat.at(i, 0)); + } + END_WRAP +} + +CVAPI(ExceptionStatus) aruco_interpolateCornersCharuco( + cv::_InputArray* image, + int squaresX, + int squaresY, + float squareLength, + float markerLength, + int arucoDictId, + cv::Point2f** markerCorners, + int markerCornersSize1, + int* markerCornersSize2, + std::vector* markerIds, + std::vector* charucoCorners, + std::vector* charucoIds + ) +{ + BEGIN_WRAP + std::vector< std::vector > markerCornerVec(markerCornersSize1); + for (int i = 0; i < markerCornersSize1; i++) + markerCornerVec[i] = std::vector(markerCorners[i], markerCorners[i] + markerCornersSize2[i]); + + cv::aruco::Dictionary dictionary = cv::aruco::getPredefinedDictionary(arucoDictId); + cv::Ptr ptr_charucoBoard = new cv::aruco::CharucoBoard(cv::Size(squaresX, squaresY), squareLength, markerLength, dictionary); + cv::Mat charucoCorners_mat, charucoIds_mat; + cv::aruco::interpolateCornersCharuco(markerCornerVec, *markerIds, *image, ptr_charucoBoard, charucoCorners_mat, charucoIds_mat); + for (int i = 0; i < charucoCorners_mat.rows; ++i) + { + charucoCorners->push_back(charucoCorners_mat.at(i, 0)); + charucoIds->push_back(charucoIds_mat.at(i, 0)); + } + END_WRAP +} + +CVAPI(ExceptionStatus) aruco_drawDetectedCornersCharuco( + cv::_InputOutputArray* image, + std::vector* corners, + std::vector* ids, + MyCvScalar cornerColor) +{ + BEGIN_WRAP + cv::aruco::drawDetectedCornersCharuco(*image, *corners, *ids, cpp(cornerColor)); + END_WRAP +}