Cv2_features2d.cs 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364
  1. using OpenCvSharp.Internal;
  2. using OpenCvSharp.Internal.Util;
  3. using OpenCvSharp.Internal.Vectors;
  4. // ReSharper disable UnusedMember.Global
  5. // ReSharper disable InconsistentNaming
  6. namespace OpenCvSharp;
  7. static partial class Cv2
  8. {
  9. /// <summary>
  10. /// Detects corners using the FAST algorithm
  11. /// </summary>
  12. /// <param name="image">grayscale image where keypoints (corners) are detected.</param>
  13. /// <param name="threshold">threshold on difference between intensity of the central pixel
  14. /// and pixels of a circle around this pixel.</param>
  15. /// <param name="nonmaxSupression">if true, non-maximum suppression is applied to
  16. /// detected corners (keypoints).</param>
  17. /// <returns>keypoints detected on the image.</returns>
  18. public static KeyPoint[] FAST(InputArray image, int threshold, bool nonmaxSupression = true)
  19. {
  20. if (image is null)
  21. throw new ArgumentNullException(nameof(image));
  22. image.ThrowIfDisposed();
  23. using var kp = new VectorOfKeyPoint();
  24. NativeMethods.HandleException(
  25. NativeMethods.features2d_FAST1(image.CvPtr, kp.CvPtr, threshold, nonmaxSupression ? 1 : 0));
  26. GC.KeepAlive(image);
  27. return kp.ToArray();
  28. }
  29. /// <summary>
  30. /// Detects corners using the FAST algorithm
  31. /// </summary>
  32. /// <param name="image">grayscale image where keypoints (corners) are detected.</param>
  33. /// <param name="threshold">threshold on difference between intensity of the central pixel
  34. /// and pixels of a circle around this pixel.</param>
  35. /// <param name="nonmaxSupression">if true, non-maximum suppression is applied to
  36. /// detected corners (keypoints).</param>
  37. /// <param name="type">one of the three neighborhoods as defined in the paper</param>
  38. /// <returns>keypoints detected on the image.</returns>
  39. public static KeyPoint[] FAST(InputArray image, int threshold, bool nonmaxSupression, FASTType type)
  40. {
  41. if (image is null)
  42. throw new ArgumentNullException(nameof(image));
  43. image.ThrowIfDisposed();
  44. using var kp = new VectorOfKeyPoint();
  45. NativeMethods.HandleException(
  46. NativeMethods.features2d_FAST2(image.CvPtr, kp.CvPtr, threshold, nonmaxSupression ? 1 : 0, (int)type));
  47. GC.KeepAlive(image);
  48. return kp.ToArray();
  49. }
  50. /// <summary>
  51. /// Detects corners using the AGAST algorithm
  52. /// </summary>
  53. /// <param name="image">grayscale image where keypoints (corners) are detected.</param>
  54. /// <param name="threshold">threshold on difference between intensity of the central pixel
  55. /// and pixels of a circle around this pixel.</param>
  56. /// <param name="nonmaxSuppression">if true, non-maximum suppression is applied to
  57. /// detected corners (keypoints).</param>
  58. /// <param name="type">one of the four neighborhoods as defined in the paper</param>
  59. /// <returns>keypoints detected on the image.</returns>
  60. public static KeyPoint[] AGAST(InputArray image, int threshold, bool nonmaxSuppression, AgastFeatureDetector.DetectorType type)
  61. {
  62. if (image is null)
  63. throw new ArgumentNullException(nameof(image));
  64. image.ThrowIfDisposed();
  65. using var vector = new VectorOfKeyPoint();
  66. NativeMethods.HandleException(
  67. NativeMethods.features2d_AGAST(image.CvPtr, vector.CvPtr, threshold, nonmaxSuppression ? 1 : 0, (int) type));
  68. GC.KeepAlive(image);
  69. return vector.ToArray();
  70. }
  71. /// <summary>
  72. /// Draw keypoints.
  73. /// </summary>
  74. /// <param name="image">Source image.</param>
  75. /// <param name="keypoints">Keypoints from the source image.</param>
  76. /// <param name="outImage">Output image. Its content depends on the flags value defining what is drawn in the output image. See possible flags bit values below.</param>
  77. /// <param name="color">Color of keypoints.</param>
  78. /// <param name="flags">Flags setting drawing features. Possible flags bit values are defined by DrawMatchesFlags.</param>
  79. public static void DrawKeypoints(
  80. InputArray image,
  81. IEnumerable<KeyPoint> keypoints,
  82. InputOutputArray outImage,
  83. Scalar? color = null,
  84. DrawMatchesFlags flags = DrawMatchesFlags.Default)
  85. {
  86. if (image is null)
  87. throw new ArgumentNullException(nameof(image));
  88. if (outImage is null)
  89. throw new ArgumentNullException(nameof(outImage));
  90. if (keypoints is null)
  91. throw new ArgumentNullException(nameof(keypoints));
  92. image.ThrowIfDisposed();
  93. outImage.ThrowIfDisposed();
  94. var keypointsArray = keypoints.CastOrToArray();
  95. var color0 = color.GetValueOrDefault(Scalar.All(-1));
  96. NativeMethods.HandleException(
  97. NativeMethods.features2d_drawKeypoints(image.CvPtr, keypointsArray, keypointsArray.Length, outImage.CvPtr, color0, (int)flags));
  98. GC.KeepAlive(image);
  99. GC.KeepAlive(outImage);
  100. }
  101. /// <summary>
  102. /// Draws the found matches of keypoints from two images.
  103. /// </summary>
  104. /// <param name="img1">First source image.</param>
  105. /// <param name="keypoints1">Keypoints from the first source image.</param>
  106. /// <param name="img2">Second source image.</param>
  107. /// <param name="keypoints2">Keypoints from the second source image.</param>
  108. /// <param name="matches1To2">Matches from the first image to the second one, which means that keypoints1[i]
  109. /// has a corresponding point in keypoints2[matches[i]] .</param>
  110. /// <param name="outImg">Output image. Its content depends on the flags value defining what is drawn in the
  111. /// output image. See possible flags bit values below.</param>
  112. /// <param name="matchColor">Color of matches (lines and connected keypoints). If matchColor==Scalar::all(-1),
  113. /// the color is generated randomly.</param>
  114. /// <param name="singlePointColor">Color of single keypoints (circles), which means that keypoints do not
  115. /// have the matches. If singlePointColor==Scalar::all(-1) , the color is generated randomly.</param>
  116. /// <param name="matchesMask">Mask determining which matches are drawn. If the mask is empty, all matches are drawn.</param>
  117. /// <param name="flags">Flags setting drawing features. Possible flags bit values are defined by DrawMatchesFlags.</param>
  118. public static void DrawMatches(
  119. Mat img1,
  120. IEnumerable<KeyPoint> keypoints1,
  121. Mat img2,
  122. IEnumerable<KeyPoint> keypoints2,
  123. IEnumerable<DMatch> matches1To2,
  124. Mat outImg,
  125. Scalar? matchColor = null,
  126. Scalar? singlePointColor = null,
  127. IEnumerable<byte>? matchesMask = null,
  128. DrawMatchesFlags flags = DrawMatchesFlags.Default)
  129. {
  130. if (img1 is null)
  131. throw new ArgumentNullException(nameof(img1));
  132. if (img2 is null)
  133. throw new ArgumentNullException(nameof(img2));
  134. if (outImg is null)
  135. throw new ArgumentNullException(nameof(outImg));
  136. if (keypoints1 is null)
  137. throw new ArgumentNullException(nameof(keypoints1));
  138. if (keypoints2 is null)
  139. throw new ArgumentNullException(nameof(keypoints2));
  140. if (matches1To2 is null)
  141. throw new ArgumentNullException(nameof(matches1To2));
  142. img1.ThrowIfDisposed();
  143. img2.ThrowIfDisposed();
  144. outImg.ThrowIfDisposed();
  145. var keypoints1Array = keypoints1.CastOrToArray();
  146. var keypoints2Array = keypoints2.CastOrToArray();
  147. var matches1To2Array = matches1To2.CastOrToArray();
  148. var matchColor0 = matchColor.GetValueOrDefault(Scalar.All(-1));
  149. var singlePointColor0 = singlePointColor.GetValueOrDefault(Scalar.All(-1));
  150. var matchesMaskArray = matchesMask?.CastOrToArray();
  151. var matchesMaskLength = matchesMaskArray?.Length ?? 0;
  152. NativeMethods.HandleException(
  153. NativeMethods.features2d_drawMatches(
  154. img1.CvPtr, keypoints1Array, keypoints1Array.Length,
  155. img2.CvPtr, keypoints2Array, keypoints2Array.Length,
  156. matches1To2Array, matches1To2Array.Length, outImg.CvPtr,
  157. matchColor0, singlePointColor0, matchesMaskArray, matchesMaskLength, (int) flags));
  158. GC.KeepAlive(img1);
  159. GC.KeepAlive(img2);
  160. GC.KeepAlive(outImg);
  161. }
  162. /// <summary>
  163. /// Draws the found matches of keypoints from two images.
  164. /// </summary>
  165. /// <param name="img1">First source image.</param>
  166. /// <param name="keypoints1">Keypoints from the first source image.</param>
  167. /// <param name="img2">Second source image.</param>
  168. /// <param name="keypoints2">Keypoints from the second source image.</param>
  169. /// <param name="matches1To2">Matches from the first image to the second one, which means that keypoints1[i]
  170. /// has a corresponding point in keypoints2[matches[i]] .</param>
  171. /// <param name="outImg">Output image. Its content depends on the flags value defining what is drawn in the
  172. /// output image. See possible flags bit values below.</param>
  173. /// <param name="matchColor">Color of matches (lines and connected keypoints). If matchColor==Scalar::all(-1),
  174. /// the color is generated randomly.</param>
  175. /// <param name="singlePointColor">Color of single keypoints (circles), which means that keypoints do not
  176. /// have the matches. If singlePointColor==Scalar::all(-1) , the color is generated randomly.</param>
  177. /// <param name="matchesMask">Mask determining which matches are drawn. If the mask is empty, all matches are drawn.</param>
  178. /// <param name="flags">Flags setting drawing features. Possible flags bit values are defined by DrawMatchesFlags.</param>
  179. public static void DrawMatchesKnn(
  180. Mat img1,
  181. IEnumerable<KeyPoint> keypoints1,
  182. Mat img2,
  183. IEnumerable<KeyPoint> keypoints2,
  184. IEnumerable<IEnumerable<DMatch>> matches1To2,
  185. Mat outImg,
  186. Scalar? matchColor = null,
  187. Scalar? singlePointColor = null,
  188. IEnumerable<IEnumerable<byte>>? matchesMask = null,
  189. DrawMatchesFlags flags = DrawMatchesFlags.Default)
  190. {
  191. if (img1 is null)
  192. throw new ArgumentNullException(nameof(img1));
  193. if (img2 is null)
  194. throw new ArgumentNullException(nameof(img2));
  195. if (outImg is null)
  196. throw new ArgumentNullException(nameof(outImg));
  197. if (keypoints1 is null)
  198. throw new ArgumentNullException(nameof(keypoints1));
  199. if (keypoints2 is null)
  200. throw new ArgumentNullException(nameof(keypoints2));
  201. if (matches1To2 is null)
  202. throw new ArgumentNullException(nameof(matches1To2));
  203. img1.ThrowIfDisposed();
  204. img2.ThrowIfDisposed();
  205. outImg.ThrowIfDisposed();
  206. var keypoints1Array = keypoints1.CastOrToArray();
  207. var keypoints2Array = keypoints2.CastOrToArray();
  208. var matches1To2Array = matches1To2.Select(m => m.ToArray()).ToArray();
  209. var matches1To2Size1 = matches1To2Array.Length;
  210. var matches1To2Size2 = matches1To2Array.Select(dm => dm.Length).ToArray();
  211. var matchColor0 = matchColor.GetValueOrDefault(Scalar.All(-1));
  212. var singlePointColor0 = singlePointColor.GetValueOrDefault(Scalar.All(-1));
  213. using var matches1To2Ptr = new ArrayAddress2<DMatch>(matches1To2Array);
  214. if (matchesMask is null)
  215. {
  216. NativeMethods.HandleException(
  217. NativeMethods.features2d_drawMatchesKnn(
  218. img1.CvPtr, keypoints1Array, keypoints1Array.Length,
  219. img2.CvPtr, keypoints2Array, keypoints2Array.Length,
  220. matches1To2Ptr.GetPointer(), matches1To2Size1, matches1To2Size2,
  221. outImg.CvPtr, matchColor0, singlePointColor0,
  222. null, 0, null, (int) flags));
  223. }
  224. else
  225. {
  226. var matchesMaskArray = matchesMask.Select(m => m.ToArray()).ToArray();
  227. var matchesMaskSize1 = matches1To2Array.Length;
  228. var matchesMaskSize2 = matchesMaskArray.Select(dm => dm.Length).ToArray();
  229. using var matchesMaskPtr = new ArrayAddress2<byte>(matchesMaskArray);
  230. NativeMethods.HandleException(
  231. NativeMethods.features2d_drawMatchesKnn(
  232. img1.CvPtr, keypoints1Array, keypoints1Array.Length,
  233. img2.CvPtr, keypoints2Array, keypoints2Array.Length,
  234. matches1To2Ptr.GetPointer(), matches1To2Size1, matches1To2Size2,
  235. outImg.CvPtr, matchColor0, singlePointColor0,
  236. matchesMaskPtr.GetPointer(), matchesMaskSize1, matchesMaskSize2, (int) flags));
  237. }
  238. GC.KeepAlive(img1);
  239. GC.KeepAlive(img2);
  240. GC.KeepAlive(outImg);
  241. }
  242. /// <summary>
  243. ///
  244. /// </summary>
  245. /// <param name="img1"></param>
  246. /// <param name="img2"></param>
  247. /// <param name="H1to2"></param>
  248. /// <param name="keypoints1"></param>
  249. /// <param name="keypoints2"></param>
  250. /// <param name="repeatability"></param>
  251. /// <param name="correspCount"></param>
  252. public static void EvaluateFeatureDetector(
  253. Mat img1, Mat img2, Mat H1to2,
  254. ref KeyPoint[] keypoints1, ref KeyPoint[] keypoints2,
  255. out float repeatability, out int correspCount)
  256. {
  257. if (img1 is null)
  258. throw new ArgumentNullException(nameof(img1));
  259. if (img2 is null)
  260. throw new ArgumentNullException(nameof(img2));
  261. if (H1to2 is null)
  262. throw new ArgumentNullException(nameof(H1to2));
  263. if (keypoints1 is null)
  264. throw new ArgumentNullException(nameof(keypoints1));
  265. if (keypoints2 is null)
  266. throw new ArgumentNullException(nameof(keypoints2));
  267. using var keypoints1Vec = new VectorOfKeyPoint(keypoints1);
  268. using var keypoints2Vec = new VectorOfKeyPoint(keypoints2);
  269. NativeMethods.HandleException(
  270. NativeMethods.features2d_evaluateFeatureDetector(
  271. img1.CvPtr, img2.CvPtr, H1to2.CvPtr,
  272. keypoints1Vec.CvPtr, keypoints2Vec.CvPtr,
  273. out repeatability, out correspCount));
  274. GC.KeepAlive(img1);
  275. GC.KeepAlive(img2);
  276. GC.KeepAlive(H1to2);
  277. keypoints1 = keypoints1Vec.ToArray();
  278. keypoints2 = keypoints2Vec.ToArray();
  279. }
  280. /// <summary>
  281. ///
  282. /// </summary>
  283. /// <param name="matches1to2"></param>
  284. /// <param name="correctMatches1to2Mask"></param>
  285. /// <returns>recallPrecisionCurve</returns>
  286. public static Point2f[] ComputeRecallPrecisionCurve(
  287. DMatch[][] matches1to2, byte[][] correctMatches1to2Mask)
  288. {
  289. if (matches1to2 is null)
  290. throw new ArgumentNullException(nameof(matches1to2));
  291. if (correctMatches1to2Mask is null)
  292. throw new ArgumentNullException(nameof(correctMatches1to2Mask));
  293. using var dm = new ArrayAddress2<DMatch>(matches1to2);
  294. using var cm = new ArrayAddress2<byte>(correctMatches1to2Mask);
  295. using var recall = new VectorOfPoint2f();
  296. NativeMethods.HandleException(
  297. NativeMethods.features2d_computeRecallPrecisionCurve(
  298. dm.GetPointer(), dm.GetDim1Length(), dm.GetDim2Lengths(),
  299. cm.GetPointer(), cm.GetDim1Length(), cm.GetDim2Lengths(),
  300. recall.CvPtr));
  301. return recall.ToArray();
  302. }
  303. /// <summary>
  304. ///
  305. /// </summary>
  306. /// <param name="recallPrecisionCurve"></param>
  307. /// <param name="lPrecision"></param>
  308. /// <returns></returns>
  309. public static float GetRecall(
  310. IEnumerable<Point2f> recallPrecisionCurve, float lPrecision)
  311. {
  312. if (recallPrecisionCurve is null)
  313. throw new ArgumentNullException(nameof(recallPrecisionCurve));
  314. var recallPrecisionCurveArray = recallPrecisionCurve.CastOrToArray();
  315. NativeMethods.HandleException(
  316. NativeMethods.features2d_getRecall(
  317. recallPrecisionCurveArray, recallPrecisionCurveArray.Length, lPrecision, out var ret));
  318. return ret;
  319. }
  320. /// <summary>
  321. ///
  322. /// </summary>
  323. /// <param name="recallPrecisionCurve"></param>
  324. /// <param name="lPrecision"></param>
  325. /// <returns></returns>
  326. public static int GetNearestPoint(
  327. IEnumerable<Point2f> recallPrecisionCurve, float lPrecision)
  328. {
  329. if (recallPrecisionCurve is null)
  330. throw new ArgumentNullException(nameof(recallPrecisionCurve));
  331. var recallPrecisionCurveArray = recallPrecisionCurve.CastOrToArray();
  332. NativeMethods.HandleException(
  333. NativeMethods.features2d_getNearestPoint(
  334. recallPrecisionCurveArray, recallPrecisionCurveArray.Length, lPrecision, out var ret));
  335. return ret;
  336. }
  337. }