/// <summary> /// Draw a set of detected ChArUco Diamond markers. /// </summary> /// <param name="image">input/output image. It must have 1 or 3 channels. The number of channels is not altered.</param> /// <param name="diamondCorners">positions of diamond corners in the same format returned by detectCharucoDiamond(). (e.g std::vector<std::vector<cv::Point2f>>). For N detected markers, the dimensions of this array should be Nx4. The order of the corners should be clockwise.</param> /// <param name="diamondIds">vector of identifiers for diamonds in diamondCorners, in the same format returned by detectCharucoDiamond() (e.g. std::vector<Vec4i>). Optional, if not provided, ids are not painted.</param> /// <param name="borderColor">color of marker borders. Rest of colors (text color and first corner color) are calculated based on this one.</param> public static void DrawDetectedDiamonds(InputArray image, Point2f[][] diamondCorners, IEnumerable <Vec4i>?diamondIds, Scalar borderColor) { if (image == null) { throw new ArgumentNullException(nameof(image)); } if (diamondCorners == null) { throw new ArgumentNullException(nameof(diamondCorners)); } using var cornersAddress = new ArrayAddress2 <Point2f>(diamondCorners); if (diamondIds == null) { NativeMethods.HandleException( NativeMethods.aruco_drawDetectedDiamonds(image.CvPtr, cornersAddress.GetPointer(), cornersAddress.GetDim1Length(), cornersAddress.GetDim2Lengths(), IntPtr.Zero, borderColor)); } else { using var ids = new VectorOfVec4i(diamondIds); NativeMethods.HandleException( NativeMethods.aruco_drawDetectedDiamonds(image.CvPtr, cornersAddress.GetPointer(), cornersAddress.GetDim1Length(), cornersAddress.GetDim2Lengths(), ids.CvPtr, borderColor)); } GC.KeepAlive(image); }
/// <summary> /// Draw detected markers in image /// </summary> /// <param name="image">input/output image. It must have 1 or 3 channels. The number of channels is not altered.</param> /// <param name="corners">positions of marker corners on input image. /// For N detected markers, the dimensions of this array should be Nx4.The order of the corners should be clockwise.</param> /// <param name="ids">vector of identifiers for markers in markersCorners. Optional, if not provided, ids are not painted.</param> /// <param name="borderColor">color of marker borders. Rest of colors (text color and first corner color) /// are calculated based on this one to improve visualization.</param> public static void DrawDetectedMarkers(InputArray image, Point2f[][] corners, IEnumerable <int>?ids, Scalar borderColor) { if (image == null) { throw new ArgumentNullException(nameof(image)); } if (corners == null) { throw new ArgumentNullException(nameof(corners)); } using var cornersAddress = new ArrayAddress2 <Point2f>(corners); if (ids == null) { NativeMethods.HandleException( NativeMethods.aruco_drawDetectedMarkers( image.CvPtr, cornersAddress.GetPointer(), cornersAddress.GetDim1Length(), cornersAddress.GetDim2Lengths(), IntPtr.Zero, 0, borderColor)); } else { var idxArray = ids.ToArray(); NativeMethods.HandleException( NativeMethods.aruco_drawDetectedMarkers( image.CvPtr, cornersAddress.GetPointer(), cornersAddress.GetDim1Length(), cornersAddress.GetDim2Lengths(), idxArray, idxArray.Length, borderColor)); } GC.KeepAlive(image); }
/// <summary> /// Try to stitch the given images. /// </summary> /// <param name="images">Input images.</param> /// <param name="rois">Region of interest rectangles.</param> /// <param name="pano">Final pano.</param> /// <returns>Status code.</returns> public Status Stitch(InputArray images, Rect[][] rois, OutputArray pano) { if (images == null) { throw new ArgumentNullException(nameof(images)); } if (rois == null) { throw new ArgumentNullException(nameof(rois)); } if (pano == null) { throw new ArgumentNullException(nameof(pano)); } images.ThrowIfDisposed(); pano.ThrowIfNotReady(); using var roisPointer = new ArrayAddress2 <Rect>(rois); NativeMethods.HandleException( NativeMethods.stitching_Stitcher_stitch2_InputArray( ptr, images.CvPtr, roisPointer.GetPointer(), roisPointer.GetDim1Length(), roisPointer.GetDim2Lengths(), pano.CvPtr, out var ret)); pano.Fix(); GC.KeepAlive(this); GC.KeepAlive(images); GC.KeepAlive(pano); return((Status)ret); }
/// <summary> /// Try to stitch the given images. /// </summary> /// <param name="images">Input images.</param> /// <param name="rois">Region of interest rectangles.</param> /// <param name="pano">Final pano.</param> /// <returns>Status code.</returns> public Status Stitch(IEnumerable <Mat> images, Rect[][] rois, OutputArray pano) { if (images == null) { throw new ArgumentNullException(nameof(images)); } if (rois == null) { throw new ArgumentNullException(nameof(rois)); } if (pano == null) { throw new ArgumentNullException(nameof(pano)); } pano.ThrowIfNotReady(); var imagesPtrs = images.Select(x => x.CvPtr).ToArray(); using var roisPointer = new ArrayAddress2 <Rect>(rois); NativeMethods.HandleException( NativeMethods.stitching_Stitcher_stitch2_MatArray( ptr, imagesPtrs, imagesPtrs.Length, roisPointer.GetPointer(), roisPointer.GetDim1Length(), roisPointer.GetDim2Lengths(), pano.CvPtr, out var ret)); pano.Fix(); GC.KeepAlive(this); GC.KeepAlive(images); GC.KeepAlive(pano); return((Status)ret); }
/// <summary> /// Detect ChArUco Diamond markers. /// </summary> /// <param name="image">input image necessary for corner subpixel.</param> /// <param name="markerCorners">list of detected marker corners from detectMarkers function.</param> /// <param name="markerIds">list of marker ids in markerCorners.</param> /// <param name="squareMarkerLengthRate">rate between square and marker length: squareMarkerLengthRate = squareLength/markerLength. The real units are not necessary.</param> /// <param name="diamondCorners">output list of detected diamond corners (4 corners per diamond). The order is the same than in marker corners: top left, top right, bottom right and bottom left. Similar format than the corners returned by detectMarkers (e.g std::vector<std::vector<cv::Point2f>>).</param> /// <param name="diamondIds">ids of the diamonds in diamondCorners. The id of each diamond is in fact of type Vec4i, so each diamond has 4 ids, which are the ids of the aruco markers composing the diamond.</param> /// <param name="cameraMatrix">Optional camera calibration matrix.</param> /// <param name="distCoeffs">Optional camera distortion coefficients.</param> public static void DetectCharucoDiamond(InputArray image, Point2f[][] markerCorners, IEnumerable <int> markerIds, float squareMarkerLengthRate, out Point2f[][] diamondCorners, out Vec4i[] diamondIds, InputArray?cameraMatrix = null, InputArray?distCoeffs = null) { if (image == null) { throw new ArgumentNullException(nameof(image)); } if (markerCorners == null) { throw new ArgumentNullException(nameof(markerCorners)); } if (markerIds == null) { throw new ArgumentNullException(nameof(markerIds)); } if (cameraMatrix == null && distCoeffs != null) { throw new ArgumentNullException(nameof(cameraMatrix)); } if (cameraMatrix != null && distCoeffs == null) { throw new ArgumentNullException(nameof(distCoeffs)); } image.ThrowIfDisposed(); cameraMatrix?.ThrowIfDisposed(); distCoeffs?.ThrowIfDisposed(); using var markerCornersAddress = new ArrayAddress2 <Point2f>(markerCorners); using var markerIdsVec = new VectorOfInt32(markerIds); using var diamondCornersVec = new VectorOfVectorPoint2f(); using var diamondIdsVec = new VectorOfVec4i(); NativeMethods.HandleException( NativeMethods.aruco_detectCharucoDiamond( image.CvPtr, markerCornersAddress.GetPointer(), markerCornersAddress.GetDim1Length(), markerCornersAddress.GetDim2Lengths(), markerIdsVec.CvPtr, squareMarkerLengthRate, diamondCornersVec.CvPtr, diamondIdsVec.CvPtr, cameraMatrix?.CvPtr ?? IntPtr.Zero, distCoeffs?.CvPtr ?? IntPtr.Zero)); diamondCorners = diamondCornersVec.ToArray(); diamondIds = diamondIdsVec.ToArray(); GC.KeepAlive(image); if (cameraMatrix != null) { GC.KeepAlive(cameraMatrix); } if (distCoeffs != null) { GC.KeepAlive(distCoeffs); } }
/// <summary> /// Constructor /// </summary> /// <param name="values"></param> public VectorOfVectorKeyPoint(KeyPoint[][] values) { if (values == null) { throw new ArgumentNullException(nameof(values)); } using var aa = new ArrayAddress2 <KeyPoint>(values); ptr = NativeMethods.vector_vector_KeyPoint_new3( aa.GetPointer(), aa.GetDim1Length(), aa.GetDim2Lengths()); }
/// <summary> /// Pose estimation for single markers /// </summary> /// <param name="corners">corners vector of already detected markers corners. /// For each marker, its four corners are provided, (e.g std::vector<std::vector<cv::Point2f>> ). /// For N detected markers, the dimensions of this array should be Nx4. The order of the corners should be clockwise.</param> /// <param name="markerLength">the length of the markers' side. The returning translation vectors will /// be in the same unit.Normally, unit is meters.</param> /// <param name="cameraMatrix">input 3x3 floating-point camera matrix /// \f$A = \vecthreethree{f_x}{0}{c_x}{0}{f_y}{c_y}{0}{0}{1}\f$</param> /// <param name="distortionCoefficients">vector of distortion coefficients /// \f$(k_1, k_2, p_1, p_2[, k_3[, k_4, k_5, k_6],[s_1, s_2, s_3, s_4]])\f$ of 4, 5, 8 or 12 elements</param> /// <param name="rvec">array of output rotation vectors (@sa Rodrigues) (e.g. std::vector<cv::Vec3d>). /// Each element in rvecs corresponds to the specific marker in imgPoints.</param> /// <param name="tvec">array of output translation vectors (e.g. std::vector<cv::Vec3d>). /// Each element in tvecs corresponds to the specific marker in imgPoints.</param> /// <param name="objPoints">array of object points of all the marker corners</param> public static void EstimatePoseSingleMarkers( Point2f[][] corners, float markerLength, InputArray cameraMatrix, InputArray distortionCoefficients, OutputArray rvec, OutputArray tvec, OutputArray?objPoints = null) { if (corners == null) { throw new ArgumentNullException(nameof(corners)); } if (cameraMatrix == null) { throw new ArgumentNullException(nameof(cameraMatrix)); } if (distortionCoefficients == null) { throw new ArgumentNullException(nameof(distortionCoefficients)); } if (rvec == null) { throw new ArgumentNullException(nameof(rvec)); } if (tvec == null) { throw new ArgumentNullException(nameof(tvec)); } cameraMatrix.ThrowIfDisposed(); distortionCoefficients.ThrowIfDisposed(); rvec.ThrowIfNotReady(); tvec.ThrowIfNotReady(); objPoints?.ThrowIfNotReady(); using var cornersAddress = new ArrayAddress2 <Point2f>(corners); NativeMethods.HandleException( NativeMethods.aruco_estimatePoseSingleMarkers( cornersAddress.GetPointer(), cornersAddress.GetDim1Length(), cornersAddress.GetDim2Lengths(), markerLength, cameraMatrix.CvPtr, distortionCoefficients.CvPtr, rvec.CvPtr, tvec.CvPtr, objPoints?.CvPtr ?? IntPtr.Zero)); GC.KeepAlive(cameraMatrix); GC.KeepAlive(distortionCoefficients); rvec.Fix(); tvec.Fix(); objPoints?.Fix(); }
/// <summary> /// Converts std::vector to managed array /// </summary> /// <returns></returns> public Point[][] ToArray() { var size1 = GetSize1(); if (size1 == 0) { return(Array.Empty <Point[]>()); } var size2 = GetSize2(); var ret = new Point[size1][]; for (var i = 0; i < size1; i++) { ret[i] = new Point[size2[i]]; } using var retPtr = new ArrayAddress2 <Point>(ret); NativeMethods.vector_vector_Point_copy(ptr, retPtr.GetPointer()); GC.KeepAlive(this); return(ret); }
/// <summary> /// /// </summary> /// <param name="matches1to2"></param> /// <param name="correctMatches1to2Mask"></param> /// <returns>recallPrecisionCurve</returns> public static Point2f[] ComputeRecallPrecisionCurve( DMatch[][] matches1to2, byte[][] correctMatches1to2Mask) { if (matches1to2 == null) { throw new ArgumentNullException(nameof(matches1to2)); } if (correctMatches1to2Mask == null) { throw new ArgumentNullException(nameof(correctMatches1to2Mask)); } using var dm = new ArrayAddress2 <DMatch>(matches1to2); using var cm = new ArrayAddress2 <byte>(correctMatches1to2Mask); using var recall = new VectorOfPoint2f(); NativeMethods.HandleException( NativeMethods.features2d_computeRecallPrecisionCurve( dm.GetPointer(), dm.GetDim1Length(), dm.GetDim2Lengths(), cm.GetPointer(), cm.GetDim1Length(), cm.GetDim2Lengths(), recall.CvPtr)); return(recall.ToArray()); }
public Status EstimateTransform(InputArray images, Rect[][] rois) { if (images == null) { throw new ArgumentNullException(nameof(images)); } if (rois == null) { throw new ArgumentNullException(nameof(rois)); } images.ThrowIfDisposed(); using var roisPointer = new ArrayAddress2 <Rect>(rois); NativeMethods.HandleException( NativeMethods.stitching_Stitcher_estimateTransform_InputArray2( ptr, images.CvPtr, roisPointer.GetPointer(), roisPointer.GetDim1Length(), roisPointer.GetDim2Lengths(), out var ret)); GC.KeepAlive(this); GC.KeepAlive(images); return((Status)ret); }
/// <summary> /// Converts std::vector to managed array /// </summary> /// <returns></returns> public DMatch[][] ToArray() { var size1 = Size1; if (size1 == 0) { return(Array.Empty <DMatch[]>()); } var size2 = Size2; var ret = new DMatch[size1][]; for (var i = 0; i < size1; i++) { ret[i] = new DMatch[size2[i]]; } using (var retPtr = new ArrayAddress2 <DMatch>(ret)) { NativeMethods.vector_vector_DMatch_copy(ptr, retPtr.GetPointer()); GC.KeepAlive(this); } return(ret); }
public Status EstimateTransform(IEnumerable <Mat> images, Rect[][] rois) { if (images == null) { throw new ArgumentNullException(nameof(images)); } if (rois == null) { throw new ArgumentNullException(nameof(rois)); } var imagesPtrs = images.Select(x => x.CvPtr).ToArray(); using var roisPointer = new ArrayAddress2 <Rect>(rois); NativeMethods.HandleException( NativeMethods.stitching_Stitcher_estimateTransform_MatArray2( ptr, imagesPtrs, imagesPtrs.Length, roisPointer.GetPointer(), roisPointer.GetDim1Length(), roisPointer.GetDim2Lengths(), out var ret)); GC.KeepAlive(this); GC.KeepAlive(images); return((Status)ret); }
/// <summary> /// Draws the found matches of keypoints from two images. /// </summary> /// <param name="img1">First source image.</param> /// <param name="keypoints1">Keypoints from the first source image.</param> /// <param name="img2">Second source image.</param> /// <param name="keypoints2">Keypoints from the second source image.</param> /// <param name="matches1To2">Matches from the first image to the second one, which means that keypoints1[i] /// has a corresponding point in keypoints2[matches[i]] .</param> /// <param name="outImg">Output image. Its content depends on the flags value defining what is drawn in the /// output image. See possible flags bit values below.</param> /// <param name="matchColor">Color of matches (lines and connected keypoints). If matchColor==Scalar::all(-1), /// the color is generated randomly.</param> /// <param name="singlePointColor">Color of single keypoints (circles), which means that keypoints do not /// have the matches. If singlePointColor==Scalar::all(-1) , the color is generated randomly.</param> /// <param name="matchesMask">Mask determining which matches are drawn. If the mask is empty, all matches are drawn.</param> /// <param name="flags">Flags setting drawing features. Possible flags bit values are defined by DrawMatchesFlags.</param> public static void DrawMatchesKnn( Mat img1, IEnumerable <KeyPoint> keypoints1, Mat img2, IEnumerable <KeyPoint> keypoints2, IEnumerable <IEnumerable <DMatch> > matches1To2, Mat outImg, Scalar?matchColor = null, Scalar?singlePointColor = null, IEnumerable <IEnumerable <byte> >?matchesMask = null, DrawMatchesFlags flags = DrawMatchesFlags.Default) { if (img1 == null) { throw new ArgumentNullException(nameof(img1)); } if (img2 == null) { throw new ArgumentNullException(nameof(img2)); } if (outImg == null) { throw new ArgumentNullException(nameof(outImg)); } if (keypoints1 == null) { throw new ArgumentNullException(nameof(keypoints1)); } if (keypoints2 == null) { throw new ArgumentNullException(nameof(keypoints2)); } if (matches1To2 == null) { throw new ArgumentNullException(nameof(matches1To2)); } img1.ThrowIfDisposed(); img2.ThrowIfDisposed(); outImg.ThrowIfDisposed(); var keypoints1Array = keypoints1.CastOrToArray(); var keypoints2Array = keypoints2.CastOrToArray(); var matches1To2Array = matches1To2.Select(m => m.ToArray()).ToArray(); var matches1To2Size1 = matches1To2Array.Length; var matches1To2Size2 = matches1To2Array.Select(dm => dm.Length).ToArray(); var matchColor0 = matchColor.GetValueOrDefault(Scalar.All(-1)); var singlePointColor0 = singlePointColor.GetValueOrDefault(Scalar.All(-1)); using var matches1To2Ptr = new ArrayAddress2 <DMatch>(matches1To2Array); if (matchesMask == null) { NativeMethods.HandleException( NativeMethods.features2d_drawMatchesKnn( img1.CvPtr, keypoints1Array, keypoints1Array.Length, img2.CvPtr, keypoints2Array, keypoints2Array.Length, matches1To2Ptr.GetPointer(), matches1To2Size1, matches1To2Size2, outImg.CvPtr, matchColor0, singlePointColor0, null, 0, null, (int)flags)); } else { var matchesMaskArray = matchesMask.Select(m => m.ToArray()).ToArray(); var matchesMaskSize1 = matches1To2Array.Length; var matchesMaskSize2 = matchesMaskArray.Select(dm => dm.Length).ToArray(); using var matchesMaskPtr = new ArrayAddress2 <byte>(matchesMaskArray); NativeMethods.HandleException( NativeMethods.features2d_drawMatchesKnn( img1.CvPtr, keypoints1Array, keypoints1Array.Length, img2.CvPtr, keypoints2Array, keypoints2Array.Length, matches1To2Ptr.GetPointer(), matches1To2Size1, matches1To2Size2, outImg.CvPtr, matchColor0, singlePointColor0, matchesMaskPtr.GetPointer(), matchesMaskSize1, matchesMaskSize2, (int)flags)); } GC.KeepAlive(img1); GC.KeepAlive(img2); GC.KeepAlive(outImg); }